运行bufbomb.exe 会让你输入一些字符串,这些字符串将存储在一个临时变量字符数组中。根据我们输入的字符串的内容我们可以让程序做一些我们希望它作的事情,比如修改函数返回地址,让程序跳转到我们给它安排的位置去执行我们所写的代码。在执行过程中程序实际执行的是机器码,我们可以写一些汇编代码,然后转化为机器码,把这些机器码再转换成ASCII码字符串,当程序提示我们输入的时候,我们再将这些准备好的字符串输入给程序。这就等于我们把自己写的代码写入了程序,让它按我们的意愿执行。本实验分为四关,每关都是独立的。
这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。
EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。
EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。
ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
EDX 则总是被用来放整数除法产生的余数。
ESI/EDI分别叫做"源/目标索引寄存器,因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
EBP是"基址指针"(BASE POINTER), 它最经常被用作高级语言函数调用的"框架指针".
在破解的时候,经常可以看见一个标准的函数起始代码:
push ebp ;保存当前ebp
mov ebp,esp ;EBP设为当前堆栈指针
sub esp, xxx ;预留xxx字节给函数临时变量.
这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数. EBP下方则是临时变量. 函数返回时作 mov esp,ebp/pop ebp/ret 即可.
esp:寄存器存放当前线程的栈顶指针
ebp:寄存器存放当前线程的栈底指针
eip:寄存器存放下一个CPU指令存放的内存地址,当CPU执行完当前的指令后,从EIP寄存器中读取下一条指令的内存地址,然后继续执行。
点开getbuf,输入随便一串字符串。为了研究我们输入指令的作用。点开寄存器,查找输入的字符串保存的位置。
我们发现,ebp+var_8这个位置等于0DEADBEEF的话,就破坏了栈,也就是这是对输入过长指令的判断;如果不等于0DEADBEEF的话,则提示还未使栈溢出,输入过短。Cookie就是我们的通行密码。ebp存放我们输入的指令。继续执行,发现运行错误。根据提示,推断我们输入的指令可以让我们到达想到的地址。
当然,我们可以查找偏移量的实际值。
于是我们找到第一关通关的地址,按照位数,前面用其他数字填充。
第一关指令:22222222222222222222222222222222D0134000。
通关。
根据第一关的经验,把地址换成第二关的位置。
这关需要比较cookie和ebp里面的值,推测我们需要在指令里面输入cookie。先随便在后面加一串数字,观察密码应该对应的位置。
第二关指令:2222222222222222222222222222222230144000222222226C16F99C
首先观察第三关的指令要求。发现要把global_value设置成cookie。如果直接把地址设置到004014D0,可是无法修改global_value的值。
注意到,“esp:寄存器存放当前线程的栈顶指针”,如果我们在线程栈顶输入指令修改global_value的值,再跳转到Trojan3便能通关。
Esp(9CFE1800) cookie(00423A30) global_value(004245CC)
A1303A4200 mov eax, ds:cookie
A3CC454200 mov ds:global_value, eax
68D0144000 push offset Trojan3
C3 retn
如果不加上最后一句retn ,那么指针将会继续执行,但是下面的代码没有意义,就会报错。
单步调试到输入完buf,发现跳转到这里Esp地址(9CFE1800)
这里是根据正确的机器语言转换为汇编语言。
然后就通关了。
第三关通关指令:222222222222222222222222222222229CFE1800A1303A4200A3CC45420068D0144000C3
首先观察第四关的指令要求。还是把global_value设置成cookie。尝试把第三关的指令中trojan3的地址改为trojan4的地址。成功通关。
第四关通关指令:
222222222222222222222222222222229CFE1800A1303A4200A3CC4542006880154000C3
最后运行结果: