本文主要是介绍后记:2020西湖论剑逆向babyre的wp,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
这个题目对我而言太难了,比赛的时候只做了个crypto简单题和逆向的flow,这个babyre没时间看,赛后花了2天才搞出来。
题目是个PE32的可执行程序,体积挺大的,调试的时候一直报非法指令,应该是有反调试的措施。
用x64dbg也是一样,看来动态调试这条路是走不通了,干脆耐心的分析代码。
首先根据报错,查到主程序逻辑是在sub_1543D0里:
这里是一个虚机,具体代码在开头会有初始化:
右边注释是我手工写的
0x01, 0x63, 0x00, load strings 0 & output
0x01, 0xF6, 0x01, load strings 1 & output
0x02, 0x23, 0x33, scanf
0x06, 0x11, 0xAC, strlen
0x08, 0x06, 0xC3, push 0x20
0x05, 0xE6, 0x2E, memcmp
0x12, 0x03, 0xE2, if = then continue
0x09, 0xBD, 0xEC, pop
0x09, 0x23, 0x5C, pop
0x09, 0x17, 0x2C, pop
0x10, 0xF2, 0xED, StringToDwordArray
0x04, 0xFF, 0x2A, encrypt
0x08, 0x05, 0xAD, push var5
0x05, 0x98, 0x7A, strcmp
0x07, 0x03, 0x02, if = then print
可以看到,最核心的是第4个功能模块,就是encrypt函数,让我们进去看下:
可以看到,这里有些数据插到函数中,而不是代码,后来再仔细分析启动过程,发现有这样的函数:
正常情况下程序不被调试是IsDebuggerPresent返回应该是0,就会进入sub_99C936:
看到这里就很明显了,是一个自调试的程序,代码中插入的数据都是"\xC7\x23"开头的,这里也有校验,同时会对执行的异常进行处理,再查到异常处理的函数:
函数基本功能是取异常时的上下文,同时读取异常时的指令字节,根据第三个字节来进行不同的操作,同样又是虚拟机!
经过一番耐心的分析(手撸~),得到之前报错sub_9a6f70函数的虚拟代码:
edi=输入内容转dword数组
loc_156F96:
C7230DD0 push edx
C7230CE0 pop eax
C7230DE0 push eax
C7230CF0 pop ebx
C7231701000000 push 1
C7230CB0 pop esi
C7230EF0B0 add ebx,esi
C7230DE0 push eax
C72326 pop dword1;push edi+dword1*4
C7230CE0 pop eax
C7230DF0 push ebx
C72326 pop dword1;push edi+dword1*4
C7230CF0 pop ebx
C7231DE0F0 xor eax,ebx
C7230DE0 push eax
C7230DD0 push edx
C7231F pop dword1;pop [edi+dword1*4]
C7230ED0B0 add edx,esicmp edx, 1Fhjnz short near ptr loc_156F96push 4mov eax, [ebp+8]push eaxcall sub_14BA3Badd esp, 8C7230DE0 push eax
C72317EFBEADDE push 0xdeadbeef
C7230CE0 pop eax
C7230EE0E0 add eax,eax
C7230FE0E0 sub eax,eax
C7230DF0 push ebx
C7231714451100 push 0x00114514
C7230CF0 pop ebx
C72313E0F0 or eax,ebx
C72342E0 not eax
C72310E0F0 mul eax,ebx
C72314F0E0 ebx%=eax
C72312E0F0 and eax,ebx
C7230CF0 pop ebx
C7230CE0 pop eax
后一部分没啥用,应该就是增加逆向的工作量,前面一部分逻辑就是
for i in range(31):flag[i]^=flag[i+1]
然后进入加密的核心了(激动~,其实里解开题还早)
sub_99C5EE是核心中的核心:
居然还TM有伪代码,继续分析,详细代码我就不贴了,太长,基本逻辑就是分四轮把32个输入的字节转成每轮两个dword数,然后用两个随机种子0xDEADBEEF和0xAA114514产生2个随机数,对其进行加密,其中还有最大的一段伪代码写的加密处理部分enc2,最后根据逻辑写出exp如下:
def enc1(x,y,z):v5=((x<<z%32)^x^(x>>y%32))&0xffffffffv6=(v5^z^(y&v5|~v5))&0xffffffffv7=(v6^y^(z&v6|~v6))&0xffffffff
# print(hex(x),hex(y),hex(z),hex(v5),hex(v6),hex(v7))return v7def enc2(x,y,z):v5=x^y^zv6=(v5<<((y+z)%32)|v5>>(32-((y+z)%32)))&0xffffffffv7=(v6^(v6&(v6>>1)))&0xffffffff
# print(hex(x),hex(y),hex(z),hex(v5),hex(v6),hex(v7))return v7def swap(x):x = (2 * (x & 0x55555555) | ((x & 0xAAAAAAAA) >> 1))&0xffffffffx = (4 * (x & 0x33333333) | ((x & 0xCCCCCCCC) >> 2))&0xffffffffx = (16 * (x & 0xF0F0F0F) | ((x & 0xF0F0F0F0) >> 4))&0xffffffffx = (((x & 0xFF00FF) << 8) | ((x & 0xFF00FF00) >> 8))&0xffffffffx = ((x << 16)| ((x & 0xFFFF0000) >> 16))&0xffffffffreturn xa=0xdeadbeef
b=0xaa114514
t0=[]
t1=[]
for i in range(512):t0.append(a)t1.append(b)v11=0for j in range(32):v11^=(a&(1<<j))>>ja=(v11^2*a)&0xffffffffv11=0for j in range(32):v11^=(b&(1<<j))>>jb=(v11^2*b)&0xffffffffresult=[0x31, 0x86, 0x5F, 0xDF, 0xD5, 0x56, 0x45, 0x71, 0x12, 0x5F, 0x7A, 0xD9, 0xBF, 0xBC, 0x59, 0x12, 0xCD, 0x43, 0xCA, 0xDC, 0xF4, 0xB3, 0x53, 0xA6, 0xFA, 0x8E, 0x89, 0x1A, 0x74, 0xFC, 0x03, 0x16]
flag=[]
for j in range(4):v0=(result[3+8*j]<<24)+(result[2+8*j]<<16)+(result[1+8*j]<<8)+result[8*j]v1=(result[7+8*j]<<24)+(result[6+8*j]<<16)+(result[5+8*j]<<8)+result[4+8*j]for i in range(511,-1,-1):a=t0[i]b=t1[i]v1=swap(v1)v0=swap(v0)if i%128>0:v3=enc1(v1,a,b)else:v3=enc2(v1,a,b)v4=v0^v3v0=v1v1=v4
# print(hex(v0),hex(v1))flag.append(v0&0xff)flag.append((v0>>8)&0xff)flag.append((v0>>16)&0xff)flag.append((v0>>24)&0xff)flag.append(v1&0xff)flag.append((v1>>8)&0xff)flag.append((v1>>16)&0xff)flag.append((v1>>24)&0xff)for i in range(31):flag[30-i]^=flag[31-i]
print(''.join(chr(i) for i in flag))
运行结果
这篇关于后记:2020西湖论剑逆向babyre的wp的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!