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

640?wx_fmt=png

公众号:640?wx_fmt=jpeg 菜鸟学Python编程 

 作者:悦来客栈的老板

请读者先完成第一篇中的例子再来学习这一篇,初探安卓逆向神器Frida

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

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

这篇需要用到 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显示成其他的值:

640?wx_fmt=png

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

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

640?wx_fmt=png

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

640?wx_fmt=png

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

640?wx_fmt=png

原来就是一个固定的返回值,那就好办了,按照官方的教程,写下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服务,然后运行脚本,先点击手机上的返回键,然后再打开软件,结果如下:

640?wx_fmt=png

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

640?wx_fmt=png

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

怎么查看包名及修改,上篇已经说过了。以后不再赘述,主要关注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中直接复制:

640?wx_fmt=png

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

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 用户。

640?wx_fmt=png

即将 Normal User 改为 Gold Vip。

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

640?wx_fmt=png

双击跟进去看看:

640?wx_fmt=png

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

640?wx_fmt=png

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

640?wx_fmt=png

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

640?wx_fmt=png

然后再选择_JNIEnv,

640?wx_fmt=png

代码变成了这样:

640?wx_fmt=png

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

640?wx_fmt=png

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

640?wx_fmt=png

这里,它传递了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的代码差不多,这里就不过多讲解了。看看效果:

640?wx_fmt=png

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

电脑端显示:

640?wx_fmt=png

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

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

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

推荐阅读

Python 爬虫面试题 170 道:2019 版

爬虫必备-JS之Dom操作大全

爬虫工程师教你如何入门Android逆向

把 Android App 逆向分为几步?三步

添加微信[italocxa].回复:加群,加入Python交流群

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值