首页 > 编程语言 >【iOS逆向与安全】某茅台App算法分析还原

【iOS逆向与安全】某茅台App算法分析还原

时间:2023-11-15 19:07:00浏览次数:32  
标签:逆向 00 log 22 App args iOS XXReserveRequest true

1.目标

某茅台软件的actParam算法分析还原。

2.使用工具

  • mac系统

  • frida-ios-dump:砸壳

  • 已越狱iOS设备:脱壳及frida调试

  • IDA Pro:静态分析

  • Charles:抓包工具

  • ss:小火箭,配合Charles使用

3.流程

处理启动闪退

在IDA Pro搜索SVC得到如下函数列表:

image-20220923154957305

NOP掉sub_函数的最后一行汇编后,即可正常运行App

image-20220923155026321

处理登录闪退

启动App,在登录页使用命令frida-trace -UF -m "-[UIViewController viewDidAppear:]",然后进入到任意页后再返回登录页,获取到当前类为XXLoginViewController,再使用命令frida-trace -UF -m "-[*LoginViewController *]",跟踪该类。点击登录后,啥也没获取到,呵呵。不过,看LoginViewController类有loginButton,在该方法看到登录按钮绑定的事件为LoginViewController类的login方法。使用IDA Pro打开该方法:

void __cdecl -[XXLoginViewController login](XXLoginViewController *self, SEL a2)
{
  if ( qword_100F492E0 )
    exit(0);
}

这么大的exit函数,查看该条件是在什么地方赋值,查看该变量的交叉引用

image-20220923163148966

在sub_1002F52F44函数里对该变量进行赋值。接下来就是处理该函数的赋值逻辑(该方法仅是保证检测环境的qword_100F492E0为0,不排除有其他地方仍然有越狱检测等操作):

1、处理embedded.mobileprovision文件。(重签名后会生成该文件)

{
  onEnter(log, args, state) {
	this.fileName = new ObjC.Object(args[2]);
    log(`-[NSBundle pathForResource:${new ObjC.Object(args[2])} ofType:${new ObjC.Object(args[3])}]`);
  },
  onLeave(log, retval, state) {
	  if (this.fileName.toString().toLowerCase().indexOf("embedded") != -1 ) {
		  retval.replace(0x0)
	  }
	  log(`-[NSBundle pathForResource:ofType:]=${new ObjC.Object(retval)}=`);
  }
}

2、对sub_1002F52F44函数里,对该变量进行赋值的地方,一一处理。保证不会执行qword_100F492E0=1,处理完后即可正常登录:

IMG_D83606B7518C-1

寻找actParam算法

在IDA Pro搜索actParam字符串,没有发现该字符,说明该字符串被处理过了。按之前的套路,既然这在body里,那我们就使用命令frida-trace -U -f com.xxx.xxx -m "*[NSMutableURLRequest setHTTPBody:]" -m "*[NSBundle pathForResource:ofType:]"获取到关键日志如下:

-[NSMutableURLRequest setHTTPBody:<7b226163 74506172 616d223a 22496469 77776474 52644542 68646548 6b614a62 71314a35 3972386a 35684c6a 33653334 76576d74 67523374 47486170 4e4c7752 73326237 31495435 614f6c32 42506a44 4f386277 7a793270 34664c34 6f483878 746c3048 78337069 716c6b50 4a4a5555 54677950 6a39397a 6c455750 34375c2f 596f397a 414b7762 6e726768 47585351 526a4f51 706c3636 31694541 347a6157 42657a51 3d3d222c 22697465 6d496e66 6f4c6973 74223a5b 7b22636f 756e7422 3a312c22 6974656d 4964223a 22313030 3536227d 5d2c2273 65737369 6f6e4964 223a3336 362c2273 686f7049 64223a22 31353135 31303132 32303031 227d>
NSMutableURLRequest setHTTPBody called from:
0x1007b8090 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[AFJSONRequestSerializer requestBySerializingRequest:withParameters:error:]
0x1007b3a24 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]
0x1007ab270 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure:]
0x1009557dc /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[XXBaseRequest startWithSuccess:failure:]
0x100a1b724 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[XXReserveViewController reserve]
0x100a1b0fc XXX!0x2770fc (0x1002770fc)
0x100b0b6e4 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[RACSubscriber sendNext:]
0x100af3654 /var/containers/Bundle/Application/AFABDD2B-D35D-4F60-A4A1-DFC92CCC3251/iXX_1_2_15.app/XXX!-[RACPassthroughSubscriber sendNext:]
0x1aeca8ac4 UIKitCore!-[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:]
0x1aecb0ccc UIKitCore!_UIGestureRecognizerSendTargetActions
0x1aecae670 UIKitCore!_UIGestureRecognizerSendActions
0x1aecadb9c UIKitCore!-[UIGestureRecognizer _updateGestureWithEvent:buttonEvent:]
0x1aeca1c78 UIKitCore!_UIGestureEnvironmentUpdate
0x1aeca13a8 UIKitCore!-[UIGestureEnvironment _deliverEvent:toGestureRecognizers:usingBlock:]
0x1aeca1188 UIKitCore!-[UIGestureEnvironment _updateForEvent:window:]
0x1af0b97d0 UIKitCore!-[UIWindow sendEvent:]

