1-12

OD 按下F2断点/关闭断点

跳到被调用的地方的下一条语句

1626103662895

按下回车会鼠标右键选择反汇编窗口中跟随

定位到指定API函数的地方

1626103810500

1626103833021

按下Ctrl + G 或 鼠标右键选择转到->表达式

或者输入命令 bp api函数名

1626103933703

查看所有断点

1626103996834

1626104011540

按下 ALT + B 或 鼠标点击菜单栏 查看->断点

将OD添加到鼠标右键菜单栏

1626104111657

1626104133252

OD断点历史记录文件夹

1626104363289

c或c++内联汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 001_MOV.cpp : 定义控制台应用程序的入口点。
//
#include<iostream>
#include <Windows.h>
int aaa = 1;
int main(int argc, TCHAR* argv[])
{



MessageBoxW(0, NULL, NULL, MB_OK);
aaa = 0x889977;//MOV DWORD PTR DS:[0x403018],0x889977
__asm {
mov aaa, 0x889977
}
__asm mov aaa, 0x889977;
MessageBoxW(0, NULL, NULL, MB_OK);
return 0;
}

MOV指令

aaa=0x889977;

MOV DWORD PTR DS:[0x403018],0x889977

dword 双字 就是四个字节 ptr pointer缩写 即指针 []里的数据是一个地址值

dd指令

查看数据的指令

dd 地址

dd 0x403018 == dd 403018



知识点:

函数与CALL

关闭C/C++优化

单步步过

单步步入

一、函数与CALL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int add(int a,int b)

{

return a+b;

}

00401030 |. 8105 18304000>ADD DWORD PTR DS:[403018],88 ; add 加法指令

未被优化

00401040 |. 68 88000000 PUSH 88

00401045 |. A1 18304000 MOV EAX,DWORD PTR DS:[403018]

0040104A |. 50 PUSH EAX

0040104B |. E8 B0FFFFFF CALL 001_MOV.00401000 ; add函数

00401050 |. 83C4 08 ADD ESP,8

00401053 |. A3 18304000 MOV DWORD PTR DS:[403018],EAX

二、关闭C/C++优化

项目配置属性—> C/C++ —>优化—>禁用/Od

三、单步步入:(进CALL)

F9(运行): 如果断下后,要继续让程序运行起来,可以用快捷键

F7(单步步入):按下后执行下一条指令。如果有CALL则进入。

四、单步步过:(不进CALL)

F8(单步步过):按下后执行下一条指令。不会进入子函数CALL里边,子函数CALL也被当成一条指令。

这两个单步执行操作的主要区别在于:如果当前的命令是一个子函数,按,将会进入子函数,并停在子函数的第一条命令上;而按,将会一次运行完这个子函数。如果您单步步过的子函数中含有断点或其他调试事件,执行将会被暂停



知识点:

立即寻址

寄存器寻址

寄存器间接寻址

直接寻址方式

直接寻址方式

寄存器相对寻址

一、了解指令常用寻址方式

寻址方式就是寻找操作数或操作数地址的方式。

寄存器是中央处理器内的组成部分。寄存器是一组容量有限的高速存贮部件,它们可用来暂存指令、数据和位址。(DWORD)

1
2
3
4
5
6
7
8
9
10
11
MOV   EAX,0x1234     //立即寻址         

MOV EAX,EBX //寄存器寻址 数据寄存器,一般称之为通用寄存器组 通用寄存器有4个 EAX,EBX,ECX,EDX

MOV EAX,[EBX] //寄存器间接寻址

MOV EAX,v //v是一个变量名

MOV EAX,[1234] //直接寻址方式

MOV EAX,[EBX+1234] //寄存器相对寻址

二、理解寻址

上边的名字看起来很多,总结起来也就2种方式:

带[]与不带[]

1、不带括号的 有立即数与寄存器

2、带括号的就是指针,从地址里边取值出来。

1
2
3
4
5
6
7
8
9
10
11
MOV   EAX,0x1234     //立即寻址     eax=0x1234;      

