语言基础
原码/反码/补码
原码不用多说
好比一个8位的数据
他的补码怎么算???
对于负数来说,取反加1是没有问题的
因为之前说的取反加1是获取相反数的方法
可为什么正数的反码与补码是它本身?
把正数取反+1肯定数值发生了变化
然后我在书上看到的知识
一个byte数据
1的补码=1+2的8次方
-1的补码=-1+2的9次方
其算法是通用的,而我们
所以其实补码并不总是取反加1,而是采用上面说到的方法
我的发现
源码+反码=无符号最大值
11111100b+00000011b=255
原码,反码,补码都是最高位表示符号位
起n-1位后面的就表示数值
所以的话,对于
整数 6=0x06
负数-1=0x86
哪些16进制是负数?
如果最高位
>= 8就是负数
__<= __7就是正数
代码书写格式规范
变量命名
全局变量用一般的名字
局部变量用@
开头
子函数的名字前面加__
下划线
一些前缀的变量
b表示byte
w表示word
dw表示dword
h表示句柄
lp表示指针,而且是长指针
sz表示0结尾的字符串
lpsz是以0结尾字符串的指针
f表示浮点数
st表示结构体
好比一个变量叫做lpsz_flag
就表示一个字符串指针
m或者m_ 表示宏
t或者t_表示临时变量
缩进
因为汇编的指令长度不一致,所以写完一个指令之后,按下一个Tab键,再去写入操作数会更加的美观
注释
;
用一个分号就可以实现注释
①.16位的注释是分号
②.32位的注释除了分号还有另外一种
COMMENT !
I am fine
!
上面那个感叹号,其实它就是一个结束的标志,只要是个符号就🆗
你也可以修改它为&,$,@,*...
你可以在main ENDP END main
后面再注释,但是在IDA里面我看不到
main ENDP
END main
"123456"
123456
//在IDA里面你是看不到后面的注释的
//在编译的时候,他不会参与
IDA对于一些很标准的DIY函数会有一个很人性化的命名,它还会给你中文注释,爱了爱了
爱了,爱了
很杂的知识
一不小心把code写进data
.code
main PROC
xor eax,eax
.data
temp dw 2001h
.code
mov ax,temp
这样写的话,IDA里面会有mov ax, xxx
.code
main PROC
xor eax,eax
.data
temp dw 2001h
mov ax,temp
.code
INVOKE ExitProcess,0;调用结束的函数
如果你这样写的话,IDA里面不会有mov ax, xxx
因为你的指令没有写在代码区,而写在了数据段
在data段你可以看到
.data:00405100 word_405100 dw 2001h ; DATA XREF: .data:00405102↓r
.data:00405102 ; ---------------------------------------------------------------------------
.data:00405102 mov ax, word_405100
你把代码写进了data段,是不会被执行的
标号
①.用于IP的标号,写在code区域
func:
mov ax,200
add ax,200
ret
②.用于数据的访问,写在data区域
Arr DD 1024,2048
DD 4096,8192
这里没有冒号.通过Arr
你可以访问那4个数据
比如
.data
sum DD ? ;?号的意思就是int sum;却不给他赋值
....
mov eax,1234h
add eax,4321h
mov sum,eax
...
在IDA里面它是这样的
mov dword ptr unk_405000, eax
unk_405000就是sum的地址
我么都知道短转移
x86说它有一个全局的标号..表达式形式是falg_xx::
于是你可以很远的地方jmp到这里来
x86的标号前面有一个点,这个点可能代表了局部变量
编译优化
如果你在x64下用64位寄存器,编译检测到你的使用有点高射炮打蚊子就会使用eax
Ubuntu使用
gdb
x/
x/s 地址,显示字符串 比如x/s &str1
x/lenb &str 从str地址开始输出len个数据,每个数据长度byte
x/dw &地址 显示地址处一个word的10进制Dec
x/xw &地址 显示地址处一个word的10进制hex
x/fg &浮点数地址 显示地址处一个10进制Dec
x/fx &浮点数地址 显示地址处一个16进制hex
x/s &xxx //显示地址的数,x是检测,s是字符串,c是字符
x/1xg &location 在地址处显示一个8字节的16进制,g代表gaint
显示寄存器
info registers //显示寄存器的数据,它等效于 i r
好比 info registers rdi rsi rsp
调试带有参数
gdb --args ./xxxname xx_arg1 xx_arg2 xx_arg3
disassemble main //显示相对地址和指令
break mian //在main处下了一个断点,等效于 b main
run //在断点的基础上单步指向,执行后会显示下以一条待运行的指令
step //步入
next //步过
continue //运行到下一个断点,等效于c
print $rax //打印变量,寄存器的值,等效于 p $rax, 以而颈椎打印,p/t $rax,16进制形式打印 p/x $rax
tui enable/disable //打开图形化界面/关闭图形化界面
system
ctrl l 清屏
杂项
字符串怎么逆序了
char temp[15]="Dqx-20019";
这么一块数据
在x86下
mov dword ptr [esp+21], 2D787144h
mov dword ptr [esp+25], 31303032h
mov dword ptr [esp+29], 39h ; '9'
mov word ptr [esp+33], 0
mov byte ptr [esp+35], 0
//也就是
mov dword ptr [esp+21], '-xqD'
mov dword ptr [esp+25], '1002'
mov dword ptr [esp+29], '9'
mov word ptr [esp+33], 0
mov byte ptr [esp+35], 0
lea eax, [esp+21]
在x64下
mov rax, 313030322D787144h
mov [rbp-32], rax
mov dword ptr [rbp-24], '9'
mov word ptr [rbp-20], 0
mov byte ptr [rbp-18], 0
//也就是
mov rax, '1002-xqD'
mov [rbp-32], rax
mov dword ptr [rbp-24], '9'
mov word ptr [rbp-20], 0
mov byte ptr [rbp-18], 0
可以看到他的数据有点逆序!!!!!
它是因为没有字符串这么一个类型...他把数据当做数值来处理
好比123
的数值形式0x333231
,
printf数据越界访问
printf("%d %d %d\n", ten );
x86
参数从栈里面获取
后面2个%d依次获取esp+8,esp+12
至于栈里面有什么,那就根据不同的我分配有不同的情况
x64下
参数从寄存器获取
后面2个%d依次从r8d,r9d获取
寄存器
x86寄存器
8个通用寄存器:EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP
1个标志寄存器:EFLAGS
6个段寄存器:CS、DS、ES、FS、GS、SS
5个控制寄存器:CR0、CR1、CR2、CR3、CR4
8个调试寄存器:DR0、DR1、DR2、DR3、DR4、DR5、DR6、DR7
4个系统地址寄存器:GDTR、IDTR、LDTR、TR
其他寄存器:EIP、TSC等**。**
32位 | 16位 | 8位 |
EAX | AX | AH、AL |
EBX | BX | BH、BL |
ECX | CX | CH、CL |
EDX | DX | DH、DL |
ESI | SI | |
EDI | DI | |
ESP | SP | |
EBP | BP |
EAX:
累加器(Accumulator), 它的低16位即是AX,而AX又可分为高8位AH和低8位AL。
用处:
是很多加法乘法的缺省寄存器,
存放函数的返回值,
EBX:
基地址寄存器(Base Register), 它的低16位即是BX,而BX又可分为高8位BH和低8位BL。
用处:
主要用于在内存寻址时存放基地址。
ECX:
计数寄存器(Count Register),它的低16位即是CX,而CX又可分为高8位CH和低8位CL。
用处:
在循环和字符串操作时,要用它来控制循环次数;
在位操作 中,当移多位时,要用CL来指明移位的位数;
是重复(REP)前缀指令和LOOP指令的内定计数器。
EDX:
数据寄存器(Data Register),它的低16位即是DX,而DX又可分为高8位DH和低8位DL。
在进行乘、除运算时,它可作为默认的操作数参与运算,
也可用于存放I/O的端口地址;
且总是被用来放整数除法产生的余数。
ESI/EDI:
分别叫做源/目标索引寄存器(Source/Destination Index Register),它们的低16位分别是SI、DI。
用处:
它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,
在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串。
此外,它们又作为通用寄存器可以进行任意的常规的操作,如加减移位或普通的内存间接寻址。
EBP/BSP:
分别是基址针寄存器(Base Pointer Register)/堆栈指针寄存器(Stack Pointer Register),低16位是BP、SP,
其内存分别放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶/底部。
用处:
主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,。
指针寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。并且规定:BP为基指针(Base Pointer)寄存器,用它可直接存取堆栈中的数据;SP为堆栈指针(Stack Pointer)寄存器,用它只可访问栈顶。在32位平台上,ESP每次减少4字节
x64寄存器
x64 结构提供了 16 个通用寄存器(以后称为整数寄存器),
16 个 XMM 寄存器。
注意xmm寄存器是128位,而不是64位
xmm寄存器主要用于浮点数处理
ymm寄存器
256位
x86-64处理器的16个通用寄存器
寄存器名 | 寄存器简介 | 主要功能 | 64bit | 32bit | 16bit | 8bit |
rax | 累加器,是算术运算的主要寄存器 | 存储返回值 | rax | eax | ax | al |
rbx | 基址寄存器,被调用者保存 | 存放存储区的起始地址 | rbx | ebx | bx | bl |
rcx | 计数寄存器 | 循环操作和字串处理的计数控制;函数调用时的第4个参数 | rcx | ecx | cx | cl |
rdx | I/O指针 | I/O操作时提供外部设备接口的端口地址;函数调用时的第3个参数 | rdx | edx | dx | dl |
rsi | (source index)源变址寄存器,与rds段寄存器联用,可以访问数据段中的任一个存储单元 | 函数调用时的第2个参数 | rsi | esi | si | sil |
rdi | (destination index)目的变址寄存器,与res段寄存器联用,可以访问附加段中的任一个存储单元 | 函数调用时的第1个参数 | rdi | edi | di | dil |
rbp | (base pointer)基址指针寄存器,用于提供堆栈内某个单元的偏移地址,与rss段寄存器联用,可以访问堆栈中的任一个存储单元,被调用者保存 | rbp | ebp | bp | bpl | |
rsp | (stack pointer)栈顶指针寄存器,提供堆栈栈顶单元的偏移地址,与rss段寄存器联用,以控制数据进栈和出栈 | rsp | esp | sp | spl | |
r8 | 函数调用时的第5个参数 | r8 | r8d | r8w | r8b | |
r9 | 函数调用时的第6个参数 | r9 | r9d | r9w | r9b | |
r10 | 调用者保存 | r10 | r10d | r10w | r10b | |
r11 | 调用者保存 | r11 | r11d | r11w | r11b | |
r12 | 被调用者保存 | r12 | r12d | r12w | r12b | |
r13 | 被调用者保存 | r13 | r13d | r13w | r13b | |
r14 | 被调用者保存 | r14 | r14d | r14w | r14b | |
r15 | 被调用者保存 | r15 | r15d | r15w | r15b |
段寄存器
寄存器 | 功能 |
CS(code segment) | 代码段地址寄存器,存放代码段的起始地址 |
DS(data segment) | 数据段地址寄存器,存放数据段的起始地址 |
SS(stack segment) | 堆栈段地址寄存器,存放堆栈段的起始地址 |
ES(extra segment) | 附加段地址寄存器,存放附加段的起始地址 |
控制寄存器 IP(Instruction Pointer):指令指针寄存器,存放代码段中指令的偏移地址。 FR(Flags Register):标志寄存器,用于存放反映处理器和运行程序执行结果状态的控制标志和条件码标志。
movsd xmm0, qword ptr [rsi]
movsd xmm1, qword ptr [rsi+8]
奇怪啊,奇怪
影响标志寄存器的指令
add
影响AC/CF/OF/PF/SF/ZF
指令对标志位的影响:
CF=1 最高有效位向高位有进位
CF=0 最高有效位向高位无进位
OF=1 两个同符号数相加(正数+正数 或 负数+负数),结果符号与其相反。
OF=0 两个不同符号数相加,或同符号数相加,结果符号与其相同。
adc (add with carry)
影响AC/CF/OF/PF/SF/ZF
指令对标志位的影响:
CF=1 最高有效位向高位有进位
CF=0 最低有效位相高位无进位
OF=1 两个同符号数相加,结果符号与其相反,
OF=0 两个同符号数相加,或同符号相加,结果符号与其相同
inc
影响AC/OF/PF/SF/ZF,不会影响CF
指令对标志位的影响
OF=1 两个同符号数相加,结果符号与其相反,
OF=0 两个同符号数相加,或同符号相加,结果符号与其相同。
他对ZF有影响!!!!!!!!!!!
sub
影响AC/CF/OF/PF/SF/ZF
指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
带借位减法指令 SBB
影响AC/CF/OF/PF/SF/ZF
指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
dec
影响AC/CF/OF/PF/SF/ZF, 对CF无影响
指令对标志位的影响:
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
cmp
AF/CF/OF/PF/sF/ZF
指令对标志位的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
求补指令 NEG (negate)
指令对标志位的影响:
CF=1 不为0的操作数求补时
CF=0 为0的操作数求补时
OF=1 操作数为-128(字节运算)或操作数为-32768(字运算)
OF=0 当求补运算的操作数不为-128(字节)或-32768(字)时
mul/imul
指令对标志位的影响:乘法指令只影响标志位CF和OF,其他条件码位无定义。
MUL指令的条件码设置为:
CF OF=0 0 乘积的高一半为0(字节操作的(AH)或字操作的(DX))
CF OF=1 1 乘积的高一半不为0
IMUL指令的条件码设置为:
CF OF=0 0 乘积的高一半为低一半的符号扩展.
CF OF=1 1 其他情况
div/idiv
指令对标志位
AC/CF/OF/PF/SF/ZF
and
指令对标志位的影响:
指令执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0
or
指令对标志位的影响:
令执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0
not
指令对标志位的影响:对标志位无影响
xor
指令对标志位的影响:
执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0
xor eax,eax zf=0
test
指令对标志位的影响:
令执行后 CF 和 OF 置零,AF无定义。
PF=1 结果操作数中1的个数为偶数时置1
PF=0 结果操作数中1的个数为奇数时置0
逻辑左移 SHL
指令对标志位的影响: CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
逻辑右移 SHR
指令对标志位的影响:CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
算术左移 SAL
指令对标志位的影响:CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
算术右移SAR
指令对标志位的影响:CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
循环左移 ROL
指令对标志位的影响:CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
循环右移 ROR
指令对标志位的影响:CF=移入的数值
OF=1 当count=1时,移动后最高位的值发生变化。
OF=0 当count=1时,移动时最高位的值未发生变化。
带进位的循环左移 RCL
指令对标志位的影响:CF=移入的数值。
OF=1 当cnt=1时,移动后最高位的值未发生变化。
OF=0 当cnt=1时,移动后最高位的值发生变化。
SF、ZF、PF标志位不受影响。
带进位的循环右移 RCR
指令对标志位的影响:CF=移入的数值。
OF=1 当cnt=1时,操作数最高位的值未发生变化。
OF=0 当cnt=1时,操作数最高位的值发生变化。
SF、ZF、PF标志位不受影响。
串传送MOVSB / MOVSW
指令对条件码的影响:不影响条件码。
存串 STOSB / STOSW
指令对条件码的影响:不影响条件码。
取串LODSB / LODSW
(load from string byte/word)
指令对条件码的影响:不影响条件码。
串比较 CMPSB / CMPSW
(compare string byte/word)
指令对条件码的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
串扫描 SCASB / SCASW
(scan string byte / word)
指令对条件码的影响:
CF=1 二进制减法运算中最高有效位向高位有借位(被减数小于减数,不够减的情况)
CF=0 二进制减法运算中最高有效为向高位无借位(被减数〉=减数,够减的情况)
OF=1 两数符号相反(正数-负数,或负数-正数),而结果符号与减数相同。
OF=0 同符号数相减时,或不同符号数相减,其结果符号与减数不同。
标志寄存器
CF: 进位标志符号比 排在第0位 PF: 奇偶标志 排在第2位 AF: 辅助进位标志 排在第4位 ZF: 零标志 排在第6位 SF: 符号标志 排在第7位 TF: 追踪标志 排在第8位 IF: 中断允许标志 排在第9位 DF: 方向标志 排在第10位 OF: 溢出标志 排在第11位
对于上面的位置,我们不需要去记住
CF 无符号溢出
无符号数溢出,无符号数进位
CF 把所有的数据都当作无符号的数来处理
Carry : 进位的意思
1:CY carry yes
0:NC not carry
1-2,发生了借位,CF==1
1+9,发生了进位,CF=1
进位后的数据被丢到了CF中
STC 让CF=1
CLC 让CF=0
CMC 让CF取反
mov al,0x7f
add al,1
不会引起CF改变
inc al,更不会
mvo al,0xff
inc al 不会
dec al
add al,1会引起改变
PF
与奇偶性有关
parity:奇数与偶数,奇偶性
Evem : 偶数
Odd : 奇数
最低有效的8位有多少个1,而不是看所有的位
1: PE Parity_Evem,二进制中有偶数个1
0: PO parity_Odd ,二进制中有奇数个1
这是代表了一个二进制位数上的奇偶性质,数值本身的奇偶与这个标志位无关
AF
辅助进位标志AF(Auxiliary Carry Flag):
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:
(1) 在 双字(dword)操作时,如eax,发生低字eal,向高字eah进位或借位时;
(2)、在字(word)操作时,如ax,发生al低字节向高字节ah进位或借位时;
(3)、在字节(byte)操作时如al,发生低4位向高4位进位或借位时
其规律就是数据宽度的一半向另外一半进位
MOV EAX,0X555EFFFF
MOV AX,0X5EFF
MOV AL,0X5F
ADD EAX,0X2
MOV AX,0X2
MOV AL,0X2
ZF:
Zero_Flag
与计算结果为0相关
1: ZR Zero
0: NZ not zero
xor eax,eax
SF:
Sign_Flag
Sign : 符号,正负
Negativbe:,负数
Positive ;正数
符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同。
1 : NG Negativbe_Large,负数
0 : PL Positive_Little ,正数
如果运算产生负数,SF=1
mov ah,0x7F
add ah,1
TF:
追踪标志位trap flag
当TF=1,cpu进入单步执行状态
每执行一条语句,产生一个中断请求
感觉可以在这里做一点文章...
该寄存器可以用于程序的调试
IF:
interrupt enable flag
中断允许标志
中断允许标志用来决定cpu是否响应CPU外部可屏蔽中断发生的请求
对于不可以屏蔽的中断还有cpu内部发生的中断,他就没有办法了,必须响应
IF=1,响应外部可屏蔽中断
IF=0,不响应可屏蔽中断
STI 让IF=1
CLI 让IF=0
DF
direction_flag
方向位标志寄存器
指令std,cld可以修改它
STD 让DF=1 表示-
CLD 让DF=0 表示+
与movsb,movsw,movsd有关
串传送指令
OF 有符号溢出
学习CF与OF,要始终牢记一点。
CF是无符号数溢出标志,
OF是有符号数溢出标志。
一个无符号的byte,超过了255就是CF=1
一个有符号的byte,超过了127,就是OF=1
OF : Over_Flow
1 : OV over_flow
0 : NV not_over_flag
OF把数据看作有符号来处理
超过max,小于min,就会OF=1
xor eax, eax
mov al, 7Fh;127 上溢
add al, 1
xor eax, eax
mov al, 80h ; -128 下溢
sub al, 1
规律2个数相加
只有0x7F到0x80,才叫OF溢出
根据10进制本质的符号运算
正数+正数=...
负数+负数=...
正数[0,1/2]
负数(1/2,1)
那么正数+负数
(1/2,3/2) 那么是不会经过0x7F->0x80的
于是正数+负数是不会溢出的
CPU是如何让OF发生改变的
1000 0000
+1100 0000
8bit位,右边0开始计数
如果[7]发生了进位,那么ret=1
如果[6]发生了进位,那么ret2=0
如果ret1^ret2=1,那么OF=1
也就是ret1和ret2中只有一个位1的时候,OF=1
IOPF :
I/O 特权标志
它可表示当前运行程序或者任务访问1/0指令的特权级
若当前运行程序或者任务的特权级别cpl<=IPOL(00B)的时候,则可以执行I/O指令,否则会发生异常
这里可以做文章
该值只能被允许与CPL=0的程序或任务通过popf/popfd指令或者iret指令进行修改
因此,一般用户程序(CPL=3)是无法执行I/O指令的
NT
nested task flag
NT=1,表示当前任务嵌套于前一任务
否则当前任务不嵌套于任何任务
当执行call指令/中断/异常时,cpu让NT=1
当任务执行返回时,通过iret让NT=0
问题来了...调用一个call就让NT=1,就这么随便吗?
另外NT=0只能是iret吗?
RF
恢复标志位 resume flag
恢复标志位用于控制cpu是否接受指令断点的调试异常
若RF=0,表示接受调试异常,否则不接受
该标志位一般友调试器设置,可以在刚进入调试异常的时暂时关闭新调试异常,
以避免调试异常时又立即进入了另外一个调试异常
VM :
virtual-8086 mode flag
虚拟8086模式标志
若VM=1,表示cpu进入了虚拟8086模式
否则进入了保护模式
AC :
Algnment check flag
对齐检查标志位
若AC=1, 且控制寄存器的cr0的AM标志位也为1,则允许内存地址进行对齐的检测
否则不允许
当程序的特权级别为3且运行于用户模式,若地址不对齐(即字访问时地址为奇数,双字访问时,地址不能被4整除),将产生对齐检查异常
之前我们只是提及过,寻址偶数地址会更加的快,国更加的发挥CPU的性能
CPU对非对齐字节地址访问内存,时间可能更加的长,若要求必须对齐,这要进行对齐检查
VIF :
virtual interrupt flag
虚拟中断标志寄存器VIF为IF标志的虚拟印象,
问题来了
什么叫虚拟印象
这个标志位与VIP标志配合使用,允许多任务环境下应用程序有虚拟的系统IF标志
VIP:
virtual interrupt pending flag
虚拟中断挂起标志
若vip=1,表示有挂起的中断
否则没有挂起的中断
一般由软件对其标志位初始化或者复位
CPU仅仅是读取,他与VIF配合使用
ID :
identification flag
识别标志位
若ID位能被置位或者复位,则说明CPU支持CPUID指令
CPUID可以提供CPU的厂商,系列号等信息
与标志寄存器有关的指令
LAHF
8086也有指令lahf/sahf
L的意思可能就是Load
lahf是把所有的标志寄存器的低8位复制到ah
sahf把ah的数据还给标志寄存器
SAHF
把AH的数据给flag的低8位
pushf/pushfd
把16/32位的寄存器数据压如栈
popf/popfd
把栈的值压入16/32位的标志寄存器flag
clc/stc/cmc
clc让CF=0
stc让CF=1
cmc 让CF取反
CLD/STD
CLD让DF=0,正向
STD让DF=1.逆向
CLI/STI
关闭响应可屏蔽中断CLI让IF=0
开启响应可屏蔽中断STI,让IF=1
x64 寄存器与调用约定
下面的约定是基于Linux下的64位elf
寄存器 | 例子 | 谁来保存 |
rax | 返回值 | |
rbx | 被我们push | |
rcx | 第4个参数/1 | |
rdx | 第3个参数/2 | |
rsi | arg_2|命令行参数字符串地址 | |
rdi | arg_1|令行参数数量, | |
rbp | 被我们push | |
rsp | 栈指针 | |
r8 | arg_3/第5个参数 | |
r9 | arg_4/第6个参数 | |
r10 | 临时 | |
r11 | 临时 | |
r12 | 用来做指针可以,[r12] | 被我们push |
r13 | 被我们push | |
r14 | 被我们push | |
r15 | 被我们push | |
xmm0 | 第一个参数和返回值 | |
xmm1 | 第2个参数和返回值 | |
xmm2-7 | 参数 | |
xmm8-15 | 临时 |
你给main函数传递参数....
其实就没有什么调用约定啥的
因为参数的利用完全依赖于你自己
调用约定是内置函数的事情,,好比print/scanf
mxcsr寄存器
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | | | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
FZ | RC | RC | PM | UM | OM | ZM | DM | IM | | | DAZ | PE | UE | OE | ZE | DE | IE |
下溢这0 | 舍入控制 | 舍入控制 | 精度掩码 | 下溢掩码 | 溢出掩码 | 除0错误掩码 | 非规格化操作掩码 | 无效操作掩码 | | | 非规格化操作 | 精度错误 | 下溢错误 | 溢出错误 | 除0错误 | 非规格化操作 | 无效操作错误 |
0-6位,也就是最后7位是异常发生的情况,发了就自动的置为1,没有发生就置位0
7-12位是对最后7位的一个异常屏蔽一一对应,
异常屏蔽置为了1,那么发生了异常也不会崩溃
异常屏蔽置为了0,那发生了异常就会崩溃
把DIY的mxcsr值放入寄存器
ldmxcsr ds:mxcsr_before
把原有的mxcsr值放入一个内存
stmxcsr ds:mxcsr_after
默认情况,没有异常
10.0 / 2.0 = 5.0000000000000000, in hex: 0x4014000000000000
mxcsr before: 0001 1111 1000 0000
mxcsr after: 0001 1111 1000 0000
精度异常
10.0 / 3.0 = 3.3333333333333335, in hex: 0x 40 0a aa aa aa aa aa ab
mxcsr before: 0001 1111 1000 0000
mxcsr after: 0001 1111 1010 0000
除0异常
10.0 / 0.0 = inf, in hex: 0x7ff0000000000000
mxcsr before: 0001 1111 1000 0000
mxcsr after: 0001 1111 1000 0100
向上取整:
10.0 / 3.0 = 3.3333333333333335, in hex: 0x400aaaaaaaaaaaab
mxcsr before: 0101 1111 1000 0000
mxcsr after: 0101 1111 1010 0000
向下取整:
10.0 / 3.0 = 3.3333333333333330, in hex: 0x400aaaaaaaaaaaaa
mxcsr before: 0011 1111 1000 0000
mxcsr after: 0011 1111 1010 0000
截断:
10.0 / 3.0 = 3.3333333333333330, in hex: 0x400aaaaaaaaaaaaa
mxcsr before: 0111 1111 1000 0000
mxcsr after: 0111 1111 1010 0000
精度异常:
11.0 / 3.0 = 3.6666666666666665, in hex: 0x400d555555555555
mxcsr before: 0001 1111 1000 0000
mxcsr after: 0001 1111 1010 0000
向上取整::
11.0 / y 3.0 = 3.6666666666666670, in hex: 0x400d555555555556
mxcsr before: 0101 1111 1000 0000
mxcsr after: 0101 1111 1010 0000
向下取整:
11.0 / 3.0 = 3.6666666666666665, in hex: 0x400d555555555555
mxcsr before: 0011 1111 1000 0000
mxcsr after: 0011 1111 1010 0000
截断:
11.0 /3.0 = 3.6666666666666665, in hex: 0x400d555555555555
mxcsr before: 0111 1111 1000 0000
mxcsr after: 0111 1111 1010 0000
这些异常处理我们针对不同的情况作了一些掩码
然后异常发生后,因为屏蔽作了掩码,就不会发生了异常
数据类型
1、存储数据的宽度 杯子有多大
2、存储数据的格式 杯子能装什么
3、作用范围(作用域) 杯子可以在哪里用
关于变量声明
变量名 数据类型 数据值
而我们的变量名是可以缺少的,因为他的地址就可以引用它
没有变量名的地址就像一个没有名字的指针,我们很难引用它
在x86下,
main函数的变量在text区域
然后放在esp的高位栈空间
变量的使用就push到低位栈空间
x64下
main函数的变量也是高位栈空间
但是你esp+32和ebp-8不都表示高位栈空间吗?于是x64用后者表示了数据分布
高频
全局变量
x68
就是定义在data区的变量
为什么说是全局?因为你可以跳过地址去访问那段一直不会消失的空间
.code
main PROC
xor eax,eax
.data
temp dw 2001h
.code
mov ax,temp
这样写的话,你的代码仍然会被放进data中,只不过是最后一个加入data的
x64
section .data
var dq 10.0
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
section .bss
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
section .text
global main
main:
mov rbp, rsp; for correct debugging
于是你的var变量任意一个函数都可以访问
有符号的立即数
.386
.model flat,stdcall
option casemap:none
.data
.code
start:
xor edx,edx
xor eax,eax
mov eax,-101
//eax=0xffffff9b
mov eax,9Bh
//eax=9bh
//9bh=-101
END start
可以看到负数与他的16进制还是有很大的区别的
所以我们才会有定义有符号的变量
好比var sdword -10
数组
特点:地址连续,成员等宽
8086数组
简要介绍
assume cs:code
code segment
a db 1,2,3,4,5,6,7,8,9,10
b dw 1,2,3,4,5,6,7,8,9,10
start:
mov ax,4c00h
int 21h
code ends
end start
上面没有冒号:
a其实代表了 1. byte ptr cs:[0]
2. 或者一个指向byte的指针
b的话就类推…..
...
code segment
a db 1,2,3,4,5,6,7,8,9,10
b dw 1,2,3,4,5,6,7,8,9,10
start:
mov al,a;相当于mov al,cs:[0]
mov bx,b;mov bx,cs:[10],因为就是b就在cs的第10个位置
mov a,66;相当于mov byte ptr cs:[0],66
mov b,88;相当于mov word ptr cs:[10],88
mov si,2
mov a[si],66;相当于mov byte ptr cs:[0+si],66;相当于mov byte ptr cs:0[si],66
mov si,3
mov b[si],77;相当于mov word ptr cs:[10+si],66;相当于mov word ptr cs:10[si],66;
指针数组
data segment
arr db 3,2,1
sum dw 0
the_ptr dw arr,sum
data ends
使用时的注意
1️⃣假设你有
data segment
table db "0123456789ABCDEF"
index dw 0,0,0
data ends
对于table的读取,只能是table[纯数字,或者bx]
你需要一个中间变量的引用
如果用mov ax,table[index]
就会报错,无论怎么修改类型
setscreen:
jmp short set
table dw sub1,sub2,sub3,sub4
s:
push bx
cmp ah,3
ja sret
mov bl,ah
mov bh,0
add bx,bx
call word ptr table[bx];重点在这里
sret:
pop bx
ret
2️⃣错误源码如下
mov bx,index
mov byte ptr ds:[index],al
inc word ptr index
IDA分析
mov bx, word_10088
mov byte ptr word_10088, al
inc word_10088
上面在直接对index写入,而不是[index]写入
正确的源码如下
mov bx,index
mov byte ptr [bx],al
inc word ptr index
IDA
mov bx, word_10088
mov [bx], al
inc word_10088
其实他就是需要一个中间值的转化
后面关于x86的形式也是一样的
x86数组
简要的介绍
一个例子
.data
Arr BYTE 5,4,3,2,1
str1 BYTE "Dqx_Gh0st"
BYTE "I am fine!",0
str2 \
db "This is Function pf '\' !"
.stack
;db 16 dup('S')
.code
main PROC
...
1️⃣.
值得注意的是
Arr BYTE 5,4,3,2,1
str1 BYTE "Dqx_Gh0st",0
类似的声明它是不能在最后加上,
逗号的,否者就报错
2️⃣.
str2 \
db "This is Function pf '\' !"
关于第一行的\
它的作用就是连接第2行的数据
于是上面的代码就等效于
str2 db "This is Function pf '\' !"
它的作用就是一行写不下,另外一行
如何调用数组
源码
mov al,[Arr+1]
mov ah,[str2+3]
mov ax,Arr[2*6]
mov ax,Arr[esi]
mov ax,Arr[esi*(type arr)];C语言 Arr[index*sizeof(int)]
在IDA里面它就是
mov al, byte_405001
mov ah, byte_40501C
mov ax, byte_40500D
mov ax, byte_405010[esi]
mov ax, word_404000[esi*2]
//间接寻址的方式会更加的好看
这就是使用寄存器的好处.很直观
另外注意的是
如果Arr是
db型, Arr+1就是Arr[1]
dw型,Arr+2*1才是Arr[1]
这不同于C语言,这里的每个++都是一个一个字节的处理
计算数组长度
1️⃣$取地址
arr db 1,2,3,4,5,6
arr_end dd $
len =(arr_end-arr)/1
//这个1是type arr
2️⃣lengthof直接算个数
arr db 1,2,3,4,5,
6,7,8,9,10
len = lengthof arr
指针
汇编指针
后面我才遇到,指针类型是ptr word
,
而指针的使用是word ptr
这还是有很大的不同的
off_404012
==*(0x404012)
off_404016
==*(0x404016)
而前面的word ptr或者byte ptr 只不过是强制类型转化
x86 指针用法
title Dqx_Gh0st
.386
.MODEL flat,stdcall
ExitProcess PROTO, dwExitCode:DWORD
db_ptr typedef ptr byte
dw_ptr typedef ptr word
.data
arr1 db 9,8,7,6,5,4,3,2
arr2 dw 9,8,7,6,5,4,3,2,1
ptr1 dd arr1
ptr2 dd arr2
.code
main PROC
xor eax,eax ....
INVOKE ExitProcess,0;调用结束的函数,
;---------------------------------------------------------------------------
main ENDP
END main
你用了一个中间值的转化
然后就接没有问题
(在8086也遇到过这种情况)
esi=*(ptr1)
mov esi,ptr1
mov al,byte ptr [ esi+ (type arr1)*4]
mov esi,ptr2
mov ax,word ptr [esi+ (type arr2)*5]
错误的用法
直接使用指针
xor eax,eax
mov al,byte ptr [ ptr1+ (type arr1)*4];错误的写法
mov ax,word ptr [ ptr2+ (type arr2)*5];错误的写法
xor eax,eax
mov ax, word ptr ptr1 [(type arr1)*4] ;错误的写法
mov ax, word ptr ptr2 [(type arr2)*5];错误的写法
于是上面的ptr1就是变量ptr1的地址
*ptr1=&arr1
mov al,[ptr1+1*4]
都是错的
反汇编和C指针
x86
不管你是几级的指针,它的指针长度都是4字节
在反汇编结果里面也直接看不出来是几级指针
对于char*的指针
我们都知道,char* ptr 中ptr++是地址+1
对于char** ptr的指针,ptr++就是+4了
其它的可以类比
指针运算
(0).
指针不可以乘除,可以加减, 运算的结果是int类型
运算,明面上是x参与运算,实际上是x*sizeof(xxx)参与运算
不同类型的指针,不可以运算
(2). 指针可以参与大小的比较
#include<stdio.h>
int main()
{
int**** x = (int****)0;
x=x+2;
printf("%p", x);
return 0;
}
最后,x=8 不用多说
#include<stdio.h>
int main()
{
short* x = (short*)100;
short* y = (short*)200;
printf("%d", y-x);
return 0;
}
/*
:50
:D:\Code\Vs-2022\work\w1\Debug\w1.exe (进程 4116)已退出,代码为 0。
:按任意键关闭此窗口. .
*/
输出50...是因为(200-100)/sizeof(short)
结构体
特点: 结构体成员的地址连续,但是一般不是等宽
如何理解?
一个地址指向了一个数据区域
这个区域有db,dw,dd,dq.....
然后通过指针可以访问这些数据
这个指针是第一个元素的地址
对齐
如果结构体成员最大字节<
默认对齐字节大小 ,按照最大成员字节对齐,而不是默认对齐
如果最大成员字节大小>默认对齐,按照默认对齐的方式对齐
默认对齐8
struct s1
{
char x;
short z;
};
//结构体4字节
struct s1
{
char x;
short z;
char y;
};
//6字节
在C语言下,字节对齐可以自己规定
#pragma pack(1)
struct str
{
}
#pragma pack()
每个成员所占空间是字节对齐的倍数,不足就补0
struct 21
{
char x;
double y;
};
struct s
{
char x;//1:
int w;//2:
struct s1 tmp1;//3:
int y;//4:
char q[10];//5:
double z;//6:
};
首先,默认对齐n=0
结构体成员中,最大也是9,那么对齐是8
在6:中,是8字节
在3: 是16字节
在4,5中,要8字节对齐,4,5中是int最大,所以5行,按照4字节对齐,所以q[10]扩展为12字节后,4行和5行可以被8字节对齐
1行2行,int最大,1行被扩展为4字节,所以1行2行一共8字节,可以对齐
声明与初始化
.data ;这些数据要在data区声明定义,struct的声明不会占据空间,他只是一个声明
my_struct struct
var1 db ?
var2 dw ?
var3 dd ?
my_struct ends;是ends!不是end
;上面的代码不会再data区显示,占据内存
var1 my_truct <>;不初始化
var2 my_struct <1,2,3>;初始话
;这里才是真正的定义了个结构体变量,是要占用内存的
这样就定义了一个结构体
我发现也可以这样定义,与初始化一起上,使得每一个结构体都是一样的数据
dqx_good STRUCT
IdNum BYTE "0000000"
LastName BYTE 20 DUP(0)
ALIGN word
Years WORD 0
ALIGN DWORD
SalaryHistory DWORD 0,0,0,0
dqx_good ENDS
其中的初始化有一些地方要注意
person1 dqx_good <"2001","Dqx_Gh0st",100,{4000h,,5000h,6000h}>
//这是在声明的时候一起初始化
对于变量1 IdNum 的初始化,如果你写入的数据小于预设长度,剩下的用空格填充,也就是0x20
对于变量2 LsatName 的初始化,如果你写入的数据小于预设长度,剩下的用0初始化
对于最后一个数组的初始话,要加一个大括号{}才可以不报错,否则报错
对单个成员初始化
对结构体里面的数组,直接使用数组名,就是对数组空间的第一个内存访问
如果要访问其它的内存空间,还是要用数组名,不过它此刻成为了地址
mov one.salary,20000
mov [one.salary+4],10000
如何调用
node struct
num dd ?
next WNDCLASS <>
stop_flag db ?
node ends
x node <20,,10>
//我们只是声明了外边2个变量,中间那个变量没有初始化
结构体变量名称.成员变量
这样的调用方式,编译器会去直接取处变量的地址,然后赋值
//源码
mov al,var1.db_var1
//IDA
mov al, byte_40300
[esi+结构体类型名称.成员变量]
前提是你的esi得获取结构体或者arr的地址
为什么是==[esi+node.num],而不是[esi+x.num]==
node.num返回的是偏移量
x.num返回的是值
//源码
mov esi,offset var1
mov al, [esi+my_struct_type.db_var1]
......
//IDA
mov esi, offset byte_403000
mov al, [esi]
上面esi指向了第一个地址,所以才会有mov al, [esi+0]
[esi].变量 用assume
用这种形式的话,我们要assuem
一下
mov esi,offset var2
assume esi:ptr my_struct_type
mov al, [esi-(type my_struct_type)].db_var1
mov ax, [esi-(type my_struct_type)].dw_var2
mov eax,[esi-(type my_struct_type)].dd_var3
assume esi:nothing
沿用上面的例子
.data
my_struct struct
var1 db ?
var2 dw ?
var3 dd ?
my_struct ends
var1 my_truct <>
var2 my_struct <1,2,3>
这里我们做了一些变化.....
我们的指针是指向结构体var2
但是我们对地址做了一些减法,让指针指向了var1
减去了多少呢????减去了一个结构体的大小
用 type 类型
计算出那个结构体的大小
接着上一种情况说明 不用assume
源码的分析
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
dqx STRUCT
ID BYTE "000000000"
LastName BYTE 20 DUP(0)
ALIGN word
year WORD 0
ALIGN DWORD
salary DWORD 0,0,0,0
dqx ENDS
one dqx <>
;--------------------------------------------------------
;function
;--------------------------------------------------------
.code
;--------------------------------------------------------
start:
;mov one.ID,"12822" ;woc,语法错误
mov one.year,18
mov esi,offset one
mov ax,(dqx ptr [esi]).year
invoke ExitProcess,NULL
;--------------------------------------------------------
;function
;----------------------------------------------------------
end start
;-----------------------------------------------------
IDA的分析
mov word_40301E, 18
mov esi, offset a000000000 ; "000000000"
mov ax, [esi+30]
仔细观看一下
mov esi,offset one
mov ax,(dqx ptr [esi]).year
dqx决定了操作数的类型,year决定了偏移量,esi决定了基地址
这和第一种[esi+结构体类型名称.成员变量]
有点像
结构体数组
假设有结构体
dqx STRUCT
ID BYTE "000000000"
LastName BYTE 20 DUP(0)
ALIGN word
year WORD 0
ALIGN DWORD
salary DWORD 0,0,0,0
dqx ENDS
然后定义一个数组
arr dqx 15 dup (< >)
调用它
mov esi,0
arr[esi].year=100
add esi,type xx_struct
arr[esi].num=200
包含结构体的结构体
a struct
x db ?
y db ?
a ends
b struct
z a <>
q a <>
b ends
;初始化一个变量
dqx b <{1,2},{3,4}>
就这样就定义了一个包含结构体的结构体
使用
mov dqx.z.x,6
mov esi,offset dqx
mov (b ptr [esi]).z.y,7
mov edi,OFFSET dqx.z
mov (a PTR [edi]).x, 8
mov edi,OFFSET dqx.z.y
mov byte PTR [edi], 9
在IDA里面分析可以得到
mov byte_403000, 6
mov esi, offset byte_403000
;访问了下一个字节,在esi的基础上,就esi+1
mov byte ptr [esi+1], 7
mov edi, offset byte_403000
;取dqx的地址,还是z的地址都是一样的,就好比取数组名的地址与数组首元素的地址都是一样的
mov byte ptr [edi], 8
mov edi, offset unk_403001
;这里直接取得第二个元素的地址,所以是unk_40300+1
mov byte ptr [edi], 9
内存布局
他和数组在堆栈中的排布类似
第一个成员在栈上方
最后一个成员在栈下方
联合体
一般的定义
Num UNION
D DWORD 1
W WORD 5
B BYTE 8
Num ENDS
把最大长度写在最前面,顺序呈降次
关于使用,Num的大小一直是最大尺度大小,也就是4字节
如果你初始化1字节,余下的字节就会用algin填充0,
1️⃣构体中的联合体
stu1 STRUCT
ID1 Num <>
Name1 BYTE 4 DUP('X')
stu1 ENDS
//沿用Num联合体
2️⃣在结构体里面声明,但是有点不一样
stu2 STRUCT
UNION ID2
D DWORD 70h
W WORD 80h
B BYTE 90h
ENDS
Name2 BYTE 4 DUP('X')
stu2 ENDS
很奇怪,他有2个ends
第一个没有写ID2 ends
注意事项1
Num UNION
D DWORD 60h
W WORD 70h
B BYTE 80h
Num ENDS
s1 Num <12345678h>
mov s1.B, 6
虽然你初始化了s1.b=6,但是以前的数据还是没有全部丢失,你只是覆盖了0x78为6
以前是0x12345678,现在是0x12345606
注意事项2
Num UNION
D DWORD 60h
W WORD 70h
B BYTE 80h
Num ENDS
像这种声明,word,byte的数据会被dword掩盖,所以后面2句的初始化都是无效的
以供调试的代码
里面的字节对齐很有意思
algin的用法见目录篇:指令集
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
Num UNION
D DWORD 60h
W WORD 70h
B BYTE 80h
Num ENDS
stu1 STRUCT
ID1 Num <>
Name1 BYTE 4 DUP('X')
stu1 ENDS
stu2 STRUCT
UNION ID2
D DWORD 70h
W WORD 80h
B BYTE 90h
ENDS
Name2 BYTE 4 DUP('X')
stu2 ENDS
s1 Num <12345678h>
s2 Num <66h>
s3 Num < >
s4 stu1 < >
s5 stu2 < >
;--------------------------------------------------------
;function
;--------------------------------------------------------
.code
;--------------------------------------------------------
start:
mov s1.B, 6
mov s2.W, 4
mov s3.D, 5
mov s4.ID1.B,9
mov s5.ID2.W,8
invoke ExitProcess,NULL
;--------------------------------------------------------
;function
;----------------------------------------------------------
end start
;-----------------------------------------------------
宏
宏实在IDA里面不会出现的,在编译阶段实现一个数据的初始化与填充
在源代码的开头我们有
;------------------------------------------------------------------------
x=1
.386
.MODEL flat,stdcall
ExitProcess PROTO, dwExitCode:DWORD
.code
main PROC
xor eax,eax
x=100
mov eax,x
x=1000
mov eax,x
INVOKE ExitProcess,0;调用结束的函数,
;---------------------------------------------------------------------------
main ENDP
END main
一般会被定义位一个32位的数据长度
在汇编里面看到的是
proc near ; CODE XREF: start↑j
xor eax, eax
mov eax, 100
mov eax, 1000
push 0 ; uExitCode
call j_ExitProcess
sub_402028 endp
可以看到,其实变量x并没有分配到内存,因为它不存在地址…
而且IDA里面也不会出现
x=100
x=1000
这些操作
可见它真的没有分配地址
正式因为这样才会有下面的错误
push x
x=6666
pop x
你可以push一个立即数,那是因为你有一个接收数据的地方
但是你不可以pop一个数据到立即数,就好比pop 100
这一看就是一个大错误
宏声明
在调用宏过程时,其代码的副本将被直接插入到程序中该宏被调用的位置。相当于一个字符串替换
位置宏定义一般出现在程序源代码开始的位置,或者是放在独立文件中,再用 INCLUDE 伪指令复制到程序里。
所以的话,你应该封装一下你的函数
就像书上恶心的第三方库函数
语法
宏名称 macro
变量/指令
endm
例子
include Dqx.inc
ok macro
mov al,'X'
mov bl,'Y'
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
.code
;--------------------------------------------------------
start:
ok
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
于是在IDA的代码里面
public start
start proc near
mov al, 58h ; 'X'
mov bl, 59h ; 'Y'
push 0 ; uExitCode
call ExitProcess
start endp
参数宏
要在宏声明的时候,后面写几个参数就欧克
include Dqx.inc
m_ok macro var1,var2
mov al,var1
mov bl,var2
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
.code
;--------------------------------------------------------
start:
m_ok 97,98
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
如果你的参数给错了,参数给少了,怎么办?
IDA
public start
start proc near
mov al, 97 ; 'X'
mov bl, 98 ; 'Y'
push 0 ; uExitCode
call ExitProcess
start endp
函数宏
普通版
这里有2个函数宏
第一个函数宏的功能是创建字符串,然后记录下他的地址
第二个函数宏的功能是创建字符串,他的地址无法二次引用
思考一下第二个宏,每一次创建的字符串都叫@string.是有问题的.所以我们才用了local来解决这个问题
值得注意的是,里面的.data与.code伪指令不要忘了,会引起很大的失误的
如果你的宏只是在data区建立的话,就用不上写data与code
include Dqx.inc
makestr MACRO @name ,@text
.data
@name BYTE @text,0
.code
ENDM
makestring MACRO @text
LOCAL @string
.data
@string BYTE @text,0
.code
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
makestr str1,"Gh0st"
mov esi,offset str1
makestr str2,"Dqx"
mov edi,offset str2
makestring "Hello"
makestring "I am fine"
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
其他版
isdefine macro button
ifdef button
exitm< 1 >
else
exitm< 0 >
endif
endm
exitm会返回一个数字给宏判断
然后
if ifdefine(flag)
include xx.inc
else
include yy.inc
endif
差不多就这样用
后续遇到再补充
宏嵌套
就是在一个宏里面嵌套另外一个宏
类似于函数调用
include Dqx.inc
makestr MACRO @name ,@text
.data
@name byte @text,0
.code
show @text
ENDM
show MACRO @text
echo @text
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
makestr str1,"hi,Gh0st,I was created!"
mov esi,offset str1
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
条件宏
如果满足条件,就把某段代码进行编译,否则就不进行编译
下面字母的缩写
B:blank,空的意思
D:define
伪指令 | 说明 |
IF 表达式 | 若 expression 为真(非零)则允许汇编。可能的关系运算符为 LT、GT、EQ、NE、LE 和 GE |
IFB <var1,var2> | 如果没有实参传入形参,就返回true,指向语句块 |
IFNB <var1,var2> | 如果非空,有参数的传进,就执行语句块 |
IFIDN< arg1>,< arg2 > | 若两个实参相等(相同)则允许汇编。采用区分大小写的比较 |
IFIDNI< arg1 >,< arg2> | 若两个实参相等(相同)则允许汇编。采用不区分大小写的比较 |
IFDIF< arg1 >,< arg2> | 若两个实参不相等则允许汇编。采用区分大小写的比较 |
IFDIFI< arg1>,< arg2> | 若两个实参不相等则允许汇编。采用不区分大小写的比较 |
IFDIF name | 若 name 已定义则允许汇编 |
IFNDEF name | 若 name 还未定义则允许汇编 |
ENDIF | 结束用一个条件汇编伪指令开始的代码块 |
ELSE | 若条件为真,则终止汇编之前的语句。若条件为假,ELSE 汇编语句直到遇到下一个 ENDIF |
ELSEIF expression | 若之前条件伪指令指定的条件为假,而当前表达式为真,则汇编全部语句直到出现 ENDIF |
EXITM | 立即退出宏,阻止所有后续宏语句的展开 |
例子:检测参数列表
试了一下,可以正确的检测2个参数,如果2个参数传递不够,就会语法错误,而不是执行else的语句
对于一个参数,是可以正常检测是否有的,如果传入就指向语句块,否则指向else
include Dqx.inc
makestr MACRO @name
ifnb <@name>
.data
@name byte "hello,i am here",0
.code
else
echo haha,you are wrong
endif
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
makestr
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
后面详细的讲一下,条件宏
宏调用的默认参数初始化
如果我们传入的参数不够,我们可以用ifb来检测,但是我们也可以使用默认参数来解决问题
语法
宏名 macro 参数名:=< xxx >
例子
include Dqx.inc
makestr MACRO @name:=<Empty_name>,@str:=<"no string">
local @name
.data
@name byte @str,0
.code
mov esi,offset @name
push esi
add esp,4
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
makestr str1,
makestr ,"no name"
makestr good,"bad"
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
上面之所以用local是因为,如果你一直不传入name的话,他会一直调用name作为关键字,导致name重复使用的语法错误
布尔表达式
之前说了一下宏条件的if
条件
他后面的表达式可以是布尔表达式
LT | little,小于的意思 | |
GT | greate,大于的意思 | |
EQ | equal,等于的意思 | |
NE | not equal,不相等的意思 | |
LE | little and equal ,表示小于等于 | |
GE | greate and equal,大于等于 |
好比这么用
if (X LT 0) or (Y GT 79)
....
IF/ELSE/ENDIF
它后面可以接
整数常量
符号常量
常量
什么叫符号常量?什么叫整数常量
但不可以接寄存器或者变量名
常有的形式有
if
endif
还有
if
esle
endif
特殊运算符
& 替换符号
include Dqx.inc
makestr MACRO b_name:=<Empty_name>,b_str:=<no string>
local b_name
.data
b_name byte &b_str,0
.code
mov esi,offset b_name
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
makestr one, "I was a str!"
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
你的参数"I was a str!"
会用在&b_str,然后直接别替换掉
感觉没太大的用
而且与原来的用法有什么区别我还不知道
% 格式转化
在TEXTEQU的运用
include Dqx.inc
;makestr MACRO
;ENDM
Digit = 4
Num textequ %(Digit+5)
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
mov eax,Num
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
在之前我们也有提及textequ的宏
例子1
include Dqx.inc
;makestr MACRO
;ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
arr db 9 dup('C')
;--------------------------------------------------------
.code
start:
echo The arr contains (SIZEOF arr) bytes ;wrong
echo The arr contains %(SIZEOF arr) bytes ;wrong
how_many textequ %(SIZEOF arr) ;正确的
% echo The arr contains how_many bytes
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
输出
Microsoft (R) Program Maintenance Utility Version 14.31.31105.0
Copyright (C) Microsoft Corporation. All rights reserved.
ml /c /coff x.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: x.asm
***********
ASCII build
***********
The arr contains (SIZEOF arr) bytes
The arr contains %(SIZEOF arr) bytes
The arr contains 9 bytes
Link /subsystem:windows x.obj
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
例子2
代码的功能就是实现 val3 = val1 * val2
里面有个检测
我们都知道乘法的eax是作为第一个因素的,不能作为第二个
所以这里有个检测
正确的代码,输出结果无
include Dqx.inc
MUL32 MACRO op1, op2, product
ifidni <op2>,<eax>
line_Num TEXTEQU %(@LINE)
ECHO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
% ECHO * Error on line line_Num: EAX cannot be as the second var
ECHO * argument when invoking the MUL32 macro.
ECHO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
exitm
;
endif
push eax
mov eax,op1
mul op2
mov product,eax
pop eax
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
val1 DWORD 3
val2 DWORD 2
result DWORD ?
;--------------------------------------------------------
.code
start:
;MUL32 val1,val2,result ; val3 = val1 * val2
mov eax,val1
MUL32 eax,val2,result
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
错误的代码,把eax作为了第二个参数
include Dqx.inc
MUL32 MACRO op1, op2, product
ifidni <op2>,<eax>
line_Num TEXTEQU %(@LINE)
ECHO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
% ECHO * Error on line line_Num: EAX cannot be as the second var
ECHO * argument when invoking the MUL32 macro.
ECHO xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
exitm
;exit会退出所有的宏定义,意思就是下面的语句不再执行
;虽然我们可以通过elseif来不执行
endif
push eax
mov eax,op1
mul op2
mov product,eax
pop eax
ENDM
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
val1 DWORD 3
val2 DWORD 2
result DWORD ?
;--------------------------------------------------------
.code
start:
;MUL32 val1,val2,result ; val3 = val1 * val2
mov eax,val1
MUL32 val2,eax,result
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
输出
Microsoft (R) Program Maintenance Utility Version 14.31.31105.0
Copyright (C) Microsoft Corporation. All rights reserved.
ml /c /coff x.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: x.asm
***********
ASCII build
***********
x.asm(36) : error A2006: undefined symbol : @LINE
MUL32(2): Macro Called From
x.asm(36): Main Line Code
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* Error on line 0: eax cannot be as the second var
* argument when invoking the MUL32 macro.
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
NMAKE : fatal error U1077: 'D:\Back\C0de_IDE\Asm\x86\masm32\bin\ml.EXE' : return code '0x1'
Stop.
< > 文字文本替换符
他会把<>里的内容当做一个文本的整体导入,这个文本不是浅层意义字符串,而是xxx,就像你现在打的字
include Dqx.inc
make macro t_str
local t_name
.data
t_name byte t_str,0
.code
mov esi,offset t_name
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
;--------------------------------------------------------
.code
start:
make "I am here",0Dh,0Ah
make <"I am there",0Dh,0Ah>
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
0Dh是回车,0Ah是换行
第一个语句看似像给str传入"I am here",0Dh,0Ah,但是被识别为3个参数,编译器发出警告,然后舍弃最后2个参数
x.asm(17) : warning A4006: too many arguments in macro call
make(1): Macro Called From
然后第二种写法才是把他们当做一个整体写入
IDA
.data:00403000 aIAmHere db 'I am here',0 ; DATA XREF: start↑o
.data:0040300A aIAmThere db 'I am there',0Dh,0Ah,0
.data:0040300A ; DATA XREF: start+5↑o
.data:00403017 align 1000h
! 强制处理运算符
在C语言里面我们有转移字符
好比\n转义为换行
在masm里面,!的作用好比这个
多的也不说,感觉没怎么用
宏循环
while
看代码会更加的清晰
一个斐波拉契数列,后一项是前2项的sum
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
x=1
y=1
byte x
byte y
z=x+y
while z LT 10
byte z
x=y
y=z
z=x+y
endm
;--------------------------------------------------------
.code
start:
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
他的效果是个啥????
首先我们创建了2个变量,x和y
然后利用循环不断的创建z变量
最后在data区我们得到了
.data:00403000 ;org 403000h
.data:00403000 db 1
.data:00403001 db 1
.data:00403002 db 2
.data:00403003 db 3
.data:00403004 db 5
.data:00403005 db 8
.data:00403006 db 0
.data:00403007 db 0
repeat
与dup类似
它适用于有一定次数的循环
用法是
repeat 循环次数
xxx
endm
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
count=10
xy struct
x byte 10h
y byte 23h
xy ends
zq struct
z byte 34h
q byte 56h
zq ends
xyzq struct
repeat count
local b_xy,b_zq
b_xy xy <>
b_zq zq <>
endm
xyzq ends
one xyzq <>
;--------------------------------------------------------
.code
start:
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
在data区的效果,创建了10个xy,zq的变量
.data:00403000 ;org 403000h
.data:00403000 db 10h
.data:00403001 db 23h ; #
.data:00403002 db 34h ; 4
.data:00403003 db 56h ; V
.data:00403004 db 10h
.data:00403005 db 23h ; #
.data:00403006 db 34h ; 4
.data:00403007 db 56h ; V
.data:00403008 db 10h
.data:00403009 db 23h ; #
.data:0040300A db 34h ; 4
.data:0040300B db 56h ; V
.data:0040300C db 10h
.data:0040300D db 23h ; #
.data:0040300E db 34h ; 4
.data:0040300F db 56h ; V
.data:00403010 db 10h
.data:00403011 db 23h ; #
.data:00403012 db 34h ; 4
.data:00403013 db 56h ; V
.data:00403014 db 10h
.data:00403015 db 23h ; #
.data:00403016 db 34h ; 4
.data:00403017 db 56h ; V
.data:00403018 db 10h
.data:00403019 db 23h ; #
.data:0040301A db 34h ; 4
.data:0040301B db 56h ; V
.data:0040301C db 10h
.data:0040301D db 23h ; #
.data:0040301E db 34h ; 4
.data:0040301F db 56h ; V
.data:00403020 db 10h
.data:00403021 db 23h ; #
.data:00403022 db 34h ; 4
.data:00403023 db 56h ; V
.data:00403024 db 10h
.data:00403025 db 23h ; #
.data:00403026 db 34h ; 4
.data:00403027 db 56h ; V
.data:00403028 db 0
.data:00403029 db 0
for 循环取整数
语法
for name,<name1,name2,name3>
xx
endm
看例子
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
count=10
xy struct
x byte 55h
y byte 66h
xy ends
for b_name,<one,two,three,four,five,six>
b_name xy <>
endm
for b_value,<97,98,99,100,101,102>
local b_name
b_name byte b_value
endm
;for b_value,{<97,98>,<99,100>,<101,102>}
;local b_name
;b_name xy b_value
;endm
;--------------------------------------------------------
.code
start:
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
最后在IDA的DATA区域得到的数据
data:00403000 ;org 403000h
.data:00403000 db 55h ; U
.data:00403001 db 66h ; f
.data:00403002 db 55h ; U
.data:00403003 db 66h ; f
.data:00403004 db 55h ; U
.data:00403005 db 66h ; f
.data:00403006 db 55h ; U
.data:00403007 db 66h ; f
.data:00403008 db 55h ; U
.data:00403009 db 66h ; f
.data:0040300A db 55h ; U
.data:0040300B db 66h ; f
.data:0040300C db 61h ; a
.data:0040300D db 62h ; b
.data:0040300E db 63h ; c
.data:0040300F db 64h ; d
.data:00403010 db 65h ; e
.data:00403011 db 66h ; f
.data:00403012 db 0
.data:00403013 align 1000h
.data:00403013 _data ends
.data:00403013
.data:00403013
.data:00403013 end start
forc 循环取字符
与上一个例子类似
语法
forc <无双引号的字符串>
xx
endm
例子
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
forc code,<!@#$%^&*!>!<>
byte "&code"
endm
;--------------------------------------------------------
.code
start:
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
在IDA的data区域
.data:00403000 ;org 403000h
.data:00403000 db '@#$%^&*><',0
.data:0040300A align 1000h
REQ
不知道什么鬼
include Dqx.inc
m_ok macro var1:REQ,var2:REQ
mov al,var1
mov bl,var2
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
.code
;--------------------------------------------------------
start:
m_ok 97,98
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
ECHO
不知道什么鬼
include Dqx.inc
m_ok macro var1:REQ,var2:REQ
ECHO hello,I am link and masm
mov al,var1
mov bl,var2
endm
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
.code
;--------------------------------------------------------
start:
m_ok 97,98
ECHO hello,I am Dqx_Gh0st
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
ECHO的效果就是在输出字符串,不过在运行时不会输出
Microsoft (R) Program Maintenance Utility Version 14.31.31105.0
Copyright (C) Microsoft Corporation. All rights reserved.
ml /c /coff x.asm
Microsoft (R) Macro Assembler Version 6.14.8444
Copyright (C) Microsoft Corp 1981-1997. All rights reserved.
Assembling: x.asm
***********
ASCII build
***********
hello,I am link and masm
hello,I am Dqx_Gh0st
Link /subsystem:windows x.obj
Microsoft (R) Incremental Linker Version 5.12.8078
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
于是上面那里就输出了hello,I am Dqx_Gh0st
但是运行x.exe是不会有输出的,就连调试也不会
x64宏
; macro.asm
extern printf
%define double_it(r) sal r, 1 ;这里带有参数
%macro prntf 2 ;2代表了2个参数,参数1是%1,参数2是%2
section .data
%%arg1 db %1,0 ; first argument
%%fmtint db "%s %ld",10,0 ; formatstring
section .text ; the printf arguments
mov rdi, %%fmtint
mov rsi, %%arg1
mov rdx,[%2] ; second argument
mov rax,0 ; no floating point
call printf
%endmacro
section .data
number dq 15
section .bss
section .text
global main
main:
push rbp
mov rbp,rsp
prntf "The number is", number
mov rax, [number]
double_it(rax)
mov [number],rax
prntf "The number times 2 is", number
leave
ret
单行宏
以%define开头
%define double_it(r) sal r, 1 ;这里带有参数
这个r是参数
根据寄存器的约束[见内联汇编扩展篇]
这个r的意思应该是任意寄存器的意思,也就是传入的参数可以使任意寄存器
多行宏
用%macro与%endmacro来包含
包含了宏的名字,参数个数
局部变量用%%
开头,其实也不是非要用
这个的意思就是local差不多的意思...保证参数的名字不会重复
%%arg1
%%fmtint
参数用%
%1 代表了第一个参数
%2 代表了第二个参数
%macro prntf 2 ;2代表了2个参数,参数1是%1,参数2是%2
section .data
%%arg1 db %1,0 ; first argument
%%fmtint db "%s %ld",10,0 ; formatstring
section .text ; the printf arguments
mov rdi, %%fmtint
mov rsi, %%arg1
mov rdx,[%2] ; second argument
mov rax,0 ; no floating point
call printf
%endmacro
链表
第一次尝试
第一次尝试,失败
还不知道为什么
关于书上的例子
..........
; 数据段
.data
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
node struct
num dd ?
next WNDCLASS <>
node ends
x node <20,>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
xor eax,eax
mov esi,offset x
assume esi: ptr node
mov eax,[esi].next.lpfnWndProc
assume esi:nothing
.....
失败了,不知道为什么....
反正我写就是报错
.data
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
node struct
num dd ?
next dd <>
node ends
x node <20,offset y>
y node <10,offset x>
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
; 代码段
.code
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
start:
xor eax,eax
mov esi,offset x
assume esi: ptr node
mov eax,[esi].next.num
assume esi:nothing
push 0
call ExitProcess
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
第二次尝试
include Dqx.inc
;>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
.data
node struct
value dword 6
next_ptr dword 6
node ends
count=0
train_head label ptr node
repeat 5
count=count+1
node <,$+count*(type node)>
endm
train_tail label ptr node
node <0,0>
;--------------------------------------------------------
.code
start:
mov esi,train_head
mov edi,train_tail
check_continue:
mov ecx,(node ptr [esi]).next_ptr
cmp ecx,0
je over
add esi,type node
jmp check_continue
over:
echo i find it
invoke ExitProcess,NULL
end start
;-----------------------------------------------------
代码的作用是
建立5个链表
建立一个尾巴链表,他的next指针为null
然后查询链表,查到就退出
IDA的data区
.data:00403000 ;org 403000h
.data:00403000 dword_403000 dd 6 ; DATA XREF: start↑r
.data:00403004 dd offset dword_403008
.data:00403008 dword_403008 dd 6 ; DATA XREF: .data:00403004↑o
.data:0040300C dd offset dword_403010
.data:00403010 dword_403010 dd 6 ; DATA XREF: .data:0040300C↑o
.data:00403014 dd offset dword_403018
.data:00403018 dword_403018 dd 6 ; DATA XREF: .data:00403014↑o
.data:0040301C dd offset dword_403020
.data:00403020 dword_403020 dd 6 ; DATA XREF: .data:0040301C↑o
.data:00403024 dd offset dword_403028
.data:00403028 dword_403028 dd 0 ; DATA XREF: start+6↑r
.data:00403028 ; .data:00403024↑o
.data:0040302C dd 0
低频
real
<<汇编语言基于x86处理器>>的Page56
里面有个real实数类型…也就是浮点数的意思
BCD
还有一个BCD编码
<<汇编语言基于x86处理器>>的Page61
label 数据类型
用法
b_var label word
变量b_var的值就是紧挨着下一个word类型变量的地址
它指向一个word的数据类型
编译器也不会给label类型分配内存,它会与下一个数据共享一个位置
于是我们就可以不用offset了
.386
.MODEL flat,stdcall
ExitProcess PROTO, dwExitCode:DWORD
.data
arr_label label word;IDA不会出现这句话
arr dd 12345678h;arr的低4位地址的数据与arr_label共享
.code
main PROC
xor eax,eax
mov ax,arr_label;直接使用名字可以访问指针指向的值
xor eax,eax
mov ax,[arr_label+1]
INVOKE ExitProcess,0;调用结束的函数,
;---------------------------------------------------------------------------
main ENDP
END main
mov ax,arr_label;直接使用名字可以访问指针指向的值
因为arr_label指向word,所以我们用ax|16位来接收,只能接收12345678h
的低位4字节
mov ax,[arr_label+1]
+1是字节+1
12345678h的低位[1]开始于是获取了3456h