这题涉及到去花和patch,难度不是很大
先file查看一下文件,32位ELF,运行一下
1 2 3
| ************** Echo Server 0.3 ALPHA **************
|
等待输入,随便输入一句
直接返回了ASCII码值
用IDA打开看看
1 2 3 4 5 6 7 8 9
| int __cdecl main() { setbuf(stdin, 0); setbuf(stdout, 0); dword_804A088 = 1; puts(" **************\n Echo Server 0.3 ALPHA\n **************"); ((void (*)(void))((char *)&loc_80487C1 + 3))(); return 0; }
|
main
函数没什么特别的,就是最后这个函数调用很古怪,所以进去看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| void __cdecl sub_804875D(unsigned __int8 *a1, unsigned int a2) { unsigned __int8 *v2; char v3; unsigned __int8 *v4; unsigned int i;
v4 = a1; if ( a1 ) { for ( i = 0; i < a2; ++i ) { v2 = v4++; printf("%02X", *v2); } } else { printf("NULL"); } putchar(10); JUMPOUT(v3, (char *)&loc_80487C1 + 1); JUMPOUT(!v3, (char *)&loc_80487C1 + 1); MEMORY[0x915A4B8F](); JUMPOUT(loc_80487C6); }
|
出现了访问内存越界的情况MEMORY[0x915A4B8F]()
,看到上面两句更加古怪
1 2
| JUMPOUT(v3, (char *)&loc_80487C1 + 1); JUMPOUT(!v3, (char *)&loc_80487C1 + 1);
|
这里很明显是强行创造多条分支,IDA使用递归行进算法,就会默认这条路径接这两个函数执行过后的指令是有效的而去扫描分析后面的指令,但实际上,后面是永远不会到达的指令,不管v3的值是多少,都会跳转到(char *)&loc_80487C1 + 1
这显然是一条花指令,所以查看一下汇编代码准备去花
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| .text:0804875D ; Attributes: bp-based frame .text:0804875D .text:0804875D sub_804875D proc near .text:0804875D .text:0804875D var_10 = dword ptr -10h .text:0804875D var_C = dword ptr -0Ch .text:0804875D arg_0 = dword ptr 8 .text:0804875D arg_4 = dword ptr 0Ch .text:0804875D .text:0804875D ; __unwind { .text:0804875D push ebp .text:0804875E mov ebp, esp .text:08048760 sub esp, 28h .text:08048763 mov eax, [ebp+arg_0] .text:08048766 mov [ebp+var_10], eax .text:08048769 cmp [ebp+arg_0], 0 .text:0804876D jnz short loc_804877D .text:0804876F mov dword ptr [esp], offset format ; "NULL" .text:08048776 call _printf .text:0804877B jmp short loc_80487B1 .text:0804877D ; --------------------------------------------------------------------------- .text:0804877D .text:0804877D loc_804877D: ; CODE XREF: sub_804875D+10↑j .text:0804877D mov [ebp+var_C], 0 .text:08048784 jmp short loc_80487A9 .text:08048786 ; --------------------------------------------------------------------------- .text:08048786 .text:08048786 loc_8048786: ; CODE XREF: sub_804875D+52↓j .text:08048786 mov eax, [ebp+var_10] .text:08048789 lea edx, [eax+1] .text:0804878C mov [ebp+var_10], edx .text:0804878F movzx eax, byte ptr [eax] .text:08048792 movzx eax, al .text:08048795 mov [esp+4], eax .text:08048799 mov dword ptr [esp], offset a02x ; "%02X" .text:080487A0 call _printf .text:080487A5 add [ebp+var_C], 1 .text:080487A9 .text:080487A9 loc_80487A9: ; CODE XREF: sub_804875D+27↑j .text:080487A9 mov eax, [ebp+var_C] .text:080487AC cmp eax, [ebp+arg_4] .text:080487AF jb short loc_8048786 .text:080487B1 .text:080487B1 loc_80487B1: ; CODE XREF: sub_804875D+1E↑j .text:080487B1 mov dword ptr [esp], 0Ah ; c .text:080487B8 call _putchar .text:080487BD jz short near ptr loc_80487C1+1 .text:080487BF jnz short near ptr loc_80487C1+1 .text:080487C1 .text:080487C1 loc_80487C1: ; CODE XREF: sub_804875D+60↑j .text:080487C1 ; sub_804875D+62↑j ... .text:080487C1 call near ptr 915A4B8Fh .text:080487C1 sub_804875D endp ; sp-analysis failed .text:080487C1 .text:080487C6 .text:080487C6 loc_80487C6: ; DMA page register 74LS612: .text:080487C6 in eax, 81h ; Channel 2 (diskette DMA) (address bits 16-23) .text:080487C8 in al, dx .text:080487C9 mov [eax], al .text:080487C9 ; ---------------------------------------------------------------------------
|
这个函数显然是不对的,堆栈本身并没有平衡,main
函数里跳到的地方是0x080487C4
已经被其它指令占了,这里去花一下,多余的指令码干脆用nop
patch掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| sub_804875D proc near .text:0804875D .text:0804875D var_10 = dword ptr -10h .text:0804875D var_C = dword ptr -0Ch .text:0804875D arg_0 = dword ptr 8 .text:0804875D arg_4 = dword ptr 0Ch .text:0804875D .text:0804875D ; __unwind { .text:0804875D push ebp .text:0804875E mov ebp, esp .text:08048760 sub esp, 28h .text:08048763 mov eax, [ebp+arg_0] .text:08048766 mov [ebp+var_10], eax .text:08048769 cmp [ebp+arg_0], 0 .text:0804876D jnz short loc_804877D .text:0804876F mov dword ptr [esp], offset format ; "NULL" .text:08048776 call _printf .text:0804877B jmp short loc_80487B1 .text:0804877D ; --------------------------------------------------------------------------- .text:0804877D .text:0804877D loc_804877D: ; CODE XREF: sub_804875D+10↑j .text:0804877D mov [ebp+var_C], 0 .text:08048784 jmp short loc_80487A9 .text:08048786 ; --------------------------------------------------------------------------- .text:08048786 .text:08048786 loc_8048786: ; CODE XREF: sub_804875D+52↓j .text:08048786 mov eax, [ebp+var_10] .text:08048789 lea edx, [eax+1] .text:0804878C mov [ebp+var_10], edx .text:0804878F movzx eax, byte ptr [eax] .text:08048792 movzx eax, al .text:08048795 mov [esp+4], eax .text:08048799 mov dword ptr [esp], offset a02x ; "%02X" .text:080487A0 call _printf .text:080487A5 add [ebp+var_C], 1 .text:080487A9 .text:080487A9 loc_80487A9: ; CODE XREF: sub_804875D+27↑j .text:080487A9 mov eax, [ebp+var_C] .text:080487AC cmp eax, [ebp+arg_4] .text:080487AF jb short loc_8048786 .text:080487B1 .text:080487B1 loc_80487B1: ; CODE XREF: sub_804875D+1E↑j .text:080487B1 mov dword ptr [esp], 0Ah ; c .text:080487B8 call _putchar .text:080487BD jz short locret_80487C2 .text:080487BF jnz short locret_80487C2 .text:080487C1 nop ; Keypatch modified this from: .text:080487C1 ; db 0E8h .text:080487C2 .text:080487C2 locret_80487C2: ; CODE XREF: sub_804875D+60↑j .text:080487C2 ; sub_804875D+62↑j .text:080487C2 leave .text:080487C3 retn .text:080487C3 ; } // starts at 804875D .text:080487C3 sub_804875D endp
|
这是这个函数本来的样子,而main
函数里面调用的根本就不是这个函数,而是下面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| .text:080487C4 ; --------------------------------------------------------------------------- .text:080487C4 ; __unwind { .text:080487C4 push ebp ; CODE XREF: main+49↓p .text:080487C5 mov ebp, esp .text:080487C7 sub esp, 88h .text:080487CD mov eax, large gs:14h .text:080487D3 mov [ebp-0Ch], eax .text:080487D6 xor eax, eax .text:080487D8 mov dword ptr [esp+8], 14h .text:080487E0 mov dword ptr [esp+4], 0 .text:080487E8 lea eax, [ebp-70h] .text:080487EB mov [esp], eax .text:080487EE call _memset .text:080487F3 .text:080487F3 loc_80487F3: ; CODE XREF: .text:loc_80487F3↑j .text:080487F3 jmp short near ptr loc_80487F3+1 .text:080487F3 ; --------------------------------------------------------------------------- .text:080487F5 db 0C0h .text:080487F6 db 48h ; H .text:080487F7 db 0C7h
|
下面并没有分析,因为又遇到了一个花指令
1
| .text:080487F3 jmp short near ptr loc_80487F3+1
|
代码重叠,分析一下本来的指令是什么
1 2 3 4 5 6 7 8 9 10 11
| text:080487F4 ; --------------------------------------------------------------------------- .text:080487F4 inc eax .text:080487F6 dec eax .text:080487F7 mov dword ptr [esp+8], 14h .text:080487FF lea eax, [ebp-70h] .text:08048802 mov [esp+4], eax .text:08048806 mov dword ptr [esp], 0 .text:0804880D call _read .text:08048812 xor eax, eax .text:08048814 jz short loc_804881D .text:08048816 jmp near ptr 6F44B961h
|
又来
1 2
| .text:08048812 xor eax, eax .text:08048814 jz short loc_804881D
|
自己和自己异或肯定是0,这个跳转是一定会实现的,后面的语句并没有什么用但是IDA还是进行了分析然后出错,patch掉继续看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| .text:08048816 nop ; Keypatch modified this from: .text:08048816 ; db 0E9h .text:08048816 ; --------------------------------------------------------------------------- .text:08048817 aF1Ga db 'F1@gA',0 .text:0804881D ; --------------------------------------------------------------------------- .text:0804881D .text:0804881D loc_804881D: ; CODE XREF: .text:08048814↑j .text:0804881D mov dword ptr [esp+8], 5 .text:08048825 mov dword ptr [esp+4], 8048817h .text:0804882D lea eax, [ebp-70h] .text:08048830 mov [esp], eax .text:08048833 call _strncmp .text:08048838 test eax, eax .text:0804883A jnz short loc_80488A3 .text:0804883C mov dword ptr [esp], offset aYouAreVeryClos ; "You are very close! Now patch me~" .text:08048843 call _puts .text:08048848 mov eax, ds:dword_804A088 .text:0804884D test eax, eax .text:0804884F jz short loc_8048866
|
去花之后变成了一个字符串,后面还有一个比较操作,如果输入这个字符串,就会输出You are very close! Now patch me~
后面肯定还有操作,往后面看
1 2 3
| .text:08048848 mov eax, ds:dword_804A088 .text:0804884D test eax, eax .text:0804884F jz short loc_8048866
|
这三句很关键,如果eax
是0,就会进行后面的跳转,所以查找一下ds:dword_804A088
交叉引用,在main
里面发现
1
| .text:08048913 mov ds:dword_804A088, 1
|
所以这个跳转是永远不会实现的,先看看不跳转后面会执行什么
1 2 3 4
| .text:08048851 loc_8048851: ; CODE XREF: .text:08048857↓j .text:08048851 mov ax, 5EBh .text:08048855 xor eax, eax .text:08048857 jz short near ptr loc_8048851+1
|
又是一个花指令,重新分析这几句
1 2 3
| .text:08048852 loc_8048852: ; CODE XREF: .text:08048857↓j .text:08048852 mov eax, 0C03105EBh .text:08048857 jz short loc_8048852
|
发现进入了一个死循环,所以题目让我们patch的意思是不走这个循环,也就是真正应该执行的是前面没走的那个分支
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| loc_8048866: ; CODE XREF: .text:0804884F↑j .text:08048866 lea eax, [ebp-70h] .text:08048869 mov [esp], eax .text:0804886C call _strlen .text:08048871 mov dword ptr [esp+8], 0 .text:08048879 mov [esp+4], eax .text:0804887D lea eax, [ebp-70h] .text:08048880 add eax, 1 .text:08048883 mov [esp], eax .text:08048886 call _MD5 .text:0804888B mov [ebp-74h], eax .text:0804888E mov dword ptr [esp+4], 10h .text:08048896 mov eax, [ebp-74h] .text:08048899 mov [esp], eax .text:0804889C call sub_804875D .text:080488A1 jmp short loc_80488C0 .text:080488A3 ; --------------------------------------------------------------------------- .text:080488A3 .text:080488A3 loc_80488A3: ; CODE XREF: .text:0804883A↑j .text:080488A3 lea eax, [ebp-70h] .text:080488A6 mov [esp], eax .text:080488A9 call _strlen .text:080488AE sub eax, 1 .text:080488B1 mov [esp+4], eax .text:080488B5 lea eax, [ebp-70h] .text:080488B8 mov [esp], eax .text:080488BB call sub_804875D .text:080488C0 .text:080488C0 loc_80488C0: ; CODE XREF: .text:080488A1↑j .text:080488C0 mov eax, ds:stdout .text:080488C5 mov [esp], eax .text:080488C8 call _fflush .text:080488CD mov eax, [ebp-0Ch] .text:080488D0 xor eax, large gs:14h .text:080488D7 jz short locret_80488DE .text:080488D9 call ___stack_chk_fail .text:080488DE ; --------------------------------------------------------------------------- .text:080488DE .text:080488DE locret_80488DE: ; CODE XREF: .text:080488D7↑j .text:080488DE leave .text:080488DF retn .text:080488DF ; } // starts at 80487C4
|
执行到结束,计算了一个md5
然后调用最开始分析的sub_804875D
函数以16进制的形式输出,所以这里有两个选择,有linux环境可以直接patch跳转语句然后执行得到flag
1 2 3 4 5 6 7
| ->./echo-server ************** Echo Server 0.3 ALPHA ************** F1@gA You are very close! Now patch me~ F8C60EB40BF66919A77C4BD88D45DEF4
|
当然也可以自己算md5
,看看关键函数
1 2 3 4 5 6 7 8 9
| .text:08048866 lea eax, [ebp-70h] .text:08048869 mov [esp], eax .text:0804886C call _strlen .text:08048871 mov dword ptr [esp+8], 0 .text:08048879 mov [esp+4], eax .text:0804887D lea eax, [ebp-70h] .text:08048880 add eax, 1 .text:08048883 mov [esp], eax .text:08048886 call _MD5
|
首先是把我们输入的字符串的地址拷贝到eax
中,把该地址写到栈顶,调用_strlen
,求的就是我们输入的字符串的长度,我们输入的是'F1@gA\n'
,返回的值是0x06
,储存在eax中,写到了栈顶的第二个位置(第三个位置的0并不知道什么意思,或许是第三个参数,没查到函数原型,暂时先不管了),但是栈顶元素并不是我们输入的字符串,而是首地址加1的位置,长度还是6,所以要用md5
加密的字符串应该是'1@gA\x0a\x00'
,python
调用hashlib
算一下
1 2 3 4 5
| import hashlib m=hashlib.md5() m.update(b'1@gA\x0a\x00') m.hexdigest().upper()
|
两种方法都可以得到flag
1
| F8C60EB40BF66919A77C4BD88D45DEF4
|
其实这题并没有什么难度,为什么写这么长呢?
(实在不想写数据结构作业)
Comments