《九阴真经: iOS黑客攻防秘籍》新书发布,干货满满,快来看看吧!

iOS 安全论坛 - 专注于研究 iOS 安全

 找回密码
 立即注册
查看: 6825|回复: 3

【Frida 实战】远程过程调用(RPC)

[复制链接]

119

主题

582

帖子

2626

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2626
发表于 2020-6-7 20:06:57 | 显示全部楼层 |阅读模式
本文是 Frida 实战系列教程的第五篇,讲解远程过程调用(RPC)的使用方法,也就是将应用进程中的 Objective-C 方法或 C 函数导出,提供给 Python 使用。
远程过程调用(RPC)对于应用逆向起到了很便捷的作用,比如目标应用有一个加解密的函数内部实现非常复杂,想分析整个加解密的实现过程的工作量较大,此时可以使用 frida 的 RPC 功能,只要知道加解密函数的名称或地址,还有相应的参数,即可直接调用得到加解密后的结果。
下面我们来做一个实例,编写一个 CrackMe 程序,定义一个 coreClass 类,里面有 4 个比较重要的方法,分别是 getDeviceId、httpPost、encrypt、decrypt,从它们的名称可以看出大概的意思,具体的定义如下:
  1. @interface coreClass : NSObject
  2. @end

  3. @implementation coreClass

  4. - (NSString*)getDeviceId{
  5.     return @"e10adc3949ba59abbe56e057f20f883e";
  6. }

  7. +(NSString*)httpPost:(NSString*)url{
  8.     NSLog(@"URL: %@", url);
  9.     return @"test";
  10. }

  11. - (NSString*)encrypt:(NSData*)data :(NSString*)key{
  12.    
  13.     NSLog(@"data: %@", data);
  14.     NSLog(@"key: %@", key);
  15.     return @"encrypt successed!";
  16. }

  17. - (NSString*)decrypt:(NSString*)str :(NSString*)key{
  18.     NSLog(@"str: %@", str);
  19.     NSLog(@"key: %@", key);
  20.     return @"decrypt successed!";
  21. }
  22. @end
复制代码
下面我们要做的事情是在 JS 脚本里去调用这 4 个方法。Objective-C 方法有两种,一种是 - 号开头,称为对象方法,还有一种是 + 号开头,称为类方法,前者需要初始化一个实例才能调用,后者不需要初始化实例,可以直接调用。先来看看对象方法在 JS 脚本里如何调用,比如我们要调用 -[coreClass getDeviceId] 方法,调用的代码如下:
  1. var coreClass = ObjC.classes.coreClass.alloc();
  2. var deviceId = coreClass['- getDeviceId'].call(coreClass);
  3. console.log("deviceId: " + deviceId.toString());
复制代码
再来看看 +[coreClass httpPost:] 方法的调用,从下面的代码可以看出,类方法是不需要初始化实例,在方法名称后面加上一个 _ 号就可以调用了。
  1. var coreClass = ObjC.classes.coreClass;
  2. var url = ObjC.classes.NSURL.URLWithString_("http://www.ioshacker.net");
  3. var retString =  coreClass.httpPost_(url);
  4. console.log("retString: " + retString);
复制代码
-[coreClass encrypt::] 的调用代码如下,输入的参数有两个,一个参数是 NSData 类型的,一个是 NSString 类型。
  1. var coreClass = ObjC.classes.coreClass.alloc();
  2. var str = ObjC.classes.NSString.stringWithString_("test");

  3. //NSData *data = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
  4. var data = str.dataUsingEncoding_(4);
  5. var key = ObjC.classes.NSString.stringWithString_("key");

  6. var encryptString = coreClass['- encrypt::'].call(coreClass, data, key);
  7. console.log("encryptString: " + encryptString);
复制代码
-[coreClass decrypt::] 的调用代码如下,两个参数都是 NSString 类型。
  1. var coreClass = ObjC.classes.coreClass.alloc();
  2. var str = ObjC.classes.NSString.stringWithString_("test");
  3. var key = ObjC.classes.NSString.stringWithString_("key");
  4. var decryptString = coreClass['- decrypt::'].call(coreClass, str, key);
  5. console.log("decryptString: " + decryptString);
