科技部:国家高新技术企业数量突破20万家
06-18
1。目标当年,MD5已经是最基本的签名算法了,但是如果你只是在输入上做一个简单的MD5,你肯定会被你的同行嘲笑。
加一点盐是基本的改善,但在这个就业形势严峻的时代,仅仅加盐肯定是不够的。今天我们就来说说修改后的MD5,让这个算法更加先进。
1、彩卷最简单的神奇修改方法就是改变MD5的初始参数。代码语言:txt复制context->state[0] = 0x;上下文->状态[1] = 0xEFCDAB89;上下文->状态[2] = 0x98BADCFE;上下文->状态[3] = 0x;只需修改这四个参数即可。
通过修改这些参数,我们可以改变MD5运算的结果。但这个方法太简单了,无法卷起来。
接下来我们将介绍更先进的滚压方法。 2. Meatloaf md5将执行64轮计算。
每轮计算都会使用一个常数组成一个常数表K,K的原始值计算为2^32 * |sin i |,然后取其整数部分。然后有理想想法的同学可以改变K值,比如把sin改成cos或者tan,这样就可以卷起来了。
3.卷中卷代码语言:txt复制//F,G,H,I四个非线性变换函数#define F(x,y,z) ((x & y) | (~x & z))#定义 G(x,y,z) ((x & z) | (y & ~z))#定义 H(x,y,z) (x^y^z)#定义 I(x,y,z) (y ^ (x | ~z))//x向左旋转n位的操作 #define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))要真正roll Up,我们需要改变MD5中的四个非线性变换函数F、G、H和I。我们可以添加XOR或减少AND运算,整个算法就会改变它的外观。
这种先进的滚动方式可以骗过boss,让算法更加强大。我们今天的目标是尝试恢复修改后的MD5算法。
通过这次实践,我们将了解算法复原的基本方法。本示例的输入参数是字符串:“3” 返回值是 32 个字符:“DD89CAD8B10F75A3D” 2. 步骤 第一步,我们需要使用 Unidbg run-through 算法。
与古代使用ida调试的前辈相比,Unidbg的出现直接将算法还原的难度降低了一个数量级。第二步,我们需要通过逆向求导将结果Z一步步返回到原始输入A。
这种方法称为逆向效应,是逆向分析的基本套路。我们假设这个样本是MD5或者修改后的MD5。
我们可以通过以下方法来还原算法: 1、调试断点 2、条件断点 3、数据打印 4、Trace内存读写 5、Trace代码 1、调试断点逆向分析是一门经验科学。虽然有一些基本套路,但还是以尝试为主。
首先使用IDA打开libnative-lib.so,从Exports导出表中找到导出函数Java_com_littleq_cryptography_md5_MainActivity_sign。该函数的起始地址为0x,结束地址为0x12B4,但主要代码逻辑在函数sub_A3C中。
让我们尝试在 sub_A3C 函数的末尾设置一个断点。代码语言:txt 复制文本:11D4 E0 07 40 F9 LDR X0, [SP,#0x+var_ ].text:11D8 03 00 00 90+ ADRL X3, aSSSS ; "%s%s%s%s".text:11D8 63 EC 0A 91.text:11E0 E4 83 01 91 ADD .text:11E4 E5 43 01 91 ADD X5, SP, #0x+var_C0.text:11E8 E6 03 01 91 ADD X6, SP, #0x+var_D0.text:11EC E7 C3 00 91 ADD var_E0.text:11F0 01 00 80 92 MOV我们将 Unidbg 中的下一个断点设置为 0x11D8。
代码语言:txt copy Debugger debugger = emulator.attach(); debugger.addBreakPoint(module.base + 0x11D8);并运行它。代码语言成功破解:txt copy debugger break at: 0x11d8 @ Function64 address=0x,arguments=[unidbg@0xfffe[libandroid.so]0x, 25, 58]>>> x0=0xbffff(-40) x1=0x0 x2=0x4 x3=0xbffffed20 x4=0xx5=0x02c0 x6=0x1 x7=0xbffffx8=0x0 x9=0x0 x10=0x1 x11=0x0 x12=0x8 x13=0x8 x14=0x8>>> x15=0x8 x16=0x8d70 22 =0x68ca89dd x 23 =0x3d5a x24=0x72ebb x25=0xddf5ac1 x26=0xd0d5adc6 x27=0x8b4d x28=0xffp=0xbffffLR=RX@0x11d4[libnative-lib.so]0x11d4SP=0xbffffPC=RX@0x11d8[libnative -lib.so]0x11d8nz CV:N=0, Z=1,C=1,V=0,EL0,使用 SP_EL0start + 0xae8=> *[libnative-lib.so*0xd8]*[]*0x11d8:*"adrp x3, #0x" [libnative- lib.so 0xdc] [63ec0a91] 0x11dc:“添加x3,x3,#0x2bb”[libnative-lib.so 0xe0] [e] 0x11e0:“添加x4,sp,#0x60”[libnative-lib.so 0xe4] [e] 0x11e4 :“添加 x5, sp, #0x50" [libnative-lib.so 0xe8] [e] 0x11e8: "添加 x6, sp, #0x40" [libnative-lib.so 0xec] [e7c1] 0x11ec: "添加 x7, sp, # 0x30" [libnative-lib.so 0xf0] [] 0x11f0: "mov x1, #-1" [libnative-lib.so 0xf4] [] 0x11f4: "mov w2, #0x40" [libnative-lib.so 0xf8] [ 5bfdff97] 0x11f8: "bl #0x" 在Arm汇编中,在调用函数之前,输入参数将被存储在x0,x1,x2...从这段代码中,我们可以看到地址0x11f8将调用0x函数并且传入的参数有7个,从x0到x7,虽然Unidbg的调试有点简陋,但是有了这样的神器,还需要什么?即遇到函数调用就进入,n单步执行,遇到函数调用则不进入函数。
c 继续执行 b 设置断点 r 取消当前断点 m 检查内存 首先 s s s 几次,单步到 0x11f8 代码语言:txt copy debugger break at: 0x11f8 @ Function64 address=0x,arguments=[unidbg@0xfffe[libandroid .so ]0x, 25, 58]>>> x0=0xbffff(-40) x1=0xffffffffffffffff x2=0x40 x3=0x12bb x4=0xbffff5d0 x5=0xbffff5c0 1 =0x0 x12=0x8 x13=0x8 x14=0x8LR=RX@0x11d4[libnative -lib.so]0x11d4SP=0xbffffPC=RX@0x11f8[libnative-lib.so]0x11f8nzcv: N=0, Z=1, C=1, V=0, EL0,使用 SP_EL0start + 0xb08=> *[libnative-lib .so*0xf8]*[5bfdff97]*0x11f8:*"bl #0x"这时候输入参数就准备好了,我们来看看这些输入。代码语言:txt复制mx7>-------------------------------------------------------- --- ----------------------------------<[10:40:26]x7=unidbg@0xbffff5a0 , md5= d6cca9ef57fc14e1bf,大小: : 35 41 37 35 37 34 33 44 00 B3 22 40 00 00 00 00 5A3D.."@....: 39 37 30 37 31 30 46 37 00 8D 09 40 00 00 0 0 00 10F7.. .@....:34 44 39 31 38 31 38 42 00 77 12 40 02 00 00 00 4D8B.w.@....:44 44 38 39 43 41 36 38 00 1B 17 40 02 00 00 00 DD89CA68 ...@....: 31 36 37 37 30 33 38 30 36 36 35 35 33 80 00 00 3...: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ………………:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 …………^---- -------------------------------------------------- -----------------------^可以看到,这次调用函数0x基本上就是组装最终的结果了。
生成的位置来分析最终结果是如何计算出来的,即Y→Z的过程。 4.跟踪内存读写 现在我们知道了结果Z的位置,下一步就是要知道Z是谁计算出来的。
我们需要用到Unidbg的一个强大的功能:内存读写监控。这次我们提前设置了调试断点,并在 sub_A3C 函数的开头处断掉。
代码语言:txt复制调试器断点处:0x0a3c @ Function64 address=0x,arguments=[unidbg@0xfffe[libandroid.so]0x, 25, 58]>>> x0=0xx1=0xbffffx2=0x0 x3=0x1 x4=0x0 x5 =0x1 x6=0x0 x7=0x0 x8=0xfffe0a70 x9=0xx10=0x0 x11=0x1 x12=0x3 x13=0xx14=0x>>> x15=0x1 x16=0xx17=0x0 x18=0x17 x19=0xfffex20=0xbffffx21=0x0 x22= 0x0 x23=0x0 x24=0x0 x25=0x0 x26=0x0 x27=0x0 x28=0x0 fp=0xbffff6f0LR=RX@0x[libnative-lib.so]0xSP=0xbffffPC=RX@0x0a3c[libnative-lib.so]0xa3cnzcv:N =0、Z=0、C=1、V=0、EL0,使用 SP_EL0start + 0x34c=> *[libnative-lib.so*0x00a3c]*[ffd1]*0x0a3c:*"sub sp, sp, #0x" traceWrite 0xbffff5d0 0xbffff5d8设置跟踪 0xbffff5d0->0xbffff5d8 内存写入成功。c[11:41:41 ] 内存写入 0xbffff5d8,数据大小 = 1,数据值 = 0x0,PC=RX@0x[libnative-lib.so]0x,LR =null[11:41:41 ] 内存写入位于 0xbffff5d0,数据大小 = 8,数据值 = 0x0,PC=RX@0xc[libnative-lib.so]0xc,LR=null[11:41:41 ] 内存写入在 0xbffff5d8 处,数据大小 = 1,数据值 =0x0, PC=RX@0xb48cc[libc.so]0xcc, LR=RX@0xb48c8[libc.so]0xc8traceWrite 是监听写内存命令。
看来0xbffff5d0的内存,DD89CA68数据写入的位置是:0xc 代码语言:txt 复制文本:C 14 00 00 90+ ADRL :C 21 01 80 52 MOV W1, #9.text:22 01 80 52 MOV W2, #9. 文本:E3 03 14 AA MOV X3, X20. 文本:FF A3 01 39 STRB WZR, [SP,#0x+ var_A8]. 文本:C FF 33 00 F9 STR XZR, [SP,#0x +var_B0].text:7D FD FF 97 BL sub_xc 的指令 STR XZR 写得正确,但看起来不像写数据。相反,地址 SP,#0x+var_B0 处的数据被清除。
那我们再试一次。 (Unidbg的优点是可以无限重放,比在真机上调试app方便很多倍。
) 这次再前进一点,在0xC处设置断点。断开连接后,每隔 s 步进检查一次 m0xbffff5d0。
最后发现运行0x后,0xbffff5d0内存的值变成了DD89CA68。这说明0xbffff5d0是由sub_函数写入的。
代码语言:txt复制调试器中断处:0x@ Function64地址=0x,arguments=[unidbg@0xfffe[libandroid.so]0x, 25, 58]>>> x0=0xbffff5d0(-32) x1=0x9 x2=0x9 x3 =0x12b8 x4=0xdd89ca68 x5=0xe6cd8e62 x6=0xx7=0x29b9cx8=0x40 x9=0xx10=0xbffff5e0 x11=0x40 x12=0x3d5ebb2b x13=0xcx14=0xfc63b7e7>>> x15=0x49ac16 b x16=0xac6afx17=0xf3db x18=0x18 x19=0x4cf3ax20= 0x12b8 x21=0x0 x22=0x68ca89dd x23=0x3d5a x24=0x72ebb x25=0xddf5ac1 x26=0xd0d5adc6 x27=0x8b4d x28=0xffp=0xbffffLR=nullSP=0xbffffPC=RX@0x[libnative-lib.so]0xnzc v:N=0,Z= 1 , C=1, V=0, EL0, 使用 SP_EL0start + 0xa80=> *[libnative-lib.so*0x0]*[7dfdff97]*0x:*"bl #0x" 但是返回0x,我们发现了一个字符串熟悉的数字x4=0xdd89ca68,那么,我们的问题就变成了如何计算x4的值? 3、总结:首先要习惯阅读Arm汇编,一步步调试,然后熟悉寄存器的变化。对一些关键数字要特别敏感。
掌握Unidbg的基本调试命令。熟悉常用的加密算法,并在开发环境中多次调试,熟悉其算法流程。
ffshow1: ffshow 多种方式区分时很容易区分对错;如果一味和谐,就分不清人我。提示: :本文的唯一目的是了解更多逆向技术和思路。
如果有人利用本文的技术进行非法经营并获取利益,法律责任将由经营者自行承担。与本文及作者无关。
本文涉及的代码项目可以从奋飞知识星球的朋友那里获取。欢迎加入知识星球,一起学习、讨论技术。
如果有什么问题请加我wx:fenfei讨论。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-18
06-17
06-08
06-06
06-06
06-06
06-06
06-06
06-17
06-17
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用