MOV EAX,EBX //寄存器寻址 eax=ebx

MOV EAX,[EBX] //寄存器间接寻址 eax=*(int*)ebx;

MOV EAX,v //v是一个变量名 eax=v; //被编译后 一般会呈后边两种形式

MOV EAX,[1234] //直接寻址方式 eax=*(int*)(0x1234)

MOV EAX,[EBX+1234] // 寄存器相对寻址 eax=*(int*)((int)ebx+1234); //这里的1234称为偏移,EBX是基址

三、对调位置

1
2
3
4
5
6
7
8
9
10
11
12
13
MOV   EAX,0x1234     //立即数不能出现在MOV指令左边。如:   MOV 0x1234,EAX 这是错的写法   

后边的都可以对换位置

MOV EAX,EBX //寄存器寻址 eax=ebx mov ebx,eax

MOV EAX,[EBX] //寄存器间接寻址 eax=*(int*)ebx; mov [ebx],eax

MOV EAX,v //v是一个变量名 eax=v; mov v,eax

MOV EAX,[1234] //直接寻址方式 eax=*(int*)(0x1234) mov [1234],eax

MOV EAX,[EBX+1234] // 寄存器相对寻址 eax=*(int*)((int)ebx+1234) mov [ebx+1234],eax

四、代码测试

int *da=a;//int a[0x10]={0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xd,0xe};

//提示 在命令栏中用 dd 地址表达式 可以以DWORD 4字节格式显示指定地址的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
__asm

