在编辑框Edit控件的消息响应函数Hi_WM_COMMAND_sub_401570中
通过每次输入是,都会调用消息响应函数,函数通过UpdateData(True)
将当前输入的key文本更新赋值给Edit控件关联的CString成员变量,
从下属代码中,可见edit控件关联成员变量在控件的0x60偏移处,
要求输入的key文本长度大于0x0B,如果是正常直接输入,在输入第0x0B个
字符时,就会响应校验,最大输入是0x0B;但这里的bug是,
如果是复制粘贴的,其长度就可以任意,如"AAAAAAAAAAAAAAAA>"
.text:0040158D push 1
.text:0040158F mov [esp+8Ch+var_74_thisPtr], esi
.text:00401593 call CWnd::UpdateData(int)
.text:00401598 lea ecx, [esp+88h+var_7C]
.text:0040159C call CString::CString(void)
.text:004015A1 mov eax, [esi+60h]
.text:004015A4 lea edx, [esi+60h]
.text:004015A7 mov [esp+88h+var_4], 0
.text:004015B2 mov ebp, [eax-8]
.text:004015B5 cmp ebp, 0Bh
核心逻辑是两个迭代异或解密
a.用用户输入的key的每一个字节异或上encKeyA = Hi_encKeyA_byte_403020,
的每一个字节,解密出 decKeyA
b.用"a."得到的decKeyA的每一个字节有符号乘0x5E后在异或上加密代码Hi_encChipCode_sub_401540
的每一个字节,解密出代码
最后调用解密的代码显示成功信息。
突破口在于迭代异或预算的交换和合并性质以及chip代码的特征。
异或交换与合并性质: x1 ^ x2 ^ x3 ^ x4 = x1 ^ (x2 ^ x3 ^ x4)
即参与异或的操作数可以任意交换并先组合异或运算,再与其余操作数异或运算。
于是上述两个迭代异或加解密就可以简化描述为简单的异或运算
a.用户输入的key的各个字节自行迭代异或得到一个字节的异或因子xortor1
然后用xortor1对encKeyA解密得到decKeyA
b.decKeyA的各个字节自行作有符号成0x5e后迭代异或得到一个字节的异或因私xortor2
然后用xortor2对encChipCode直接解密得到解密代码decChipCode
只需要确定xortor2,进而确定xortor1,就可以拼凑出迭代异或得到xortor1的key
xortor2的确定很简单,
下述python代码得到加密的代码片段为 encChipCode
for i in xrange(0,0x2C):
print "0x{:02X},".format(Byte(0x401540+i)),
if (i+1)%0x10 == 0:
print ""
encChipCode = [
0xAE, 0xC5, 0xAC, 0xF0, 0xF4, 0x84, 0xC4, 0xAC, 0xF0, 0xF4, 0x84, 0xC4, 0xAE, 0xC4, 0x3B, 0xD1,
0x30, 0xE5, 0x84, 0xC4, 0x4F, 0x88, 0xE0, 0xC0, 0xAC, 0x2C, 0xC7, 0xC4, 0xC4, 0x2C, 0x4C, 0xC7,
0xC4, 0xC4, 0xAE, 0xC4, 0x4F, 0x0C, 0x2C, 0xBD, 0xC7, 0xC4, 0xC4, 0x07]
由于堆栈平衡,下面可见对加密代码的调用是__cdecl方式,
即加密代码解密出来的函数尾部是"retn"(而不是"retn 4"),retn的操作码是 0xC3
.text:0040168D push edx
.text:0040168E call Hi_encChipCode_sub_401540
.text:00401693 add esp, 4
即要求 xortor2 ^ encChipCode[0x1B] = 0xC3
于是 xortor2 = 0xC3 ^ encChipCode[0x1B] = 0xC3 ^ 0x07 = 0xC4
于是用xortor2=0xC4对加密代码直接解密就可以得到函数体,
在IDAPython执行以下解密代码(也是加密代码),
#-------
for i in xrange(0,0x2C):
ea = 0x401540+i
PatchByte(ea,(Byte(ea) ^ 0xC4))
#-------
然后快捷键"C"分析代码并创建函数,得到
.text:00401540 Hi_encChipCode_sub_401540 proc near ;
.text:00401540 push 1 ; uType
.text:00401542 push offset Caption ; "成功"
.text:00401547 push offset Caption ; "成功"
.text:0040154C push 0 ; hWnd
.text:0040154E call ds:MessageBoxA
.text:00401554 mov ecx, [esp+arg_0]
.text:00401558 push 3E8h
.text:0040155D call CWnd::GetDlgItem(int)
.text:00401562 push 0
.text:00401564 mov ecx, eax
.text:00401566 call CWnd::EnableWindow(int)
.text:0040156B retn
.text:0040156B Hi_encChipCode_sub_401540 endp
求xortor1
#-------
for i in xrange(0,0x14):
print "0x{:02X},".format(Byte(0x403020+i)),
#-------
通过上述python代码,我们可以得到encKeyA
这里要求xortor1对encKeyA异或解密后的decKeyA有符号乘以0x5E后的迭代异或等于xortor2=0xC4,
简单枚举下就可以得到xortor1,如执行以下python枚举代码
#-------
encKeyA = [0xCC, 0xAA, 0xBD, 0xDD, 0xCB, 0xBA, 0xB2, 0x92, 0xAF, 0xBA, 0xB4, 0xB9, 0xB0, 0xAC, 0xCB, 0xBA, 0xCE, 0xD0, 0xDF, 0xDD]
for xors in xrange(0,0x100):
xorkey = [c_ubyte(c_byte((exor ^ xors)).value * 0x5E).value for exor in encKeyA]
xorx = xorkey[0]
for xorv in xrange(1,xorkey.__len__()):
xorx = xorx ^ xorkey[xorv]
if xorx == 0xC4:
print "xortor1 >> {:02X}".format(xors)
#-------
得到xortor1
xortor1 >> 1E
xortor1 >> 3E
xortor1 >> 5E
xortor1 >> 7E
xortor1 >> 9E
xortor1 >> BE
xortor1 >> DE
xortor1 >> FE
其中0x3E,0x5E,0x7E分别对应字符">","^","~"
只要0x0B长度的key各字符异或的结果时上述0x1E,0x3E,0x5E,0x9E,0xBE,0xDE,0xFE的任何一个即可满足条件。
根据相同两个操作数异或为零的特性,只要其余十个字符成五对或全部相同即可忽略,于是可以快速得到几组key
">>>>>>>>>>>","^^^^^^^^^^^","~~~~~~~~~~~"
"AABBCCDDEE>","ABABCDCDEE>"
即只要是">","^","~"三个字符中的任意一个加上其他五对字符,位置任意,就是可行的key,这是其中一种解集。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)