数据宽度
或许你曾经有这样的疑惑,有符号数在二进制的表示中,以1
开头的表示为负数,那么10000000
是表示的-1
还是128
呢 ?
在这里引入数据宽度的概念,即8位二进制数能够存储的数据范围为-128~127
,很显然,上面存储的数,在数据宽度为8位时,表示-1
,同理-127
表示为10000001
,-2
表示为11111110
,这里关于原码、补码的概念不加以赘述,其本质都是为了表示数值。
基于x86架构的CPU的数据宽度为32位,表示正负范围为:0~7FFFFFFF
80000000~FFFFFFFF
计量单位有:byte、word、dword、qword,分别表示字节,字,双字,四字,单字节表示8位。
逻辑运算
运算符 | 说明 | 实例 |
---|---|---|
&(and) | 按位与,同时为1,才能得1 | 10110011 & 10110101 = 10110001 |
|(or) | 按位或,存在1时,结果为1 | 10110011 | 10110101 = 10110011 |
^(xor) | 按位异或,不同时,结果为1 | 10110011 ^ 10110101 = 00000110 |
!(not) | 按位非,单运算符,结果取反 | !10110011=01001100 |
异或加密解密
加密数据:00110001
密钥:00010101
加密结果:00110001 ^ 00010101 = 00100100
解密:00100100 ^ 00010101 = 00110001
基本指令
mov
格式:mov 目标操作数,源操作数
作用:拷贝源操作数到目标操作数。
注意:
1、源操作数可以是立即数、通用寄存器、段寄存器、或者内存单元.
2、目标操作数可以是通用寄存器、段寄存器或者内存单元.
3、操作数的宽度必须一样.
4、源操作数和目标操作数不能同时为内存单元.
指令 | 说明 |
---|---|
MOV r/m8,r8 | 将8位寄存器的内容拷贝到8位寄存器或8位的内存中。 |
MOV r/m16,r16 | 将16位寄存器的内容拷贝到16位寄存器或16位的内存中。 |
MOV r/m32,r32 | 将32位寄存器的内容拷贝到32位寄存器或32位的内存中。 |
MOV r8,r/m8 | 将8位寄存器或8位的内存操作数拷贝到8位寄存器中。 |
MOV r16,r/m16 | 将16位寄存器或16位的内存操作数拷贝到16位寄存器中。 |
MOV r32,r/m32 | 将32位寄存器或32位的内存操作数拷贝到32位寄存器中。 |
MOV r8, imm8 | 将8位立即数的内容拷贝到8位寄存器中。 |
MOV r16, imm16 | 将16位立即数的内容拷贝到16位寄存器中。 |
MOV r32, imm32 | 将32位立即数的内容拷贝到32位寄存器中。 |
以上指令取自Intel白皮书,释义如下:
r 通用寄存器
m 代表内存
imm 代表立即数
r8 代表8位通用寄存器
m8 代表8位内存
imm8 代表8位立即数
lea
LEA指令将存储器操作数mem的4位16进制偏移地址送到指定的寄存器,这里,源操作数必须是存储器操作数,目标操作数必须是通用寄存器。
格式:lea r,m。
功能:获取内存编号而非内存中的内容。
别问,问就是背
add | and | or | xor | not | sub |
---|---|---|---|---|---|
ADD AL, imm8 | AND AL, imm8 | OR AL, imm8 | XOR AL, imm8 | NOT r/m8 | SUB AL, imm8 |
ADD AX, imm16 | AND AX, imm16 | OR AX, imm16 | XOR AX, imm16 | NOT r/m16 | SUB AX, imm16 |
ADD EAX, imm32 | AND EAX, imm32 | OR EAX, imm32 | XOR EAX, imm32 | NOT r/m32 | SUB EAX, imm32 |
ADD r/m8, imm8 | AND r/m8, imm8 | OR r/m8, imm8 | XOR r/m8, imm8 | SUB r/m8, imm8 | |
ADD r/m16,imm16 | AND r/m16,imm16 | OR r/m16,imm16 | XOR r/m16,imm16 | SUB r/m16,imm16 | |
ADD r/m32,imm32 | AND r/m32,imm32 | OR r/m32,imm32 | XOR r/m32,imm32 | SUB r/m32,imm32 | |
ADD r/m16, imm8 | AND r/m16, imm8 | OR r/m16, imm8 | XOR r/m16, imm8 | SUB r/m16, imm8 | |
ADD r/m32, imm8 | AND r/m32, imm8 | OR r/m32, imm8 | XOR r/m32, imm8 | SUB r/m32, imm8 | |
ADD r/m8, r8 | AND r/m8, r8 | OR r/m8, r8 | XOR r/m8, r8 | SUB r/m8, r8 | |
ADD r/m16, r16 | AND r/m16, r16 | OR r/m16, r16 | XOR r/m16, r16 | SUB r/m16, r16 | |
ADD r/m32, r32 | AND r/m32, r32 | OR r/m32, r32 | XOR r/m32, r32 | SUB r/m32, r32 | |
ADD r8, r/m8 | AND r8, r/m8 | OR r8, r/m8 | XOR r8, r/m8 | SUB r8, r/m8 | |
ADD r16, r/m16 | AND r16, r/m16 | OR r16, r/m16 | XOR r16, r/m16 | SUB r16, r/m16 | |
ADD r32, r/m32 | AND r32, r/m32 | OR r32, r/m32 | XOR r32, r/m32 | SUB r32, r/m32 |
OD
技巧
快捷键
ctrl+g
: 跳转到指定输入地址
ctrl+f
:搜索命令的位置
f2
: 设置断点,切换断点
f8
: 单步步过
f9
:运行
f12
:暂停
窗口布局
- 数据窗口和其他窗口的数据位置相反,为小端序,即高位数据存放在低位地址。
- 命令接口常用命令:
- dd、dw、db,按四字、双字、字节显示对应内存数据。
缓存清理
有时OD打开之后,会出现反汇编窗口一段代码变灰或者提示哪里下了断点的情况,这是OD同级目录下的udd文件夹下的缓存文件导致的,删掉里面的文件即可解决该问题。
寄存器
通用寄存器 | 主要用途 | 编号(二进制) | ||
---|---|---|---|---|
32位 | 16位 | 8位 | ||
EAX | AX | AL | 累加器 | 000 |
ECX | CX | CL | 计数 | 001 |
EDX | DX | DL | I/O指针 | 010 |
EBX | BX | BL | DS段的数据指针 | 011 |
ESP | SP | AH | 堆栈指针 | 100 |
EBP | BP | CH | SS段的数据指针 | 101 |
ESI | SI | DH | 字符串操作的源指针;SS段的数据指针 | 110 |
EDI | DI | BH | 字符串操作的目标指针;ES段的数据指针 | 111 |
32位专用寄存器 | 主要用途 | 存储数据的范围 |
---|---|---|
EIP | 保存当前下一条运行指针地址 | 0 - 0xFFFFFFFF |
辅以神兵OD,逆向美哉!
内存
基本概念
寄存器位于CPU内部,执行速度快,但比较贵;而内存通过引脚与CPU相连,速度相对较慢,但成本较低,所以可以做的很大。寄存器和内存没有本质区别,都是用于存储数据的容器,都是定宽的。
内存的数量特别庞大,无法每个内存单元都起一个名字,所以用编号来代替,我们称计算机CPU是32位或者64位,主要指的就是内存编号的宽度(寻址宽度),而不是寄存器的宽度。
计算机内存的每一个字节会有一个编号(即内存编号的单位是字节),编号从0开始,32位计算机的编号最大是32位,也就是FFFFFFFF,也就是说,32位计算机内存寻址的最大范围是FFFFFFFF+1 , 即4GB,这也是为什么我们在一个XP的系统上面如果物理内存超过4G是没有意义的原因。
[内存编号]表示地址,每个内存单元的宽度为8,超过8位的数据会溢出到下一个内存单元。
内存读写
- 内存读:
mov eax,dword ptr ds:[0x0012FF34]
- 内存写:
mov dword ptr ds:[0x0012FF34],0x12345678
- ptr: Point 代表后面是一个指针 (指针的意思就是里面存的不是普通的值,而是个地址)
- 注意:地址编号不要随便写,因为内存是有保护的,并不是所有的内存都可以直接读写(需要特别处理)
寻址公式
1. [立即数]
#内存读
mov eax,dword ptr ds:[0x19FF74]
#内存写
mov dword ptr ds:[0x19FF74],0xAABBCCDD
#获取内存编号
lea ecx,dword ptr ds:[0x19FF74]
2. [reg]
#内存读
mov ecx,0x19ff74
mov eax,dword ptr ds:[ecx]
#内存写
mov dword ptr ds:[ecx],0x87654321
#获取内存编号
lea eax,dword ptr ds:[ecx]
3. [reg+立即数]
#内存读
mov ecx,0x19ff74
mov eax,dword ptr ds:[ecx+4]
#内存写
mov dword ptr ds:[ecx+0xd],0x87654321
#获取内存编号
lea eax,dword ptr ds:[ecx+4]
*4. [reg+reg{1,2,4,8}]**:注意,这里只能取1,2,3,4中的一个。
#内存读
mov ecx,0x19ff74
mov edx,2
mov eax,dword ptr ds:[ecx+edx*4]
#内存写
mov dword ptr ds:[ecx+edx*4],0x87654321
#获取内存编号
lea eax,dword ptr ds:[ecx+edx*4]
*5. [reg+reg{1,2,4,8}+立即数]**
#内存读
mov ecx,0x19ff74
mov edx,2
mov eax,dword ptr ds:[ecx+edx*4+4]
#内存写
mov dword ptr ds:[ecx+edx*4+4],0x87654321
#获取内存编号
lea eax,dword ptr ds:[ecx+edx*4+4]
堆栈操作
堆栈模型:
压入数据:
#模拟栈顶栈底
mov ebx,0x13ffdc #base
mov edx,0x13ffdc #top
#方式1
mov dword ptr ds:[edx-4],0xaaaaaaaa
sub edx,4
#方式2
sub edx,4
mov dword ptr ds:[edx],0xaaaaaaaa
#方式3
mov mov dword ptr ds:[edx-4],0xaaaaaaaa
lea edx,dword ptr ds:[edx-4]
#方式4
lea edx,dword ptr ds:[edx-4]
mov mov dword ptr ds:[edx],0xaaaaaaaa
读取数据:
#通过base加偏移读取第n个压入的数,n需指定
mov esi,dword ptr ds:[ebx-4*n]
#通过top加偏移读取
mov esi,dword ptr ds:[ebx+4*(n-1)]
弹出数据:
#方法1
mov ecx,dword ptr ds:[edx]
lea edx,dword ptr ds:[edx+4]
#方法2
mov esi,dword ptr ds:[edx]
add edx,4
#方法3
lea edx,dword ptr ds:[edx+4]
mov edi,dword ptr ds:[edx-4]
push指令:完成上述模拟的压入数据操作,ebx和esp分别为占地和栈顶,由cpu调配,随着压栈操作而自动变化,与容器宽度有关加减2或4。
- push r32:esp-4
- push r16:esp-2
- push m16:esp-2
- push m32:esp-4
- push imm8/imm16/imm32:esp-4
pop指令:完成上述模拟的弹出数据操作,ebx和esp分别为占地和栈顶,由cpu调配,随着出栈操作而自动变化。
- pop r32:esp+4
- pop r16:esp+2
- pop m16:esp+2
- pop m32:esp+4
pushad指令:将8个通用寄存器的内容压栈,现场保护。
popad指令:pushad的逆操作,恢复现场。
标志寄存器
别问,问就是背
CF
进位标志(Carry Flag),如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0。
MOV AX,0xFF00
ADD AX,0x0101
PF
奇偶标志PF(Parity Flag):奇偶标志PF用于反映运算结果中最后一个字节中“1”的个数的奇偶性,如果“1”的个数为偶数,则PF的值为1,否则其值为0。
00010011 00010010
mov ax,0x1312
00010011 00010011 偶数 ,最后一字节为奇数-->p=0
add ax,1
00010011 00010100 奇数,最后一字节为偶数-->p=1
add ax,1
- 特别的,当最低有效字节为0时,PF=1
AF
辅助进位标志(Auxiliary Carry Flag),在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
- 在字操作时,发生低字节向高字节进位或借位时;
- 在字节操作时,发生低4位向高4位进位或借位时。
MOV AX,0x100f
ADD AX,0x4
ZF
零标志(Zero Flag):零标志ZF用来反映运算结果是否为0,如果运算结果为0,则其值为1,否则其值为0。
SF
符号标志(Sign Flag):符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
mov al,0x82
add al,1
OF
溢出标志(Overflow Flag):溢出标志OF用于反映有符号数加减运算所得结果是否溢;如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
最高位进位与溢出的区别:
- 进位标志表示无符号数运算结果是否超出范围.
- 溢出标志表示有符号数运算结果是否超出范围.
是否溢出的判别规律:
- 正 + 正 = 正 如果结果是负数,则说明有溢出
- 负 + 负 = 负 如果结果是正数,则说明有溢出
- 正 + 负 永远都不会有溢出.
DF
方向标志位,默认从右操作数到左操作数,可以通过CLD
和STD
指令修改DF
的值。
清方向位指令CLD(Clear Direction Flag):DF←0
置方向位指令STD(Set Direction Flag):DF←1
相关指令
ADC
带进位加法(C位参与运算),两边不能同时为内存,且数据宽度要一致。
格式:ADC R/M,R/M/IMM
ADC AL,CL
ADC BYTE PTR DS:[19ff74],2
ADC BYTE PTR DS:[19ff74],AL
SBB
带借位减法(C位参与运算),两边不能同时为内存,且数据宽度要一致。
格式:SBB R/M,R/M
SBB AL,CL
SBB BYTE PTR DS:[19ff74],2
SBB BYTE PTR DS:[19ff74],AL
XCHG
交换数据,两边不能同时为内存,且数据宽度要一致。
格式:XCHG R/M,R/M
XCHG AL,CL
XCHG DWORD PTR DS:[19ff74],EAX
XCHG BYTE PTR DS:[19ff74],AL
MOVS
移动数据,把ESI
存储的内存地址中的数据复制到EDI
存储的内存地址中,每复制一次,两个操作数均向上或下偏移4/2/1
个字节(地址递增或递减,便于复制下一串内容),当D=0
时递增,D=1
时递减。
MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
,简写为movsb
MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
,简写为MOVSW
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
,简写为movsd
STOS
将AL/AX/EAX
的值存储到[EDI]
指定的内存单元,D=0
时地址递增,D=1
时地址递减。
STOS BYTE PTR ES:[EDI]
,简写为STOSB
STOS WORD PTR ES:[EDI]
,简写为STOSW
STOS DWORD PTR ES:[EDI]
,简写为STOSD
REP
按计数寄存器 (ECX
) 中指定的次数重复执行字符串指令。
JCC
比较指令
cmp
格式:CMP R/M,R/M/IMM
该指令是比较两个操作数,实际上,它相当于SUB指令,但是相减的结果并不保存到第一个操作数中;只是根据相减的结果来改变零标志位的(其他标志位也可以更改),当两个操作数相等的时候,零标志位置1。
test
指令格式:TEST R/M,R/M/IMM
该指令在一定程序上和CMP指令时类似的,两个数值进行与操作,结果不保存,但是会改变相应标志位;用这个指令,可以确定某寄存器是否等于0。
jmp
无条件转移指令,仅可以修改EIP
的值,相当于mov eip,立即数/寄存器
。
格式:jmp 寄存器/立即数
call
call指令用于调用子程序,先将当前的EIP
压栈保存,然后jmp 目标地址
,相当于:
PUSH eip
mov eip,地址A/寄存器
格式:call 地址A/寄存器
ret
ret指令用于回到程序调用点,与call配套使用,先将eip
出栈,然后jmp eip
,相当于:
lea esp,[esp+4]
mov eip,[esp-4]
格式:ret
别问,问就是背
指令 | 解释 | 条件 | 例子 |
---|---|---|---|
JE、JZ | 结果为0时跳转(相等时跳转) | ZF=1 | cmp al,al jz 0x19ff74 |
JNE, JNZ | 结果不为零则跳转(不相等时跳转) | ZF=0 | cmp al,cl jnz 0x19ff74 |
JS | 结果为负则跳转 | SF=1 | mov al,1 sub al,3 js 0x19ff74 |
JNS | 结果为非负则跳转 | SF=0 | mov al,2 sub al,1 jns 0x19ff74 |
JP, JPE | 结果中1的个数为偶数则跳转 | PF=1 | cmp al,al jp 0x19ff74 |
JNP, JPO | 结果中1的个数为偶数则跳转 | PF=0 | mov al,1 cmp al,0xff jnp 0x19ff94 |
JO | 结果溢出了则跳转 | OF=1 | mov al,07f add al,4 jo 0x19ff74 |
JNO | 结果没有溢出则跳转 | OF=0 | mov al,07f add al,0 jno 0x19ff74 |
JB, JNAE | 小于则跳转 (无符号数) | CF=1 | mov al,2 mov cl,1 cmp cl,al jb 0x19ff90 |
JNB, JAE | 大于等于则跳转 (无符号数) | CF=0 | mov al,2 mov cl,1 test cl,al jb 0x19ff90 |
JBE, JNA | 小于等于则跳转 (无符号数) | CF=1 or ZF=1 | cmp al,al jbe 0x19ff90 |
JNBE, JA | 大于则跳转(无符号数) | CF=0 and ZF=0 | mov al,2 mov cl,1 cmp al,cl jnbe 0x19ff90 |
JL, JNGE | 小于则跳转 (有符号数) | SF≠ OF | mov al,1 mov cl,2 cmp al,cl jl 0x19ff74 |
JNL, JGE | 大于等于则跳转 (有符号数) | SF=OF | mov al,1 mov cl,2 cmp cl,al jnl 0x19ff74 |
JLE, JNG | 小于等于则跳转 (有符号数) | ZF=1 or SF≠ OF | mov al,2 mov cl,1 cmp cl,al jle 0x19ff90 |
JNLE, JG | 大于则跳转(有符号数) | ZF=0 and SF=OF | mov al,2 mov cl,1 cmp cl,al jnle 0x19ff90 |