从简单破解学到的汇编基础
基本汇编指令复习
lea 与 mov 与 []
-
lea 指令
第二个操作数可以是寄存器或者变量
若为寄存器:必须加 [] ,相当于取值操作
mov eax, 2 lea ebx, [eax] ;执行后 ebx = 2 mov ebx, eax ;等同于上句 lea ebx, eax ;编译器报错: error A2070: invalid instruction operands
优点:
lea 可以作简单的算术计算,特别是有了32位指令的增强寻址方式,比如要算EAX*4+EBX+3,结果放入 EDX,怎么办?
mov edx, eax
shl edx, 2
add edx, ebx
add edx, 3现在用 lea 一条指令搞定: lea edx, [ebx + eax * 4 + 3]
注意:
在函数执行时,经常会看到这种指令:
lea esi, [ebp-30]
用于获取局部变量的地址;注意不能用 OFFSET 获得堆栈参数的地址,因为 OFFSET 只适用于编译时已知的地址。下面的语句无法汇编:mov esi,OFFSET [ebp-30 ] ;错误
若为变量:有无 [] 都一样,为取地址操作,相当于 C语言中的 &
num dword 2 lea ebx, num lea eax, [num] ; eax 为 num 的地址,如 eax = 4206598,随程序不同不同,这时 ebx = eax
-
mov 指令
若第二个操作数为变量:有无 [] 都一样
num dword 2 mov eax, 2 mov ebx, num mov ecx, [num] ;执行完 ebx = ecx = 2
若第二个操作数为寄存器:加 [] 表示将其内存地址中的值传值
mov ecx,[eax] ; 相当于 mov ecx, ds:[eax],将内存地址为 eax 处的值传给 ecx
set 指令
作用为根据EFLAGS寄存器中的状态标识设置目标操作数的值为 0 或 1。目标操作数指向一个字节寄存器或内存中的一字节。
sete(setz) al ; 取当前标志寄存器中 ZF 的值,放到 AL 中
setne(setnz) al ; 取得 ZF 值后,取反,再放到 AL 中。
其中 e 表示 equal (相等)
经常接在会改变标志寄存器的指令之后,这样就省去了条件跳转
cmov 指令
条件传送表示要满足某个或某些条件时才执行传送数据。
条件传送传送数据的格式为: cmovx src, dst
前面的字母c为英文condition(意思是条件)的首字母,后面的x表示条件,不同的条件有不同的表示,通常是一到三个字母
cmovne/cmovnz 表示 ZF = 0 时传送数据
mov eax, 2
cmp eax, 2
cmovne ebx, 3 ; 因为 cmp 指令执行后,ZF = 1,因此 3 不会被传给 ebx
即不相等时才传值
参考链接:条件传送数据cmov指令
05 初试破解-控制台 汇编程序解读
其实稍微有点经验的一看就知道这是个密码验证过程,且一下就能发现密码的 ascii
但我还是想把每一条指令都搞明白,记录一下
(因为 OD 版本不同,所以这里指令看上去和视频里不一样,这个无所谓)
(假设用户输入就是密码 rkvir)
lea eax,[local.14] ; 取局部变量的地址到 eax
push eax ; 将 eax 压栈
push 0046D6B8 ; 压入C语言的格式符 %s
call scanf ; 获取用户输入,以上这就是函数调用的过程
mov eax,[local.14] ; 将局部变量的值传给 eax
xor edx,edx
add esp,0x18 ; 平栈
cmp al,0x72 ; 比较密码的第一位,ZF=1
sete dl ; edx = 1
cmp ah,0x6B ; 比较第二位,ZF=1
lea ecx,dword ptr ds:[edx+0x1] ; ecx = 1+1 = 2
cmovne ecx,edx ; 因为 ZF=1,因此不传值,ecx 不变
cmp byte ptr ss:[ebp-0x36],0x76 ; 比较第三位,ZF=1
lea eax,dword ptr ds:[ecx+0x1] ; eax = 2+1 = 3
cmovne eax,ecx ; 因为 ZF=1,因此不传值
cmp byte ptr ss:[ebp-0x35],0x69 ; 同上
lea ecx,dword ptr ds:[eax+0x1]
cmovne ecx,eax
cmp byte ptr ss:[ebp-0x34],0x72
lea edx,dword ptr ds:[ecx+0x1]
cmovne edx,ecx
cmp byte ptr ss:[ebp-0x33],0x0
lea eax,dword ptr ds:[edx+0x1]
cmovne eax,edx
cmp eax,0x5
这里一开始不明白为什么一个简单的比较字符串的程序这么冗长,后来才发现这就是故意设置的,其中 ecx edx eax 相互增加的过程就是原C程序的 nflag 增加的过程。如果某一位密码错误,eax 都不会顺利地增加到 6,则登录失败