1.启动函数
功能:检索新进程命令指针,环境指针,全局变量初始化,内存堆栈初始化
比如:
GetCommandLineA 命令指针
GetStartupInfoA 启动信息
GetModuleHandleA 执行文件基地址
编译器自动加入的代码:
00401020 >/$ 55 push ebp
00401021 |. 8BEC mov ebp, esp
00401023 |. 6A FF push -1
00401025 |. 68 A0504000 push 004050A0
0040102A |. 68 7C1C4000 push 00401C7C ; SE 处理程序安装
0040102F |. 64:A1 0000000>mov eax, dword ptr fs:[0]
00401035 |. 50 push eax
00401036 |. 64:8925 00000>mov dword ptr fs:[0], esp
0040103D |. 83EC 10 sub esp, 10
00401040 |. 53 push ebx
00401041 |. 56 push esi
2.函数及其参数
反编译分析时,将注意力集中在函数的识别以及参数的传递上
函数多以call(保存函数返回信息)地址为跳转 也有动态计算或者寄存器传地址
函数的参数传递方式有三种:
堆栈方式
寄存器方式
全局变量
3.函数的返回值
1. return操作符返回值
od遇到了一个bug 就是不是每个指令都会下到int3断点 从而debug时F8单步有一些代码是一起执行的。(这让想起了原子操作)
一般的返回值时存放在eax寄存器中,如果超过了容量则会放到edx寄存器中。
2.通过参数按照传引用的方式返回
注意ds和ss的区别 然后一般通过引用来传值需要使用变量的地址,在调用某个函数时再把变量的地址传给函数。
3.通过全局变量返回
4.数据结构
逆向算法和数据结构
1.局部变量
1.堆栈分配局部变量
在堆栈中进行分配,分配完在释放。或者直接存放在寄存器中。
分配[ebp-xxx]
访问[ebp+xxx]
初始化局部变量的两种方法:
mov [ebp-xxx],5
push 5
2.利用寄存器存放局部变量
堆栈占用两个寄存器,编译器会利用剩下的6个通用寄存器尽可能的有效存放局部变量。寄存器不够时才会使用堆栈,且局部变量的生命周期比较短,需要确定时那个寄存器存放的那个变量。
2.全局变量
全局变量存放在内存区,静态pe的.data的可读写区域的固定地址上。访问时即访问硬编码的特定地址即可。
(静态变量 static 作用范围有限)
如果是常量 则在只可读区域内。
3.数组
数组保存在.data段中 使用基地址+偏移来寻址
4.虚函数
虚函数实在运行时刻定义的函数,地址无法在编译时确定(在即将调用时确定)
对所有虚函数的引用常放在一个专用的数组虚函数表VTBL里,
这里使用4050A0存放着虚表(虚表里有两个方法的地址 利用两次间接寻址找到虚函数的正确地址)
每太搞懂右边的流程,为什么有个失败的流程 xor esi,esi
还有为什么每次任然需要新建一个this指针( mov ecx,esi )ecx来传递参数给成员函数
(更好奇的是如何复原ida里面的函数流程)
5.控制语句
1.if-then-else的循环控制语句
整数比较实用cmp 浮点数使用fcom fcomp
不太懂这个初始化的时候的值。书上说是指向局部变量空间
这里使用test替换cmp指令 如果为0则zf=1 不跳转继续执行
2.Switch-case语句
未优化前的代码都含有printf函数
编译优化后使用dec eax 来代替cmp指令 指令更短执行更快速
使用调转表
cmp 和ja 无符号大于则跳转 jmp table
3.转移指令的机器码的计算
短转:无条件和条件转移的机器码都是两个字节
长转:无条件转移是5个字节 条件转移是6个字节
子程序调用:call
6.循环语句
受用Maximize Speed优化后的代码使用寄存器来传值
7.数学运算符
1.一般使用add和sub指令,优化后比较喜欢使用lea指令来代替add和sub指令
这两个变量是什么时候初始化的
004007030 是%d
004701020 是printf函数
2.整数的乘法
mul和imul指令。
如果一个数是2的幂,会使用指令shl来实现乘法。
3.除法
div或者idiv指令
符号拓展指令cdq,作用是把eax寄存器中的数视为有符号的数字。
编译优化后为:
用于将除法转化为乘法
8.文本字符串
1.字符串存储格式
C \0 也成为ASCII字符串 广泛用于windows和unix中
DOS $
Pascal Y
Delphi Y
2.字符寻址指令
80x86支持直接和间接寻址等模式
mov eax,[40010h]
mov eax,[ecx]
load effective address
lea eax,[40040h]== mov eax,40040h
lea eax,[eax+8]== add eax,8
3.字符大小写转换
大写41~5A 小写 61~7A 大小写转换为减去20h
或者将第五位置0
4.计算字符串的长度
strlen()的实现
repne scasb指令获取字符串长度 strlen.c文件中的内联汇编代码如下:
#include <string.h>
size_t
strlen (const char *str)
{
int cnt;
asm("cld\n" /* Search forward. */
/* Some old versions of gas need `repne' instead of `repnz'. */
"repnz\n" /* Look for a zero byte. */
"scasb" /* %0, %1, %3 */ :
"=c" (cnt) : "D" (str), "0" (-1), "a" (0));
return -2 - cnt;
}
并没有使用scasb指令,而是直接比对字节内容。
转载于:https://blog.51cto.com/10509896/2150774