{

mov eax,0x1111

mov ebx,eax

mov ebx,bbb //全局变量bbb

mov ebx,da //取数组基址

mov eax,[da+4]

mov eax,[ebx+4]

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//下边指令,前边是寄存器相对寻址,后边是立即寻址

0040105A |. C745 FC 20304>MOV DWORD PTR SS:[EBP-4],001_MOV.00403020 ; int *da=a //a是数组的地址

00401061 |. B8 11110000 MOV EAX,1111 ; eax=0x1111 立即寻址

00401066 |. 8BD8 MOV EBX,EAX ; mov ebx,0x1111-->ebx=0x1111 //寄存器寻址

00401068 |. 8B1D 18304000 MOV EBX,DWORD PTR DS:[403018] ; mov ebx,008899FF-->ebx=009988FF 直接寻址

0040106E |. 8B5D FC MOV EBX,DWORD PTR SS:[EBP-4] ; mov ebx,da 寄存器 相对寻址

00401071 |. 8B45 00 MOV EAX,DWORD PTR SS:[EBP] //寄存器间接寻址

00401074 |. 8B43 04 MOV EAX,DWORD PTR DS:[EBX+4] ; eax=da[1]=1 寄存器 相对寻址

dd命令

1
2
3
4
5
dd eax + 2 * 8

dd eax

dd 0x11111

定位到指定地址

1626164503122



知识点:

加法汇编指令ADD

一、加法指令 ADD(Addition) 格式

格式: ADD A,B //A=A+B;

功能: 两数相加

  1. OPRD1为任一通用寄存器或存储器操作数,可以是任意一个通用寄存器,而且还可以是任意一个存储器操作数.

OPRD2为立即数,也可以是任意一个通用寄存器操作数.立即数只能用于源操作数B.

  1. A和B均为寄存器是允许的,一个为寄存器而另一个为存储器也是允许的, 但不允许两个都是存储器操作数.

//也就是说A与B不能同时是指针 如: add [eax],[ebx] 这类情况是错的

例子:

第三课中的add函数里 有如下2句代码

1
2
3
00401003 |. 8B45 08    MOV EAX,DWORD PTR SS:[EBP+8]       ; a

00401006 |. 0345 0C ADD EAX,DWORD PTR SS:[EBP+C] ; b //eax=a+b

二、ADD指令测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int _tmain(int argc, _TCHAR* argv[])

{

int i=0;

printf("");



i=i+0x100;

i=i+0x111;



int *p=&i;

__asm

{

mov eax,0x222

mov ebx,0x100

add ebx,eax //ebx322

//

mov ebx,p

add [ebx],0x111
//00401048 |. 8003 11 ADD BYTE PTR DS:[EBX],11 ; i=i+11

add i,0x111 //DWORD WORD

}

/*

0040101C |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8] ; eax=i=0

0040101F |. 05 00010000 ADD EAX,100 ; eax=eax+100=0+100=100

00401024 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX ; i=eax=100

00401027 |. 8B4D F8 MOV ECX,DWORD PTR SS:[EBP-8] ; ecx=i=100

0040102A |. 81C1 11010000 ADD ECX,111 ; ecx=ecx+111=100+111=0x211

00401030 |. 894D F8 MOV DWORD PTR SS:[EBP-8],ECX ; i=ecx=0x211

00401033 |. 8D55 F8 LEA EDX,DWORD PTR SS:[EBP-8] ; edx=&i=ebp-8;

00401036 |. 8955 FC MOV DWORD PTR SS:[EBP-4],EDX ; p=&i

00401039 |. B8 22020000 MOV EAX,222

0040103E |. BB 00010000 MOV EBX,100

00401043 |. 03D8 ADD EBX,EAX ; ebx=ebx+eax=100+222=322

00401045 |. 8B5D FC MOV EBX,DWORD PTR SS:[EBP-4] ; ebx=i=edx=ebp-8

00401048 |. 8003 11 ADD BYTE PTR DS:[EBX],11 ; i=i+11

0040104B |. 8145 F8 11010>ADD DWORD PTR SS:[EBP-8],111 ; i=i+111



*/



printf("");

return 0;

}

OD按下-号可以返回上一步



知识点:

十六进制

字节,字,双字

汇编中的内存单元长度修饰

add [ebx],0x111 //00401048 |. 8003 11 ADD BYTE PTR DS:[EBX],11 ; i=i+11

一、十六进制

十六进制同我们日常中的十进制表示法不一样。它由0-9,A-F,组成。与10进制的对应关系是:0-9对应0-9;A-F对应10-15;

十进制元素(0,1,2,3,4,5,6,7,8,9)

十六进制元素(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)

9+1=10; //十进制 A

F+1=10; //十六进制

99+1=100;//十进制 0x64

FF+1=100;//十六进制 11616=256 //0xBDA=111616+13*16+10

十六进制一般会加上前缀0x 汇编中也可以加上h后缀

//想一想 十六进制中

0x0F+2=0x11 0xF9+1=0xFA 0x10+1=0x11 0x18+8=0x20 8+9=0x11

二、字节,字,双字

字节 (1字节):BYTE类型 ( unsigned char) 0-255 表示成16进制0–0xFF

字 (2字节):WORD类型 (unsigned short) 0-65535, 表示成16进制0–0xFFFF

双字 (4字节):DWORD类型(unsigned long) 0-4294967295 表示成16进制0–0xFFFFFFFF

四字(8字节) QWORD 类型 (unsigned long long ) 64 位整数,Q 代表四(字)

三、汇编中的内存单元长度修饰

1、自动加上修饰前缀规律

后面是寄存器会自动加上前缀修饰符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 int pb=3;

mov [eax],ebx

mov edx,[ecx]

mov ebx,pb

BYTE c=3;

WORD w=4;

DWORD dw=5;

mov c,0x87654321

mov w,0x87654321

mov dw,0x87654321

2、必须手动添加内存单元长度修饰的情况

前边是存储器,后边是常量的情况,需要手动添加长度修饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
add [ebx],0x111 //特殊... 因为编译器不知道具体长度,会默认为 字节长度

mov byte ptr [ebx],0x87654321

mov word ptr [ebx],0x87654321

mov dword ptr [ebx],0x87654321



BYTE c=3;

WORD w=4;

DWORD dw=5;

__asm

{

add c,0x87654321 // mov [ebp-??],0x21

add w,0x87654321

add dw,0x87654321

/* 自动添加的长度修饰符

00401071 |. 8045 EF 21 ADD BYTE PTR SS:[EBP-11],21 ; c=21

00401075 |. 66:8145 FC 21>ADD WORD PTR SS:[EBP-4],4321 ; w=4321

0040107B |. 8145 F0 21436>ADD DWORD PTR SS:[EBP-10],87654321 ; dw=87654321

*/





}


知识点:

寄存器EAX

寄存器AX

寄存器AH

寄存器AL

一、EAX与AX,AH,AL关系图

EAX,EBX,ECX,EDX都有低16位和低8位,高8位寄存器

ESP,EBP,ESI,EDI只有低16位寄存器例如 SP,BP,SI,DI,而没有低8位和高8位

数据查看命令

dd 查看DWORD内存

dw 查看WORD内存

db 查看BYTE内存

1626179488871

一格表示一字节

1626178921052

二、代码测试

1、OD命令栏 ?号指令 (用计显示表达式的值)

? ax 查看ax的值

? eax 查看eax的值

? al 查看al的值

? ah查看ah的值

1626179249330

1626178885198

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

printf("");
int i_AX,i_AH,i_AL;
__asm
{
mov eax,0x87654321 ;
mov word ptr [i_AX],ax
mov byte ptr [i_AH],ah
mov byte ptr [i_AL],al

}

#include <Windows.h>

int _tmain(int argc, _TCHAR* argv[])
{
DWORD iEax;
WORD iAx;
BYTE iAh,iAl;
printf("\n");
__asm
{
mov eax,0x87654321
mov iEax,eax
mov iAx,ax
mov iAh,ah
mov iAl,al

}
printf("EAX=%x,AX=%x,AH=%x,AL=%x \n",iEax,iAx,iAh,iAl);
getchar();
return 0;
}


知识点:

MOVSX符号扩展传送

MOVZX零扩展传送

一、MOVSX与MOVZX格式

MOVSX 操作数A ,操作数B

MOVZX 操作数A ,操作数B

相同点:操作数B 空间必须小于 操作数A

1、格式与MOV基本相同

2、能完成小存储单元向大存储单元的数据传送 比如 movsx eax,bx movzx ebx,ax movsx eax,bx

正确语法:

1
2
3
4
MOVSX EAX,AX
MOVSX EBX,BL
MOVZX EDX,DX
MOVZX EDX,DL

错误写法:

1
2
MOVSX EAX,EAX //第二个操作数内存等于第一个操作数
MOVSX AX,EAX //第二个操作数内存大于第一个操作数

MOVSX,MOVZX 与MOV指令区别:

1、MOVSX,MOVZX的操作数B所占空间必须小于操作数A.

2、MOV指令是原值传送,不会改动。而MOVSX与MOVZX有可能会改动

MOVSX与MOVZX的区别:

1、MOVSX将用操作数B的符号位扩展填充操作数A的余下空间,如果是负数则符号位为1,如果是正数则和MOVZX功能相同

2、MOVZX将用0来扩展填充操作数A的余下空间。

操作数A

1626181356116

操作数B

1626181370217

二、代码测试

1
2
3
4
5
6
7
8
9
10
11
 int i=0x888;

char ci=-100;

printf("\n");

i=ci; //111111111111111111111111 11001101

i=(unsigned char)ci; // 11001101

printf("");

判断有符号十六进制是正数还是负数

char 0-FF //FF/2

DWORD 0-0xFFF 8F8F

最高为大于等于8的是负数,1至7是正数

OD按下F4键可以运行到鼠标指定的指令,前提是程序必须会经过这条指令


知识点:

LEA指令

&与LEA

OD里修改汇编代码 在指定的指令位置按下空格键或

一、LEA指令格式

有效地址传送指令 LEA

格式: LEA 操作数A, 操作数B

功能: 将操作数B的有效地址传送到指定的的某个寄存器,操作数A必须是寄存器。(32位系统上就是32位寄存器)

不能两个操作数都是内存操作的

1
2
错误写法
LEA DWORD PTR DS:[EBX],DWORD PTR DS:[0X123456]

二、C++代码测试

1
2
3
int i;

int *p=&i;

三、OD里修改汇编代码

按下空格后 直接输入汇编代码

1626186522912

四、汇编代码测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int i=0:

char a[4]={0};

__asm

{

lea ebx,i // ebx=&i

mov dword ptr [ebx],0x113388

mov i,0x776628

}




知识点:

OD调试

命令栏指令

一、OD调试

重新开始:Ctrl+F2

转到地址:CTRL+G

断点切换: F2

断点窗口: Alt+B

运行 : F9

暂停 : F12

单步步过: F8 //遇到CALL跳过

单步步入: F7 //遇到CALL进入

运行到选定位置 :F4 //这个主菜单上没有,右键菜单-断点-F4

反汇编窗口中跟随 :回车键enter

跟随: 回车键enter //进入某个地址

执行到返回 CTRL + F9

右键菜单–转到

+号:转到下一步 //Plus

-号:转到上一步 //Minus

*号:转到当前指令地址 //EIP

寄存器窗口:

二、命令栏指令

bp 下断点

bc 清除断点

dd 以双字方式显示数据

dw 以字方式显示数据

db 以字节方式显示数据

? 计算表达式的值



知识点:

汇编减法指令sub

初识标志位

标志寄存器PSW

ZF(零标志)

一、标志寄存器PSW

标志寄存器PSW(程序状态字寄存器PSW)

标志寄存器PSW是一个16为的寄存器。它反映了CPU运算的状态特征并且存放某些控制标志。8086使用了16位中的9位,包括6个状态标志位和3个控制标志位。

1626190827047

CF(进位标志位):当执行一个加法(减法)运算时,最高位产生进位(或借位)时,CF为1,否则为0。

ZF零标志位:若当前的运算结果为零,则ZF为1,否则为0。

SF符号标志位:该标志位与运算结果的最高位相同。即运算结果为负,则SF为1,否则为0。

OF溢出标志位:若运算结果超出机器能够表示的范围称为溢出,此时OF为1,否则为0。判断是否溢出的方法是:进行二进制运算时,最高位的进位值与次高位的进位值进行异或运算,若运算结果为1则表示溢出OF=1,否则OF=0

PF奇偶标志:当运算结果的最低16位中含1的个数为偶数则PF=1否则PF=0

AF辅助进位标志:一个加法(减法)运算结果的低4位向高4位有进位(或借位)时则AF=1否则AF=0

另外还有三个控制标志位用来控制CPU的操作,可以由程序进行置位和复位。

TF跟踪标志:该标志位为方面程序调试而设置。若TF=1,8086/8088CPU处于单步工作方式,即在每条指令执行结束后,产生中断。

IF中断标志位:该标志位用来控制CPU是否响应可屏蔽中断。若IF=1则允许中断,否则禁止中断。

DF方向标志:该标志位用来控制串处理指令的处理方向。若DF=1则串处理过程中地址自动递减,否则自动递增。

OD里能查看到除IF标志外的 8个标志位

1626190975291

二、SUB指令

减法指令SUB (SUBtract)

格式: SUB A,B //A=A-B;

功能: 两个操作数的相减,即从A中减去B,其结果放在A中.

SUB指令会影响标志位

如果SUB EAX,EBX 等于0的话,ZF标志位会置为1,说明两数相等



知识点:

比较指令CMP

条件跳转指令JZ

ZF(零标志)

CMP 和JZ 指令

比较指令CMP

格式: CMP A,B // A-B;

功能: 两个操作数的相减,即从A中减去B,其结果会影响标志位, 对标志位的影响与SUB指令相同。本条指令主要是用于配合条件转移指令使用。如JZ ZF=0时,跳转

条件转移指令 JE/JZ

格式: JE/JZ标号 //等于跳转

功能: ZF=1,转到指定地址执行

说明:

  1. 指令JE与JZ等价,它们是根据标志位ZF进行转移的指令

  2. JE,JZ均为一条指令的两种助记符表示方法

1
2
3
4
5
6
7
8
9
10
11
12
13
   printf("begin\n");

int a=3;

if (a!=3)

{

printf("do code\n");

}

printf("end");