2021HWS冬令营选拔赛-ChildRe-WP
前言
这种类型的题依稀记得在18年做过类似的,好久没看确实手生。
解题
考点:Debug Blocker反调试;Tea加密算法。
关于Debug Blocker原理不多写了,可以参考我之前看的一篇
文章
主进程作为调试器附加调试子进程,当子进程遇到
int 3
指令时会发送异常给调试器也就是主进程,然后主进程会在
sub_413BE0
循环等待接收子进程的异常。
根据异常事件的不同,会进入不同的处理函数,重点关注一下
sub_411023
函数
当子进程发生异常时,并且
int 3
的后一个值为
178
时,会进入到
WriteProcessMemorry
函数进行处理,也就是在
00412F39
处,这里的目的是将
1946127526
这个常数写入到子进程中,而后通过
SetThreadContext
和
ContinueDebugEvent
函数使子进程继续运行,为了能够调试子进程,我们需要使用
DebugActiveProcessStop
函数使主进程脱离对子进程的调试,以使我们可以通过IDA附加子进程进行调试。
不过为了使子进程可以被我们附加调试,而不会退出,我们可以在相应的位置,写入一个死循环。
具体为将
00412F3D
地址处的值
EB 00
patch为
EB FE
然后apply,这样就可以使程序运行到这里的时候进入一个死循环,以便等待我们调试器的附加,当调试器附加上之后,在将其修改为原来指令,并在下一条指令处下断点,然后运行,这样我们就可以调试子进程了。
之后就是去了解加密过程。总体上如下:
子进程:
获取用户输入,判断长度 == 0x20
input = get()
if len(input) != 0x20:
exit()
else:
int 3
...
int 3 # 陷入中断,跳过垃圾指令 eip += 9 jmp junk_code
...
int 3 # 陷入中断,获取常数 get const 0x73ff8ca6
...
input[i] 占4bytes,倒序
for i in range(8):
input[i] = _byteswap_ulong(input[i])
...
根据常数,生成一组固定常数,并将input和每个常数进行异或
num = 0x73ff8ca6 # [0x19FBB4]
for i in range(8):
input[i] = input[i] ^ num
num = (num - 0x50FFE544)&0xffffffff
...
倒序
for i in range(8):
input[i] = _byteswap_ulong(input[i])
init_key:初始化key
dword_432358 = [0x82ABA3FE, 0xAC1DDCA8, 0x87EC6B60, 0xA2394568, 0x432BEEE0, 0x2392B7C0, 0xC8C7FB80, 0xE7CC394D]
将key倒序
tea(key,input,5)
经过tea加密后和固定常量逐位比较。根据比较的结果输出wrong或者correct。
最后的解密脚本如下:
from libnum import s2n,n2s
def encrypt(v, k):
v0 = v[0]
v1 = v[1]
x = 0
delta = 0x9E3779B9
k0 = k[0]
k1 = k[1]
k2 = k[2]
k3 = k[3]
for i in range(32):
x += delta
x = x & 0xFFFFFFFF
v0 += ((v1 << 4) + k0) ^ (v1 + x) ^ ((v1 >> 5) + k1)
v0 = v0 & 0xFFFFFFFF
v1 += ((v0 << 4) + k2) ^ (v0 + x) ^ ((v0 >> 5) + k3)
v1 = v1 & 0xFFFFFFFF
v[0] = v0
v[1] = v1
return v
def decrypt(v, k):
v0 = v[0]
v1 = v[1]
x = 0xC6EF3720
delta = 0x9E3779B9
k0 = k[0]
k1 = k[1]
k2 = k[2]
k3 = k[3]
for i in range(32):
v1 -= ((v0 << 4) + k2) ^ (v0 + x) ^ ((v0 >> 5) + k3)
v1 = v1 & 0xFFFFFFFF
v0 -= ((v1 << 4) + k0) ^ (v1 + x) ^ ((v1 >> 5) + k1)
v0 = v0 & 0xFFFFFFFF
x -= delta
x = x & 0xFFFFFFFF
v[0] = v0
v[1] = v1
return v
# aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
# how to DebugActiveProcessStop()
# 0x413BE0
# 0x413A8E: EB FE jmp self
# push 0 6A 00 ; lpNumberOfBytesWritten
# push 2 6A 02 ; nSize
# push 0x00413a8e 68 8E 3A 41 00 ; lpBuffer
# push 0x00413E73 68 73 3E 41 00 ; lpBaseAddress
# push 0xFC 68 FC 00 00 00 ; hProcess
# call ds:WriteProcessMemory
# push 0x00010002 ; dwContinueStatus
# push 0x00001D84 ; dwThreadId
# push 0x000003D0 ; dwProcessId
# call ds:ContinueDebugEvent
# push 0x00000368 ; dwProcessId
# call 0x76C32F40 ;DebugActiveProcessStop
# re_1 = [0 for i in range(32)]
# for i in range(4):
# for j in range(8):
# re_1[j*4+i] = inp[j*4+i] | re_1[j*4+i]
# if i == 3:
# break
# else:
# re_1[j*4+i] = re_1[j*4+i] >> 8
# example
# 61 62 63 64 65 66 67 68
# ==>
# 64 63 62 61 68 67 66 65
# change bytes to 8 round into 4 tytes
# inp = [0x12131415,0x12131415,0x12131415,0x12131415,0x12131415,0x12131415,0x12131415,0x12131415]
# for i in range(8):
# re_inp = _byteswap_ulong(inp[i])
# 412F3A breakpoint
# 19FCE0
# [0x73ff8ca6,0x22ffa762,0xd1ffc21e,0x80ffdccda]
# num = 0x73ff8ca6 # [0x19FBB4]
# for i in range(8):
# inp[i] = inp[i] ^ num
# num = (num - 0x50FFE544)&0xffffffff
# reverse
# 61 62 63 64 65 66 67 68
# ==>
# 64 63 62 61 68 67 66 65
# for i in range(8):
# re_3 = ((re_2[i*4] & 0xff000000)>>0x18)&0xff
# for i in range(8):
# re_inp[i] = _byteswap_ulong(inp[i])
# init_dword_432358
# dword_432358 = [0x82ABA3FE, 0xAC1DDCA8, 0x87EC6B60, 0xA2394568, 0x432BEEE0, 0x2392B7C0, 0xC8C7FB80, 0xE7CC394D]
# reverse dword_432358
# 61 62 63 64 65 66 67 68
# ==>
# 64 63 62 61 68 67 66 65
# for i in range(8):
# re_dword_432358[i] = _byteswap_ulong(dword_432358[i])
# tea(,,5)
# for i in range(8):
# re_inp_1[i] = _byteswap_ulong(re_inp[i])
# inp = [0xC7ED9E12, 0x03C69E43, 0x7FA39EB0, 0xBBBD9EE1, 0xF7969E4E, 0x337361BE, 0x6F4C61EF, 0xAB26615C]
# print(len(inp))
# dec
print("----------------- dec --------------------")
dword_432358 = [0x82ABA3FE, 0xAC1DDCA8, 0x87EC6B60, 0xA2394568, 0x432BEEE0, 0x2392B7C0, 0xC8C7FB80, 0xE7CC394D]
cm = [0x000000ED, 0x000000E9, 0x0000008B, 0x0000003B, 0x000000D2, 0x00000085, 0x000000E7, 0x000000EB, 0x00000051, 0x00000016, 0x00000050, 0x0000007A, 0x000000B1, 0x000000DC, 0x0000005D, 0x00000009, 0x00000045, 0x000000AE, 0x000000B9, 0x00000015, 0x0000004D, 0x0000008D, 0x000000FF, 0x00000050, 0x000000DE, 0x000000E0, 0x000000BC, 0x0000008B, 0x0000009B, 0x000000BC, 0x000000FE, 0x000000E1]
# cm_1 = [0xEDE98B3B,0xD285E7EB,0x5116507A,0x]
cm_1 = [0 for i in range(8)]
for i in range(8):
cm_1[i] = cm[i*4]<<24 | cm[i*4+1]<<16 | cm[i*4+2]<<8 | cm[i*4+3]
# print(cm_1)
dec = [0 for i in range(8)]
for i in range(4):
enc = cm_1[i*2:i*2+2]
dec[i*2],dec[i*2+1] = decrypt(enc,dword_432358)
print(dec)
num = 0x73ff8ca6 # [0x19FBB4]
for i in range(8):
dec[i] = dec[i] ^ num
num = (num - 0x50FFE544)&0xffffffff
print(dec)
flag = b""
for i in range(8):
flag += n2s(dec[i])
print(flag)
总结
主要是绕过Debug Blocker,附加子进程花了很多时间,下次再遇到类似的题目,应该会快很多。