-
-
[原创]看雪 2016 CTF 第二十六 Solution
-
发表于: 2016-12-24 12:13 5244
-
在主函数体00401BB0 _main中
程序先解密出局部存储的加密注入目标进程镜像名称"Broiler.exe"
其作为下属启动的调试线程Hi_debug_handler_sub_401040的线程参数
.text:00401C20 push eax ; lpThreadId
.text:00401C21 push 0 ; dwCreationFlags
.text:00401C23 push ecx ; lpParameter
.text:00401C24 push offset Hi_debug_handler_sub_401040 ; lpStartAddress
.text:00401C29 push 0 ; dwStackSize
.text:00401C2B push 0 ; lpThreadAttributes
.text:00401C2D call ds:CreateThread
调试线程会启动目标进程"Broiler.exe"并捕捉调试事件,主要处理是注入线程的两次int 3中断事件
下属是处理两次int 3事件的调用栈
Hi_debug_handler_sub_401040
0040104A call Hi_startDebug_P1ProcessName_sub_401D40
00401E5B call Hi_CaseDebugEventCode1_sub_401F20
00401F87 call Hi_debugEvent_80000003_sub_401FA0
在Hi_debugEvent_80000003_sub_401FA0中为两次处理int3的分支
.text:00401FFE cmp eax, 2
.text:00402001 jnz loc_4020D2 //-------响应第一次int3事件
.text:00402007 xor eax, eax //-------响应第二次int3事件
第一次int3事件响应通过WriteProcessMemory将中断EIP处的两字节修正为 0x90,0x2A
004020E3 call ds:WriteProcessMemory
实际将原 "int 3"、"add al,cl" 两指令修正为 "nop"、"sub al,cl"
第二次int3事件响应会读取注入线程(算法2,alg2)处理后的注册码key2,
再与局部存储的key比对,完全匹配则显示成功
.text:0040209E call ds:ReadProcessMemory //读取经过注入线程alg2算法变换后的keyInfo
.text:004020A4 xor ecx, ecx
.text:004020A6 xor eax, eax
.text:004020A8 loc_4020A8: ;
.text:004020A8 mov dl, [esp+eax+40h+Buffer_key2] //key2 after alg1 and alg2
.text:004020AC cmp dl, [esp+eax+40h+var_30_tgtKey]
.text:004020B0 jz short loc_4020B3 //-var_30_tgtKey数组为局部变量存储的目标比对key
.text:004020B2 inc ecx //----------------------ecx统计不匹配个数
.text:004020B3 loc_4020B3:
.text:004020B3 inc eax
.text:004020B4 cmp eax, 15h
.text:004020B7 jl short loc_4020A8
.text:004020B9 cmp ecx, ebx
.text:004020BB jnz short loc_4020D2
.text:004020BD push ebx ; uType
.text:004020BE push offset aJ_0 ; "成功了"
.text:004020C3 push ebx ; lpText
.text:004020C4 push ebx ; hWnd
.text:004020C5 call ds:MessageBoxA //--------------显示成功信息
在主函数体00401BB0 _main 中,接着是读取用户输入的注册码key,
然后经过算法1函数Hi_alg1_sar2xorIxor41h_sub_401060处理得到key1,
处理后传递给后续注入函数,
注入函数Hi_inject_alg2_thread_sub_4010D0会在捕捉Broiler.exe进程的目标线程
然后注入算法2-alg2的chip代码片段执行算法2对key做第二步处理
.text:00401C79 call _scanf
.text:00401C7E lea ecx, [esp+30h+var_18_key] //-------key
.text:00401C82 push ecx
.text:00401C83 call Hi_alg1_sar2xorIxor41h_sub_401060
.text:00401C88 lea edx, [esp+34h+var_18_key] //-------key1 after alg1
.text:00401C8C push edx
.text:00401C8D call Hi_inject_alg2_thread_sub_4010D0
.text:00401C92 push offset aPause ; "pause"
.text:00401C97 call _system
注入的chip代码片段如下,
其中 770000 存放输入的key,也作为chip代码算法处理后的keyInfo输出地
007800D1 和 007800D2两处的指令已经直接手工修正
算法alg2看起来有点绕,这里我们不需要从算法原理上做逆向攻击,
因为(alg2)其对key的各个字符的变换是独立的(alg1不是)。
所以我们可以直接用alg2的变换码表来实现逆向解密。
如何获取码表?
1.调试注入的chip代码
a. OD 启动 CrackMe.exe主进程,在入口处停下,
将Hi_debug_handler_sub_401040函数体修改为无用函数,即,
在函数首地址401040处汇编为 xor eax,eax 和 ret 4
b. OD 启动 Broiler.exe被注入进程,直接跑起
c. CrackMe.exe断点设置于00401B84 call ds:ResumeThread
跑起"a.",任意输入回车断下。
下述全局变量存储了key和注入的chip代码的首地址
Hi_va200h_for_key_dword_40CA6C
Hi_va1000h_chip_dword_40CA68
d.在Broiler.exe进程的Hi_va1000h_chip_dword_40CA68地址下F2断点,
(需要查看进程内存布局M控件刷新下),然后对"c."断下的进程直接单步
或F9跑起,这时Broiler.exe进程就会断下在下述注入的chip代码片段处
00780000 68 00007700 PUSH 770000
00780005 C745 FC 00000000 MOV DWORD PTR SS:[EBP-4],0
0078000C 8965 FC MOV DWORD PTR SS:[EBP-4],ESP
0078000F 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00780012 B3 2A MOV BL,2A
00780014 B2 3F MOV DL,3F
00780016 C645 C4 8C MOV BYTE PTR SS:[EBP-3C],8C
0078001A 8B08 MOV ECX,DWORD PTR DS:[EAX]
0078001C B0 B6 MOV AL,0B6
0078001E C645 C5 4C MOV BYTE PTR SS:[EBP-3B],4C
00780022 C645 C6 96 MOV BYTE PTR SS:[EBP-3A],96
00780026 C645 C7 5B MOV BYTE PTR SS:[EBP-39],5B
0078002A 885D C8 MOV BYTE PTR SS:[EBP-38],BL
0078002D 8855 C9 MOV BYTE PTR SS:[EBP-37],DL
00780030 8845 CA MOV BYTE PTR SS:[EBP-36],AL
00780033 C645 CB 5B MOV BYTE PTR SS:[EBP-35],5B
00780037 885D CC MOV BYTE PTR SS:[EBP-34],BL
0078003A C645 CD 11 MOV BYTE PTR SS:[EBP-33],11
0078003E C645 CE B1 MOV BYTE PTR SS:[EBP-32],0B1
00780042 C645 CF 15 MOV BYTE PTR SS:[EBP-31],15
00780046 C645 D0 AC MOV BYTE PTR SS:[EBP-30],0AC
0078004A C645 D1 C3 MOV BYTE PTR SS:[EBP-2F],0C3
0078004E C645 D2 53 MOV BYTE PTR SS:[EBP-2E],53
00780052 8845 D3 MOV BYTE PTR SS:[EBP-2D],AL
00780055 885D D4 MOV BYTE PTR SS:[EBP-2C],BL
00780058 8855 D5 MOV BYTE PTR SS:[EBP-2B],DL
0078005B C645 D6 B2 MOV BYTE PTR SS:[EBP-2A],0B2
0078005F C645 D7 FC MOV BYTE PTR SS:[EBP-29],0FC
00780063 C645 D8 69 MOV BYTE PTR SS:[EBP-28],69
00780067 C645 D9 10 MOV BYTE PTR SS:[EBP-27],10
0078006B C645 DA BF MOV BYTE PTR SS:[EBP-26],0BF
0078006F C645 DB FD MOV BYTE PTR SS:[EBP-25],0FD
00780073 8845 DC MOV BYTE PTR SS:[EBP-24],AL
00780076 C645 DD 5B MOV BYTE PTR SS:[EBP-23],5B
0078007A 8855 DE MOV BYTE PTR SS:[EBP-22],DL
0078007D 894D E4 MOV DWORD PTR SS:[EBP-1C],ECX
00780080 C745 E0 15000000 MOV DWORD PTR SS:[EBP-20],15
00780087 8D55 C4 LEA EDX,DWORD PTR SS:[EBP-3C]
0078008A 8D45 C4 LEA EAX,DWORD PTR SS:[EBP-3C]
0078008D C745 F4 00000000 MOV DWORD PTR SS:[EBP-C],0
00780094 8955 EC MOV DWORD PTR SS:[EBP-14],EDX
00780097 8945 F0 MOV DWORD PTR SS:[EBP-10],EAX
0078009A 8B55 F0 MOV EDX,DWORD PTR SS:[EBP-10]
0078009D 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14]
007800A0 33FF XOR EDI,EDI
007800A2 8955 FC MOV DWORD PTR SS:[EBP-4],EDX
007800A5 897D E8 MOV DWORD PTR SS:[EBP-18],EDI
007800A8 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
007800AB 8B75 FC MOV ESI,DWORD PTR SS:[EBP-4]
007800AE 33D2 XOR EDX,EDX
007800B0 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
007800B3 8A19 MOV BL,BYTE PTR DS:[ECX]
007800B5 8A0410 MOV AL,BYTE PTR DS:[EAX+EDX]
007800B8 3AD8 CMP BL,AL
007800BA 75 22 JNZ SHORT 007800DE
007800BC 8D0C17 LEA ECX,DWORD PTR DS:[EDI+EDX]
007800BF 8B7D F4 MOV EDI,DWORD PTR SS:[EBP-C]
007800C2 8D3C4F LEA EDI,DWORD PTR DS:[EDI+ECX*2]
007800C5 03CF ADD ECX,EDI
007800C7 0FBEFB MOVSX EDI,BL
007800CA 0FBE4C0D C4 MOVSX ECX,BYTE PTR SS:[EBP+ECX-3C]
007800CF 33CF XOR ECX,EDI//-------下述相应第一次int3事件后修正
007800D1 90 NOP //-------int 3 修正为 nop
007800D2 2AC1 SUB AL,CL //-------add al,cl 修正为 sub al,cl
007800D4 8B4D E4 MOV ECX,DWORD PTR SS:[EBP-1C]
007800D7 F62E IMUL BYTE PTR DS:[ESI]
007800D9 8B7D E8 MOV EDI,DWORD PTR SS:[EBP-18]
007800DC 8801 MOV BYTE PTR DS:[ECX],AL
007800DE 42 INC EDX
007800DF 83C6 09 ADD ESI,9
007800E2 83FA 04 CMP EDX,4
007800E5 ^7C C9 JL SHORT 007800B0
007800E7 8B5D FC MOV EBX,DWORD PTR SS:[EBP-4]
007800EA 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8]
007800ED 83C7 03 ADD EDI,3
007800F0 43 INC EBX
007800F1 83C2 03 ADD EDX,3
007800F4 83FF 06 CMP EDI,6
007800F7 895D FC MOV DWORD PTR SS:[EBP-4],EBX
007800FA 897D E8 MOV DWORD PTR SS:[EBP-18],EDI
007800FD 8955 F8 MOV DWORD PTR SS:[EBP-8],EDX
00780100 ^7C A9 JL SHORT 007800AB
00780102 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
00780105 8B7D F0 MOV EDI,DWORD PTR SS:[EBP-10]
00780108 8B75 EC MOV ESI,DWORD PTR SS:[EBP-14]
0078010B 40 INC EAX
0078010C 83C7 03 ADD EDI,3
0078010F 83C6 09 ADD ESI,9
00780112 83F8 03 CMP EAX,3
00780115 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX
00780118 897D F0 MOV DWORD PTR SS:[EBP-10],EDI
0078011B 8975 EC MOV DWORD PTR SS:[EBP-14],ESI
0078011E ^0F8C 76FFFFFF JL 0078009A
00780124 8B45 E0 MOV EAX,DWORD PTR SS:[EBP-20]
00780127 41 INC ECX
00780128 48 DEC EAX
00780129 894D E4 MOV DWORD PTR SS:[EBP-1C],ECX
0078012C 8945 E0 MOV DWORD PTR SS:[EBP-20],EAX
0078012F ^0F85 52FFFFFF JNZ 00780087
00780135 90 NOP
00780136 90 NOP
00780137 90 NOP
00780138 CC INT3 //第二次触发int3事件,调试进程会检验keyInfo
2.产生算法alg2变换码表,
a.执行到上述chip代码片段 00780087 处,修正ebp-20,key长度为0x100,
当然,也可以在00780080执行前将该指令的立即数修改为0x100,
00780080 C745 E0 15000000 MOV DWORD PTR SS:[EBP-20],15
00780087 8D55 C4 LEA EDX,DWORD PTR SS:[EBP-3C]
b.修改注入的key,00770000缓冲区做为输入的key1(也作为输出的key2缓冲区)
修改为下述[0x00,0xFF]字符,这里由于用的是OD,修改起来有点麻烦
如果是IDA(WINDBG),直接执行下述两行IDAPython代码即可
for i in xrange(0,0x100):
PatchByte(0x770000+i,i)
没办法,一开始选择了OD,自己挖的坑,哭着也得跳,只能用笨办法补救了。
以下是鼠标手和键盘手展示时间,
OD修改 00770000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F,不用16秒吧
Binary复制上述一行,
将后续15行全部粘贴为00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F,不用48秒吧
对00770010行,"1"和"->"交替按键速度应该不用8秒吧,另外8秒歇息下逼下一招;
对00770020行,"2"和"->"交替按键速度应该不用8秒吧,另外8秒歇息下逼下一招;
...
对007700F0行,"F"和"->"交替按键速度应该不用8秒吧,另外8秒逼下一招;没有下一招了!
c.经过a,b的修改,和第一个int3附近的修正,直接断在上述chip代码片段尾部也是第二个int3处,
直跑断下,就会得到下述key对应的keyInfo码表。我们用keyInfo码表解密最终的比对key2,
就可得到alg1加密后的key1
00770000:[IN]key1
00770000 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00770010 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
00770020 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
00770030 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
00770040 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
00770050 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
00770060 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
00770070 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
00770080 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
00770090 90 91 92 93 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F
007700A0 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF
007700B0 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF
007700C0 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
007700D0 D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
007700E0 E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
007700F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF
007700F0 F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB 86 F9 FE FF
00770000:[OUT]key2Table for key2
$ ==> >00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
$+10 >00 86 12 13 14 70 16 17 18 19 1A 1B 1C 1D 1E 1F
$+20 >20 21 22 23 24 25 26 27 28 29 64 2B 2C 2D 2E 2F
$+30 >30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 28
$+40 >40 41 42 43 44 45 46 47 48 49 4A 4B 85 4D 4E 4F
$+50 >50 51 52 A6 54 55 56 57 58 59 5A EF 5C 5D 5E 5F
$+60 >60 61 62 63 64 65 66 67 68 04 6A 6B 6C 6D 6E 6F
$+70 >70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F
$+80 >80 81 82 83 84 85 86 87 88 89 8A 8B 90 8D 88 8F
$+90 >90 91 92 93 94 95 0C 97 98 99 9A 9B 9C 9D 9E 9F
$+A0 >A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB 00 AD AE AF
$+B0 >B0 C8 F4 B3 B4 B5 74 B7 B8 B9 BA BB BC BD BE 9E
$+C0 >C0 C1 C2 89 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF
$+D0 >D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 DA DB DC DD DE DF
$+E0 >E0 E1 E2 E3 E4 E5 E6 E7 E8 E9 EA EB EC ED EE EF
$+F0 >F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB 86 F9 FE FF
在上面的Hi_debugEvent_80000003_sub_401FA0函数中,追踪比对的key2从局部数组变量中提取为
key2 = [0xEF, 0x86, 0x85, 0x0C, 0xD2, 0x89, 0x64, 0xA6, 0x9E, 0xC8, 0xCC, 0x70, 0x00, 0x90, 0x09, 0xF4, 0x28, 0x6E, 0x5A, 0x04, 0xF9]
对于key2[0]=0xEF,通过查表,我们可以得到对应的key1[0]为 0x5B 或 0xEF
我们通过后面的anti_alg1类枚举key1对应的输入key,sar2xorIxor41h为alg1算法的python实现
由于alg1算法加密时前后关联的,所以只能从左到右一位一位来
anti_alg1(ktp = [],i=0,alg1v = 0x5b)函数中,ktp为已经确定的key前面的字符,i为要确定的第i个字符(0起),
alg1v为key[i]变换后的key1[i]的值
求key[0]
anti_alg1([],0,0x5B)
anti_alg1([],0,0xEF)
得到
68 5B h
69 5B i
6A 5B j
6B 5B k
94 5B
95 5B
96 5B
97 5B
即key[0]可以是"h","i","j","k"
对于key2[1]查表可得,key1[1]取值为 0x11,0x86,0xFC
求key[1]
>>> anti_alg1(['j'],1,0x11)
>>> anti_alg1(['j'],1,0x86)
>>> anti_alg1(['j'],1,0xFC)
>>> anti_alg1(['h'],1,0xFC)
>>> anti_alg1(['h'],1,0x86)
>>> anti_alg1(['h'],1,0x11)
>>> anti_alg1(['i'],1,0x11)
>>> anti_alg1(['i'],1,0x86)
>>> anti_alg1(['i'],1,0xFC)
>>> anti_alg1(['k'],1,0xFC)
00 5B
即这时候只能是key[0]="k",key[1]="\0"(有点不合常理,这个该整么输入?不急,继续往后看看)
同理key2[2]=0x85,对于key1[2]取值为 0x4C,0x85
>>> anti_alg1(['k','\0'],2,0x85)
>>> anti_alg1(['k','\0'],2,0x4C)
00 4C
得到key[2] = '\0'
再来求得下述依然是key[3]='\0'
>>> alg1(['k','\0','\0'],3,0x96)
00 96
这时候我就没再往后尝试了,直接在提交框提交"k",好吧,通过了,就这么简单
实际的key="k",等效于key="k\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
#-------
kt = create_stirng_buffer("\0"*0x15)
def anti_alg1(ktp = [],i=0,alg1v = 0x5b):
kt=create_string_buffer('\0'*0x15)
for j in xrange(0,ktp.__len__()):
kt[j] = ktp[j]
for ch in xrange(0,0x100):
kt[i] = chr(ch)
sar2xorIxor41h(kt)
if ord(kt[i])==alg1v:
print "{:02X} {:02X} {}".format(ch,ord(kt[0]),chr(ch))
def sar2xorIxor41h(kt):
esi = c_ulong(0)
eax = c_ulong(0)
cl = c_ubyte(0)
ecx = c_ulong(0)
ebx = c_ulong(0)
esi.value = c_byte(ord(kt[0])).value
edx = 0
while edx < 0x15:
cl.value = c_byte(ord(kt[0])).value
eax.value = esi.value
eax.value = c_long(eax.value).value >> 8
cl.value = c_byte(cl.value).value >> 2
eax.value = (eax.value ^ cl.value) & 0xFF
kt[edx]=chr(eax.value)
eax.value += esi.value
ecx.value = eax.value * 9
ecx.value = (ecx.value * 2) + eax.value
ecx.value = ecx.value * 3
ecx.value = ecx.value << 4
ecx.value += eax.value
eax.value = esi.value
eax.value = c_long(eax.value).value>>2
ebx.value = ecx.value * 3
ecx.value = eax.value
ecx.value = ecx.value << 5
ecx.value = ecx.value - eax.value
ecx.value = ecx.value * 3
ecx.value = eax.value + (ecx.value * 4)
ecx.value = ecx.value * 3
eax.value = eax.value + (ecx.value * 2)
eax.value = eax.value << 2
esi.value = eax.value + (ebx.value * 2)
kt[edx] = chr(ord(kt[edx]) ^ 0x41)
edx += 1
#-------
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课