pwnable新的一题。
download: http://pwnable.kr/bin/codemap.exe
ssh [email protected] -p2222 (pw:guest)
这道题虽然是在pwnable下,但是是一道逆向题。。。 //前web狗膜一发二进制大佬
根据提示,需要查看 0x403E65 运行时,寄存器 EAX,EBX 的内容。
先不考虑运行的内容,先看程序。首先这个程序没有加壳,直接可以用ida查看内容.
然后可以看到程序的框架,在main函数中,默默按下F5...
int __cdecl main(int argc, const char **argv, const char **envp) { int seed; // [email protected] int (***v4)(void); // [email protected] int (***v5)(void); // [email protected] int v6; // [email protected] int (**v7)(void); // [email protected] int (***random_funtion)(void); // [email protected] unsigned int v9; // [email protected] unsigned int v10; // [email protected] char *eax_now_str; // [email protected] unsigned int i; // [email protected] char *max_eax_str; // [sp+10h] [bp-60h]@0 unsigned int eax_now; // [sp+14h] [bp-5Ch]@8 unsigned int count; // [sp+18h] [bp-58h]@1 unsigned int max_eax; // [sp+1Ch] [bp-54h]@1 char word_str; // [sp+20h] [bp-50h]@9 int v19; // [sp+6Ch] [bp-4h]@3 printf("I will make 1000 heap chunks with random size\n"); printf("each heap chunk has a random string\n"); printf("press enter to start the memory allocation\n"); sub_3440B1(); max_eax = 0; count = 0; srand(0); while ( 1 ) { seed = 10000 * rand() % 1337; v4 = (int (***)(void))operator new(8u); v5 = v4; v19 = 0; if ( v4 ) { *v4 = (int (**)(void))&off_34F2EC; v6 = (10000 * seed >> 1) + 123; v7 = (int (**)(void))operator new(8u); if ( v7 ) { v7[1] = (int (*)(void))v6; v5[1] = v7; random_funtion = v5; } else { v5[1] = 0; random_funtion = v5; } } else { random_funtion = 0; } v19 = -1; v9 = (**random_funtion)(); v10 = v9 % 0x186A0; eax_now = v9 % 0x186A0; eax_now_str = (char *)malloc(v9 % 0x186A0); if ( v10 >= 0x10 ) { qmemcpy(&word_str, "abcdefghijklmnopqrstubwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890", 0x3Fu); i = 0; do eax_now_str[++i - 1] = *(&word_str + rand() % 62); while ( i < 0xF ); eax_now_str[15] = 0; if ( eax_now > max_eax ) { max_eax = eax_now; max_eax_str = eax_now_str; } } if ( ++count >= 0x3E8 ) // 0x3e8 = 1000 break; srand(count); } printf("the allcated memory size of biggest chunk is %d byte\n", max_eax); printf("the string inside that chunk is %s\n", max_eax_str); printf("log in to pwnable.kr and anwer some question to get flag.\n"); sub_3440B1(); return 0; }
这是把F5以后的反编译程序,加上自己的理解改成了这个样子。
这个是一个循环申请内存空间,并随机填充16个a~Z0~9字符的程序,循环次数为1000次。
每次循环后,找到申请空间最大的那次,并打印出来。
上面最重要的是random_function变量,它的结果是下图这些sub_*的函数,内容就是根据现在的执行上下文生成一个随机数。
由于上面代码中用的随机函数都是伪随机,或者种子固定,因此,每次运行该程序,申请的大小、字符串的添加都是一样的。
而题目中给的提示,EAX、EBX是执行的结果,其中EAX存储的是申请内存的大小、EBX存储一个char指针,指向填充的字符串。
做了以上分析之后,可以想出解决思路,每次在该位置下断点,获取EAX、EBX的内容,最后做删选即可。
而通过服务器上所给的提示,随后需要提交第二、第三大分配内存块所填充的字符串内容。
由于做了1000次循环,因此人工寻找几乎不可能。
还好ida工具有ida 脚本这样一种工具,可以动态获取指定内容。
可以编写ida脚本来完成查找。我使用了IDA Python这一工具。其实和idc基本相同。
思路就是下断点——获取EAX\EBX的值,最终进行比较,由于没想到好的排序算法,就直接采用最简单粗暴的方法
脚本如下,另外每次程序加载到内存中的位置不同,使用时应修改添加断点的内存地址。
from idaapi import * from idc import * import os count = 0 eax_list = list() ebx_list = list() try: if debugger: print("Removing previous hook ...") debugger.unhook() except: pass AddBpt (0x403e65) print "[*] set hook OK...\n" StartDebugger("","","") for i in range(0,999): GetDebuggerEvent(WFNE_SUSP|WFNE_CONT, -1) print "[+]",i eax = GetRegValue("EAX") eax_list.append(eax) ebx = GetRegValue("EBX") ebx_list.append(ebx) if i == 998: print ‘[+] eax max : ‘,max(eax_list) index = eax_list.index(max(eax_list)) a = ebx_list[index] #message( "[+] ebx max : %x",%(ebx_list[eax_list.index(max(eax_list))])) Message("%x"%a) print "max",GetString(a) del(eax_list[index]) del(ebx_list[index]) # print ‘[+] eax second : ‘,max(eax_list) index = eax_list.index(max(eax_list)) a = ebx_list[index] #message( "[+] ebx max : %x",%(ebx_list[eax_list.index(max(eax_list))])) Message("%x"%a) print "second",GetString(a) del(eax_list[index]) del(ebx_list[index]) # print ‘[+] eax third : ‘,max(eax_list) index = eax_list.index(max(eax_list)) a = ebx_list[index] #message( "[+] ebx max : %x",%(ebx_list[eax_list.index(max(eax_list))])) Message("%x"%a) print "third",GetString(a) del(eax_list[index]) del(ebx_list[index])
最终,运行的结果如下:
nc 0 9021输入字符串之后可以获得该题的flag: