首页
社区
课程
招聘
[原创]iOS逆向初探-变种AES加密的分析与还原
发表于: 2023-6-10 15:08 33980

[原创]iOS逆向初探-变种AES加密的分析与还原

2023-6-10 15:08
33980

从事移动安全行业以来,一直在做Android方面的安全及逆向,也曾想过了解下iOS的机制,奈何总是对自己下不了决心,一方面觉得精力有限,Android上好多东西自己也并没有完全熟练掌握。另一方面可能自己太懒,不太想花费太多时间成本,何况如果没有实操,所学的一切很快就会忘记,所以之前也仅仅是在心里埋下了这个种子而已。不过大概几周前,笔者实属有幸,因机缘巧合,向iOS逆向大佬猫大人好好请教了一番,至此也算是入了个iOS的小门。

因此本文也算是一个从Android视角来看待iOS逆向,iOS相关的深入点不会进行阐述。也仅以文本纪念下和大佬们在一起做安全逆向的时光~

从这里开始,是比较基础的东西,但是由于我也是刚刚学习到,所以就列出来记录一下,有基础可以直接跳过第二节,从第三节/或第四节阅读。

在Android上通常用于逆向的手机是pixel/nexus系列,Android系统6~13都可,然后自己刷入magisk进行root。而iOS呢肯定是iPhone了,但是如何选系统如何自己越狱呢?因此在了解相关版本后,为了方便,在某宝买了一台二手的iphone8(800左右),ios14左右的系统,商家已经帮你越狱好了(unc0ver,14系统上每次手机重启还需点击软件进行越狱,不麻烦)。

当然其他手机也可以,按照大佬的建议,iOS系统最好不要最新,像13,14左右就可以,手机的话像年头久一点的iPhone 6(大概3,4百左右),iphone8/X/SE,也都可以做逆向。切记!买手机时要问有无id锁,是否可以刷机。如果手机来源不正规,自然就会被锁住,也不方便逆向用了。

手机选好后,自然就要安装一些相关软件,就像android逆向root之后要按各种插件,比如抓包用AlwaysTrustUserCerts信任证书之类的。iOS也是一样,iOS越狱后有个Cydia的商店,里边可以下载安装各种越狱插件,包括自己写的越狱开发插件也会在这里进行管理。

常用的iOS插件:

当然以上的插件,是我在买好手机后,就已经安装好了,自己并没有额外做什么操作,除了frida。

