安卓逆向入门之使用frida框架简单Hook native层的函数

声明:本文只做技术交流,如有侵权,请告知删除,谢谢。

请读者先完成第一篇中的例子再来学习这一篇,传送门:

https://mp.weixin.qq.com/s/2kykNy-Dyp8c983LPwkZIQ

都是比较简单的东西,像我这种初学者先找找逆向的自信吧。

这篇是我的学习笔记,所以文章比较水,大佬还请轻喷。

这篇需要用到 IDA,所以请先安装IDA。

今天的案例主要会用到这里面教程的apk:

https://www.52pojie.cn/thread-313869-1-1.html

帖子比较早,但是用来学习还是有帮助的。

我们来破解他的第二个例子和第三个例子,分别修改入参和返回值,理解frida框该怎么对native层的函数进行简单的Hook。如果想了解更高深的知识,可以参考尼古拉斯_赵四的这篇文章:

https://blog.csdn.net/jiangwei0910410003/article/details/80372118

好了,言归正传,下载他的例在电脑上,并在手机端进行安装,例子链接:

http://pan.baidu.com/s/1i3wzetf

打开软件,按照他的要求,将100显示成其他的值:

安卓逆向入门之使用frida框架简单Hook native层的函数_第1张图片

既然知道是函数在native层定义的直接,将 so文件拖入 IDA进行分析吧。

切换到 Exports面板,看看有哪些JNI函数:

安卓逆向入门之使用frida框架简单Hook native层的函数_第2张图片

选择 第二个红色方框内的函数,双击跟进去:

安卓逆向入门之使用frida框架简单Hook native层的函数_第3张图片

如果我没有猜错的话,这里的 #0x64就是它返回的数字 100吧,继续按F5,看看它的伪C代码:

安卓逆向入门之使用frida框架简单Hook native层的函数_第4张图片

原来就是一个固定的返回值,那就好办了,按照官方的教程,写下frida脚本:

import frida,sys	

	
def on_message(message, data):	
    if message['type'] == 'send':	
        print("[*] {0}".format(message['payload']))	
    else:	
        print(message)	

	
jscode = """	
    var nativePointer = Module.findExportByName("libgg-jni.so","Java_com_ggndktest1_JniGg_getCoin");	
    send("native pointers:" + nativePointer);	
    Interceptor.attach(nativePointer,{	
        onEnter:function(args){	
            send(args);	
          },	
        onLeave:function(retval){	
            send(retval.toInt32());	
            retval.replace(1000000);	
            send(retval.toInt32());	
          }	
        	
    });	
"""	
process = frida.get_usb_device().attach('com.ggndktest1')	
script = process.create_script(jscode)	
script.on('message', on_message)	
print('[*] Running CTF')	
script.load()	
sys.stdin.read()

这段代码的作用将 100 改为了 1000000,先看看效果,再做说明。

手机连接电脑,开启frida服务,然后运行脚本,先点击手机上的返回键,然后再打开软件,结果如下:

安卓逆向入门之使用frida框架简单Hook native层的函数_第5张图片

可以看到,已经修改成功了,再看看电脑端:

安卓逆向入门之使用frida框架简单Hook native层的函数_第6张图片

下面对代码进行一些说明:

怎么查看包名及修改,上篇已经说过了。以后不再赘述,主要关注jscode代码段就好:

    var nativePointer = Module.findExportByName("libgg-jni.so","Java_com_ggndktest1_JniGg_getCoin");

看看官方教程对 Module.findExportByName的解释:

Module.findExportByName(moduleName|null, exportName),Module.getExportByName(moduleName|null, exportName)

returns the absolute address of the export named exportName in moduleName. If the module isn’t known you may pass null instead of its name, but this can be a costly search and should be avoided. In the event that no such module or export could be found, the find-prefixed function returns null whilst the get-prefixed function throws an exception.

这样就比较好理解,第一个参数是so的文件名,第二个参数导出的函数名,可以在IDA中直接复制:

安卓逆向入门之使用frida框架简单Hook native层的函数_第7张图片

再来看看怎么修改返回值:

Interceptor.attach(target, callbacks): intercept calls to function attarget. This is a NativePointer specifying the address of the function you would like to intercept calls to. Note that on 32-bit ARM this address must have its least significant bit set to 0 for ARM functions, and 1 for Thumb functions. Frida takes care of this detail for you if you get the address from a Frida API (for example Module.getExportByName()).

