2015_NSCTF_Reverse1_writeup
链接:http://pan.baidu.com/s/1x6Ywm 密码:j4we
OD加载程序 alt+M,对rsrc区段F2下断
执行到窗口提示输入:输入如图字符串nsF0cuS!x01
但这是错误的= =。
如何找到正确flag?
1、OD加载程序,F9运行起来,此时弹出窗口提示输入密码。OD右键“查看”——>“所有参考文本字串”:
如果首次运行没有看到字串,跳到“%s”的地方,重新分析下代码就好。
2、下面是进行第一判定的函数,如果输入字符串“nsF0cuS!x01 ”则第一轮验证通过,为了方便调试,我们输入字符串之前在004010DF处设断点,接着输入程序期望的字符串“nsF0cuS!x01 ” ,回车后程序断在004010DF1。
00401086 |. 68 FF000000 push 0xFF ; /n = FF (255.)
0040108B |. 8D85 FDFEFFFF lea eax,dword ptr ss:[ebp-0x103] ; |
00401091 |. C685 FCFEFFFF>mov byte ptr ss:[ebp-0x104],0x0 ; |
00401098 |. 6A 00 push 0x0 ; |c = 00
0040109A |. 50 push eax ; |s
0040109B |. E8 14090000 call Reverse0.004019B4 ; \memset
004010A0 |. 8B35 94204000 mov esi,dword ptr ds:[0x402094] ; msvcr120.printf
004010A6 |. 68 50214000 push Reverse0.00402150 ; /format = "please input ns-ctf password: "
004010AB |. FFD6 call esi ; \printf
004010AD |. 8B1D 90204000 mov ebx,dword ptr ds:[0x402090] ; msvcr120.scanf_s
004010B3 |. 8D85 FCFEFFFF lea eax,[local.65]
004010B9 |. 50 push eax
004010BA |. 68 70214000 push Reverse0.00402170 ; ASCII "%s"
004010BF |. FFD3 call ebx
004010B9 |. 50 push eax
004010BA |. 68 70214000 push Reverse0.00402170 ; ASCII "%s"
004010BF |. FFD3 call ebx
004010C1 |. 6A 0B push 0xB ; /maxlen = B (11.)
004010C3 |. 8D85 FCFEFFFF lea eax,[local.65] ; |
004010C9 |. BF 01000000 mov edi,0x1 ; | 初始化edi=1
004010CE |. 50 push eax ; |s2
004010CF |. 68 10214000 push Reverse0.00402110 ; |s1 = "nsF0cuS!x01"
004010D4 |. FF15 9C204000 call dword ptr ds:[0x40209C] ; \strncmp
004010DA |. 83C4 24 add esp,0x24
004010D4 |. FF15 9C204000 call dword ptr ds:[0x40209C] ; \strncmp
004010DA |. 83C4 24 add esp,0x24
004010DD |. 85C0 test eax,eax
004010DF |. 74 4B je XReverse0.0040112C ;判断部分,验证通过跳转,跳转到
004010E1 |> 68 74214000 /push Reverse0.00402174 ; ASCII "try again!\n",下面这一段为输错之后要重新输入执行的代码
004010E6 |. FFD6 |call esi
004010E8 |. 68 00010000 |push 0x100 ; /n = 100 (256.)
004010ED |. 8D85 FCFEFFFF |lea eax,[local.65] ; |
004010E8 |. 68 00010000 |push 0x100 ; /n = 100 (256.)
004010ED |. 8D85 FCFEFFFF |lea eax,[local.65] ; |
004010F3 |. 6A 00 |push 0x0 ; |c = 00
004010F5 |. 50 |push eax ; |s
004010F6 |. E8 B9080000 |call Reverse0.004019B4 ; \memset
004010FB |. 68 50214000 |push Reverse0.00402150 ; ASCII "please input ns-ctf password: "
00401100 |. FFD6 |call esi
00401102 |. 8D85 FCFEFFFF |lea eax,[local.65]
004010FB |. 68 50214000 |push Reverse0.00402150 ; ASCII "please input ns-ctf password: "
00401100 |. FFD6 |call esi
00401102 |. 8D85 FCFEFFFF |lea eax,[local.65]
00401108 |. 50 |push eax
00401109 |. 68 70214000 |push Reverse0.00402170 ; ASCII "%s"
0040110E |. FFD3 |call ebx
00401110 |. 6A 0B |push 0xB ; /maxlen = B (11.)
00401112 |. 8D85 FCFEFFFF |lea eax,[local.65] ; |
00401118 |. 47 |inc edi ; | 出错一次edi+1
00401119 |. 50 |push eax ; |s2
0040111A |. 68 10214000 |push Reverse0.00402110 ; |s1 = "nsF0cuS!x01"
0040111A |. 68 10214000 |push Reverse0.00402110 ; |s1 = "nsF0cuS!x01"
0040111F |. FF15 9C204000 |call dword ptr ds:[0x40209C] ; \strncmp 比较函数
00401125 |. 83C4 28 |add esp,0x28
00401128 |. 85C0 |test eax,eax
0040112A |.^ 75 B5 \jnz XReverse0.004010E1 ;验证失败跳转到“try again!\n”
上面的函数很清晰明了了,我们看看验证通过之后程序还会做些什么。
3、F8单步运行,程序跳转到这里:
0040112C |> \8D8D FCFEFFFF lea ecx,[local.65] ; F8单步运行,这句之后寄存器窗口查看ECX=0012FE7C,保存了“nsF0cuS!x01 ”
00401132 |. C705 68334000>mov dword ptr ds:[0x403368],0x1
0040113C |. 8D51 01 lea edx,dword ptr ds:[ecx+0x1] ;edx=“sF0cuS!x01 ”
0040113F |. 90 nop
00401140 |> 8A01 /mov al,byte ptr ds:[ecx]
00401142 |. 41 |inc ecx
00401143 |. 84C0 |test al,al
00401140 |> 8A01 /mov al,byte ptr ds:[ecx]
00401142 |. 41 |inc ecx
00401143 |. 84C0 |test al,al
00401145 |.^ 75 F9 \jnz XReverse0.00401140
00401147 |. 2BCA sub ecx,edx ;计算“sF0cuS!x01 ”长度
00401149 |. 74 27 je XReverse0.00401172
0040114B |. 83FF 03 cmp edi,0x3 ;edi表示判断的次数(上面一段程序中有定义),这里将edi和3比较
0040114E |. 7E 18 jle XReverse0.00401168 ;edi=<3跳转
00401150 |. E8 ABFEFFFF call Reverse0.00401000
4.上面的0040114B一行是关键的一行,这里将edi和3进行比较,当小于等于3的时候跳到下面:
00401168 |> \68 1C214000 push Reverse0.0040211C ; ASCII "flag:{NSCTF_md5065ca>01??ab7e0f4>>a701c>cd17340}"
0040116D |. FFD6 call esi
到这里,屏幕打印出"flag:{NSCTF_md5065ca>01??ab7e0f4>>a701c>cd17340}",高高兴兴地拿去提交吧
- -。可是这还不是正确答案,为什么呢?这串字符看起来像是加密过的,于是联想到0040114B这行代码,如果edi>3会怎么样呢?edi又是什么?我们往回找edi,在004010C9行将edi初始化为1,00401118一行,edi在比较函数里面出现了,出错一次edi+1,即edi代表验证的次数,照此推理,只需要验证次数达到4次,0040114E这一行就不会跳转。我们来验证一下。
5.重新运行程序,在00401118、0040114B处下断点,输入3次错误密码,程序运行后edi=4,与3比较后进入函数Reverse0.00401000 :
6.在函数Reverse0.00401000 里找到了解密函数,鼠标选中00401043,数据窗口跟随内存地址,可以看到内存中的数据变化。
00401021 |. 6A 30 push 0x30
00401023 |. 68 1C214000 push Reverse0.0040211C ; ASCII "flag:{NSCTF_md5065ca>01??ab7e0f4>>a701c>cd17340}"
00401028 |. 8D45 C8 lea eax,[local.14]
0040102B |. 6A 31 push 0x31
0040102D |. 50 push eax
0040102E |. FF15 8C204000 call dword ptr ds:[0x40208C] ; msvcr120.strncpy_s
00401034 |. 83C4 1C add esp,0x1C
00401037 |. 8D45 D7 lea eax,dword ptr ss:[ebp-0x29]
0040103A |. 807D D7 7D cmp byte ptr ss:[ebp-0x29],0x7D
0040103E |. 74 0B je XReverse0.0040104B
00401040 |> 8030 07 /xor byte ptr ds:[eax],0x7
00401043 |. 8D40 01 |lea eax,dword ptr ds:[eax+0x1]
00401023 |. 68 1C214000 push Reverse0.0040211C ; ASCII "flag:{NSCTF_md5065ca>01??ab7e0f4>>a701c>cd17340}"
00401028 |. 8D45 C8 lea eax,[local.14]
0040102B |. 6A 31 push 0x31
0040102D |. 50 push eax
0040102E |. FF15 8C204000 call dword ptr ds:[0x40208C] ; msvcr120.strncpy_s
00401034 |. 83C4 1C add esp,0x1C
00401037 |. 8D45 D7 lea eax,dword ptr ss:[ebp-0x29]
0040103A |. 807D D7 7D cmp byte ptr ss:[ebp-0x29],0x7D
0040103E |. 74 0B je XReverse0.0040104B
00401040 |> 8030 07 /xor byte ptr ds:[eax],0x7
00401043 |. 8D40 01 |lea eax,dword ptr ds:[eax+0x1]
00401046 |. 8038 7D |cmp byte ptr ds:[eax],0x7D
00401049 |.^ 75 F5 \jnz XReverse0.00401040 ;解密算法,每个字节与7抑或,遇到}结束。
到这里答案就出来啦,将原字符串“NSCTF_md5”后的字符串替换成解密后的字符就对啦~~
flag:{NSCTF_md5 712df97688fe0b7a399f076d9dc60437}
ps:初学逆向,欢迎指正!感谢Lnju的指点(http://www.plaype.cc/)~~~
by.Vi