frida-server与frida-gadget:直接注⼊与沙盒注⼊【转】
1,frida-server
下⾯是frida官⽹给出的Android环境下hook的使⽤demo:
其中中间绿⾊部分是js代码,上下部分是python语⾔;
python语⾔部分只是单纯的为了将js代码发送到设备⽽已,核⼼功能还是在js部分实现;
1,js语⾔是弱语⾔,不对变量类型做强检查,所以我们可以都⽤var表⽰;
2,java中的类都⽤java.use获取;xposed
3,js代码function⼤括号内部是⽤于hook的主要代码,其余部分基本不变
import frida, sys
def on_message(message, data):
if message['type'] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
jscode = """
Java.perform(function () {
// Function to hook is defined here
var MainActivity = Java.use('k_paper_scissors.MainActivity');
// Whenever button is clicked
var onClick = Click;
onClick.implementation = function (v) {
/
/ Show a message to know that the function got called
send('onClick');
// Call the original onClick handler
onClick.call(this, v);
// Set our values after running the original onClick handler
thist.value = 999;
// Log to the console that it's done, and we should have the flag!
console.log('Done:' + JSON.stringify(thist));
};
});
"""
process = _usb_device().attach('k_paper_scissors')
script = ate_script(jscode)
<('message', on_message)
print('[*] Running CTF')
script.load()
ad()
下⾯以姜维在CSDN上给出的⼏个例⼦来讲解记录⼀下这部分js代码的⽤法:
java层hook:
1,hook构造⽅法
原⽅法:
原⽅法
hook代码:
hook代码
⾸先脚本中使⽤Java.use⽅法通过类名获取类类型,然后构造⽅法是固定写法:$init;这个要记住,然后因为需要重载所以⽤overload(……)形式即可,参数和参数之间⽤逗号隔开即可,这⾥⽤return重新调⽤了原来⽅法。
原⽅法:
原⽅法
hook代码:
hook代码
这⾥的⽤法和构造⽅法基本⼀致,就是把$init换成了⽅法名⽽已;
3,修改参数和返回值
参数和返回值的修改在上⾯内容很容易看出来,就是在⽅法拦截后通过arguments参数去获取传⼊参数,然后修改,返回值的话直接修改return函数就好了,这⾥重点讲的是⾃定义类型的参数:
构造⼀个新对象的⽅法有很多,这⾥选择最简单的$new即可,上⾯那⾏注释也是可以⽤的,这⾥的返回值直接修改成了我们构造出来的这个对象coinObj,但是如果要修改⼀个对象的内部值的话,直接⽤对象名加参数是不⾏的,那么就需要⽤反射了:
反射修改对象参数
其中java.Class(),).getDeclaredField("")的固定⽤法记住即可;
4,打印⽅法的堆栈信息
可以⽤来查看⽅法的调⽤链关系,这⾥提供两种思路,⼀种是⾃⼰编写⼀个打印堆栈的⽅法,弄成dex注⼊到程序中,需要打印的时候直接调⽤;第⼆种就是简单粗暴的直接在打印的地⽅构造⼀个异常对象然后抛出,但是这种⽅法会因为异常直接导致程序崩溃,不过⽆所谓了,我们可以看到调⽤关系就好了;
Native层hook:
1,hook未导出的函数
⾸先计算出⽬标函数实际地址=函数偏移+so基址+1,+1是因为要标识arm和thumb指令区别;
然后通过实际地址构造NativePointer对象;
通过模式attach到⽬标开始hook,这⾥的onEnter和onLeave与xposed中的before和after很像,其实功能也⼀样,都是在函数开始前和结束后;
var nativePointer = new NativePointer(实际地址)
这⾥读取到的参数其实⼀个指针,也就是⼀个地址,想要获取内容需要⽤到adCString(地址)来打印参数;读取返回值的时候,因为C 语⾔中是多数情况是通过参数来返回函数结果的,我们就把参数地址⾥的内容打印出来,如果有多个参数就全打印出来再分析,这⾥是将返回结果的字节数组按字节打印,然后拼接在⼀起在打印出来的;
2,hook导出的函数
这⾥省去了⼿动计算地址的过程,只需要传⼊so名字和函数名字即可,如果是c++的话则需要注意,因为C++⽀持重载,所以导出函数名会不同;
这⾥通过函数名可以知道就是⼀个native函数了,那么他第⼀个参数肯定是JNIEnv指针,第⼆个参数是jclass类型,这个是标准的如果是静态⽅法第⼆个参数没啥⽤,后⾯的参数就是真的传递到native层的值了,⽐如这⾥Java层的⽅法:
放到native层就变成4个参数了:
只有后⾯两个才是我们需要的,然后查看hook返回结果:
这⾥可以看到传⼊参数都返回了,但是函数return值是空的,这是因为这⾥的返回值是⼀个jString类型,看过jni.h⽂件的同学应该知道,jni ⾥有⼀套⾃⼰的数据类型定义,虽然格式都和java⼀致,但是他属于⾃定义类型;
这⾥new⼀个NativeFunction的⽤法可以查看api:
⾄于获取jString的⽅法官⽹也有说明,很简单:
var env =Env();
var jstring = wStringUtf("HelloWorld");
2,frida-Gadget
frida-gadget作为⼀个动态库⽂件⽆法直接在linux/Android环境下运⾏,需要⽤过将apk反编译成smali代码,然后通过System.loadlibrary("frida-Gadget");的形式将so动态库运⾏起来,此时Frida-server就是以应⽤的权限运⾏起来的,在应⽤的沙箱中是拥有全部权限的,包括拦截、插桩等操作;
1,程序⾸先要打开可调试状态开关,
⽐如AndroidManifest⽂件中的Application中增加android:debuggable="true"标签
2,将frida-gadget.so放到⽬标apk的lib⽂件夹下,将so的名字改成lib***.so的格式
3,植⼊点通常放在Application的⼊⼝处,因为尽可能早的hook住程序就可以避免漏过⽅法执⾏;
在smali代码下表现为Application中的构造函数处,.methods static constructor <clinit>()V
static{
// //loadlibary⾥要把SO⽂件名的lib和后缀去掉。libfgma.so --> fgma
System.loadLibrary("fgma");
}
⼊⼝
修改后 [注意此处的.locals标识的此函数中⽤的寄存器的数量]
如果⼀个应⽤有多个进程的话则不必将so的植⼊点放到最初application的构造函数中,可以选择将植⼊点选在新进程开启后的合适的地点,最好还是构造函数初始化过程,但不限于最早组件,可选择适当时机启动的组件,如下,并在加载后使进程休眠20s的时间,⽤来等待粘贴js代码;
这⾥在植⼊so之后增加了20s的进程睡眠时间,⽤来等带⽤户的⼿动操作
4,程序运⾏到so启动时会卡住,此时是在等待客户端像服务端发送建⽴连接的命令
此时在pc端命令⾏输⼊adb shell netstat -ls查看⽹络端⼝会发现有27042的消息端⼝;
如果在pc端命令⾏输⼊frida -U Gadget命令即可与之建⽴连接,此过程相当于frida-server模式下的python注⼊过程,在此建⽴连接之后我们可以直接输⼊js代码去执⾏⽬的代码,
例如:
//打印⽬的内存值
var tar = new NativePointer(0x94b778d9);send(hexdump(this.tar));
//获取⽬的函数模块
var nativePointer = Module.findExportByName(null,'SeedDecryptAndIDEncrypt');
//打印⽬的函数地址
send("smm native pointers:" + nativePointer);
//连接⽬的函数进程
Interceptor.attach(nativePointer,{
onEnter: function(args){
//打印⽬标函数传⼊参数值
send("sDIDE args: args[0]:" + args[0] + ", args[1]:" + args[1] + ", args[2]:" + args[2] + ", args[3]:" + args[3]);
//打印⽬的函数参数作为地址指向的内存的数值
send("smm sDIDE Memory args: args[0]:" + adInt(args[0]).toString() +
", args[2]:" + adInt(args[2]).toString() +
", args[3]:" + adInt(args[3]).toString());
},
onLeave: function(retval){
//打印⽬标函数的返回值
send("smm SeedDecryptAndIDEncrrypt retval:" + String());
}
});
注意此处的⽅法名使⽤单引号,findExportByName()函数的第⼀个参数可以⽤so名字或者为空;
以上就是frida的主要⽤法,其实frida的功能还有很多,⽽且不限于Android平台,总之,⼤佬们的总结是个好东西,官⽹api也是个好东西。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论