0x00 前言
纯动调,这玩意IDA看不了一点
因为用了iTruth版本的x64dbg,整合了不少插件(好多不知道啥用的)所以
听说有反调试?无駄!
0x01 解
IDA看不了一点(好像是ollvm),上x64dbg一步步调
从输入下手,运行直到要求输入然后暂停,随便输点东西就会停下在syscall
后面,回到用户空间后一步步ret
回去,直到找到类似scanf
参数的地方
之后一步步调,同时开启追踪记录,字节为单位,重点查看访问多次的内容,注意因为有大量花指令的存在,所以一定要用步进而不是步过来调,要不然随便哪个带call的花就会退出
首先可以发现这里调用了输入的内容
之后进了一个函数(其他花指令的call都是很近的,但是这个call跳转地址很远)
根据返回的值可以知道这是获取输入长度的(可能是strlen
?)
继续跟踪发现这里被调用了三次之多,下个断点重启程序看看
重新执行直到调用strlen
,然后看看输入长度被存到了哪里
发现存到了下面两个地址里面
下个两个硬断看看啥时候调用
发现丢到rdx
里面去了
跟下去,发现rdx
被拿去作比较了
估计这就是flag的长度了,试试发现确实是
然后应该可以推进了,找到存输入的位置(栈里面可以找到)下硬断,然后会断在这
往下走发现rdx的值先加了0x2400D5A6
,然后加了0x40
,最后又减去了0x2400D5A6
,所以最后结果就是加了0x40
然后存回了这个地方
之后继续走发现每个字节都做了这个加0x40
的操作
全部走完之后再给第一个下硬断,发现接下来的操作是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #读一字节给到edx mov r8d, edx # 设传入为00 edx = 00, r8d = 00 xor r8d, 0xFFFFFFFF # edx = FFFF FFFF, r8d = FFFF FFFF and edx, 0x16030876 # edx = 00, r8d = FFFF FFFF and r8d, 0xE9FCF789 # edx = 00, r8d = E9FC F789 xor edx, 0xFFFFFFFF # edx = FFFF FFFF, r8d = E9FC F789 xor r8d, 0xFFFFFFFF # edx = FFFF FFFF, r8d = 1603 0876 and r8d, edx # edx = FFFF FFFF, r8d = 1603 0876 xor r8d, 0xFFFFFFFF # edx = FFFF FFFF, r8d = E9FC F789 mov edx, r8d # edx = E9FC F789, r8d = E9FC F789 xor edx, 0xFFFFFFFF # edx = 1603 0876, r8d = E9FC F789 and r8d, 0xF49D22FF # edx = 1603 0876, r8d = E09C 2289 and edx, 0xB62DD00 # edx = 0202 0800, r8d = E09C 2289 xor r8d, 0xFFFFFFFF # edx = 0202 0800, r8d = 1F63 DD76 xor edx, 0xFFFFFFFF # edx = FDFD F7FF, r8d = 1F63 DD76 and edx, r8d # edx = 1D61 D576, r8d = 1F63 DD76 xor edx, 0xFFFFFFFF # edx = E29E 2A89, r8d = 1F63 DD76 mov r8d, edx # edx = E29E 2A89, r8d = E29E 2A89 xor r8d, 0xFFFFFFFF # edx = E29E 2A89, r8d = 1D61 D576 and r8d, 0xE29E2AF6 # edx = E29E 2A89, r8d = 76 and edx, 0x1D61D509 # edx = 9, r8d = 76 xor r8d, 0xFFFFFFFF # edx = 9, r8d = FFFF FF89 xor edx, 0xFFFFFFFF # edx = FFFF FFF6, r8d = FFFF FF89 and edx, r8d # edx = FFFF FF80, r8d = FFFF FF89 xor edx, 0xFFFFFFFF # edx = 7F mov r8, 0xFFFF4AC263967EF6 sub r10,r8 #将edx最低位放回
|
好恐怖,这么多的操作加起来只有一个^0x7F
之后故技重施,全部加密完之后再在第一个下硬断
之后断在这
慢慢调,发现每次加密会先取8个字节存起来,之后才加密
然后调到这会发现rax
的值很是熟悉
看来是TEA相关算法,8字节一组来加密
那么是那个TEA呢,继续调,特别注意调用内存地址的地方
到这里
[rsp+0xC]
里面的内容就是0x9E3779B9
而根据shr r8d, 0xB
可以知道这里是右移了11位,而将加密中出现右移11位操作的是XTEA,所以可以知道这边的加密算法用的是XTEA
继续调,可以发现r8d
经过一系列运算之后拿去做rax
的索引了,所以上图中赋值给rax
的内容即上面的这个[rsp+0x28]
存的就是密钥了
1 2
| # 注意在内存窗口里面是little-endian 0xEF6FD9DB, 0xD2C273D3, 0x6F97E412, 0x72BFD624
|
至于轮数,在这块内存地址周围找找应该是能找到的,或者在这可以找到,内存选中的为index
,后面那个就是轮数,下面一点就可以看到密钥
至此基本都知道了
提一下这块内存地址什么都有
标出来的是密钥,向上那个0x9DD3FFFDF0是key的索引,其左边0x7FF74C194CF8是原数据的索引,之后0x66是轮数,0x10是index
,0x63F13C25是v0,0x7F0B9E5F是v1,0x81AF1549是sum,0x9E3779B9是delta
XTEA相关内容出来之后就去找密文了,继续走,在取最后8Bytes的时候在第一个下硬件断点(要不然直接就退了)
断在这
之后一步步走,发现在这有个比较
反向跟一下可以找到r8d的来源在这
估计就是密文了,提取一下
1
| 0x9851E3A1, 0x49765686, 0x812B6B6F, 0X9612CECF, 0X3C3570A2, 0XF15C6231, 0XAA6B77FA, 0XBE056D9E, 0XF8A424E8, 0X0B3A23DB, 0X03CC2016, 0XA92BB5AD, 0X1D789F34, 0X9EF9B92E
|
后面就是写脚本解密了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import struct
def decrypt(rounds, v, k): v0 = v[0] v1 = v[1] delta = 0x9E3779B9 x = delta * rounds for i in range(rounds): v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (x + k[(x >> 11) & 3]) v1 = v1 & 0xFFFFFFFF x -= delta x = x & 0xFFFFFFFF v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (x + k[x & 3]) v0 = v0 & 0xFFFFFFFF v[0] = v0 v[1] = v1 return v
if __name__ == '__main__': k = [0xEF6FD9DB, 0xD2C273D3, 0x6F97E412, 0x72BFD624] rounds = 0x66 enctxt = [0x9851E3A1, 0x49765686, 0x812B6B6F, 0X9612CECF, 0X3C3570A2, 0XF15C6231, 0XAA6B77FA, 0XBE056D9E, 0XF8A424E8, 0X0B3A23DB, 0X03CC2016, 0XA92BB5AD, 0X1D789F34, 0X9EF9B92E] i = 0 for j in range(int(len(enctxt)/2)): tmp = [0]*2 tmp[0] = enctxt[i] tmp[1] = enctxt[i+1] tmp = decrypt(rounds, tmp, k) enctxt[i] = tmp[0] enctxt[i+1] = tmp[1] i += 2 for i in range(len(enctxt)): enctxt[i]^=0x7F7F7F7F enctxt[i]-=0x40404040 tmp = b'' for i in range(len(enctxt)): tmp += struct.pack('<L', enctxt[i]) print(tmp)
|
附一下断点地址