lab1B
lab1B@warzone:/levels/lab01$ file lab1B lab1B: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=c9f07b581bd8d97cdc7c0ff1a288e20aea2df0f5, stripped
继续R2 analyze all (aaa)
lab1B@warzone:/levels/lab01$ r2 lab1B -- command not found: calc.exe [0x08048850] aaa
list all functions (afl)
[0x08048850] afl 0x08048850 34 1 entry0 0x08048820 6 1 sym.imp.__libc_start_main 0x08048826 10 2 fcn.08048826 0x08048760 12 1 section..plt 0x0804876c 10 1 sub.strcmp_12_76c 0x08048776 10 1 fcn.08048776 0x08048780 6 1 sym.imp.printf 0x08048786 10 1 fcn.08048786 0x08048790 6 1 sym.imp.fflush 0x08048796 10 1 fcn.08048796 0x080487a0 6 1 sym.imp.getchar 0x080487a6 10 1 fcn.080487a6 0x080487b0 6 1 sym.imp.time 0x080487b6 10 1 fcn.080487b6 0x080487c0 6 1 sym.imp.__stack_chk_fail 0x080487c6 10 1 fcn.080487c6 0x080487d0 6 1 sym.imp.puts 0x08048830 6 1 sym.imp.rand 0x08048836 10 1 fcn.08048836 0x08048840 6 1 sym.imp.__isoc99_scanf 0x08048846 10 1 fcn.08048846 0x08048880 4 1 fcn.08048880 0x08048890 42 4 fcn.08048890 0x080488ba 61 4 fcn.080488ba 0x080488f7 39 3 fcn.080488f7 0x08048920 45 8 fcn.08048920 0x0804894d 34 5 sym.clear_stdin 0x0804896f 55 1 sym.get_unum 0x080489a6 17 1 sym.prog_timeout 0x080489b7 189 11 sym.decrypt 0x08048a74 368 3 sym.test 0x08048be4 133 3 sym.main 0x08048800 6 1 sym.imp.srand 0x08048806 10 1 fcn.08048806 0x08048810 6 1 sym.imp.strlen 0x08048816 10 1 fcn.08048816 0x08048c70 97 4 sym.__libc_csu_init 0x08048cd1 17 2 fcn.08048cd1 0x08048ce2 22 1 section_end..text 0x08048738 35 3 section..init 0x080487f0 6 1 sym.imp.__gmon_start__ 0x080487f6 10 1 fcn.080487f6 0x0804875b 17 1 section_end..init 0x080487e0 6 1 sym.imp.system 0x080487e6 10 1 fcn.080487e6
main 函数里面
密码输入提示在 前两行代码里面,str.npassword, call sym.imp.printf;
后面scanf 提示用户输入密码,获取到的两个参数,一个是 [esp+0x1c] 一个是 0x8048dee。
| 0x08048c2e c70424e28d04. mov dword [esp], str._nPassword: ; [0x8048de2:4]=0x7361500a ; str._nPassword: │ 0x08048c35 e846fbffff call sym.imp.printf ;sym.imp.printf() │ 0x08048c3a 8d44241c lea eax, [esp + 0x1c] ; 0x1c │ 0x08048c3e 89442404 mov dword [esp + 4], eax ; [0x4:4]=0x10101 │ 0x08048c42 c70424ee8d04. mov dword [esp], 0x8048dee ; [0x8048dee:4]=0x6425 │ 0x08048c49 e8f2fbffff call sym.imp.__isoc99_scanf ;sym.imp.__isoc99_scanf()
打印0x8048dee ps(print string)
[0x08048850] ps @ 0x8048dee %d
输入之后跟着的代码是 把用户输入的密码 和 0x1337d00d 一起传入了 自定义函数 test;
| 0x08048c4e 8b44241c mov eax, dword [esp + 0x1c] ; [0x1c:4]=52 │ 0x08048c52 c74424040dd0. mov dword [esp + 4], 0x1337d00d ; [0x1337d00d:4]=-1 │ 0x08048c5a 890424 mov dword [esp], eax │ 0x08048c5d e812feffff call sym.test ;sym.test()
反推出来的C代码应该是这样色的
int userinput; printf("password: \n"); scanf("%d", &userinput); test(userinput, 0x1337d00d);
接着看 test 函数
[0x08048850] pdf @ sym.test ╒ (fcn) sym.test 368 │ ; arg int arg_2 @ ebp+0x8 │ ; arg int arg_3 @ ebp+0xc │ ; arg int arg_5_1 @ ebp+0x15 │ ; var int local_3 @ ebp-0xc │ ; CALL XREF from 0x08048c5d (sym.main) │ ;-- sym.test: │ 0x08048a74 55 push ebp │ 0x08048a75 89e5 mov ebp, esp │ 0x08048a77 83ec28 sub esp, 0x28
首先,我们先找到传入进来的2个 参数, arg2 arg3 ,此时栈的状态是
ebp + 0x0 [ saved ebp ] <— push ebp
ebp + 0x4 [ return address ] <—return address pushed by call-instrunction
ebp + 0x8 [integer userinput] <— 1st argument
ebp + 0xc [constant 0x1337d00d] <— 2nd argument
go on
│ 0x08048a74 55 push ebp │ 0x08048a75 89e5 mov ebp, esp │ 0x08048a77 83ec28 sub esp, 0x28 │ 0x08048a7a 8b4508 mov eax, dword [ebp + 8] ; [0x8:4]=0 │ 0x08048a7d 8b550c mov edx, dword [ebp + 0xc] ; [0xc:4]=0 │ 0x08048a80 29c2 sub edx, eax │ 0x08048a82 89d0 mov eax, edx │ 0x08048a84 8945f4 mov dword [ebp-local_3], eax │ 0x08048a87 837df415 cmp dword [ebp-local_3], 0x15 ; [0x15:4]=0x50000000 │ ┌─< 0x08048a8b 0f8744010000 ja 0x8048bd5
eax(userinput) edx(constant) 然后 edx – eax 最后把 值 再赋给eax
然后把eax的值 传给 dword[ebp-local_3] , 再与0x15 compare;
反推出来的C代码是这样几的;
void test(int userinput, int constant) { int result; result = constant - userinput; if(result <= 0x15){ }else{ 0x8048bd5; } return; }
如果没有大于 0x15 ,那就不会 jmp 到 0x8048bd5, 所以就说明 constant(0x1337d00d) 与userinput 的差 小于0x15;
那么继续跟踪代码如下:
│ │ 0x08048a91 8b45f4 mov eax, dword [ebp-local_3] │ │ 0x08048a94 c1e002 shl eax, 2 │ │ 0x08048a97 05308d0408 add eax, 0x8048d30 │ │ 0x08048a9c 8b00 mov eax, dword [eax] │ │ 0x08048a9e ffe0 jmp eax │ │ 0x08048aa0 8b45f4 mov eax, dword [ebp-local_3] │ │ 0x08048aa3 890424 mov dword [esp], eax │ │ 0x08048aa6 e80cffffff call sym.decrypt ;sym.decrypt()
把 [ebp-local_3] 给 eax(两者的差),然后算数左移 2位 之后再加上 0x8048d30,再把 [eax] 的值给eax,然后 jmp eax;
把 差传递给 eax,把 差 入栈;调用函数 sym.decrypt;
这段汇编 反推出来的C 代码应该是this:
if(result <= 0x15){ void *ptr = (void*)(result << 2); ptr += 0x8048d30; goto *ptr; }
现在让我们看一下 result<= 15 存在可能继续的地址;
result | result << 2 | ptr += 0x8048d30 |
---|---|---|
0x15 | 0x54 | 0x8048d84 |
0x14 | 0x50 | 0x8048d80 |
0x13 | 0x4c | 0x8048d7c |
0x12 | 0x48 | 0x8048d78 |
··· | ||
0x01 | 0x04 | 0x8048d34 |
0x00 | 0x00 | 0x8048d30 |
范围从 0x8048d30(0x00) 到 0x8048d84(0x15)
[0x08048850]> pxw 88 @ 0x8048d30 0x08048d30 0x08048bd5 0x08048aa0 0x08048ab0 0x08048ac0 ................ 0x08048d40 0x08048ad0 0x08048ae0 0x08048af0 0x08048b00 ................ 0x08048d50 0x08048b10 0x08048b20 0x08048b30 0x08048b40 .... ...0...@... 0x08048d60 0x08048b50 0x08048b60 0x08048b6d 0x08048b7a P...`...m...z... 0x08048d70 0x08048b87 0x08048b94 0x08048ba1 0x08048bae ................ 0x08048d80 0x08048bbb 0x08048bc8 ........
88bytes(= 0x15= 22dwords)
现在拿到了 jmp eax 的值; 也就是 goto *ptr 的值, 现在我们看看这些地址的汇编代码是怎么样的
例如:0x8048d30(result=0x00)
pd(print disassmeble)
[0x08048850] > pd 5 @ [0x8048d30] ; JMP XREF from 0x08048a8b (sym.test) │ 0x08048bd5 e856fcffff call sym.imp.rand ;sym.imp.rand() │ 0x08048bda 890424 mov dword [esp], eax │ 0x08048bdd e8d5fdffff call sym.decrypt ;sym.decrypt() │ 0x08048be2 c9 leave ╘ 0x08048be3 c3 ret
call imp.rand() 调用随机数,然后传给eax, 再 call decrypt();
其他结果:
|result=0x01, 0x02, 0x03 look like this;
[0x08048850]> pd 4 @ [0x8048d34] │ 0x08048aa0 8b45f4 mov eax, dword [ebp-local_3] │ 0x08048aa3 890424 mov dword [esp], eax │ 0x08048aa6 e80cffffff call sym.decrypt ;sym.decrypt() │ 0x08048aab e932010000 jmp 0x8048be2 [0x08048850]> pd 4 @ [0x8048d38] │ 0x08048ab0 8b45f4 mov eax, dword [ebp-local_3] │ 0x08048ab3 890424 mov dword [esp], eax │ 0x08048ab6 e8fcfeffff call sym.decrypt ;sym.decrypt() │ 0x08048abb e922010000 jmp 0x8048be2 [0x08048850]> pd 4 @ [0x8048d3c] │ 0x08048ac0 8b45f4 mov eax, dword [ebp-local_3] │ 0x08048ac3 890424 mov dword [esp], eax │ 0x08048ac6 e8ecfeffff call sym.decrypt ;sym.decrypt() │ 0x08048acb e912010000 jmp 0x8048be2
调用4条重复的汇编指令,将 [ebp-local_3] (constant-userinput的差)作为参数,call decrypt() functions;ret 后 jmp 0x8048be2 sym.test functions的末尾;
so let’s have a look at the function sys.decrypt
[0x08048850]> pdf @ sym.decrypt ╒ (fcn) sym.decrypt 189 │ ; arg int arg_2 @ ebp+0x8 │ ; arg int arg_215506719_1 @ ebp+0x33617c7d │ ; arg int arg_433691864 @ ebp+0x67667360 │ ; arg int arg_492773204_1 @ ebp+0x757c7d51 │ ; arg int arg_517577951_2 @ ebp+0x7b66737e │ ; var int local_3 @ ebp-0xc │ ; var int local_3_1 @ ebp-0xd │ ; var int local_7_1 @ ebp-0x1d │ ; var int local_9 @ ebp-0x24 │ ; var int local_10 @ ebp-0x28 │ ; CALL XREF from 0x08048bdd (sym.test) │ ;-- sym.decrypt: │ 0x080489b7 55 push ebp │ 0x080489b8 89e5 mov ebp, esp │ 0x080489ba 83ec38 sub esp, 0x38 │ 0x080489bd 65a114000000 mov eax, dword gs:[0x14] ; [0x14:4]=1 │ 0x080489c3 8945f4 mov dword [ebp-local_3], eax │ 0x080489c6 31c0 xor eax, eax │ 0x080489c8 c745e3517d7c. mov dword [ebp-local_7_1], 0x757c7d51 ; [0x757c7d51:4]=-1 │ 0x080489cf c745e7607366. mov dword [ebp - 0x19], 0x67667360 ; [0x67667360:4]=-1 │ 0x080489d6 c745eb7e7366. mov dword [ebp - 0x15], 0x7b66737e ; [0x7b66737e:4]=-1 │ 0x080489dd c745ef7d7c61. mov dword [ebp - 0x11], 0x33617c7d ; [0x33617c7d:4]=-1 │ 0x080489e4 c645f300 mov byte [ebp-local_3_1], 0 │ 0x080489e8 50 push eax │ 0x080489e9 31c0 xor eax, eax │ ┌─< 0x080489eb 7403 je 0x80489f0 │ │ 0x080489ed 83c404 add esp, 4 │ └ ; JMP XREF from 0x080489eb (sym.decrypt) │ └─> 0x080489f0 58 pop eax │ 0x080489f1 8d45e3 lea eax, [ebp-local_7_1] │ 0x080489f4 890424 mov dword [esp], eax │ 0x080489f7 e814feffff call sym.imp.strlen ;sym.imp.strlen() │ 0x080489fc 8945dc mov dword [ebp-local_9], eax │ 0x080489ff c745d8000000. mov dword [ebp-local_10], 0 │ ┌──< 0x08048a06 eb20 jmp 0x8048a28 │ ┌ ; JMP XREF from 0x08048a2e (sym.decrypt) │ ┌───> 0x08048a08 8d55e3 lea edx, [ebp-local_7_1] │ ││ 0x08048a0b 8b45d8 mov eax, dword [ebp-local_10] │ ││ 0x08048a0e 01d0 add eax, edx │ ││ 0x08048a10 0fb600 movzx eax, byte [eax] │ ││ 0x08048a13 89c2 mov edx, eax │ ││ 0x08048a15 8b4508 mov eax, dword [ebp + 8] ; [0x8:4]=0 │ ││ 0x08048a18 31d0 xor eax, edx │ ││ 0x08048a1a 8d4de3 lea ecx, [ebp-local_7_1] │ ││ 0x08048a1d 8b55d8 mov edx, dword [ebp-local_10] │ ││ 0x08048a20 01ca add edx, ecx │ ││ 0x08048a22 8802 mov byte [edx], al │ ││ 0x08048a24 8345d801 add dword [ebp-local_10], 1 │ │└ ; JMP XREF from 0x08048a06 (sym.decrypt) │ │└──> 0x08048a28 8b45d8 mov eax, dword [ebp-local_10] │ │ 0x08048a2b 3b45dc cmp eax, dword [ebp-local_9] │ └───< 0x08048a2e 72d8 jb 0x8048a08 │ 0x08048a30 c7442404038d. mov dword [esp + 4], str.Congratulations_ ; [0x8048d03:4]=0x676e6f43 ; "Congratulations!" @ 0x8048d03 │ 0x08048a38 8d45e3 lea eax, [ebp-local_7_1] │ 0x08048a3b 890424 mov dword [esp], eax │ 0x08048a3e e82dfdffff call sym.imp.strcmp ; sub.strcmp_12_76c+0x4 ;sub.strcmp_12_76c() ; sym.imp.strcmp │ 0x08048a43 85c0 test eax, eax │ ┌────< 0x08048a45 750e jne 0x8048a55 │ │ 0x08048a47 c70424148d04. mov dword [esp], str._bin_sh ; [0x8048d14:4]=0x6e69622f ; "/bin/sh" @ 0x8048d14 │ │ 0x08048a4e e88dfdffff call sym.imp.system ;sym.imp.system() │ ┌─────< 0x08048a53 eb0c jmp 0x8048a61 │ │└ ; JMP XREF from 0x08048a45 (sym.decrypt) │ │└────> 0x08048a55 c704241c8d04. mov dword [esp], str._nInvalid_Password_ ; [0x8048d1c:4]=0x766e490a ; str._nInvalid_Password_ │ │ 0x08048a5c e86ffdffff call sym.imp.puts ;sym.imp.puts() │ └ ; JMP XREF from 0x08048a53 (sym.decrypt) │ └─────> 0x08048a61 8b45f4 mov eax, dword [ebp-local_3] │ 0x08048a64 653305140000. xor eax, dword gs:[0x14] │ 0x08048a6b 7405 je 0x8048a72 │ 0x08048a6d e84efdffff call sym.imp.__stack_chk_fail ;sym.imp.__stack_chk_fail() │ ; JMP XREF from 0x08048a6b (sym.decrypt) │ 0x08048a72 c9 leave ╘ 0x08048a73 c3 ret
如我们所见, this funcstions takes one arguments witch can be accessed with ebp+0x8 ( ; arg int arg_2 @ ebp+0x8) (传进来的差)
在第21-24行 有4个dword 和 1 个 byte 在 栈上 ,look like this
│ 0x080489c8 c745e3517d7c. mov dword [ebp-local_7_1], 0x757c7d51 ; [0x757c7d51:4]=-1 │ 0x080489cf c745e7607366. mov dword [ebp - 0x19], 0x67667360 ; [0x67667360:4]=-1 │ 0x080489d6 c745eb7e7366. mov dword [ebp - 0x15], 0x7b66737e ; [0x7b66737e:4]=-1 │ 0x080489dd c745ef7d7c61. mov dword [ebp - 0x11], 0x33617c7d ; [0x33617c7d:4]=-1 │ 0x080489e4 c645f300 mov byte [ebp-local_3_1], 0
这些指令被执行后,栈中的存放的值如下:
ebp-0x1d( ; var int local_7_1 @ ebp-0x1d) :517d7c75607366677e73667b7d7c613300
结合后面指令的strlen来看, 这段值看起来像是个字符串;
[0x08048850]> !python -c 'print("517d7c75607366677e73667b7d7c613300".decode("hex"))' Q}|u`sfg~sf{}|a3
continue reversing the function decrypt,The function strlen is called, The pass argument is a reference the string we just found [ebp-0x1d] (Q}|u`sfg~sf{}|a3)
│ 0x080489f1 8d45e3 lea eax, [ebp-local_7_1] │ 0x080489f4 890424 mov dword [esp], eax │ 0x080489f7 e814feffff call sym.imp.strlen ;sym.imp.strlen()
the return length of the string stored in eax is move to local variable [ebp-local_9], another local variable [ebp-local_10] is initialized with 0;
│ 0x080489fc 8945dc mov dword [ebp-local_9], eax │ 0x080489ff c745d8000000. mov dword [ebp-local_10], 0
continue , The disassemble look like is a loop-condition
│ ┌───> 0x08048a08 8d55e3 lea edx, [ebp-local_7_1] │ ││ 0x08048a0b 8b45d8 mov eax, dword [ebp-local_10] │ ││ 0x08048a0e 01d0 add eax, edx │ ││ 0x08048a10 0fb600 movzx eax, byte [eax] │ ││ 0x08048a13 89c2 mov edx, eax │ ││ 0x08048a15 8b4508 mov eax, dword [ebp + 8] ; [0x8:4]=0 │ ││ 0x08048a18 31d0 xor eax, edx │ ││ 0x08048a1a 8d4de3 lea ecx, [ebp-local_7_1] │ ││ 0x08048a1d 8b55d8 mov edx, dword [ebp-local_10] │ ││ 0x08048a20 01ca add edx, ecx │ ││ 0x08048a22 8802 mov byte [edx], al │ ││ 0x08048a24 8345d801 add dword [ebp-local_10], 1 │ │└ ; JMP XREF from 0x08048a06 (sym.decrypt) │ │└──> 0x08048a28 8b45d8 mov eax, dword [ebp-local_10] │ │ 0x08048a2b 3b45dc cmp eax, dword [ebp-local_9] │ └───< 0x08048a2e 72d8 jb 0x8048a08
The loop-body load the baseaddress of the string(line 40) and adds the loop-counter(line 41)。
after the execution of the 44 line, edx stored a single chr through a loop-counter; The chr is xor with value stored at [ebp + 8](line 46)
The XORed chr is writte back to the string ,At last the loop-counter is increment;
The decrypt function look like this :
void decrypt(int v){ char str[] = "Q}|u`sfg~sf{}|a3"; int len = strlen(str); for(i=0; i < len; i++){ str[i] = str[i] ^ v; } }
After the loop there is call the strcmp function
│ 0x08048a30 c7442404038d. mov dword [esp + 4], str.Congratulations_ ; [0x8048d03:4]=0x676e6f43 ; "Congratulations!" @ 0x8048d03 │ 0x08048a38 8d45e3 lea eax, [ebp-local_7_1] │ 0x08048a3b 890424 mov dword [esp], eax │ 0x08048a3e e82dfdffff call sym.imp.strcmp ; sub.strcmp_12_76c+0x4 ;sub.strcmp_12_76c() ; sym.imp.strcmp
The first string being compared is “Congratulations!”, The second string stored at [ebp-local_7_1] is the string has just been Xored in the loop;
│ 0x08048a43 85c0 test eax, eax │ ┌────< 0x08048a45 750e jne 0x8048a55 │ │ 0x08048a47 c70424148d04. mov dword [esp], str._bin_sh ; [0x8048d14:4]=0x6e69622f ; "/bin/sh" @ 0x8048d14 │ │ 0x08048a4e e88dfdffff call sym.imp.system ;sym.imp.system() │ ┌─────< 0x08048a53 eb0c jmp 0x8048a61 │ │└ ; JMP XREF from 0x08048a45 (sym.decrypt) │ │└────> 0x08048a55 c704241c8d04. mov dword [esp], str._nInvalid_Password_ ; [0x8048d1c:4]=0x766e490a ; str._nInvalid_Password_
If the result not equal, Is printed ‘ Invalid Password!’ and the functions returns; If euqal call the /bin/sh; We can complete the decrypt function:
void decrypt(int v){ // str contains "Q}|u`sfg~sf{}|a3" XORed with v if(strcmp("Congratulations!", str) == 0){ system("/bin/sh"); } else{ puts("Invalid Password!"); } }
总结下来,我们必须找到一个值 可以将 “Q}|u`sfg~sf{}|a3” 转换成 “Congratulations!”, 直接将两个值进行异或,就可以拿到结果了;
[0x08048856]> !python Python 2.7.6 (default, Mar 22 2014, 22:59:38) [GCC 4.8.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. s1 = "Q}|u`sfg~sf{}|a3" s2 = "Congratulations!" [ord(a) ^ ord(b) for a,b in zip(s1, s2)] [18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18]
结果就是 18 = 0x12 ;
正如文章一开头讲的那样,传进 decrypt 来的值 是 userinput 和 constant(0x1337d00d) 的差;
0x1337d00d – 0x12 = 0x1337cffb = 322424827
lab1B@warzone:/levels/lab01$ ./lab1B .---------------------------. |-- RPISEC - CrackMe v2.0 --| '---------------------------' Password: 322424827 $ whoami lab1A