frida打印与参数构造
title: frida打印与参数构造
categories: 逆向与协议分析
toc: true
mathjax: true
tags:
- frida
- HOOK
- 逆向
widgets: - type: toc
position: left - type: profile
position: left
author: RunopeAuthor title
author_title: 不知不论,不做不论
Author's current location
location: Nanjin,jiangsu
URL or path to the avatar image
avatar: https://en.gravatar.com/userimage/194935117/7129e2095de79a9dd97e5cc344acaba2?size=200
Whether show the rounded avatar image
avatar_rounded: false
Email address for the Gravatar
gravatar: 275358499@qq.com
URL or path for the follow button
follow_link: 'https://github.com/runope'
- type: recent_posts
position: left
本文将系统讨论,frida的打印与参数构造问题。
参数打印
bytes2Hex
function bytes2Hex(arrBytes){
var str = "";
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num = arrBytes[i];
if (num < 0) {
//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
tmp = (255 + num + 1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
if(i>0){
str += " "+tmp;
}else{
str += tmp;
}
}
return str;
}
传入的参数为byte[],example:[12, 0, 156, -127] return:[0c,00,9c,fe],注意别传成了string。
string2Bytes
function string2Bytes(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
bytes2String
function bytes2String(arr) {
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
try {
str += String.fromCharCode(parseInt(store, 2));
} catch (error) {
str += parseInt(store, 2).toString();
console.log(error);
}
i += bytesLength - 1;
} else {
try {
str += String.fromCharCode(_arr[i]);
} catch (error) {
str += parseInt(store, 2).toString();
console.log(error);
}
}
}
return str;
}
这个脚本没问题,在chrome的console中随便用,但是在frida script中如果不是在unicode的解码范围内会报错,所以我直接用的python解的byte2string.后面那个通过java string函数的转换可以直接用BytesToString
BytesToString
function bytesToString(value) {
var buffer = Java.array('byte', value);
var StringClass = Java.use('java.lang.String');
return StringClass.$new(buffer);
}
char[]
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
Java.use("java.lang.Character").toString.overload('char').implementation = function(char){
var result = this.toString(char);
console.log("char,result",char,result);
return result;
}
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){
var result = this.toString(charArray);
console.log("charArray,result:",charArray,result)
console.log("charArray Object Object:",gson.$new().toJson(charArray));
return result;
}
byte[]
使用google的gson库,来辅助构造,为防止app已包含此库并混淆,干扰我们调用,将该库提取出来,改名。
下载链接:https://raw.githubusercontent.com/r0ysue/AndroidSecurityStudy/master/FRIDA/r0gson.dex.zip
把文件push到系统和frida-server放在一起
使用方法:
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
Java.use("java.util.Arrays").toString.overload('[B').implementation = function(byteArray){
var result = this.toString(byteArray);
console.log("byteArray,result):",byteArray,result)
console.log("byteArray Object Object:",gson.$new().toJson(byteArray));
return result;
}
ByteBuffer
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
my_class.b.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function(x0,x1) {
var result = this.b(x0,x1);
//Suppose the byte[] in the Bytebuffer has a key value of 'hb'.
var bytesArray = bytesBuffer2bytesArray(x0, 'hb');
var tmp = gson.$new().toJson(x0);
tmp = JSON.parse(tmp);
console.log("tmp:"+typeof(tmp));
console.log("decrypt --> message: ",tmp["backingArray"]);
// console.log("decrypt --> message*hexdump: ",hexdump(gson.$new().toJson(x0)["backingArray"]));
// console.log("decrypt --> decryptTo: ",gson.$new().toJson(x1));
// console.log("decrypt --> decryptTo*hexdump: ",hexdump(gson.$new().toJson(x1)["backingArray"]));
return result;
}
function bytesBuffer2bytesArray(bytesBuffer, key) {
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
var tmp = gson.$new().toJson(bytesBuffer);
var dataKey = key
tmp = JSON.parse(tmp);
tmp = new Array(tmp[dataKey]);
tmp = tmp.toString();
var tmp_array = tmp.split(",");
var tmp_int_array=[];
tmp_int_array=tmp_array.map(function(data){
return +data;
});
return tmp_int_array
}
example
下面给一个样例,hook的xxqg的chacha20poly1305算法。
python:
import time
import frida
import sys
import binascii
def on_message(message , data): #定义错误处理
if message['type'] == 'send':
# print("[*] {0}".format(message['payload']))
print()
hex_str = message['payload']
hex_str_len = len(hex_str)
// 这个包尾部的0x00有点多,看着不舒服,所以这个是去除尾部00的空bytebuffer的
n_0 = 0
for k in hex_str[::-1]:
if k == '0':
n_0 += 1
else:
break
print(hex_str[0:hex_str_len-n_0])
n_0 = n_0//2
hex_str_remove_zero = hex_str
for i in range(0,hex_str_len//2-n_0):
// 这里是核心的bytes[] to string,这里一个个字符解,以及try/catch都是为了防止不可见编码影响效果
try:
hex0 = hex_str[2*i:2*i+2].encode('utf-8')
str_bin = binascii.unhexlify(hex0)
print(str_bin.decode('utf-8'),end="")
except:
pass
# file.write("[*] {0}".format(message['payload']))
else:
print(message)
# file.write(message)
# 连接安卓机上的frida-server
device = frida.get_usb_device()
# 启动app
pid = device.spawn(["cn.xuexi.android"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载脚本
with open("./xxqg.js",encoding='UTF-8') as f:
script = session.create_script(f.read())
script.on("message" , on_message) #调用错误处理
script.load()
# 脚本会持续运行等待输入
sys.stdin.read()
JavaScript:
console.log("Script loaded successfully ");
Java.perform(function x() {
console.log("Inside java perform function");
var my_class = Java.use("com.laiwang.protocol.android.x");
console.log("Java.Use.Successfully!");//定位类成功!
//在这里更改类的方法的实现(implementation)
console.log("Inside java perform function");
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
my_class.b.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function(x0,x1) {
var result = this.b(x0,x1);
var bytesArray = bytesBuffer2bytesArray(x1, 'hb');
send(bytes2Hex_nin_zero(bytesArray));
return result;
}
my_class.a.overload('java.nio.ByteBuffer', 'java.nio.ByteBuffer').implementation = function(x0,x1) {
var result = this.a(x0,x1);
var bytesArray = bytesBuffer2bytesArray(x0, 'hb');
send(bytes2Hex_nin_zero(bytesArray));
return result;
}
})
function bytesBuffer2bytesArray(bytesBuffer, key) {
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson');
var tmp = gson.$new().toJson(bytesBuffer);
var dataKey = key
tmp = JSON.parse(tmp);
tmp = new Array(tmp[dataKey]);
tmp = tmp.toString();
var tmp_array = tmp.split(",");
var tmp_int_array=[];
tmp_int_array=tmp_array.map(function(data){
return +data;
});
return tmp_int_array
}
function bytes2Hex(arrBytes){
var str = "";
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num = arrBytes[i];
if (num < 0) {
//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
tmp = (255 + num + 1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
if(i>0){
str += " "+tmp;
}else{
str += tmp;
}
}
return str;
}
function bytes2Hex_nin_zero(arrBytes){
var str = "";
for (var i = 0; i < arrBytes.length; i++) {
var tmp;
var num = arrBytes[i];
if (num < 0) {
//此处填坑,当byte因为符合位导致数值为负时候,需要对数据进行处理
tmp = (255 + num + 1).toString(16);
} else {
tmp = num.toString(16);
}
if (tmp.length == 1) {
tmp = "0" + tmp;
}
if(i>0){
str += ""+tmp;
}else{
str += tmp;
}
}
return str;
}
function string2Bytes(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
function bytes2String(arr) {
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
try {
str += String.fromCharCode(parseInt(store, 2));
} catch (error) {
str += parseInt(store, 2).toString();
console.log(error);
}
i += bytesLength - 1;
} else {
try {
str += String.fromCharCode(_arr[i]);
} catch (error) {
str += parseInt(store, 2).toString();
console.log(error);
}
}
}
return str;
}
hexdump
var _fillUp = function (value, count, fillWith) {
var l = count - value.length;
var ret = "";
while (--l > -1)
ret += fillWith;
return ret + value;
}
hexdump = function (arrayBuffer, offset, length) {
var view = new DataView(arrayBuffer);
offset = offset || 0;
length = length || arrayBuffer.byteLength;
var out = _fillUp("Offset", 8, " ") + " 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n";
var row = "";
for (var i = 0; i < length; i += 16) {
row += _fillUp(offset.toString(16).toUpperCase(), 8, "0") + " ";
var n = Math.min(16, length - offset);
var string = "";
for (var j = 0; j < 16; ++j) {
if (j < n) {
var value = view.getUint8(offset);
string += value >= 32 ? String.fromCharCode(value) : ".";
row += _fillUp(value.toString(16).toUpperCase(), 2, "0") + " ";
offset++;
}
else {
row += " ";
string += " ";
}
}
row += " " + string + "\n";
}
out += row;
return out;
};
打印memorybuffer
// 打印内存
var view = new DataView(this.context.r0.readByteArray(12));
var value = '0x' + view.getUint8(11).toString(16) + view.getUint8(10).toString(16) + view.getUint8(9).toString(16) + view.getUint8(8).toString(16);
打印non-ascii
https://api-caller.com/2019/03/30/frida-note/#non-ascii
类名非ASCII字符串时,先编码打印出来, 再用编码后的字符串去 hook.
//场景hook cls.forName寻找目标类的classloader。
cls.forName.overload('java.lang.String', 'boolean', 'java.lang.ClassLoader').implementation = function (arg1, arg2, arg3) {
var clsName = cls.forName(arg1, arg2, arg3);
console.log('oriClassName:' + arg1)
var base64Name = encodeURIComponent(arg1)
console.log('encodeName:' + base64Name);
//通过日志确认base64后的非ascii字符串,下面对比并打印classloader
//clsName为特殊字符o.ÎÉ«
if ('o.%CE%99%C9%AB' == base64Name) {
//打印classloader
console.log(arg3);
}
return clsName;
}
hook enum
enum Signal {
GREEN, YELLOW, RED
}
public class TrafficLight {
public static Signal color = Signal.RED;
public static void main() {
Log.d("4enum", "enum "+ color.getClass().getName().toString());
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
Java.perform(function(){
Java.choose("com.r0ysue.a0526printout.Signal",{
onMatch:function(instance){
console.log("instance.name:",instance.name());
console.log("instance.getDeclaringClass:",instance.getDeclaringClass());
},onComplete:function(){
console.log("search completed!")
}
})
})
打印hash map
Java.perform(function(){
Java.choose("java.util.HashMap",{
onMatch:function(instance){
if(instance.toString().indexOf("ISBN")!= -1){
console.log("instance.toString:",instance.toString());
}
},onComplete:function(){
console.log("search complete!")
}
})
})
参数构造
Java array构造
如果不只是想打印出结果,而是要替换原本的参数,就要先自己构造出一个charArray,使用Java.array
API
Java.use("java.util.Arrays").toString.overload('[C').implementation = function(charArray){
var newCharArray = Java.array('char', [ '一','去','二','三','里' ]);
var result = this.toString(newCharArray);
console.log("newCharArray,result:",newCharArray,result)
console.log("newCharArray Object Object:",gson.$new().toJson(newCharArray));
var newResult = Java.use('java.lang.String').$new(Java.array('char', [ '烟','村','四','五','家']))
return newResult;
}
类的多态:转型/Java.cast
可以通过getClass().getName().toString()
来查看当前实例的类型。
找到一个instance,通过Java.cast
来强制转换对象的类型。
public class Water { // 水 类
public static String flow(Water W) { // 水 的方法
// SomeSentence
Log.d("2Object", "water flow: I`m flowing");
return "water flow: I`m flowing";
}
public String still(Water W) { // 水 的方法
// SomeSentence
Log.d("2Object", "water still: still water runs deep!");
return "water still: still water runs deep!";
}
}
...
public class Juice extends Water { // 果汁 类 继承了水类
public String fillEnergy(){
Log.d("2Object", "Juice: i`m fillingEnergy!");
return "Juice: i`m fillingEnergy!";
}
var JuiceHandle = null ;
Java.choose("com.r0ysue.a0526printout.Juice",{
onMatch:function(instance){
console.log("found juice instance",instance);
console.log("juice instance call fill",instance.fillEnergy());
JuiceHandle = instance;
},onComplete:function(){
console.log("juice handle search completed!")
}
})
console.log("Saved juice handle :",JuiceHandle);
var WaterHandle = Java.cast(JuiceHandle,Java.use("com.r0ysue.a0526printout.Water"))
console.log("call Waterhandle still method:",WaterHandle.still(WaterHandle));
interface/Java.registerClass
frida可以构建一个新的class
public interface liquid {
public String flow();
}
首先获取要实现的interface,然后调用registerClass来实现interface。
Java.perform(function(){
var liquid = Java.use("com.r0ysue.a0526printout.liquid");
var beer = Java.registerClass({
name: 'com.r0ysue.a0526printout.beer',
implements: [liquid],
methods: {
flow: function () {
console.log("look, beer is flowing!")
return "look, beer is flowing!";
}
}
});
console.log("beer.bubble:",beer.$new().flow())
})
frida打印与参数构造的更多相关文章
- jpa hibernate 打印sql,format日志,打印SQL参数,打印什么指令
环境说明:IntelliJ IDEA 2017.3.4 版本:SpringBoot 2.0.0.RELEASE:hibernate用的是JPA自带. 打印SQL 到控制台: 首先,我使用的是appli ...
- android黑科技系列——分析某直播App的协议加密原理以及调用加密方法进行协议参数构造
一.前言 随着直播技术火爆之后,各家都出了直播app,早期直播app的各种请求协议的参数信息都没有做任何加密措施,但是慢慢的有人开始利用这个后门开始弄刷粉关注工具,可以让一个新生的小花旦分分钟变成网红 ...
- Post请求data参数构造及巧用js脚本显示爬虫进度
小爬最近随着对python中字符串.json等理解进一步加深,发现先前我随笔中提到的data构造和传参方法略复杂,原本有更简单的方法,Mark如下. 先前小爬我使用的requests.post请求中d ...
- 巧用c++11 的forward实现可变参数构造,生成智能指针
C++11 提供强大的智能指针shared_ptr来管理内存,避免使用裸指针带来的各种不确定访问造成的程序崩溃. 为了强制使用智能指针,一个简单的办法是,将类的构造函数析构函数声明为protected ...
- Linux有名信号量的创建(sem_open中name参数构造)【转】
转自:http://blog.csdn.net/gfeng168/article/details/40740865 版权声明:本文为博主原创文章,未经博主允许不得转载. 一.sem_open函数nam ...
- Linux 打印可变参数日志
实现了传输进去的字符串所在的文档,函数和行数显示功能. 实现了将传入的可变参数打印到日志功能. #include<stdio.h> #include<stdarg.h> #in ...
- SpringBoot使用Aspect切面拦截打印请求参数
引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>sp ...
- IE打印的参数配置说明
IE自动给我们在页眉和页脚处加上了这些不必要的打印信息.如果我们不想要任何页眉和页脚的话,直接删除它们就行了.:-)大部分时候我都是这样做的.但如果你想自定义页眉和页脚的时候,该怎么做呢?上面的那些“ ...
- frida框架hook参数获取方法入参模板
python脚本 # -*- coding: utf-8 -*- import logging import frida import sys logging.basicConfig(level=lo ...
随机推荐
- Machine Learning-特征工程之特征选择
特征工程之特征选择 目录 简介 1 Filter(过滤式选择) 1.1 移除低方差特征(variance threshold) 1.2 信息增益(information gain) 1.3 单变量特征 ...
- 免费开源工作流Smartflow-Sharp v2.0
@font-face { font-family: 宋体 } @font-face { font-family: "Cambria Math" } @font-face { fon ...
- python使用xpath(超详细)
使用时先安装 lxml 包 开始使用 和beautifulsoup类似,首先我们需要得到一个文档树 把文本转换成一个文档树对象 from lxml import etree if __name__ = ...
- 05 C语言的数据类型
C语言的数据类型 在C 中,数据类型是用来声明不同类型的变量或函数的一个广泛的概念.变量的数据类型决定了变量存储占用的空间大小,以及如何去解释存储的位模式. C 中的数据类型可分为以下几大类: 序号 ...
- Matlab 中 imshow 函数
转自: https://blog.csdn.net/xiaochou87/article/details/43488829 matlab中显示图像的语句是: ...
- ansible-playbook文件结构
ansible-playbook文件结构: 1 --- 2 - name: play1 #指定的playbook名字 3 hosts: webservers #指定主机组 4 remote_user: ...
- MeteoInfoLab脚本示例:LaTeX写数学公式
LaTeX是排版常用的语法,科学计算软件中也常用它来写数学公式(比如MatLab, Matplotlib等),MeteoInfo通过调用JMathLaTeX库也可以实现这样的功能.LaTeX的语法介绍 ...
- 身为电气人,为什么也要学习C语言编程?人生苦短,我学编程!
说起编程大家可能都听过,但编程究竟是怎么一回事你弄懂了吗? 编程=对计算机程序进行编写,这些程序可以是现在手里拿着的手机.办公的电脑.你点击的页面.浏览的网页,都是有程序让它执行你要它做的事情. PL ...
- navicate premium黄色版本破解下载
百度网盘下载 提取码: tsua 按照电脑安装32位或者64位 安装完成后点击最后一个进行破解汉化
- js 如何获取浏览器的高度?
<SCRIPT LANGUAGE="JavaScript"><!--var s = ""; s += " 网页可见区域宽:" ...