body里正是我们提交里的参数,根据堆栈可知网络请求是在[XXReserveViewController reserve]发起的,使用IDA Pro查看对应的代码可知网络请求类是XXReserveRequest。使用命令frida-trace -U -f com.xxx.xxx -m "*[XXReserveRequest *]" -m "*[NSBundle pathForResource:ofType:]"跟踪该类,获取到的日志如下:

-[XXReserveRequest setShopId:0x280dfd920]
-[XXReserveRequest setSessionId:0x16f]
-[XXReserveRequest setItemInfoList:0x280ec7f20]
-[XXReserveRequest ydLogId]
-[XXReserveRequest itemInfoList]
-[XXReserveRequest sessionId]
-[XXReserveRequest shopId]
-[XXReserveRequest ydToken]
-[XXReserveRequest requestParams]
-[XXReserveRequest ydLogId]
-[XXReserveRequest itemInfoList]
-[XXReserveRequest sessionId]
-[XXReserveRequest shopId]
-[XXReserveRequest ydToken]
-[XXReserveRequest getTYWZlObj]
-[XXReserveRequest requestParams]={
    actParam = "IdiwwdtRdEBhdeHkaJbq1J59r8j5hLj3e34vWXXgR3sN9Cp03vhtPomwYoD2EtZM6dvuKXTI3BIjWffzkuwBXUIsms+sHaMW2D+1CMCwdZe2sLG0FXnHUIJIpXblOBJrlRAHk9Bn3fFSZsjYhaJoNw==";
}=
-[XXReserveRequest requestHeader]
-[XXReserveRequest getTYWZlObj]
+[XXReserveRequest requestPath]
+[XXReserveRequest requestPath]
+[XXReserveRequest requestType]
+[XXReserveRequest responseDataMapping]
-[XXReserveRequest .cxx_destruct]

根据日志可发现actParam在requestParams方法里返回的,继续使用IDA Pro查看该代码

image-20220926092438499

sub函数一直点进去,就会发现(unsigned int)CCCrypt(v6, 0LL, 1LL, &v43, 32LL, v27, v28, v29, v41, v26, &v42) ,接下来就是对该函数进行调试,由于该方法,需要特写时间段才能使用。所以在此,我们查看此sub函数的交叉引用,发现在下单时,也有调用该加密函数。

使用命令frida-trace -U -f com.xxx.xxx -m "*[NSBundle pathForResource:ofType:]" -i CCCrypt打印该参数,在创建订单时,获取到日志如下:

js代码

{
	onEnter: function(log, args, state) {
		this.op = args[0]
		this.alg = args[1]
		this.options = args[2]
		this.key = args[3]
		this.keyLength = args[4]
		this.iv = args[5]
		this.dataIn = args[6]
		this.dataInLength = args[7]
		this.dataOut = args[8]
		this.dataOutAvailable = args[9]
		this.dataOutMoved = args[10]

		log('CCCrypt(' +
			'op: ' + this.op + '[0:加密,1:解密]' + ', ' +
			'alg: ' + this.alg + '[0:AES128,1:DES,2:3DES]' + ', ' +
			'options: ' + this.options + '[1:ECB,2:CBC,3:CFB]' + ', ' +
			'key: ' + this.key + ', ' +
			'keyLength: ' + this.keyLength + ', ' +
			'iv: ' + this.iv + ', ' +
			'dataIn: ' + this.dataIn + ', ' +
			'inLength: ' + this.inLength + ', ' +
			'dataOut: ' + this.dataOut + ', ' +
			'dataOutAvailable: ' + this.dataOutAvailable + ', ' +
			'dataOutMoved: ' + this.dataOutMoved + ')')

		if (this.op == 0) {
			log("dataIn:")
			log(hexdump(ptr(this.dataIn), {
				length: this.dataInLength.toInt32(),
				header: true,
				ansi: true
			}))
			log("key: ")
			log(hexdump(ptr(this.key), {
				length: this.keyLength.toInt32(),
				header: true,
				ansi: true
			}))
			log("iv: ")
			log(hexdump(ptr(this.iv), {
				length: this.keyLength.toInt32(),
				header: true,
				ansi: true
			}))
		}
	},
	onLeave: function(log, retval, state) {
		if (this.op == 1) {
			log("dataOut:")
			log(hexdump(ptr(this.dataOut), {
				length: Memory.readUInt(this.dataOutMoved),
				header: true,
				ansi: true
			}))
			log("key: ")
			log(hexdump(ptr(this.key), {
				length: this.keyLength.toInt32(),
				header: true,
				ansi: true
			}))
			log("iv: ")
			log(hexdump(ptr(this.iv), {
				length: this.keyLength.toInt32(),
				header: true,
				ansi: true
			}))
		} else {
			log("dataOut:")
			log(hexdump(ptr(this.dataOut), {
				length: Memory.readUInt(this.dataOutMoved),
				header: true,
				ansi: true
			}))
		}
		log("CCCrypt did finish")
	}
}

