每日逆向的理解和积累。
汇编阅读练习
练习1-1
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序未开启canary保护机制。
练习1-2
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。该程序的实现是为了解决下述问题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。
练习2
1.汇编代码及解释如下:
- main函数
2.对应c++语言源码如下:
3.该程序中几个值得注意的知识点如下:
- 关于输入输出函数调用的理解——
对于上图所示代码的理解:输入的全部是cin>>x
,x
是我们定义的变量,然后>>
在其它地方不是输入流的意思。在这个stl
库里面通过用operator
对重定义,让他和stl
库连用的时候是输入流,从而实现对于输入函数的调用。这里要注意,ida中如上图显示的应该就是C++系统库中的函数,分析时可以根据名称查询其具体功能。(C++中的cin和cout分别相当于c语言中的scanf和printf,operator在这里的作用是重定义操作符>>
。)
- 对于VS的安全检查函数
__RTC_CheckEsp
(见图中注释信息)和CheckStackVars
(见下图及描述)的理解——
具体理解参考下图(深入C/C++之基于CheckStackVars的安全检查(VS2008))——
- 参考资料如下:
(1)深入C/C++之基于CheckStackVars的安全检查(VS2008);
(2)c – 如何实现__RTC_CheckEsp?;
练习3
1.汇编代码及解释如下:
- main函数
- Stack::push函数
- Stack::pop函数
2.对应c++语言源码如下:
- main.cpp
- stack.h
3.该程序中几个值得注意的知识点如下:
- 对于程序中switch-case(以及跳转表)在汇编层面上的表现形式的理解
- 对于c++中的cin.get()(无参数形式)的理解——如下图c++程序所示
- 关于用VS编译的C++程序中的各种保护机制在汇编层面上的体现——
(1)Security_Cookie(GS)机制
首先看红色的一句指令。是将___security_cookie变量的值给取出来。然后蓝色的指令就是将取出来的Cookie全局变量与当前EBP的值进行异或运算。与EBP异或有如下作用。
可以增加随机性,尽可能使不同函数的安全Cookie都不同。
可以检查EBP是否被破坏,因为在函数结束检查Cookie时,还会将Cookie变量值再次与EBP异或,如果EBP的值没有变化,那么就能恢复成原来的___security_cookie值。
上面绿色的指令便是关键的一步,它是将异或后的值存入[ebp-4]中。当前的EBP就是最开始压入EBP后ESP的值,也就是压入的EBP的地址。减4就刚好挨着一进函数时压入的EBP的地址减4。此时Cookie变量已经在栈帧中了。
接下来看最后的检查部分,粉色的部分前两句指令是将Cookie变量的值重新取出来并异或还原并保存到ECX中。第三句粉色的CALL就是调用__security_check_cookie 函数。
备注:在以下情况中,编译器不会对易受攻击的参数提供安全保护:
- 函数不包含缓冲区;
- 如果未启用优化 ( /O 选项(优化代码));
- 函数具有可变参数列表 (…);
- 函数标记为 naked (C++);
- 函数的第一行语句包含内联程序集代码;
- 如果仅通过在缓冲区溢出事件中不太可能利用的方式使用参数。
参考链接——
练习4
1.汇编代码及解释如下:
- main函数(对于红框中代码的具体作用还不太清楚)
- fish函数
2.对应c语言源码如下:
问题描述——C语言递归解决分鱼问题:A、B、C、D、E这5个人合伙夜间捕鱼,凌晨时都已经疲惫不堪,于是各自在河边的树丛中找地方睡着了。第二天日上三竿时,A第一个醒来,他将鱼平分为5份,把多余的一条扔回河中,然后拿着自己的一份回家去了;B第二个醒来,但不知道A已经拿走了一份鱼,于是他将剩下的鱼平分为5份,扔掉多余的一条,然后只拿走了自己的一份;接着C、D、E依次醒来,也都按同样的办法分鱼。问这5人至少合伙捕到多少条鱼?每个人醒来后所看到的鱼是多少条?
具体编程实现如下——
注意:此处c语言源码是有问题的,在main函数中定义的数组和递归函数fish的名字相同,会导致main函数中的所有fish都被解释成为数组,并不会调用递归函数fish,在汇编层面我们也可以看出这个问题。
3.该程序中几个值得注意的知识点如下:
- 关于对于如下图所示中的DWARF的理解——
在用ida加载用CodeBlocks
编译生成的exe文件时,分析完成之后ida会弹出上图所示对话框,对于其中的DWARF信息的官方说明如下:
DWARF: Store file/line number information in IDB (only if requested, since it comes with a performance penalty)
参考链接——
(1)How to prevent IDA Pro from loading DWARF debug info in batch mode?;
(2)DWARF 中的 Debug Info 格式;
- 对于如下指令的理解——
在main函数起始处有如下代码。
push ebp
mov ebp, esp
and esp, 0FFFFFFF0h //0xFFFFFFF0 = 11111111 11111111 11111111 11110000
//相当于把esp的最后4个bit位清零, 也就是把stack往低地址对齐使其能够被16整除...
//这样对32位或者64位CPU来说,所有的的stack上的基本变量都可以一次访问到(如果地址没有对齐,那么有可能CPU需要访问两次内存来获得一个8 byte的变量) --
//这样看来,实际上将stack地址在8 byte上对齐对32位甚至64位的CPU都足够了...
//但是问题是对于一些 SIMD (single instruction, multiple data) 的指令*规定*访问的地址必须向16 byte对齐 (也就是必须被16为整除 -- 这是从指令效率的方面来设计的).
参考链接——
练习5
1.汇编代码及解释如下:
- main函数
- TravelChessBoard函数
- nextxy函数
2.对应c语言源码如下:
问题描述——C语言马踏棋盘:国际象棋的棋盘为8×8的方格棋盘。现将“马”放在任意指定的方格中,按照“马”走棋的规则将“马”进行移动。要求每个方格只能进入一次,最终使得“马”走遍棋盘的64个方格。编写一个C程序,实现马踏棋盘操作,要求用1〜64这64个数字标注马移动的路径,也就是按照求出的行走路线,将数字1,2,……64依次填入棋盘的方格中,并输出。
问题分析如下——
算法设计如下——
具体编程实现如下——
3.该程序中值得注意的知识点如下:
- lea指令——
对于寄存器来说:第二个操作数是寄存器必须要加[],这里lea就是取[寄存器]的值,如:
mov eax,2
lea ebx,[eax] ;执行后ebx=2
mov ebx,eax ;等同于上句
lea ebx,eax ;编译器报错: error A2070: invalid instruction operands
对于变量来说加不加[]都是一样的效果,都是取变量的地址,相当于指针,如:
num dword 2
lea ebx,num
lea eax,[num] ; eax为num的地址,如eax=4206598,随程序不同不同,这时ebx==eax
- mov指令——
对于寄存器来说如:
mov ebx,eax;ebx==2
mov ecx,[eax] ;可能会报错,因为这里翻译成汇编是mov ecx,DS:[eax]
对于变量来说如:
num dword 2
mov eax,2
mov ebx,num
mov ecx,[num] ;执行完ebx==ecx==2
- 总结——加不加中括号[]的区别
lea对变量没有影响是取地址,对寄存器来说加[]时取值,第二操作数不加[]非法;
mov对变量来说没有影响是取值,对寄存器来说是加[]时取地址,第二操作数不加[]是取值。
参考链接——