某笔记app深度混淆魔改md5逆向思路
前言看标题大家都知道是哪个软件了由于特殊原因本文作为发布各平台的免费文章因为传播性会更广 所以不会去写那么直白的而且本文也重点讲其中最核心的魔改md5算法的逆向思路文中会附带我自己开发的逆向工具使用会的朋友可以选择跳过本文了当然你也可以看看我的分析方法估计和你的会不太一样这里的样本是9.24.0是最新版不过初步看和其他版本差不多除非使用太老的版本的话好像说没有魔改md5这个我不是很清楚了没有验证。我希望通过这个文章能够给大家讲明白魔改md5的一种通用打法吧因为这个案例魔改点位非常的多而且还经过了定制化的ollvm以前的方法基本都失效了基本上也别指望ida了老老实实看trace了但是看trace还不让你省心因为存在一个hmac这会导致交叉多轮md5。。。 这时候你跟trace一定要跟紧了。参数逆向整体流程shie** 参数 在哪生成怎么hook到的首先抓包看一下看一下ua如果有什么okhttp那么大概率是okhttp框架如果是http协议也可以默认大概率是如果抓不到或者是其他情况就需要小心其他协议或者其他框架了那okhttp又怎么hook呢这里需要知道一些开发知识首先这个header参数是很多请求都有的那么他大概了不会针对某一个请求来添加header参数而是用一套可以复用的逻辑来添加header;这在okhttp中就是典型的拦截器了举一个生活化的案例处理污水污水流经某一个管道你加了点试剂让他沉淀这样流经的所有污水都会被处理这在okhttp框架中就是拦截器机制这个机制太常见了常见到你在很多开源工具都能看见本质统一处理链Interceptor Pipeline那我们怎么定位他某一个拦截器添加了header呢你得先 hook 到他添加了哪些拦截器然后在去 hook 这些拦截器的实际拦截函数然后进入和退出的时候打印一下header也可以做个diff对比这样你可以知道 拦截器1加了shie** 拦截器2加了x-mini-sig效果图如下2. 怎么用unidbg来补在native层这个可以用unidbg来补主要难点就是okhttp框架的一堆东西这个可以在unidbg的pom.xml中引入okhttp框架往上文章太多了而这个案例呢要用unidbg补的话你最好是利用jnitrace看日志然后补不会补可以看网上文章或者直接丢给ai来补最后补出来会在某个jni日志看到shie** 这个过程需要一定的耐心3. 关键数据 切分多部份这里我就省略了前期的部分字节的逆向过程了主要就是1683 字节后面83字节是什么rc4得到的然后83字节中又有好几部分其中最后一部分是16字节是魔改md54.魔改MD5特点初始变量改了, 不过只是部分交换但是要怎么确保用的是对的64轮的函数由于恶心的ollvm导致完全展开ida是真的基本看不了一点T表全换可以从trace中定位偏移规律然后找到存在ida中的位置然后写ida脚本 dump出来也可以找trace规律写正则脚本提取T表轮间乱序但T表dump出来的还不行中间md5的64轮 某些轮次比如39和40之间还有交换所以你会看到T表乱飞的情况要同步改这个乱飞的过程实际上是很恶心的如果你只是简单交换T表是不行的因为他的abcd也有不同, 所以有的时候要用展开的md5写法才行整个md5涉及的所有变量都是一个地址不管是abcd还是f还是中间变量全是查表偏移的基本上用不了类似什么v1496 v520这种然后还不是所有的都是查表偏移而且偏移是在移动的有点类似与滑动窗口的样子 具体看前面的图然后为了计算一次hash 会md5 多个64字节的情况这一点你需要计算出80后面的长度位然后计算字节来判断但是你还得知道多轮md5到底是怎么做的我们可以利用一些规律比如第一次的md5和第二次的md5在 64轮的第一轮的T表都是一样的然后作为锚点借机找到明文加载的pc地址然后写批量脚本提取每一轮md5的明文但是这里还会出现某一轮特殊的情况其实原因是因为他是多次md5交叉的情况你在trace中会看见5次md5那么哪几次是连续的要怎么根据trace日志还原呢还有就是怎么识别出hmac吧。虽然其实你知不知道hmac都不影响算法还原因为可以视作两轮hash和两个salt但是如果知道的话你在逆向的过程肯定是能省一定时间的而且写还原算法会简洁一点✔ 静态分析基本失效✔ 必须依赖 trace✔ 需自动化辅助深度魔改混淆的md5通杀打法首先必须trace下面的讲解如果看不懂的话建议关注我的同名b站账号我今天会出一期进行讲解会比文章详细得多因为文字有的时候描述不清楚初始 abcd 定位下面我画个图圈一下涉及到的初始abcd关键点与正则搜索规则第一轮参与 第一处是参与64大轮的第一小轮但是你怎么保证你的a不会看错db和c不会看错如果是b的话都好说应该还有其他特征最终累加第二处是末尾的和算法出来的abcd相加这一点是非常有用的下面给两个trace日志首先要明确我们可以知道些什么东西 首先密文的16字节你肯定是知道的那么也就是说最后的abcd你是知道的那么最后的abcd是从64轮算出来的abcd再加上原始的abcd来的这时候你就知道谁是a谁是b了比如我这里的是ef29bddaca2a67b2dcff858ad588a2b6那么根据端序还有md5特点我知道 dabd29ef 是a而且这个a应该是两个值加出来的所以汇编中用正则来搜肯定是add.*.*0xdabd29ef接下来要进行候选判断 搜出来了0x9bccab01和0x3ef07eee就可以思考谁是原始的a谁是64轮的a了这时候如果我们找到了4个初始的abcd那么这两个中间肯定有一个是哪个是哪个就是初始a另一个是64轮算出来的a3. 那么这时候就取决于你知道的条件了因为要根据这个算法的魔改程度来判断是否参与第一轮是否参与累加是否符合 T / f / M 关系极端情况啥都不知道突破点f_oldT明文接下来就可以重点突破这几个地方这里假设他都魔改了那么我们先突破一波明文明文恢复方法扫描 0x80 padding定位写入点![image-20比如这个app的案例这里就是hmac所以有两次hash全被我逮到了换别的大厂看看 jd/ks这里是适配使用了md结构的hash包括md家族和sha家族的hash还有别的扫描方案其实我感觉大家都会写代码直接让ai给你写一个就完事了不过需要加过滤条件才能稳定不然的话可能2000-3000条日志这种就失去了意义了所以如果需要的朋友进我星球自取吧一是我能赚点米二是我后面也还有更新为了这东西单独发篇文章说明更新了什么没啥意义。这样我们就可以知道他在哪写入0x80了我们可以根据pc和lr地址跳过去分析了这时候你就可以找到他魔改md5的函数入口可以hook拿到输入和输出MD5 特征必有 0x80, 除非他魔改掉这个字节T表恢复通过前面的明文还有a那么我们就差f_old和T了这时候你只需要知道谁是参与、^、| 算出来的谁就是f_old谁是固定ldr出来的谁大概率是T比如我们前面不是搜到了[libxyass.so 0x120000000x84010] 0x12084010: add w8, w11, w8 ; w110x9bccab01 w80x3ef07eee w80xdabd29ef么然后这里的0x9bccab01 和 0x3ef07eee 肯定有一个是b然后我们直接往上翻这几个东西结果在这里就翻到了第一个值是经过ror得到的那不就是f么所以0x3ef07eee 是b所以0x9bccab01 是被0xb025f196左移得到的那0xb025f196是ff f_old a T M[g]然后你的aM都可以知道了那么T不就确定了这时候你就知道T了然后你如果知道了第二轮的T。第三轮的T那你可以利用ldr的规律找到他们的加载偏移从而找到最开始的点位直接dump。64个T出来如果64轮的每一轮的代码是通用的你可以根据pc来搜到下一轮T的加载但是xhs这里是64轮完全展开的每个pc都不一样所以要找规律这时候还能推出b来你可以借助第一轮的abcd得到f_old也可以通过^|排查出来 还知道T M还知道左移你可以算出第一轮新的b64轮之间的关系md5的64大轮本质就是生成了64个新b把md5函数打开然后每一轮输出下图这样的日志那么你就可以根据这里的b去trace中搜如果搜到了大概率那一轮及以前算的没问题这时候如果错了可以利用二分法来定位哪个b算错了这时候可以排查出到底是哪一轮及以前开始出问题了。这个过程中可以关注f函数是否魔改 左移量是否魔改 T表是否魔改明文块是否交叉这个案例中除了f函数没有魔改意外其余全部都会出现。。。所以你一定要会这个逐步定位错误点的问题。公式可以看上图新b 上一轮的旧b f被左移后的值这里是算出一个f然后和上一轮的b相加得到新的b,那么你如果知道了上一轮的b,你是不是可以根据add.*上一轮的b 然后搜到下一轮的b的生成点位?比如上一轮的b是0x7e9b89fb 那你就可以写正则add.*0x7e9b89fb.*.*这里为啥会有两个因为abcd在64轮中 会有轮转可能在某一轮就变成了a然后和某个值add了得到了f这里简单排除一下就知道了肯定是最早的。然后你就知道下一轮的b是0x7fb057bc或者0x77ec1bb1 这时候你可以排除一下可以根据pc地址跳过去看看谁可疑 还可以根据这两个值得生成比如第一个值add w8, w8, w6 ; w80x114cdc1 w60x7e9b89fb w80x7fb057bc那么我可以搜0x114cdc1是不是经过了左移得到的比如这个图就很明显了对吧。那么你就从第一轮一直可疑延续到64轮结束这个过程会很累有没有什么办法批量正则 提取 左移量/T表批量提取 f二分定位错误批量验证 b在这里实际上不是很好搞但是还是值得的因为我64轮主要逆向的是20轮左右其他的都是写脚本dump出来的常量然后再改的比如你发现左移的汇编大部分都是如下那么你就可以根据f要准备左移得到新的f这个新的f会和上一轮的b相加对吧那你可以利用这个过程写正则脚本然后提取出相似的汇编块注意这几句汇编可能中间是有其他汇编的所以你写的模式最好是智能一点比如我在这里根据这个规律来进行匹配然后筛选出了250多个左移量那我就得根据我前面手动逆向的得到的61123然后在这个250候选里面找到然后取连续的64个左移量然后再尝试如果不对的话那么就是我的匹配算法没写好当然会出现你匹配了前10个但是后6个没和前10个连续这时候也没事再对后6个的前几个进行逆向就可以了然后再去切片中找这个样本因为存在多次交叉md5所以左移量是交叉的这时候用我上面的打补丁的方案可以搞 如果是普通案例只需要匹配出来就直接薄纱了甚至如果是没咋ollvm的如果64轮的每一轮左移量的汇编pc都差不多你都可以根据pc地址直接搜到那一行然后提取出左移量T表同样是可以的不同次md5的明文正则提取这里可以发现明文是每一轮都会用的然后会算出一个g的index从而切片出来然后第一大轮的g就是循环的索引那么我们第一大轮0-15每次4字节就可以提取出完整64字节了这在后面写脚本提取出5次md5的明文非常有用看日志会看的很清楚这里面可以看到第一大轮明文是连续的如果我们要提取每次md5的明文就可以利用这个规律然后连续提取组成64字节即可。还有就是64轮中每个明文块会被使用4次对应4大轮 这个和g的设计有关详情请看md5理论设计论文或者其他理论博客然后每一次的64轮ldr明文的pc都是一样的只需要根据这个pc搜然后提取寄存器的值就行了分块 MD5的特点首先看源码每64字节是一组然后每组都会生成更新一波self.A BCD然后下一次md5的第一轮内循环又把上一次md5的abcd作为初始值来进行运算所以你如果是3次md5那么前两次肯定都是64字节然后第3次因为要留8字节长度位还有0x80的填充位 所以你可以根据长度位来判断出前面到底有几次比如这个案例中发现后面的长度位是508那么0x508/81288/8 16112833也就是两个64字节一个33字节。我们前面写的内存扫描插件因此不能捕获完整明文的原因就是因为这个因为前两个64字节不填充md5啊所以拿不到明文。。。。这时候可以根据实际内存中的明文块包含的长度位来判断所以相当于也是一套闭环打法了。那么逆向到底怎么打呢这个可以看我日这个案例的详细文章还有视频在这里讲不清楚简单说就是把md5的填充给干掉然后用填充好的64字节并且把长度位改正确掉如65646562342d333030642d336334392d383465322d35623834653831646431303580000000000000000000000000000000000000000000000805000000000000这在后面80后面最后还有什么8050的长度位。然后去trace中找到初始abcd然后直接开始还原这个魔改md5然后呢还原完毕后再利用前面提到的明文提取脚本提取出每一次的md5 64字节明文以及初始abcd然后都跑一下算出来第64轮的abcd这时候会有一个衔接的过程。总结一波就是 链式结构明文1 → MD5 → abcd1 abcd1 → 明文2 → MD5 → abcd2 abcd2 → 明文3 → MD5如 标准abcd 明文1算出来 abcd。这个abcd是下一次md5的初始值然后再算出来abcd这时候是第3次md5的初始值那么你就可以把三个明文块连在一起然后使用第一轮的初始abcd来跑算法了。类似这张图里面存在两个md5然后某个md5有三块64字节有一个有两块64字节这个过程还是交叉的哈哈哈哈。这时候你再去掉第三轮填充的0x800x00以及长度位就拿到明文了这个过程可以应对超过64字节的明文的md5还可以应对hmac天生就超过64字节因为ipad和opad本来就是64字节。HMAC直接用通俗的说法来吧如下两轮 hash两个 saltipad和opad可直接视作多轮 MD5整理一波 完整还原流程找初始 abcd用插件扫描0x80提取写入0x80前的64字节明文拿标准算法来改逐轮对比计算定位魔改点如果有多块64字节就先找到初始abcd这时候就认为是魔改的iv就行然后从最后一块开始逆向然后去掉填充部分然后还原算法然后再去还原上一块是怎么生成abcd的直到根据内存中的明文长度比特位