每日逆向的理解和积累。
汇编阅读练习
练习1-1
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序并未开启canary保护机制。补充:C语言源码中的a=077
的意思是将a初始化为八进制的77。
练习1-2
1.汇编代码及解释如下:
- main函数
- print_arr函数
- move函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。
4.补充1:本程序的分析难点在于move函数中通过指针变量实现数组元素循环右移的操作,重点区分指针变量和具体的取值操作;
mov rax, [rbp+var_8] //这里地址rbp+var_8处存放的是一个地址,所以变量[rbp+var_8]是一个指针变量。这句操作是将变量[rbp+var_8]的值(即其中存放的地址)赋值给rax
mov eax, [rax-4] //rax中存放的是地址值,故[rax-4]指的是取出rax-4这个地址处存放的具体值,于是此时eax中存放的就不再是地址而是一个具体的值
mov [rbp+var_14], eax //所以此时变量[rbp+var_14]中存放的就是一个具体的值,其就不是一个指针变量,只是一个普通的int型变量
5.补充2:在print_arr函数中主要是对于以下几句汇编代码的理解。
(1)lea rdx, ds:0[rax*4] //此句代码等同于lea rdx, [rax*4+0],都是混合运算;
//此时rax中存的是一个具体的变量值;
//执行之后rdx=(rax中的值*4+0);
(2)mov edi, 10
call _putchar
- putchar(10)中的10是换行符的ASCII码,putchar(10)输出一个换行符,作用与putchar(‘\n’) 相同;
- CS:段寄存器,CS是代码段;
- DS:段寄存器,DS是数据段;
- 16位CPU中,段寄存器有4个,分别是CS(代码段)、DS(数据段)、SS(堆栈段)、ES(附加段);
- 32位寄存器中,段寄存器从4个扩展到6个,分别是CS、DS、SS、ES、FS和GS。
- FS和GS段寄存器也属于附加的段寄存器。
参考资料:
https://codeday.me/bug/20171223/111586.html
http://www.xumenger.com/windows-assembly-20161208/
https://www.jianshu.com/p/061d971424c6
https://zhidao.baidu.com/question/282296134.html
练习2
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。补充知识:对于源码中的冒泡排序算法的理解。
for(i=strlen(str)-1;i>1;i--) //外层循环是从字符串的最后一个字符开始向前循环的
for(j=0;j<i;j++) //内层循环是从前开始,控制条件:j<i
if(str[j]>str[j+1]) //如果前一个>后一个则交换顺序
//这样一轮内层循环之后能保证最后一个元素一定是循环包含的元素中最大的元素
{
tem=str[j];
str[j]=str[j+1];
str[j+1]=tem;
}
还可以改变循环条件,另一种形式的代码如下:
之所以本题没有采取第二种形式,目测是因为第二种内层循环的计数变量初始值是一个变量(字符串的长度),第一种是常量0,所以第一种比较节省内存空间。
练习3-1
1.汇编代码及解释如下:
- main函数
- strconnect函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。唯一不能在分析过程中准确确定的是两个字符数组的长度,在汇编层面来看给变量分配的内存空间大小是24或者32,均大于源码中的20。
练习3-2
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序并未开启canary保护机制。
练习4
1.汇编代码及解释如下:
- main函数
- create_list函数
- selection_sort函数
- delete_node函数
- concatenate函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。
4.在分析该程序的过程中,存在以下错误——
(1)main函数——
(1)
mov [rbp+var_20], 3
mov [rbp+var_1C], 0Ch
mov [rbp+var_18], 8
mov [rbp+var_14], 9
mov [rbp+var_10], 0Bh //这一串连续的mov操作并不一定是结构体初始化赋值(结构体中一般都会包含不同类型的成员变量);
//这5个变量类型相同,又占用一片连续的内存空间,所以是int型数组的可能性更大。
(2)
call create_list ;
mov [rbp+var_28], rax ; //由于对create_list函数分析有误,导致对返回值的判断出错;
(2)create_list函数——
(1)
mov edi, 10h //size
call _malloc //注意到每次malloc的地址都是10h,共16个字节,而分析时误以为其中只存放着8个字节的指针变量;
//由于5次循环中每次循环时都会申请10h大小的空间,其实应该想到这10h大小的空间可能是用来存放一个包含有指针成员变量的结构体变量的。
mov [rbp+var_10], rax //malloc申请的大小为16个字节的内存空间首地址;
mov rax, [rbp+var_28]
mov edx, [rax]
mov rax, [rbp+var_10]
mov [rax], edx //这里其实是将main函数传进来的int型数组的第一个值存入malloc申请的内存空间中;
(2)
mov edi, 10h //size
call _malloc
mov [rbp+var_8], rax //[rbp+var_8]是一个指针变量,指向malloc申请的内存空间(16字节)
mov rax, [rbp+var_8]
mov qword ptr [rax+8], 0 //这里将malloc申请的内存空间的后8个字节初始化为0,故可猜测该结构体的第二个成员变量占16个字节的后8个字节
mov eax, [rbp+var_1C]
cdqe
lea rdx, ds:0[rax*4]
mov rax, [rbp+var_28]
add rax, rdx
mov edx, [rax]
mov rax, [rbp+var_8]
mov [rax], edx //依次取出main函数传进来的整形数组中的第i个值,存入每次循环申请的第i个16个字节大小的空间中;
//i=2~5;
//也就是说,到这里为止,[rbp+var_8]这个结构体变量的前8个字节是一个int型的成员变量,后8个字节初始化为0。
mov rax, [rbp+var_18]
mov rdx, [rbp+var_8]
mov [rax+8], rdx ; //这里就给前一个结构体中的第二个成员变量(也即后8个字节)赋值为指向本结构体的第一个int型成员变量的指针变量。
(3)
mov rax, [rbp+var_10] //此函数返回的是一块连续的内存空间的首地址,该内存空间中依次存放着5个结构体变量;
leave
(4)经过分析,最终应该归纳出结构体的基本结构如下,这5个结构体应该组成了一个单向链表的结构——
struct list
{
int data;
struct list *next; //这个指针变量指向下一个结构体的首地址
};
(3)selection_sort函数——
//由对应图中红框框起来的汇编代码可以看出[rbp+var_14]应该是个整型变量;
//在错误的分析中,结构体的第一个元素分析的是指针,所以这里就会出现前后矛盾的情况;
//以后在碰到这样的情况时,应该意识到可能是对于结构体变量的分析出问题了,考虑重新分析变量结构。
mov rax, [rbp+var_28]
mov [rbp+var_10], rax
mov rax, [rbp+var_10]
mov eax, [rax]
mov [rbp+var_14], eax ; //[rbp+var_14]应该是结构体的第一个元素
5.总结——本题的难点在于对于链表这种数据结构的熟悉,以及对于结构体变量结构的识别,同时注意区分一段连续内存的赋值操作是数组、字符串还是结构体变量。
练习5-1
1.汇编代码及解释如下:
- main函数
2.对应c语言源码如下:
3.该程序并未开启canary保护机制。
练习5-2
1.汇编代码及解释如下:
- main函数
- evenumber函数
- oddnumber函数
2.对应c语言源码如下:
3.该程序开启了canary保护机制。补充:双精度浮点数其实就是double型,对于浮点数的操作分析与整型操作数相似,区别在于进行操作的主要寄存器不同,为xmm0系列寄存器。