frida:frida的安装在cydia里默认是最新的,因此可以去frida官网的release(https://github.com/frida/frida/releases)下载对应的包,这里和android不同的是,要下载deb的包,然后通过如ifunbox的工具,安装到手机的目录,然后在手机上通过Filza点击deb包进行安装。安装好后,frida由cydia进行管理,frida-server默认开启,类似Android上的MagiskFrida

1

这里记录一下遇到过的问题,及一些杂项。比如手机越狱后,发现开不开机无法进入主界面,有可能是注入的插件有问题。可以通过ssh进入手机目录:$ ssh root@127.0.0.1 -p 2222 ,默认密码是alpine。然后可以进入插件列表 cd /Library/MobileSubstrate/DynamicLibraries,这里是所有安装过的插件列表,比如我这里是这样的,也可以看到我这个二手手机可能也是用了好几年淘汰下来的

所以如果你怀疑哪个插件有问题,可以重命名这个插件,然后在上述目录重启系统进程:killall -9 SpringBoard; killall -9 backboard。

或者可以重启所有进程:ldrestart 。总之到这里,我开不开机的问题是解决了。

通过上边的知识,即便没有ios逆向基础,也可以开始准备逆向了。

由于软件要上架appstore,苹果市场是默认会对应用进行加壳的。因此我们的第一步在appstore下载好相关软件后,就可以进行脱壳。

手机上安装好软件后,电脑开启端口转发:iproxy 2222 22 。然后进入frida-ios-dump脚本的目录直接执行./dump 包名

稍等片刻,在当前目录会生成脱壳后的ipa文件。然后我们需要找到该应用的主包,以便拖入ida分析:

首先将脱壳后的.ipa文件改后缀为.zip(和Android APK一样,也是个压缩包),解压后进入Payload,会有一个.app的文件包

点击显示包内容,通常我们要拖入ida里分析的包,名字和上层xxx.app是相同的,然后就可以拖进ida,由于包比较大,ida分析时间会很慢

这里也使用class-dump将头文件导出class-dump -H ./osee2unifiedRelease.app/osee2unifiedRelease -o ./osee2unifiedReleaseH,导出头文件的作用是,方便我们查看OC中类的所有方法/属性

这里和Android没什么区别,在手机上安装charles证书,信任证书,然后抓包。我们关注下相关的登录接口

发现body是加密的,于是看看body是如何加密的。

首先在ida里搜索登录相关字符串/api/account/prod/sign_in,发现可以直接找到,查看相关交叉引用

发现很多,随便找几个先看看,都是调用了同一个函数sub_1063DF0A8(),但是奇怪的是这个函数的第二个参数/api/account/prod/sign_in,在F5里并没有看到

但是在汇编里是能看到的,我不知道这样做的目的是什么,看了很多iOS逆向的帖子也没有看到这样的情况,或者说这是ida反混淆的问题?总之,这不重要。(如果有大佬知道,烦请解答)

然后通过回溯堆栈(console.log(Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n'));),看能否定位到关键信息。

这里和Android逆向so也完全一样,稍有区别的是,iOS查找基址填入的是整个库名,如:

回溯出来堆栈之后,可以对整个堆栈链路的函数进行分析及hook,不过遗憾的是,或许是对iOS网络框架不熟,我并没有办法仅凭查找url,就能定位到加密算法。不过逆向有意思的地方也在这里,当一条路走不通了,放松下自己换一条。

我们观察body其实可以发现他是个base64,那我们大概猜一下,他使用系统库的方式。

经查资料,可以hook OC中NSData的base64EncodedStringWithOptions方法,在OC的语法中函数调用的方式可以用[类名 方法名:参数],hook的方式发现网上大多采用frida-trace。在我印象里好像没什么印象,即便有,也是听了个名词,因为在Android中我基本没用到过。于是使用这个命令进行hook,减号代表实例方法,相反加号代表类方法,只是个格式而已,也可以用*匹配

这个脚本会在当前目录生成./__handlers__/文件夹,并生成对应函数的js代码,发现其实这就是Interceptor.attach的那个回调函数,只不过frida-trace帮你自动生成好了,方便你改脚本。

当然到这里,运气也比较好,发现返回值的确可以跟抓包的body对应上,于是打堆栈

进入[ encryptDataBase64String:]函数看看,发现密钥(93020...)是写死的360位的hex字符串(hex转换为bytes后长度是180)

总体是进行了三种加密,分别是

sub_106B3E2A0 -> preDataIn160:secureKey:iv:

sub_106B1CA20 -> laesEncryptData:secureKey:iv:

sub_106B3E2E0 -> preDataOut160:secureKey:iv:

这里的_objc_msgSend是OC底层通过发送消息,来进行函数调用的,其中a1是类,第二个参数是方法名,其余是参数。我们也可以到之前class-dump出来的头文件里看看,还是很清晰的

然后我们hook这个三个函数的入参和出参,就可以得到整个从明文到密文的加密链路。当然,这里需要注意的,虽然我们还是可以使用Android frida hook的方式(基址+偏移),但是我们打印参数时,却不能脱离OC的方式。

比如我们hook 这个函数laesEncryptData,即便我们知道真正的参数从a3开始(类似OC的调用约定吧,从第三个参数开始传参),但是我们像在Android那样,仅通过hexdump是无法打印出预期的值的。打印OC有点类似打印JNI,需要使用对应的方法,比如在输出a3时,可以先使用new ObjC.Object(this.arg2) 打印下对象,如果输出的类似这种{length = 32, bytes = 0x36666161 39316535 38616339 63346661 ... 37363438 38323730 } (如是字符串类型直接能输出)就可以使用Memory.readByteArray(data.bytes(),data.length()) 来进行hexdump了,其余的没什么区别。

在我们定位好关键算法之后,通常为了测试方便,往往是需要主动调用函数的,和Android无异。比如这个app,他总是有线程在做加密,即便把网关掉了也不行,这对于我们分析输出日志是很不方便的。我们可以通过Interceptor.replace函数替换掉某个方法,而我们自己主动调用时,调其内部的方法即可。

比如,我们这里分析到laesEncryptData函数内部会调用sub_1000902A8方法,这个sub_1000902A8方法内部会调用sub_100090420这个方法,因此我们可以主动调用sub_100090420,替换掉sub_1000902A8,就可以去除干扰(另两个函数preDataIn160,preDataOut160不是核心算法,也不复杂,这里不做过多阐述)

这里还有一点和Android不一样,就是地址偏移,在iOS中,使用基址+偏移的方式hook时,ida中的地址要减去10000000。下为替换算法

当然,在这里我遇到了一点小坑,其实如上的反汇编代码是不准确的,该函数共有9个参数,根据arm64的调用约定,超过8个参数,会通过栈传递,也就是最后一个参数v10,并不是如伪代码那样直接传递的。

另外一种定位a9是如何传值的:跳转进函数后,查看a9的交叉引用,可以发现v20是个数组,最多用到了v20[7]

结合frida hook的结果

可以判断,a9是一个int数组,长度为4(int占4字节空间)*8=32(0x20)大小。

因此使用frida构造a9参数时使用:

主动调用的代码如下,其中sub_100090420这个函数的前8个参数分别的,输入/长度,输出/长度,iv/长度,key/长度,输入随便找的hook时真实的数据

至此主动调用成功,后要详细分析sub_100090420算法

在我们正式分析魔改的aes算法之前,我想应该是要介绍下aes标准算法的原理,就当是回顾下知识点,所以这一节可能会比较枯燥,不过这里还是只介绍下相关的概念,不会太深入细节。因此这一节可以粗略的过下,甚至跳过,后面的内容如果迷惑了,可以返回来看看。下面我们大概概述下标准AES算法的加密流程。

AES-128接收16字节的明文输入,16字节的密钥,输出16字节的密文结果。且每增加64位,AES-128/192/256算法的循环会增加2轮。

以AES-128为例,共加密10轮,其中包含的操作为:

AddRoundKey:轮密钥加(通过密钥编排得来,首次使用为主密钥)

其中初始变换只执行AddRoundKey,算法循环第1~9轮依次执行SubBytes,ShiftRows,MixColumns,AddRoundKey。最终轮(第10轮)不包含MixColumns。算法完毕

上边就是AES的整体流程,和要用到的知识点。

回过头来,接着看sub_100090420这个函数,确实已经脱离了OC,进入了熟悉的C环境,虽然没有混淆,但是其内部分支跳转太多,静态看起来也不是很方便。

于是我用到了Virenz大佬写好的stalker脚本,分函数trace和指令trace,格式非常清晰方便,推荐使用

分别对sub_100090420进行函数/指令trace,首先看下function trace,发现调用的函数并不多,这里先将关键函数的作用写出,后续将详细分析该算法是如何魔改aes的

通过function trace打印了函数执行流程后,可以查看密文result的交叉引用,并hook相关函数(从function trace来看并不多),打印输入输出,最终定位到了这里

看下sub_100094360这个函数

发现这个函数将result分割16个字节,每次循环首先将明文与iv异或并作为sub_100091FAC(v10)的参数,调用完后,将结果重新赋值给iv,并进行下一轮循环。

这里其实就是分组密码常见的CBC模式,因为aes也是分组密码,在进行加密之前,先将明文分组,如果不够分了,就进行相应规则填充数据。过程就是将明文分组与前一个密文分组进行XOR异或运算,首轮的话就与iv异或,上述代码ida反编译的很好了,对照下图,应该就可以理解了

接下来我们要分析核心算法sub_100091fac,先看下好像并不多

他的第一个参数和第二个参数是相同的,都是明文,在函数结束后也都变成了密文,第三个参数是密钥key,从上面来看也并不是16位的。

其实逆向到这里,我一直怀疑着,就是bangcle算法究竟把aes魔改到什么程度?虽然最外层的算法名写的是laes,而且上层函数也的确明文分组与iv异或,并且根据trace及分析来看,中间也的确是9轮循环。那么他是否仅仅改了码表而已?还是说不仅改了码表,甚至连aes内部算法也重写了?我能否对照标准的aes来还原他?以及他的key为什么是180位,而标准的aes仅仅是16位,又如何用key呢?带着这些个疑问,我开始了进入了使用trace还原算法的世界。

首先我这里还原的方式,是通过之前的指令trace日志+clion还原代码时调试一步步分析。

如上边这一段代码(1)处,把ida里的伪代码拷贝进去,并控制好和trace时一样的入参

发现这里的v3返回0x80,查看ida汇编地址,找到对应的trace结果,ida的伪码分析没错

那这里判断算法是否输出正确,有两种方式。首先是传统的方式,这个循环里最后生成的值是*(&v44 + i),而这个值最终是通过异或得来,因此我们查看ida里汇编的地址

也即w12,也就是这16次for循环的结果,因此去trace里对照1000920B8地址

第二种方式,得益于frida stalker在trace时可以定制化输出,比如在大佬的trace脚本中,我们可以将readCString()改成hexdump出了两行内容

于是我们也可以直接去trace搜整个for循环的结果4c da e9 c4 5a a1 0f 28 1e a2 01 ed 5b b6 62 b9,发现内存里有很多地方都有,也即证明了此步还原准确。

接下来,又进入了一个大循环中for ( j = 1; j < (v3 >> 5) + 6; ++j ) 因为上一步中已经还原出v3=0x80,因此手动计算下(v3 >> 5) + 6 = (0x80 >> 5) + 6 = 10。也即aes标准算法中的9轮循环。

其实上一步的算法还原,还算容易,只需要照抄ida代码即可,但是这一轮算法里,虽然看起来伪码很整洁很规律,4个一组4个一组

但我遇到了很疑惑的问题,甚至还怀疑了ida是不是有问题。

首先就是,这一部分代码里,无论是输入还是输出都是局部变量。比如像这一行还好说v28 = dword_106EF9168[v44] >> 24; v44也就是上一个算法的结果,但v28是谁呢?甚至于下一个4组v32 = dword_106EF9168[v48] >> 24; v32,v48都是局部变量,这又该如何还原呢?

首先还是先猜,最开始计算了16个字节的v44的值,那就先尝试下使用v44,于是我还原的代码如下

和之前一样去ida找地址,在trace里查结果,像这个计算查右移相关的指令lsr即可。很幸运,尝到了一丝甜头。

于是,第一个四组的计算已经成功。可是到了下一个四组

我尝试将v48认为是v44+1的值(0xda)来进行计算(v44 = 4c da e9 c4 5a a1 0f 28 1e a2 01 ed 5b b6 62 b9,最最初计算的16字节的结果,在第一组v44等于4c),但遗憾的是我得出来的值却无法与trace结果相对应

我计算出来的v32/v33是0x18/0x67,但trace的结果却是0x2d/0x37 ??? 看来事情并没有我所猜测这么简单。于是分析trace,看看值是怎么来的。

发现两个值都是从0x2d3737af偏移而来,而0x2d3737af也是魔改后码表里的值dword_106EF9168[v48]; ,也就是说真正要看的是v48如何等于5a。跟到5a最初被赋值的地方ldurb w10, [x29, #-0x14]; # x10: 0x6d --> 0x5a 发现是从x29-0x14的地方取值,按正常逻辑,只要搜,谁往[x29, #-0x14]的地方赋值就行,不过trace里搜不到。所以到这里差不多就比较懵,值跟不下去了。

于是我又换了另一种猜想,如果v44不是一个char数组呢?假设他是一个int指针,那么如果v48=*(v44+1),那么v48的值应该是v44往后偏移4个字节,于是查看完整的v44: 4c da e9 c4 5a a1 0f 28 1e a2 01 ed 5b b6 62 b9,第一个值是4c没问题,第一个四组验证过了。如果按照刚刚的猜想,往后偏移4个字节,那么v48应该是?5a!发现对上了!那赶紧趁热打铁,验证接下来的两个值是不是1e和5b,也就是v49=1e,v50=5b。查看下trace

漂亮!那么到这里我心中大概有点数了,接下来的4组应该是继续从下一个偏移0xda开始,然后分别使用da,a1,a2,b6。也即他把这个16字节的数组"立"了过来

然后继续验证,发现猜想中本应是da的值,但是却变成了a1?本应是a1的值,却变成了a2?

咦?这难道,就是AES的行移位算法?!没错

我们最初介绍了aes的标准流程时,提到了aes的内部小算法,这个就是行移位算法。aes将16个字节先看成是一个4*4的矩阵,然后分别对矩阵进行变化,所谓的行移位算法也是固定的一种模式,如下图

也就是说,我们这16个字节真正的使用方式是,先进行ShiftRows行移位,然后在进行SubBytes字节替换(魔改码表里取值),这也是bangcle_laes的一个混合小算法。

那么我们还原算法时,就要自己写一个行移位了,而之前猜想v44是一个int指针也完全不对,他仍是一个char指针,只不过取值之前,已经对里边的内容进行了变换!

其次在将行移位后的矩阵进行转置。

因此,我们还原算法时,就可以按照标准aes那样,先将16字节转成一个4*4的矩阵,然后对矩阵进行行移位操作等变换。

最终在内存里的格式变换为:

还原过程中最困难的部分已经完成,其余部分按照之前的思路也都可以对照,结果不对就跟trace分析。

至此,9轮循环里的混合算法还原完毕。

其实到这里,虽说算法还原成功,但是过程却极其艰难。我也抱怨过ida里为什么不把算法的过程表现出来呢?看来还是ida反汇编有问题?这确实是我当时的疑惑。后来与Virenz大佬讨论一番,发现并不是ida没有表现,而是因为你并不理解ida的"想法"。

回过头来看,发现ida早已清清楚楚的告诉了你,虽然他不能精准的将代码全部还原,仅仅以一些局部变量表示。但是他会告诉你他反汇编的内在逻辑。比如上图中可分为两块,v28~v43这16个字段是顺序的,通过结果来看其在内存中也是连续的,也即可以表示为一个数组。

再比如v44,v48,v52这些码表里的索引值,也都清清楚楚告诉你他们的关系,仔细观察的话,其实是可以看出行移位的,内存值里为:

上图第1块里取的4个索引为,v44,v48,v52,v56

到了第2块里取的4个索引为,v49,v53,v57,v45

可以明显发现,的确进行了行移位操作。如果还原时能了解这一点,可能就不用费劲追trace,或许只用看也能看出大概了。

通过上边的分析,这里其实也大差不差,唯一有些注意的点就是看好每一步小算法的入参出参,也就是谁进行了运算,又返回给了谁。

最后我们对照下完整算法的返回结果:

还是得益于frida trace时的定制化,我们可以直接在结果中搜即可

也就证明了算法还原成功。

通过上边的分析,发现这个bangcle的AES魔改的很厉害,基本就是一个AES的架子,内部已经完全混乱了。其次还有他的密钥key我们还没有分析,也在这里说明下。我们都知道,正常的AES key是16位的,他的主要作用就是在AES算法中进行AddRoundKey(轮密钥加)的过程。

AddRoundKey的算法就是将16字节的“输入”与16字节子密钥进行异或得到输出数据,而子密钥的获取是通过密钥拓展编排算法得来(密钥编排算法就不做过多介绍,较复杂)。从之前的AES算法流程中也可以看到,从初始变换到10轮加密计算,总共用到了11次AddRoundKey,也就是说,密钥扩展编排后,总共会占11*16=176个字节的内存空间。

而bangcle的AES原本传入的密钥就是180位的,也可以说,他把密钥编排的算法前移了。那可能有小伙伴就问了,你不是说密钥编排后,总共是176位吗,那多出来的4位呢?其实在进行加密算法前,他也对密钥key进行了处理。我们看下相关的计算

可以清楚的看到,他key的前4位( key[i%3] )实际上是用于"解密"后边176位的密钥,也就是说原始的key实际是加密(异或)过的。这样做的目的,我也只是有个猜想,那就是他解密后的176字节的key真的是用密钥编排算法算出来的,而不是没有规则的key。因为密钥编排算法编出来的子密钥,实际上是能逆推出主密钥的,有兴趣的小伙伴可以去了解下DFA差分故障攻击的原理,也是会用到这一点。

至此,我们完整的分析并还原了魔改的aes算法,想必如果这个算法再加了混淆,难度可想而知。

到这里,本文也已经结束了,也许各位已经看的很累了,但总之还是希望对你有所帮助!本文的样本相信仔细看的小伙伴都能看出是哪个app,想练手的话就在AppStore下载最新版就行。

最后感谢观看,谢谢!

参考:

https://github.com/Virenz/frida-js

https://blog.csdn.net/u012620515/article/details/49634749

 
 
 
 
 
 
./dump.py com.xxx
Dumping xxx to /var/folders/rl/6nvyvpmj3z352q0m8xvm0db40000gn/T
[frida-ios-dump]: ZmFFmpeg.framework has been loaded.
[frida-ios-dump]: libswift_Concurrency.dylib has been dlopen.
...
libswift_Concurrency.dylib.fid: 100%|█████████████████████████████| 408k/408k [00:00<00:00, 5.97MB/s]
Validated.plist: 251MB [00:14, 18.2MB/s]                                                            
0.00B [00:00, ?B/s]Generating "xxx.ipa"
./dump.py com.xxx
Dumping xxx to /var/folders/rl/6nvyvpmj3z352q0m8xvm0db40000gn/T
[frida-ios-dump]: ZmFFmpeg.framework has been loaded.
[frida-ios-dump]: libswift_Concurrency.dylib has been dlopen.
...
libswift_Concurrency.dylib.fid: 100%|█████████████████████████████| 408k/408k [00:00<00:00, 5.97MB/s]
Validated.plist: 251MB [00:14, 18.2MB/s]                                                            
0.00B [00:00, ?B/s]Generating "xxx.ipa"
 
 
 
 
 
 
 
 
0x106913598 osee2unifiedRelease!0x63d7598 (0x1063d7598)
0x1069131fc osee2unifiedRelease!0x63d71fc (0x1063d71fc)
0x1068e27d0 osee2unifiedRelease!0x63a67d0 (0x1063a67d0)
0x10690767c osee2unifiedRelease!0x63cb67c (0x1063cb67c)
0x1020724bc osee2unifiedRelease!0x1b364bc (0x101b364bc)
0x10207256c osee2unifiedRelease!0x1b3656c (0x101b3656c)
0x102061e10 osee2unifiedRelease!0x1b25e10 (0x101b25e10)
0x100608f90 osee2unifiedRelease!0xccf90 (0x1000ccf90)
0x1a0ec1298 libdispatch.dylib!_dispatch_call_block_and_release
0x106913598 osee2unifiedRelease!0x63d7598 (0x1063d7598)
0x1069131fc osee2unifiedRelease!0x63d71fc (0x1063d71fc)
0x1068e27d0 osee2unifiedRelease!0x63a67d0 (0x1063a67d0)
0x10690767c osee2unifiedRelease!0x63cb67c (0x1063cb67c)
0x1020724bc osee2unifiedRelease!0x1b364bc (0x101b364bc)
0x10207256c osee2unifiedRelease!0x1b3656c (0x101b3656c)
0x102061e10 osee2unifiedRelease!0x1b25e10 (0x101b25e10)
0x100608f90 osee2unifiedRelease!0xccf90 (0x1000ccf90)
0x1a0ec1298 libdispatch.dylib!_dispatch_call_block_and_release
var base = Module.getBaseAddress("osee2unifiedRelease");
console.log("base: ",base);
var base = Module.getBaseAddress("osee2unifiedRelease");
console.log("base: ",base);
 
frida-trace -UF -m "-[NSData base64EncodedStringWithOptions:]"
frida-trace -UF -m "-[NSData base64EncodedStringWithOptions:]"
{
  onEnter(log, args, state) {
    this.self = args[0];
  },
  onLeave(log, retval, state) {
    var before = ObjC.classes.NSString.alloc().initWithData_encoding_(this.self, 4);
    var after = new ObjC.Object(retval);
    log(`-[NSData base64EncodedStringWithOptions:]before=${before}=`);
    log(`-[NSData base64EncodedStringWithOptions:]after=${after}=`);
    if(after.toString().indexOf("sEn8t")>=0){
      console.log(Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n'));
    }
  }
}
{
  onEnter(log, args, state) {
    this.self = args[0];
  },
  onLeave(log, retval, state) {
    var before = ObjC.classes.NSString.alloc().initWithData_encoding_(this.self, 4);
    var after = new ObjC.Object(retval);
    log(`-[NSData base64EncodedStringWithOptions:]before=${before}=`);
    log(`-[NSData base64EncodedStringWithOptions:]after=${after}=`);
    if(after.toString().indexOf("sEn8t")>=0){
      console.log(Thread.backtrace(this.context, Backtracer.ACCURATE) .map(DebugSymbol.fromAddress).join('\n'));
    }
  }
}
0x102e0a7d8 osee2unifiedRelease!+[ZHWhiteBoxEncryptTool encryptDataBase64String:]
0x102e0a6b8 osee2unifiedRelease!+[ZHWhiteBoxEncryptTool encryptData:]
0x10444b098 osee2unifiedRelease!+[NSURLRequest zh_whiteBoxEncryptRegisterLoginURLHTTPBody:]
0x10447c3f0 osee2unifiedRelease!+[ZHURLProtocol canonicalRequestForRequest:]
0x1a1863ffc CFNetwork!0x3ffc (0x180a47ffc)
...
0x102e0a7d8 osee2unifiedRelease!+[ZHWhiteBoxEncryptTool encryptDataBase64String:]
0x102e0a6b8 osee2unifiedRelease!+[ZHWhiteBoxEncryptTool encryptData:]
0x10444b098 osee2unifiedRelease!+[NSURLRequest zh_whiteBoxEncryptRegisterLoginURLHTTPBody:]
0x10447c3f0 osee2unifiedRelease!+[ZHURLProtocol canonicalRequestForRequest:]
0x1a1863ffc CFNetwork!0x3ffc (0x180a47ffc)
...
 
 
 
void *__fastcall sub_106B3E2A0(void *a1, void *a2, void *a3, void *a4, void *a5)
{
  return _objc_msgSend(a1, "preDataIn160:secureKey:iv:", a3, a4, a5);
}
void *__fastcall sub_106B3E2A0(void *a1, void *a2, void *a3, void *a4, void *a5)
{
  return _objc_msgSend(a1, "preDataIn160:secureKey:iv:", a3, a4, a5);
}
void *__fastcall sub_106B1CA20(void *a1, void *a2, void *a3, void *a4, void *a5)
{
  return _objc_msgSend(a1, "laesEncryptData:secureKey:iv:", a3, a4, a5);
}
void *__fastcall sub_106B1CA20(void *a1, void *a2, void *a3, void *a4, void *a5)
{
  return _objc_msgSend(a1, "laesEncryptData:secureKey:iv:", a3, a4, a5);
}
void *__fastcall sub_106B3E2E0(void *a1, void *a2, void *a3, void *a4, void *a15)
{
  return _objc_msgSend(a1, "preDataOut160:secureKey:iv:", a3, a4, a15);
}
void *__fastcall sub_106B3E2E0(void *a1, void *a2, void *a3, void *a4, void *a15)
{
  return _objc_msgSend(a1, "preDataOut160:secureKey:iv:", a3, a4, a15);
}
 
 
 
id __cdecl +[BangcleCryptoTool laesEncryptData:secureKey:iv:](BangcleCryptoTool_meta *self, SEL a2, id a3, id a4, id a5)
id __cdecl +[BangcleCryptoTool laesEncryptData:secureKey:iv:](BangcleCryptoTool_meta *self, SEL a2, id a3, id a4, id a5)
 
__int64 __fastcall sub_1000902A8(__int64 a1, unsigned int a2, __int64 a3, __int64 a4, __int64 a5, unsigned int a6, __int64 a7, unsigned int a8, int a9)
{
  v24 = a1;
  v23 = a2;
  v22 = a3;
  v21 = a4;
  v20 = a5;
  v19 = a6;
  a7a = a7;
  a8a = a8;
  v16 = a9;
  LODWORD(v10) = 1;
  HIDWORD(v10) = 4;
  v14 = 1;
  v15 = a9;
  v12 = 1;
  v13 = 0;
  v11 = 0;
  return sub_100090420(a1, a2, a3, (int *)a4, a5, a6, a7, a8, &v10);
}
__int64 __fastcall sub_1000902A8(__int64 a1, unsigned int a2, __int64 a3, __int64 a4, __int64 a5, unsigned int a6, __int64 a7, unsigned int a8, int a9)
{
  v24 = a1;
  v23 = a2;
  v22 = a3;
  v21 = a4;
  v20 = a5;
  v19 = a6;
  a7a = a7;
  a8a = a8;
  v16 = a9;
  LODWORD(v10) = 1;
  HIDWORD(v10) = 4;
  v14 = 1;
  v15 = a9;
  v12 = 1;
  v13 = 0;
  v11 = 0;
  return sub_100090420(a1, a2, a3, (int *)a4, a5, a6, a7, a8, &v10);
}
function replace(){
    var base = Module.getBaseAddress("osee2unifiedRelease");
    Interceptor.replace(base.add(0x902A8),new NativeCallback(function(a,b,c,d,e,f,g,h,i){
        return 0;
    },'int',['pointer','int','pointer','int','pointer','int','pointer','int','pointer']));
}
function replace(){
    var base = Module.getBaseAddress("osee2unifiedRelease");
    Interceptor.replace(base.add(0x902A8),new NativeCallback(function(a,b,c,d,e,f,g,h,i){
        return 0;
    },'int',['pointer','int','pointer','int','pointer','int','pointer','int','pointer']));
}
__text:00000001000902A8 sub_1000902A8
__text:00000001000902A8                 STP             X29, X30, [SP,#-0x10]!
__text:00000001000902AC                 MOV             X29, SP
__text:00000001000902B0                 SUB             SP, SP, #0x70
__text:00000001000902B4                 LDR             W8, [X29,#0x10]
__text:00000001000902B8                 ADD             X9, SP, #0x10 ; 赋值给x9,作为参数传递,后面又将x9赋值给sp
__text:00000001000902BC                 MOV             W10, #1;这个a9的一个参数
__text:00000001000902C0                 MOV             W11, #4;这也是一个参数
__text:00000001000902C4                 STUR            X0, [X29,#-8]
__text:00000001000902C8                 STUR            W1, [X29,#-0xC]
__text:00000001000902CC                 STUR            X2, [X29,#-0x18]
__text:00000001000902D0                 STUR            X3, [X29,#-0x20]
__text:00000001000902D4                 STUR            X4, [X29,#-0x28]
__text:00000001000902D8                 STUR            W5, [X29,#-0x2C]
__text:00000001000902DC                 STR             X6, [SP,#0x38]
__text:00000001000902E0                 STR             W7, [SP,#0x34]
__text:00000001000902E4                 STR             W8, [SP,#0x30]
__text:00000001000902E8                 STR             W10, [SP,#0x10] ; a11 传递到栈上 ,也就是传到x9上
__text:00000001000902EC                 STR             W11, [SP,#0x14]
__text:00000001000902F0                 STR             W10, [SP,#0x28] ; 传递到栈上
__text:00000001000902F4                 LDR             W8, [SP,#0x30]
__text:00000001000902F8                 STR             W8, [SP,#0x2C]
__text:00000001000902FC                 STR             W10, [SP,#0x20]; 传递到栈上
__text:0000000100090300                 STR             WZR, [SP,#0x24]
__text:0000000100090304                 STR             WZR, [SP,#0x18]
__text:0000000100090308                 LDUR            X0, [X29,#-8]
__text:000000010009030C                 LDUR            W1, [X29,#-0xC]
__text:0000000100090310                 LDUR            X2, [X29,#-0x18]
__text:0000000100090314                 LDUR            X3, [X29,#-0x20]
__text:0000000100090318                 LDUR            X4, [X29,#-0x28]
__text:000000010009031C                 LDUR            W5, [X29,#-0x2C]
__text:0000000100090320                 LDR             X6, [SP,#0x38] ; a7
__text:0000000100090324                 LDR             W7, [SP,#0x34] ; a8
__text:0000000100090328                 STR             X9, [SP] ; a9 a9参数通过sp传递
__text:000000010009032C                 BL              sub_100090420
__text:0000000100090330                 MOV             SP, X29
__text:0000000100090334                 LDP             X29, X30, [SP+var_s0],#0x10
__text:0000000100090338                 RET
__text:00000001000902A8 sub_1000902A8
__text:00000001000902A8                 STP             X29, X30, [SP,#-0x10]!
__text:00000001000902AC                 MOV             X29, SP
__text:00000001000902B0                 SUB             SP, SP, #0x70
__text:00000001000902B4                 LDR             W8, [X29,#0x10]
__text:00000001000902B8                 ADD             X9, SP, #0x10 ; 赋值给x9,作为参数传递,后面又将x9赋值给sp
__text:00000001000902BC                 MOV             W10, #1;这个a9的一个参数
__text:00000001000902C0                 MOV             W11, #4;这也是一个参数
__text:00000001000902C4                 STUR            X0, [X29,#-8]
__text:00000001000902C8                 STUR            W1, [X29,#-0xC]
__text:00000001000902CC                 STUR            X2, [X29,#-0x18]
__text:00000001000902D0                 STUR            X3, [X29,#-0x20]
__text:00000001000902D4                 STUR            X4, [X29,#-0x28]
__text:00000001000902D8                 STUR            W5, [X29,#-0x2C]
__text:00000001000902DC                 STR             X6, [SP,#0x38]
__text:00000001000902E0                 STR             W7, [SP,#0x34]
__text:00000001000902E4                 STR             W8, [SP,#0x30]
__text:00000001000902E8                 STR             W10, [SP,#0x10] ; a11 传递到栈上 ,也就是传到x9上
__text:00000001000902EC                 STR             W11, [SP,#0x14]
__text:00000001000902F0                 STR             W10, [SP,#0x28] ; 传递到栈上
__text:00000001000902F4                 LDR             W8, [SP,#0x30]
__text:00000001000902F8                 STR             W8, [SP,#0x2C]
__text:00000001000902FC                 STR             W10, [SP,#0x20]; 传递到栈上
__text:0000000100090300                 STR             WZR, [SP,#0x24]
__text:0000000100090304                 STR             WZR, [SP,#0x18]
__text:0000000100090308                 LDUR            X0, [X29,#-8]
__text:000000010009030C                 LDUR            W1, [X29,#-0xC]
__text:0000000100090310                 LDUR            X2, [X29,#-0x18]
__text:0000000100090314                 LDUR            X3, [X29,#-0x20]
__text:0000000100090318                 LDUR            X4, [X29,#-0x28]
__text:000000010009031C                 LDUR            W5, [X29,#-0x2C]
__text:0000000100090320                 LDR             X6, [SP,#0x38] ; a7
__text:0000000100090324                 LDR             W7, [SP,#0x34] ; a8
__text:0000000100090328                 STR             X9, [SP] ; a9 a9参数通过sp传递
__text:000000010009032C                 BL              sub_100090420
__text:0000000100090330                 MOV             SP, X29
__text:0000000100090334                 LDP             X29, X30, [SP+var_s0],#0x10
__text:0000000100090338                 RET
 
 
 
 
 
    var dword = Memory.alloc(32);
Memory.writeUInt(dword,1);
Memory.writeUInt(dword.add(4),4);
Memory.writeUInt(dword.add(4*2),0);
Memory.writeUInt(dword.add(4*3),1);
Memory.writeUInt(dword.add(4*4),1);
Memory.writeUInt(dword.add(4*5),0);
Memory.writeUInt(dword.add(4*6),1);
Memory.writeUInt(dword.add(4*7),1);
    var dword = Memory.alloc(32);
Memory.writeUInt(dword,1);
Memory.writeUInt(dword.add(4),4);
Memory.writeUInt(dword.add(4*2),0);
Memory.writeUInt(dword.add(4*3),1);
Memory.writeUInt(dword.add(4*4),1);
Memory.writeUInt(dword.add(4*5),0);
Memory.writeUInt(dword.add(4*6),1);
Memory.writeUInt(dword.add(4*7),1);
function call_aes(){
    var base = Module.getBaseAddress("osee2unifiedRelease");
    console.log("base: ",base);
    var aes = new NativeFunction(base.add(0x90420),'int',['pointer','int','pointer','pointer','pointer','int','pointer','int','pointer']);
    //输入
    var data_len = 0x20;
    const data = Memory.alloc(data_len);
    Memory.writeByteArray(data,[0xca,0xcc,0x6e,0x68,0x64,0x63,0xc6,0x6e,0x60,0xc2,0x66,0xc4,0xc8,0x6c,0xc4,0xc6,0xca,0xc2,0x60,0xc4,0x6c,0x64,0x61,0x61,0x61,0xc4,0xc6,0xc4,0xc2,0xc8,0x6c,0x62]);
 
    //输出:空的byte数组,函数返回后,有值
    var result_len = 16 * (data_len / 16 + 1);
    var result = Memory.alloc(result_len);
    var result_len_ptr = Memory.alloc(Process.pointerSize);
    result_len_ptr.writeUInt(result_len);
 
    //iv
    var iv_len = 0x10;
    const iv =  Memory.alloc(iv_len);
    Memory.writeByteArray(iv,[0x4c,0x41,0xb2,0xc9,0xb4,0xba,0xff,0x8a,0x6a,0x69,0xa5,0x99,0x02,0x5f,0x03,0x15]);
 
    //key
    var key_len = 0xb4; //长度180
    const key =  Memory.alloc(key_len);
    Memory.writeByteArray(key,[0x93,0x02,0x01,0x9f,0xbf,0xa1,0xbb,0x6b,0xdb,0x9f,0xca,0x46,0x84,0xb3,0xe7,0xf6,0x38,0x30,0x44,0x18,0x14,0x06,0x35,0x60,0x29,0x7e,0x4f,0x00,0xde,0x63,0x69,0x41,0x66,0x4f,0x7e,0xa3,0x94,0x29,0xb2,0x60,0x4e,0x4f,0x93,0xa7,0x84,0x0e,0xcf,0x12,0x54,0xcb,0xa8,0xd9,0xea,0x29,0xcd,0xf4,0xf7,0xe4,0x01,0x97,0xb5,0x0d,0xf7,0x7e,0x19,0xfb,0x07,0xf2,0xf9,0x74,0xe7,0x87,0xcf,0x87,0x32,0xa6,0x2a,0x1e,0x2e,0x0f,0xcb,0xfa,0x2a,0xcb,0xac,0x63,0x76,0xc8,0x32,0xc0,0x82,0x39,0xa0,0xb5,0xd9,0xe0,0xe7,0x06,0xeb,0x27,0xb8,0x31,0xe5,0xef,0xfc,0xdb,0x3d,0x00,0x08,0x7e,0x62,0xa6,0x02,0x92,0x31,0xf6,0x4a,0x2b,0x30,0x99,0x72,0x07,0x59,0xe3,0x1f,0x9d,0xfa,0x12,0x8b,0xc7,0xe9,0x6a,0x83,0xd7,0x1a,0xf7,0x9a,0xa4,0x89,0xb9,0xe5,0x6f,0xfd,0xd5,0xe2,0xf1,0x42,0xa3,0xf9,0xac,0x11,0xe4,0xab,0xce,0x01,0xc6,0xf2,0xfb,0xca,0x01,0xb7,0x59,0xac,0x84,0x2f,0x14,0x91,0xa1,0xa5,0x8d,0x74,0xea,0xdd,0x2b,0x38,0x09,0x1e,0xb8,0x21,0x16])
        //最后一个参数
    var dword = Memory.alloc(32);
    Memory.writeUInt(dword,1);
    Memory.writeUInt(dword.add(4),4);
    Memory.writeUInt(dword.add(4*2),0);
    Memory.writeUInt(dword.add(4*3),1);
    Memory.writeUInt(dword.add(4*4),1);
    Memory.writeUInt(dword.add(4*5),0);
    Memory.writeUInt(dword.add(4*6),1);
    Memory.writeUInt(dword.add(4*7),1);
        //主动调用
    var aes_r = aes(data,data_len,result,result_len_ptr,iv,iv_len,key,key_len,dword);
    console.log("aes_r",aes_r,hexdump(result,{length:result_len}));
}
function call_aes(){
    var base = Module.getBaseAddress("osee2unifiedRelease");
    console.log("base: ",base);
    var aes = new NativeFunction(base.add(0x90420),'int',['pointer','int','pointer','pointer','pointer','int','pointer','int','pointer']);
    //输入
    var data_len = 0x20;
    const data = Memory.alloc(data_len);
    Memory.writeByteArray(data,[0xca,0xcc,0x6e,0x68,0x64,0x63,0xc6,0x6e,0x60,0xc2,0x66,0xc4,0xc8,0x6c,0xc4,0xc6,0xca,0xc2,0x60,0xc4,0x6c,0x64,0x61,0x61,0x61,0xc4,0xc6,0xc4,0xc2,0xc8,0x6c,0x62]);
 
    //输出:空的byte数组,函数返回后,有值
    var result_len = 16 * (data_len / 16 + 1);
    var result = Memory.alloc(result_len);
    var result_len_ptr = Memory.alloc(Process.pointerSize);
    result_len_ptr.writeUInt(result_len);
 
    //iv
    var iv_len = 0x10;
    const iv =  Memory.alloc(iv_len);
    Memory.writeByteArray(iv,[0x4c,0x41,0xb2,0xc9,0xb4,0xba,0xff,0x8a,0x6a,0x69,0xa5,0x99,0x02,0x5f,0x03,0x15]);
 
    //key
    var key_len = 0xb4; //长度180
    const key =  Memory.alloc(key_len);
    Memory.writeByteArray(key,[0x93,0x02,0x01,0x9f,0xbf,0xa1,0xbb,0x6b,0xdb,0x9f,0xca,0x46,0x84,0xb3,0xe7,0xf6,0x38,0x30,0x44,0x18,0x14,0x06,0x35,0x60,0x29,0x7e,0x4f,0x00,0xde,0x63,0x69,0x41,0x66,0x4f,0x7e,0xa3,0x94,0x29,0xb2,0x60,0x4e,0x4f,0x93,0xa7,0x84,0x0e,0xcf,0x12,0x54,0xcb,0xa8,0xd9,0xea,0x29,0xcd,0xf4,0xf7,0xe4,0x01,0x97,0xb5,0x0d,0xf7,0x7e,0x19,0xfb,0x07,0xf2,0xf9,0x74,0xe7,0x87,0xcf,0x87,0x32,0xa6,0x2a,0x1e,0x2e,0x0f,0xcb,0xfa,0x2a,0xcb,0xac,0x63,0x76,0xc8,0x32,0xc0,0x82,0x39,0xa0,0xb5,0xd9,0xe0,0xe7,0x06,0xeb,0x27,0xb8,0x31,0xe5,0xef,0xfc,0xdb,0x3d,0x00,0x08,0x7e,0x62,0xa6,0x02,0x92,0x31,0xf6,0x4a,0x2b,0x30,0x99,0x72,0x07,0x59,0xe3,0x1f,0x9d,0xfa,0x12,0x8b,0xc7,0xe9,0x6a,0x83,0xd7,0x1a,0xf7,0x9a,0xa4,0x89,0xb9,0xe5,0x6f,0xfd,0xd5,0xe2,0xf1,0x42,0xa3,0xf9,0xac,0x11,0xe4,0xab,0xce,0x01,0xc6,0xf2,0xfb,0xca,0x01,0xb7,0x59,0xac,0x84,0x2f,0x14,0x91,0xa1,0xa5,0x8d,0x74,0xea,0xdd,0x2b,0x38,0x09,0x1e,0xb8,0x21,0x16])
        //最后一个参数
    var dword = Memory.alloc(32);
    Memory.writeUInt(dword,1);
    Memory.writeUInt(dword.add(4),4);
    Memory.writeUInt(dword.add(4*2),0);
    Memory.writeUInt(dword.add(4*3),1);
    Memory.writeUInt(dword.add(4*4),1);
    Memory.writeUInt(dword.add(4*5),0);
    Memory.writeUInt(dword.add(4*6),1);
    Memory.writeUInt(dword.add(4*7),1);
        //主动调用
    var aes_r = aes(data,data_len,result,result_len_ptr,iv,iv_len,key,key_len,dword);
    console.log("aes_r",aes_r,hexdump(result,{length:result_len}));
}
 
 
 
 
 
 
function trace:
[函数地址]([调用地址]) -- 调用层级
 [0x100091054]( [0x10009047c] ) -- 0 //1. 密钥编排后的处理
  [0x106a9b340]( [0x100091074] ) -- 1     //malloc
 [0x100091b3c]( [0x100090520] ) -- 0
 [0x100091bcc]( [0x100090548] ) -- 0
 [0x106a9a530]( [0x100090614] ) -- 0    //calloc
 [0x106a9a1e8]( [0x10009062c] ) -- 0    //memcpy
 [0x100091c7c]( [0x100090664] ) -- 0    //类似pkcs填充
  [0x106a9a1f4]( [0x100091cf8] ) -- 1     //使用memset进行填充
  [0x106a9a1f4]( [0x100091d6c] ) -- 1        //使用memset进行填充
 [0x100094360]( [0x100090b18] ) -- 0    //2.关键函数,CBC模式,明文异或
  [0x100091fac]( [0x10009440c] ) -- 1 //3.真正魔改aes的加密,测试时输出明文为32个字节,通过填充后,输出为48个字节,且aes128每轮循环加密16字节,故48/16=30x100091fac函数循环3
  [0x100091fac]( [0x10009440c] ) -- 1
  [0x100091fac]( [0x10009440c] ) -- 1
 [0x106a9aaa0]( [0x100091038] ) -- 0    //free
 [0x100091ef4]( [0x100091040] ) -- 0
  [0x106a9aaa0]( [0x100091f18] ) -- 1        //free
function trace:
[函数地址]([调用地址]) -- 调用层级
 [0x100091054]( [0x10009047c] ) -- 0 //1. 密钥编排后的处理
  [0x106a9b340]( [0x100091074] ) -- 1     //malloc
 [0x100091b3c]( [0x100090520] ) -- 0
 [0x100091bcc]( [0x100090548] ) -- 0
 [0x106a9a530]( [0x100090614] ) -- 0    //calloc
 [0x106a9a1e8]( [0x10009062c] ) -- 0    //memcpy
 [0x100091c7c]( [0x100090664] ) -- 0    //类似pkcs填充
  [0x106a9a1f4]( [0x100091cf8] ) -- 1     //使用memset进行填充
  [0x106a9a1f4]( [0x100091d6c] ) -- 1        //使用memset进行填充
 [0x100094360]( [0x100090b18] ) -- 0    //2.关键函数,CBC模式,明文异或
  [0x100091fac]( [0x10009440c] ) -- 1 //3.真正魔改aes的加密,测试时输出明文为32个字节,通过填充后,输出为48个字节,且aes128每轮循环加密16字节,故48/16=30x100091fac函数循环3
  [0x100091fac]( [0x10009440c] ) -- 1
  [0x100091fac]( [0x10009440c] ) -- 1
 [0x106a9aaa0]( [0x100091038] ) -- 0    //free
 [0x100091ef4]( [0x100091040] ) -- 0
  [0x106a9aaa0]( [0x100091f18] ) -- 1        //free
 
 
__int64 __fastcall sub_100094360(__int64 a1, __int64 a2, int a3, __int64 a4, __int64 a5, void (__fastcall *a6)(__int64, __int64, __int64, unsigned int *))
{
  __int64 iv_1; // [xsp+0h] [xbp-40h]
  signed int i; // [xsp+8h] [xbp-38h]
  unsigned int v9; // [xsp+Ch] [xbp-34h]
  void (__fastcall *v10)(__int64, __int64, __int64); // [xsp+10h] [xbp-30h]
  __int64 keyptr; // [xsp+18h] [xbp-28h]
  __int64 iv; // [xsp+20h] [xbp-20h]
  int result_len; // [xsp+2Ch] [xbp-14h]
  __int64 result; // [xsp+30h] [xbp-10h]
  __int64 data; // [xsp+38h] [xbp-8h]
 
  data = a1;
  result = a2;
  result_len = a3;
  iv = a4;
  keyptr = a5;
  v10 = a6;
  v9 = 0;
  iv_1 = a4;
  while ( result_len >= 16 )
  {
    for ( i = 0; i < 16; ++i )
      *(result + i) = *(data + i) ^ *(iv_1 + i);
    (v10)(result, result, keyptr, &v9); // aes加密算法
    iv_1 = result;
    result_len -= 16;
    data += 16LL;
    result += 16LL;
  }
  return v9;
}
__int64 __fastcall sub_100094360(__int64 a1, __int64 a2, int a3, __int64 a4, __int64 a5, void (__fastcall *a6)(__int64, __int64, __int64, unsigned int *))
{
  __int64 iv_1; // [xsp+0h] [xbp-40h]
  signed int i; // [xsp+8h] [xbp-38h]

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-6-10 15:27 被SilverBullet编辑 ,原因:
上传的附件:
收藏
免费 13
支持
分享
最新回复 (9)
雪    币: 3098
活跃值: (4222)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
tql
2023-6-10 18:22
0
雪    币: 3535
活跃值: (31011)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢分享
2023-6-10 22:06
0
雪    币: 167
活跃值: (966)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
Tql
2023-6-12 09:05
0
雪    币: 626
活跃值: (3926)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
Tql
2023-6-12 11:06
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
真的强,看的迷迷糊糊
2023-8-16 10:52
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
感谢分享,tql!
2023-11-23 11:34
0
雪    币: 535
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
感谢分享!!
2024-6-21 18:43
0
雪    币: 2208
活跃值: (3350)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9

大佬,这里的F5看不到字符串,反而是一串很大的数值,你有深入分析为什么吗?我在学习ios的时候,还遇到很多方法的参数也是0x..比较大的值,无法hexdump打印,好像也不是oc类型,还伴随了很多switf,想请教请教

2024-9-30 13:44
1
雪    币: 22
活跃值: (110)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
mark,学习
2024-10-15 14:18
0
游客
登录 | 注册 方可回帖
返回
//