The callbacks argument is an object containing one or more of:

  • onEnter: function (args): callback function given one argumentargs that can be used to read or write arguments as an array of NativePointer objects.

  • onLeave: function (retval): callback function given one argumentretval that is a NativePointer-derived object containing the raw return value. You may call retval.replace(1337) to replace the return value with the integer 1337, or retval.replace(ptr("0x1234")) to replace with a pointer. Note that this object is recycled across onLeave calls, so do not store and use it outside your callback. Make a deep copy if you need to store the contained value, e.g.: ptr(retval.toString()).

onEnter 这里函数传入的是一个数组,保存这传递进来的参数,可读可写。可以理解为 在Hook的那个函数调用之前执行的代码。

onLeave 这里是一个返回值,并可以将其进行修改,修改的方式又两种:

retval.replace(1337)  integer

retval.replace(ptr("0x1234")) pointer

有了官方的说明,我这里直接将100改为100000:

            retval.replace(1000000);

所以能修改成功。这是修改返回值,那怎么修改传入的参数呢?

不急,我们来看他的第三个案例,下载地址:

http://pan.baidu.com/s/1jG22HMY

他的要求:将当前用户类型修改为Gold Vip 用户

安卓逆向入门之使用frida框架简单Hook native层的函数_第8张图片

即将 Normal User 改为 Gold Vip

那我们依然对so文件拖入到 IDA中,按照教程,我们需要了解这个函数的逻辑:

安卓逆向入门之使用frida框架简单Hook native层的函数_第9张图片

双击跟进去看看:

安卓逆向入门之使用frida框架简单Hook native层的函数_第10张图片

看汇编代码也能看懂,比较简单,不过是几个比较的指令,分别对 2,3,1进行比较,然后跳转不同的地方。我们还是看看它的伪C代码吧:

安卓逆向入门之使用frida框架简单Hook native层的函数_第11张图片

这什么鬼????请教了 Lilac 大佬后,告诉我需要导入 jni.h头文件,按CTRL + F9进行头文件导入:

安卓逆向入门之使用frida框架简单Hook native层的函数_第12张图片

然后在函数的第一个参数位置单击右键,选择 Convert to struct*:

安卓逆向入门之使用frida框架简单Hook native层的函数_第13张图片

然后再选择_JNIEnv,

安卓逆向入门之使用frida框架简单Hook native层的函数_第14张图片

代码变成了这样:

安卓逆向入门之使用frida框架简单Hook native层的函数_第15张图片

然后我们在NewStringUTF右键,选择Force call type:

安卓逆向入门之使用frida框架简单Hook native层的函数_第16张图片

然后点击确定,就看到了伪C代码:

安卓逆向入门之使用frida框架简单Hook native层的函数_第17张图片

这里,它传递了3个参数,我们看逻辑,得知修改第三个参数就可以改变其逻辑,写下如下frida脚本,并运行:

import frida,sys	

	
def on_message(message, data):	
    if message['type'] == 'send':	
        print("[*] {0}".format(message['payload']))	
    else:	
        print(message)	

	
jscode = """	
    var nativePointer = Module.findExportByName("libgg-jni.so","Java_com_ggndktest1_JniGg_VipLevel");	
    send("native pointers:" + nativePointer);	
    Interceptor.attach(nativePointer,{	
        onEnter:function(args){	
            send(args[2]);	
            args[2]=ptr(1); 	
          },	
          	
        onLeave:function(retval){	
            send(retval)	
          }	
        	
    });	
"""	
process = frida.get_usb_device().attach('com.ggndktest1')	
script = process.create_script(jscode)	
script.on('message', on_message)	
print('[*] Running CTF')	
script.load()	
sys.stdin.read()

代码和案例2的代码差不多,这里就不过多讲解了。看看效果:

安卓逆向入门之使用frida框架简单Hook native层的函数_第18张图片

可以看到,已经变成了 Gold Vip,说明修改已经成功了。

电脑端显示:

安卓逆向入门之使用frida框架简单Hook native层的函数_第19张图片

这里的 0x5就是它原来传递的参数,被我改为了 1,也就将逻辑改变了。

比较简单,这只是一篇学习笔记,大佬们请轻喷。

再次感谢 Lilac 大佬提供的IDA相关的知识,省去了很多时间,果然有个大佬带要少走很多弯路呀。

你可能感兴趣的:(安卓逆向入门之使用frida框架简单Hook native层的函数)