日志

 96341 ms  CCCrypt(op: 0x0[0:加密,1:解密], alg: 0x0[0:AES128,1:DES,2:3DES], options: 0x1[1:ECB,2:CBC,3:CFB], key: 0x16eecea70, keyLength: 0x20, iv: 0x2821adbe0, dataIn: 0x2815a80c0, inLength: undefined, dataOut: 0x281794580, dataOutAvailable: 0xa4, dataOutMoved: 0x16eecea68)
 96341 ms  dataIn:
 96341 ms              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
2815a80c0  7b 22 64 65 6c 69 76 65 72 4d 65 74 68 6f 64 22  {"deliverMethod"
2815a80d0  3a 2d 31 2c 22 61 64 64 72 65 73 73 49 6e 66 6f  :-1,"addressInfo
2815a80e0  22 3a 7b 22 73 68 69 70 41 64 64 72 65 73 73 49  ":{"shipAddressI
2815a80f0  64 22 3a 22 33 37 39 36 31 36 30 22 7d 2c 22 75  d":"3711160"},"u
2815a8100  73 65 72 49 64 22 3a 22 31 30 36 39 38 36 36 32  serId":"10008662
2815a8110  38 30 22 2c 22 69 74 65 6d 4c 69 73 74 22 3a 5b  80","itemList":[
2815a8120  7b 22 63 6f 75 6e 74 22 3a 31 2c 22 73 70 75 49  {"count":1,"spuI
2815a8130  64 22 3a 22 34 32 35 22 2c 22 73 74 6f 72 65 49  d":"425","storeI
2815a8140  64 22 3a 22 32 35 31 35 31 30 31 38 38 30 31 30  d":"251510182010
2815a8150  22 7d 5d 7d                                      "}]}
 96341 ms  key:
 96341 ms              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
16eecea70  71 62 68 61 6a 69 6e 6c 64 65 70 6d 75 63 73 6f  qbhajinldepmucso
16eecea80  6e 61 61 61 63 63 67 79 70 77 75 76 63 6a 61 61  naaaccgypwuvcjaa
 96341 ms  iv:
 96341 ms              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
2821adbe0  32 30 31 38 35 33 34 37 34 39 39 36 33 35 31 35  2018534749963515
2821adbf0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 96345 ms  dataOut:
 96345 ms              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
281794580  b0 fd c3 5f a8 e8 de 45 80 ac 23 e6 2b d1 3f 2c  ..._...E..#.+.?,
281794590  5d 4b 88 e9 88 99 7a 76 ab 7b c2 c5 88 40 fd 4f  ]K....zv.{[email protected]
2817945a0  38 37 c5 48 61 06 b1 a9 33 0d 1a 7d a7 59 3d fb  87.Ha...3..}.Y=.
2817945b0  05 88 cc 18 b8 68 cb ce a7 33 1e 32 f1 1f a2 ae  .....h.....2....
2817945c0  0b 50 04 75 ed 19 36 eb 64 bd d7 79 a8 13 6a 4d  .P.u..6.d..y..jM
2817945d0  9c bf 72 1c 42 6c 41 cc 76 86 85 cf bd 58 5b db  ..r.BlA.v....X[.
2817945e0  05 74 ec e6 55 6e 84 10 ae zz 61 b8 64 81 7b 6f  .t..Un....a.d.{o
2817945f0  02 df 42 a6 13 8e a6 41 ee 1a 0a b5 65 cb 45 29  ..B....F....e.E)
281794600  9b 86 ea 63 f6 8e 92 6c 1a 11 8b 70 09 b7 b5 ad  ...c...l...p....
281794610  c6 f7 2c 70 91 12 eb 62 45 a4 0e b5 75 29 c4 be  ..,p...bE...u)..
 96345 ms  CCCrypt did finish

日志里的dataOut数据,转换为base64后,和接口里的参数一致(注:日志里的敏感信息被我处理过)。

结果

见CCCrypt日志。在分析过程中,当我们发现参数是base64的时候,也可以先拦截base64EncodedStringWithOptions或CCCrypt函数,运气好的话,也能快速定位到加密算法。

提示:阅读此文档的过程中遇到任何问题,请关住工众好【移动端Android和iOS开发技术分享】或+99 君羊【*812546729*】

image-20230225231548837

标签:逆向,00,log,22,App,args,iOS,XXReserveRequest,true
From: https://blog.51cto.com/witchan/8400118

相关文章

  • 如何用SaleSmartly集成WhatsApp账号(内含WhatsApp个人号、商业号、API号对比图)
    用SaleSmartly集成WhatsApp账号如果企业有多个WhatsApp账号,无论是个人账号还是工作账号,员工操作起来可能会觉得难以管理和切换。SaleSmartly就可以解决这个问题,让员工在一个平台上同时使用多个WhatsApp账号,不需要频繁地登录和退出,方便客服查看和回复所有的消息和通话。在SaleSmartl......
  • WhatsApp个人号、Business号、API号到底有什么区别
    WhatsApp作为全球苹果应用商店中用户下载量最多的社交网络应用,在全球有25亿的用户,每日活跃人数超过5亿人。很多人都以为WhatsApp就是一个软件,但是其实它是个家族,里面共有三个成员,分别是WhatsApp Messenger,WhatsApp Business和WhatsApp Business API。按照它们推出市场的时间来......
  • mkfs.xfs报错 mkfs.xfs: /dev/new/new_box appears to contain an existing fil
    在设置逻辑卷文件类型时候报错mkfs.xfs:/dev/new/new_boxappearstocontainanexistingfilesystem(ext4).mkfs.xfs:Usethe-foptiontoforceoverwrite.上面是说目标分区,已经存在一个文件系统但是我们有很需要他更改文件系统的话就加一个-f选项[root@server~]......
  • springboot~ConfigurableListableBeanFactory和ApplicationContext的使用场景
    在工具类中封装getBean,使用哪个接口来实现实事上,在工具类中,实现BeanFactoryPostProcessor和ApplicationContextAware接口后,使用它们构造方法里的对象ConfigurableListableBeanFactory和ApplicationContext都可以很方便的获取spring容器里的bean,而在实际应用时,还有有些不同的,比如在......
  • 顶象保障熊猫银行App安全,助力柬埔寨数字经济发展
    柬埔寨电子支付业迎来新发展契机,越来越多的商家推行电子支付方式。银行卡和扫码支付,深受民众青睐。据Visa发布的《2021消费者支付态度研究》报告显示,柬埔寨有36%的消费者不使用现金支付,这一比例在东南亚国家中位居第一。同时,柬埔寨也是东南亚国家中手机银行用户数量最多的国家,占比......
  • 短视频app源码,自定义快速滚动条FastScrollBar
    短视频app源码,自定义快速滚动条FastScrollBarAndroidMainfest.xml中  <activityandroid:name=".MainActivity"      android:theme="@style/FastScrollTheme">      <intent-filter>        <actionandroid:name="androi......
  • 苹果iOS系统有几种开发者账号,分别有什么区别,我们该如何选择
    我们常见的开发者账号就是三种:个人开发者账号、公司开发者账号和企业开发者账号之外,当然除了这三种还有另外的如教育机构开发者账号和政府机构开发者账号,通过今天的这篇文章可以更清晰的了解并选择自己的需求。在移动开发领域中,苹果已经发展成最强大的平台之一,苹果为了支持开发者构......
  • 信用卡额度生成器在线使用,用户输入信息自动绘图,e4a开发的APP代码
    e4a本身的标签是支持透明的,所以要实现这个效果那就再简单不过了,但是提前你需要找好一个模版,这个也是网上我找的一个图,提前用工具消除了带水印带信息的部分,然后把干净的图片载入到assets资源库里面,然后在导入到生成窗口的图片框里面,图片上面加了e4a的透明标签,然后针对安卓分辨率做......
  • 贷款额度生成器,支持信用卡APP版,虚拟截图E4a开发,仅提供源码
    闲着无聊用易安卓开发了一个额度生成器,原理非常简单,就网上找了一个模版,加了几个编辑框,用PS消除了原有的信息内容,在上面加了一些标签,实现用户输入确定按钮后改变图片上的标签内容,下面是框架图:框架界面图:  生成框架图:  代码:【e4a】:==================================......
  • token以及axios响应拦截器请求拦截器
    一、token的介绍1.概念访问权限的令牌,本质上是一串字符串2.创建正确登录后,由后端签发并返回3.作用判断是否有登录状态等,控制访问权限注意:前端只能判断有无token,而后端才能判断token的有效性4.使用目标:只有登录状态,才能访问内容页面1.在utils/auth.js中判断有无token令牌字符串,则强......