复制代码
在上面我们已经学会如何在 JS 脚本里调用目标应用的 Objective-C 方法,接下面要做的就是将功能代码导出提供给 Python 使用。在 JS 代码里定义4个函数,分别是 getDeviceId、httpPost、encrypt、decrypt,这 4 个函数分别代表了调用目标进程的 4 个 Objective-C 方法,然后再使用 rpc.exports 将这 4 个函数导出,代码如下:
  1. function getDeviceId(){
  2.         var coreClass = ObjC.classes.coreClass.alloc();
  3.         var deviceId = coreClass['- getDeviceId'].call(coreClass);
  4.         console.log("deviceId: " + deviceId.toString());
  5.         console.log("--------------------");
  6. }

  7. function httpPost(inputurl){
  8.         var coreClass = ObjC.classes.coreClass;
  9.         var url = ObjC.classes.NSURL.URLWithString_(inputurl);
  10.         var retString =  coreClass.httpPost_(url);
  11.         console.log("retString: " + retString);
  12.         console.log("--------------------");
  13. }

  14. function encrypt(inputstr, inputkey){
  15.         var coreClass = ObjC.classes.coreClass.alloc();
  16.         var str = ObjC.classes.NSString.stringWithString_(inputstr);

  17.         //NSData *data = [@"data" dataUsingEncoding:NSUTF8StringEncoding];
  18.         var data = str.dataUsingEncoding_(4);
  19.         var key = ObjC.classes.NSString.stringWithString_(inputkey);

  20.         var encryptString = coreClass['- encrypt::'].call(coreClass, data, key);
  21.         console.log("encryptString: " + encryptString);
  22.         console.log("--------------------");
  23. }

  24. function decrypt(inputstr, inputkey){
  25.         var coreClass = ObjC.classes.coreClass.alloc();
  26.         var str = ObjC.classes.NSString.stringWithString_(inputstr);
  27.         var key = ObjC.classes.NSString.stringWithString_(inputkey);
  28.         var decryptString = coreClass['- decrypt::'].call(coreClass, str, key);
  29.         console.log("decryptString: " + decryptString);
  30.         console.log("--------------------");
  31. }

  32. //getDeviceId();
  33. //httpPost("http://www.ioshacker.net");
  34. //encrypt("123", "456");
  35. //decrypt("123", "456");

  36. // 导出RPC函数
  37. rpc.exports = {
  38.     deviceid: function () {
  39.         getDeviceId();
  40.     },
  41.     httppost: function(urlstr) {
  42.         httpPost(urlstr);
  43.     },
  44.     encrypt: function (inputstr, inputkey) {
  45.         encrypt(inputstr, inputkey);
  46.     },
  47.     decrypt: function(inputstr, inputkey) {
  48.         decrypt(inputstr, inputkey);
  49.     },
  50. };
复制代码
接着我们再编写 python 代码,首先调用 get_usb_device().attach 附加到目标进程,附加成功会返回一个 Session 实例,然后读取 call.js 文件里的内容,这里的内容就是上面我们编写的 JS 代码,然后调用 session.create_script 创建脚本,再调用 load 加载脚本,最后调用 script.exports 获取 JS 导出的 RPC 函数,此时就可以调用 RPC 函数,代码如下:
  1. # -*- coding: utf-8 -*-
  2. import codecs
  3. import frida

  4. if __name__ == '__main__':
  5.         #附加到目标进程
  6.         session = frida.get_usb_device().attach(u'CrackMe')

  7.         #读取JS脚本
  8.         with codecs.open('./call.js', 'r', 'utf-8') as f:
  9.             source = f.read()

  10.         script = session.create_script(source)
  11.         script.load()

  12.         rpc = script.exports

  13.         #调用导出的函数
  14.         rpc.deviceid()
  15.         rpc.httpPost("http://www.ioshacker.net")
  16.         rpc.encrypt("123", "456")
  17.         rpc.decrypt("123", "456")
  18.         session.detach()
复制代码
上面我们讲到了如何将 Objective-C 方法导出给 Python 使用,那如何导出 C 函数呢?比如调用 NSHomeDirectory 这个系统提供的函数,知道它所在的模块是 Foundation,找到它的地址在 JS 脚本里就可以调用,然后在 rpc.exports 里添加一个导出函数,这样 Python 就可以调用了。代码如下:
  1. function getHomeDir(){
  2.         var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []);
  3.         var path = new ObjC.Object(NSHomeDirectory());
  4.         console.log('homeDir: ' + path);
  5. }

  6. // 导出RPC函数
  7. rpc.exports = {
  8.     //......
  9.     homedir: function(){
  10.             getHomeDir();
  11.     }
  12. };
复制代码
最后希望读者能够仔细领悟,翻一翻前面几篇教程,多加练习,相信一定会融会贯通。期待下一篇教程我们还会再见,感谢各位的支持。

第一篇:在 iOS 上分析应用
第二篇:Hook 大法,拦截器的使用
第三篇:如何拦截 sub_xxxx 这种函数
第四篇:API查找器和拦截器的组合使用

想了解最新的iOS安全资讯、技术干货请关注 iOS安全论坛(ioshacker.net)微信公众号

回复

使用道具 举报

0

主题

1

帖子

16

积分

新手上路

Rank: 1

积分
16
发表于 2020-6-7 23:16:04 | 显示全部楼层
本帖最后由 YunsChou 于 2020-6-7 23:17 编辑

感谢分享,非常强大,正在学习中
回复

使用道具 举报

0

主题

1

帖子

14

积分

新手上路

Rank: 1

积分
14
发表于 2020-7-9 11:46:42 | 显示全部楼层
谢谢陈大,自己组装Dictionary组装了半天还是没成功,下一篇能不能顺便提一下
回复

使用道具 举报

0

主题

1

帖子

28

积分

新手上路

Rank: 1

积分
28
发表于 2022-6-21 12:28:01 | 显示全部楼层
你好,大神,想问下void __cdecl +[Encrypt encWithString:withSuccess:],其中withSuccess这个block参数,这种怎么远程调用?谢谢
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|iOSHacker

GMT+8, 2023-6-9 13:12 , Processed in 0.021203 second(s), 19 queries .

iOS安全论坛

© 2017-2020 iOS Hacker Inc. 京ICP备17074153号-2

快速回复 返回顶部 返回列表