2020.03.23-2020.03.29
32位软件逆向技术
控制语句
条件设置指令(SETcc)
条件设置指令的形式是“SETcc r/m8”, r/m8表示8位寄存器或单字节内存单元
条件设置指令测试一些标志位,把结果记录的目标操作数中
测试:
- of(=1溢出)
- zf(=1操作后等于0/相等)
- cf(=1进位/低于)
- cf、zf结合判断
- sf(=1操作后结果为负)
- pf(=1,1个数为偶数)
- sf、of结合判断(=1小于)
- zf(=1或zf!=of,不大于)
条件设置指令可以用来消除程序中的转移指令
如:
纯算法实现逻辑判断
一些编译器在优化的时候,会不改变原逻辑的情况下,使用数学技巧把代码中的一些逻辑分支语句转换为算术操作,减少程序中的条件转移指令,提高cpu的流水线的性能(…tql)
循环语句
可以进行反向引用
其他类型的分支语句,如if-else都是由低地址向高地址区域引用的(向下跳),依此可以将循环语句识别出来(调回去)
确定某段代码是循环代码后就可以分析计数器(一般用ecx)
如果编译时设置优化“Maximize Speed”:
1 | xor ecx,ecx ;变量初始化sum=0 |
数学运算符
如果编译器没有进行优化,则这些运算符很容易理解(…)所以下面都是经过优化的
整数的加法和减法
用lea代替add和sub
lea指令允许用户在一个时钟内完成对c=a+b+78h(某个数)的计算,其中abc都是在有寄存器的情况下才有效的,如:
1 | lea c,[a+b+78] |
时钟周期:一个时钟脉冲所需要的时间。(计组)
整数的乘法
一般被编译成mul(无符号)、imul(有符号)指令(运行慢),为了提高效率会用其他指令完成
如果一个数是2的幂,用左移指令shl;3、5、6、7、9等数用加法提高效率:如把exa*5写成"lea eax,[eax+4 *4];lea指令可以实现寄存器乘以2、4、8的运算
整数的除法
一般被编译成div、idiv指令,大概比乘法运算多消耗10倍cpu时钟
如果除数是2的幂,可用右移指令’shr a,n’:a是被除数,n是2的指数(进行无符号数计算)有符号数用sar指令
除法指令需要用到符号扩展指令cdq,作用是把eax中的数视为有符号数,将eax的最高位(符号位)扩展到edx中。如果eax最高位是1,执行后edx=FFFFFFFF;如果是0,edx=00000000。通过这种方法把32位有符号数变为edx:eax的64位有符号数,满足64位运算指令的需要。
对除法优化时会用乘法代替除法,优化的公式较多,一例如:倒数相乘a*b=a *(1/b)
(在下面的64位中描述更完整)
文本字符串
字符寻址指令
与字符指针处理相关的指令有mov、lea等
mov指令将地址/寄存器中的数据放到目的寄存器中,如mov eax [401000]/[ecx]
lea(load effective address)操作数是地址,lea eax,[addr] 就是把addr的地址放入eax中
1 | lea eax,[401000] |
计算字符串长度
c语言的strlen():
64位软件逆向技术
x64通用寄存器的名称第一个字母由e改为r,共16个;16个128位xmm寄存器(通常用于优化代码)
函数
栈平衡
每8字节的栈空间用来保存一个数据
尽量保证栈顶对齐值为16(可以被16整除)
调用约定
x86(32位)的函数调用有stdcall、__cdecl、fastcall等,x64只有一种寄存器快速调用约定:
- 整数:前四个参数用寄存器rcx→rdx→r8→r9,其余放在栈里,入栈顺序从右到左,任何大于8字节或不是1、2、4、8字节的参数用地址传递
- 浮点数:前四个参数用xmm寄存器完成:xmm0→xmm1→xmm2→xmm3
栈为函数的前四个参数预留了32字节的空间,当函数功能复杂时将寄存器的参数保存到这个预留栈空间。预览栈空间由函数调用者申请、平衡
数学运算
整数的除法
- 有符号数,除数为2^n
x>=0,x/2^n=x>>n
x<0,x/2^n=(x+(2^n-1))>>n
除数为-2^n时最后用’neg 寄存器’指令求补 - 有符号数,除数为正,非2^n
32位:x>=0,result=(xc>>32)+x>>n,<0时结果再+1
64位:x>=0,result=(xc>>64)+x>>n,<0时结果再+1 - 有符号数,除数为负,非2^n
32位:x>=0,result=(xc>>32)-x>>n,<0时结果再+1
64位:x>=0,result=(xc>>64)-x>>n,<0时结果再+1
- c为魔数
- 无符号数,除数位2^n
用shr右移 - 无符号数,除数非2^n
32位:x>=0,result=xc>>32>>n
62位:x>=0,result=xc>>62>>n
整数取模
可以用除法指令,但是除法指令执行周期较长,通常优化将其转换成位运算或除法运算,再用除法运算进行优化
- 除数b=2^n
取得被除数二进制数的最后n位,负数要在n位之前补1:
x>=0,result=x&(b-1);x<0,result=(x&(b-1))-1|(~b-1)+1 - 除数b!=2^n
采用“余数=被除数-商除数”
result=x-x/bb
使用 OllyDbg 从零开始 Cracking
数制系统
十六进制负数
从00000000~FFFFFFFF
正数:00000000~7FFFFFFF
负数:80000000~FFFFFFFF(-1)
在od左下角的commandbar可查询十六进制数的十进制值和对应的ASCII字符:? [十六进制值]
寄存器
标志寄存器
-
o,溢出标志
记录了有符号数运算的结果是否发生了溢出,如果发生溢出OF=1,如果没有OF=0 -
p,奇偶标志
指令的结果用二进制表示时1的总数,如果为偶数PF=1,如果不为偶数PF=0 -
z,零标志
记录相关指令执行后结果是否为0,如果为0那么ZF=1,如果不为0那么ZF=0 -
s,符号标志
运算结果为负设置为1,结果为正设置为0 -
c,进位标志
记录了无符号运算的结果是否有向更高位的进位(9位),有置1,无置0 -
其他