DOS模拟器 DOSbox 挂载主机文件夹
开机自动配置如下:
1 2 3 4 mount c C:\Users\admin\Desktop\tool\debug mount d C:\Users\admin\Desktop\binary\16asm set path=c: d:
msdosplayer VS插件-MASM/TASM 16ASM汇编 8086cpu组织结构——寄存器(cpu的“局部变量”)
•EU部件:执行部件(excution unit)、译码、执行指令
•BIU部件:总线接口部件(bus interface unit)、取指令、读取数据、写入数据
常见反汇编指令 查看地址汇编-u 1 2 3 -u[range] -u 11c 120 -u 11c l 2
执行汇编指令-a 1 2 3 4 5 6 -a[address] -a 110 mov ax, ax mov dx, dx mov ax, dx mov byte ptr [110],al
寄存器操作-r
内存查看-d 1 2 3 -d[range] -d 120 130 -d 120 l 10
修改内存-e 1 2 3 -e 110 -e 120 11,12,13,14,15,16 17 18 19 20 -e 120 "Hello world"
调试命令(g,t,p)
写入文件(n,cx,w)
标志寄存器
条件标志位 SF ZF OF CF AF PF CPU执行完一条指令后自动设置。 反映算术、逻辑运算等指令执行完毕后,运算结果的特征。
控制标志位 DF IF TF 控制CPU的运行方式,工作状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 溢出OV(overflow,OF=1) 无溢出NV(no overflow,OF=0) 减量DN(direction down,DF=1) 增量UP(direction up,DF=0) 允许中断EI(enable interrupt,IF=1) 进制中断DI(disable interrupt,IF=0) 负NG(negative,SF=1) 正PL(plus,SF=0) 零ZR(zero,ZF=1) 非零NZ(no zero,ZF=0) 辅助进位AC(auxiliary carry,AF=1) 无辅助进位NA(no auxiliary carry,AF=0) 偶校验PE(even parity,PF=1) 奇校验PO(odd parity,PF=0) 进位CY(carry,CF=1) 无进位NC(no carry,CF=0)
CF进位标志 当运算结果的最高有效位有进位(加法)或借位(减法)时设置。
进位标志置1,即CF = 1;否则CF = 0
用途:用于表示两个无符号数高低。
举例:
3AH + 7CH=B6H,没有进位:CF = 0
AAH + 7CH=(1)26H,有进位:CF = 1
1 2 3 4 5 -a mov al, 3a add al, 7c -u -t
ZF零标志 运算结果为0则ZF=1,否则ZF=0。表示两个无符号数高低。
用途:用于表示两个无符号数高低。
1 2 3AH + 7CH=B6H,结果不是零:ZF = 0 84H + 7CH=(1)00H,结果是零:ZF = 1
OF溢出标志 •使用该标志位判断运算结果是否溢出。(当将操作数作为有符号数时)
–加法:若同符号数相加,结果的符号与之相反则OF=1,否则OF置0。
–减法:被减数与减数异号,而结果的符号与减数相同则OF=1,否则置0。
•发生了溢出,说明了运算结果不可信。
1 2 3AH + 7CH=B6H,产生溢出:OF = 1 AAH + 7CH=(1)26H,没有溢出:OF = 0
•进位针对的是无符号数运算,溢出针对的是有符号数运算。
•当看成无符号数,则关注CF标志,看成有符号数,则关注OF标志。
SF符号标志 •运算结果最高位为1,SF为1,否则为0。
•有符号数据用最高有效位表示数据的符号,最高有效位是符号标志的状态。
1 2 3AH + 7CH=B6H,最高位D7=1:SF = 1 84H + 7CH=(1)00H,最高位D7=0:SF = 0
PF奇偶标志位 •当运算结果(指低8位)中1的个数为偶数时,PF置1,否则置0。
•作用:该标志位主要用于检测数据在传输过程中的错误。
1 2 3AH + 7CH=B6H=10110110B 结果中有5个1,是奇数:PF = 0
AF辅助进位标志位 •表示一个字节的低4位是否有进位和借位。运算时D3位(低半字节)有进位或借位时,AF = 1;否则AF = 0。
•处理器内部使用,用于十进制算术运算调整指令中,用户一般不必关心
1 3AH + 7CH=B6H,D3有进位:AF = 1
分段和指令 8086是16位cpu,最多可访问(寻址)多大内存? 2^16 = 65536 = 2^6 * 2^10 = 64k
8086允许最大内存1M,1M = 10K = 2 ^ 20 内存[00000]-[FFFFF]
段基址(CS) + 段偏移 。的方式一般写作 段地址:段偏移 ,称作逻辑地址
偏移地址 称作EA
通过逻辑地址计算出来的内存地址称作物理地址
逻辑地址 -> 地址加法器 -> 物理地址
内存地址 = 段基址 *** 10h +** 段偏移
073F:0100 = 073 * 10 + 100
8086中,段基址都是存储在段寄存器中,段偏移可以用立即数或者通用寄存器指明。
立即寻址 操作数的值存储在指令 中的方式称作立即寻址。
汇编中整数常量称作立即数。
立即数可以是8位数,也可以是16位数。
1 2 MOV AL, 80H ;将8位立即数80H送入AL寄存器 MOV AX, 1234H ;将16位立即数1234H送入AX寄存器
寄存器寻址 操作数的值存储在寄存器 的寻址方式称作寄存器寻址。
寄存器包括通用寄存器,段寄存器。
PS:
段寄存器之间不能赋值。
指令指针寄存器不能用作寻址。
直接寻址 操作数值在内存中,机器码中存储16位段内偏移 的寻址方式称作直接寻址。
1 2 3 4 5 6 7 8 9 -r -r ds :2000 -r -e 1064 45 78 -d 1064 l 20 -r -a xxx mov ax, [1064]
PS:
-r 查看当前寄存器状态,能查看到各种寄存器的值,CS用于存储基地址 ,IP用于指向偏移地址 ,最下面的指令是后续将要执行的指令地址和内容,常用于直接-a编辑。
间接寻址 a.操作数值在内存中,段内偏移存储在寄存器中 的寻址方式称作寄存器间接寻址。
b.间接寻址的寄存器有BX, BP, SI, DI。
1 2 MOV AX, [SI] ;将SI中的值作为段内偏移,从内存中取出数据赋值AX MOV [BX], AL ;将BX中的值作为段内偏移,把AL中的值赋值给对应内存
相对寻址 a.操作数值在内存中,段内偏移存储由[寄存器 +立即数 ]计算得来的的寻址方式称作寄存器相对寻址。
b.寄存器相对寻址的寄存器有BX, BP, SI, DI。
c.寄存器相对寻址的立即数可以是8位,可以是16位的。
1 2 MOV [SI+10H], AX MOV CX,[BX+COUNT]
基址变址寻址 a. 操作数值在内存中,段内偏移由[寄存器 +寄存器 ]计算得来的寻址方式称作基址变址寻址。
b. 可用做基址的寄存器有BX, BP。(只能基址+变址/变址+基址)
c. BX默认DS段,BP默认SS段。
d.可用作变址的寄存器有SI, DI。
1 2 MOV [BX+DI], AX MOV CX, [BP+SI]
基址变址相对寻址 a. 操作数值在内存中,段内偏移由[基址寄存器 +变址寄存器 +偏移常量 ]计算得来的寻址方式称作基址变址寻址。
b. 可用做基址的寄存器有BX, BP。
c. BX默认DS段,BP默认SS段。
d.可用作变址的寄存器有SI, DI。
e.可用作常量的数值可以是8位,可以是16位。
可用于模拟数据运算
PS:
不可内存到内存,mov [bx],[ax]
关于机器码
手动分析:
1 2 3 4 5 6 7 8 mov ax,bx mov ax,cx mov ax,dx mov ax,ax mov ax,bp mov ax,si mov ax,di mov ax,sp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 89D8 MOV AX,BX 11 011 000 89C8 MOV AX,CX 11 001 000 89D0 MOV AX,DX 11 010 000 89C0 MOV AX,AX 11 000 000 89E8 MOV AX,BP 11 101 000 89F0 MOV AX,SI 11 110 000 89F8 MOV AX,DI 11 111 000 89E0 MOV AX,SP 11 100 000 AX 000 CX 001 DX 010 BX 011 SP 100 BP 101 SI 110 DI 111 MOV DX,BP 89 11 101 010 =>89 1110 1010 =>89EA
内存里运行的是机器码
数据传送和算术指令 机器码、内存、寄存器、立即数(8086、286、386、486)
MOV传送指令 把一个字节或字的操作数从源地址传送至目的地址。注意:不存在存储器向存储器的传送指令。
1 2 3 4 mov [bx],ax mov ax,[bx] mov byte ptr[bx], 12 ;要指定字节数,mov双方寄存器长度要一致 mov byte word[bx], 12 ;两个字节,可能存在溢出覆盖
1 2 mov byte ptr[bx],12 mov word ptr[bx],12
XCHG交换指令 1.寄存器与寄存器之间对换数据
2.寄存器与存储器之间对换数据
3.不能在存储器与存储器之间对换数据
XLAT换码指令 将BX(数组首地址)指定的缓冲区中、AL(数组下标索引)指定的位移处的一个字节取出赋给AL
al <– ds:[ bx + al ]
数组首地址是0,索引是e,取值赋给al
push、pop堆栈操作指令 1 2 3 4 5 6 7 8 9 1. 进栈(push reg) sub sp , 2 mov [sp] , reg 2.出栈(pop reg) mov reg, [sp] add sp , 2 3.保存所有寄存器环境 16位:pusha / popa 32位:pushad / popad
PUSHF、POPF标志进出栈指令 •PUSHF指令将标志寄存器的内容压入堆栈,同时栈顶指针SP减2
•POPF指令将栈顶字单元内容送标志寄存器,同时栈顶指针SP加2
LEA地址传送指令 •地址传送指令将存储器单元的逻辑地址送至指定的寄存器
–有效地址传送指令 LEA
–指针传送指令 LDS和LES
•注意不是获取存储器单元的内容
1 2 3 4 5 6 7 LEA和MOV的区别(bx+si = ea) LEA bx,[bx+si] // bx = ea 不取地址内容 mov bx,[bx+si] // bx = [ea] 取地址内容 add bx,si mov ax,bx => lea ax,[bx,si]
算数运算类指令 加法(ADD|ADC|INC) ADD:加法,影响标识位
1 2 3 4 ADD reg,imm/reg/mem ;reg←reg+imm/reg/mem ADD mem,imm/reg ;mem←mem+imm/reg
ADC:带进位加法,带标识位
1 2 3 4 ADC reg,imm/reg/mem ;reg←reg+imm/reg/mem+CF ADC mem,imm/reg ;mem←mem+imm/reg+CF
INC:加一,不影响CF标志位
减法(SUB|SBB|DEC) SUB:减法,影响标识位
1 2 3 4 SUB reg,imm/reg/mem ;reg←reg-imm/reg/mem SUB mem,imm/reg ;mem←mem-imm/reg
SBB:带借位的减法
1 2 3 4 SBB reg,imm/reg/mem ;reg←(reg-(imm/reg/mem)-CF) SBB mem,imm/reg ;mem←mem-imm/reg-CF
DEC: -1,不影响CF位
求补(NEG) •NEG指令对操作数执行求补运算:用零减去操作数,然后结果返回操作数
•求补运算也可以表达成:将操作数按位取反后加1
•neg ax ;如果ax = 0,则CF标志位 = 0;若ax != 0, 则CF = 1
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 问题: Reg == 0 ? 0 : -1 Reg == 1 ? 1 : 0 Reg == 8 ? 9 : 8 Reg == 6 ? 8 : 9 mov ax, X sub ax, 0 neg ax sbb ax, ax sbb ax, ax 1.X = 8(1000) mov ax, X ; ax = 8 sub ax, 0 ; ax = 8 neg ax ; ax = -8, CF = 1 sbb ax, ax; ax = -1, CF = 1 sbb ax, ax; ax = -1 2.X = 0 mov ax, X ; ax = 0 sub ax, 0 ; ax = 0 neg ax ; ax = 0, CF = 0 sbb ax, ax; ax = 0, CF = 0 sbb ax, ax; ax = 0
比较(CMP) •格式:: CMP OPD,OPS
•功能:(OPD)-(OPS)
•说明:目的操作数减去源操作数,然后根据结果设置标志位,但该结果并不存入目的地址 (sub存入)
•影响标志位:AF、CF、OF、PF、SF、ZF
•作用:一般的后面跟一条条件转移指令,根据比较结果转向不同的程序分支,用于处理OPD和OPS大小比较的不同情况。
乘法(MUL/IMUL) 无符号乘法 格式:MUL Reg/Mem
功能:显式操作数*隐含操作数(看成无符号数)。
影响标志位:CF和OF。
位数
隐含的被乘数
乘积的存放位置
举例
8位
AL
AX
MUL BL
16位
AX
DX&AX
MUL BX
32位
EAX
EDX&EAX
MUL ECX
1 2 3 4 5 6 7 8 9 10 11 两个byte相乘要用word放(FF*FF=FE01) 两个word相乘要用dword放(FFFF*FFFF=FFFE0001,没dword就两个寄存器) mov bl, 88 mov al, 99 mul bl //存到AX mov word ptr[0], 1234 mov bx, 4567 mov ax, 4567 mul word ptr[0], 4567 //存到DX&AX
如果乘积的高一半位(AH/DX/EDX)包含有乘积的有效位,则CF=1、OF=1; 否则,CF=0,OF=0。
OF=CF=1则说明: 字节乘字节结果超过了8位 字乘字结果超过了16位 双字乘双字结果超过了32位
1 2 3 4 mov bl, 15 mov al, 4 mul bl
有符号乘法 IMUL Reg/Mem
IMUL Reg, Imm ;80286+
IMUL Reg,Reg,Imm;80286+
IMULReg,Reg/Mem;80386+
功能:有符号数相乘
影响标志位:CF和OF。会扩展符号
1 2 3 4 5 6 7 8 9 如果乘积的高一半位(AH,DX,EDX)不是低位的符号扩展,则CF=1、OF=1;否则,CF=0,OF=0。 -1 * 1 = -1 FF * 1 => AX => 00FF 会被当做正数,则补全符号位 => FFFF -举例: mov bl, -2 mov al, 1 imul bl
1 2 3 mov bl, -15 mov al, 15 imul bl //不完全是符号位扩展,有数据位
EB * 15 = 1347
除法(DIV/IDIV) 无符号除法 格式:DIV Reg/Mem 被除数/除数 = 商 .. 余数
位数
被除数
除数
商
余数
8位
AX
8位ops
AL
AH
16位
DX,AX
16位ops
AX
DX
32位
EDX,EAX
32位ops
EAX
EDX
影响标志位:未定义
未定义:指令执行后这些标志是任意的,不可预测的。没有影响:指令执行后不改变标志状态
1 2 3 mov ax, 9 mov bl, 4 div bl
1 2 3 4 mov dx, 1234 // dx作用暂存 mov ax, 5678 mov bx, 4569 div bx // 12345678 / 4569 = 4324 . 18B4
有符号数除法 格式:IDIV Reg/Mem
位数
被除数
除数
商
余数
8位
AX
8位ops
AL
AH
16位
DX,AX
16位ops
AX
DX
32位
EDX,EAX
32位ops
EAX
EDX
影响标志位:AF、CF、OF、PF、SF和ZF
1 2 3 mov ax, -19 // 十六进制的-19 0001 1001 => 1110 0111 => E 7 mov bl, 2 // 02 idiv bl // E7/2=F4
被除数远大于除数时,所得的商就有可能超出它所能表达的范围。
idiv除法溢出:-字节除时商不在-128~127范围内,或者在字除时商不在-32768~32767范围内
div除法溢出: 8位除法运算结果大于8位,16位除法运算结果大于16位。
举例: ax= FFFF, bl = FF, div bl
结果:相当于FFFF/FF=101,此时AH显然放不所以商溢出
符号扩展指令(CBW/CWD) CBW(将字节转换成字指令):
-语句格式:CBW(convert byte to word)
-功能:将AL中的符号扩展至AH中,操作数是隐含且固定的。把数值最高位扩展
1 2 3 4 MOV AL, 04 CBW MOV AL, FE CBW
CWD(将字转换成双字指令):
-语句格式:CWD
-功能:将AX中的符号扩展至DX中,操作数是隐含且固定的
1 2 3 4 MOV AX, 1234 CWD MOV AX, FFF7 CWD
逻辑运算 逻辑与:AND -指令的格式:AND Reg/Mem, Reg/Mem/lmm
-受影响的标志位:CF(O)、OF(O)、PF、SF和ZF(AF无定义)
1 2 3 4 5 6 mov ax, 4569 ; 0100 0101 0110 1001 and ax, 1234 ; 0001 0010 0011 0100 mov bx, 4541 and ax, bx mov word ptr [0], 1111 and ax, [0]
逻辑或:OR -指令的格式:OR Reg/Mem, Reg/Mem/Imm
-受影响的标志位:CF(O)、OF(O)、PF、SF和ZF(AF无定义)
1 2 3 mov ax, 4569 ; 0100 0101 0110 1001 or ax, 1234 ; 0001 0010 0011 0100 ; 0101 0111 0111 1101
逻辑非:NOT -指令的格式:NOT Reg/Mem
-受影响的标志位:无
1 2 mov ax, 4569 ; 0100 0101 0110 1001 not ax
异或:XOR -指令的格式:XOR Reg/Mem,Reg/Mem/lmm
-受影响的标志位:CF(O)、OF(O)、PF、SF和ZF(AF无定义)
(相同为0不同为1,用于求两个数是否不同)
1 2 3 mov ax, 4569 ; 0100 0101 0110 1001 mov bx, 1234 ; 0001 0010 0011 0100 xor ax,bx ; 0101 0111 0101 1101
TEST 指令 格式:TEST Reg/Mem,Reg/Mem/lmm
作用:执行AND,但是不影响目标操作数
受影响的标志位:CF(O)、OF(O)、PF、SF和ZF(AF无定义)。
1 2 3 Eg: test ax, ax // ax为0,则ZF=0; // ax不为0,则zF = 1
总结 1)如果要将目的操作数中某些位清O,用AND,称之为屏蔽
2)如果要将目的操作数中某些位置1,用OR
3)用来测试目的操作数中某一位或某几位是否为0或1,而目的操作数不变,TEST
4)TEST与CMP的区别,前者是测试一位或几位,后者测试整个字节/字/双字是否相等
5)操作数自身相或、相与结果不变6)xOR AX,Ax 将Ax置0,比mov ax,0 更高效。
1 2 3 4 5 6 7 8 mov ax, 4 => mox dx, cx not cx and dx, cx mov bx, 4 xor bx, dx mov ax, bx
逻辑恒等式 1 2 3 4 5 6 a and 1 = a a and 0 = 0 a xor 1 = not a a xor 0 = a a xor a = 0 a and not a = 0
无分支求绝对值 1 2 3 4 mov ax, X cwd xor ax, dx sub ax, dx
无分支求三目运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1、reg == 8 ? 13:7 mov ax, X sub ax, 8 neg ax sbb ax, ax ;if X=8, ax=0 ;if X!=8, ax=-1 mov dx. -6 and dx, ax add dx, 13 2、reg == 4 ? 6:11 mov ax, X sub ax, 4 neg ax sbb ax, ax ;if X=4, ax=0 ;if X!=4, ax=-1 mov dx. 5 and dx, ax add dx, 6
反汇编
反编译
汇编不会优化、高级语言会优化
反汇编引擎的基本框架
默认都是ds
bp -> ss
ip -> cs
用winhex从内存寻找到dbox对应的值
DAA/DAS、 AAA/ AAS/AAM/AAD
特权指令
指令手册,不同系统
808x、286、386、486
移位指令 逻辑移位&算数移位
指令
名称
格式
意义
影响标志
SAL(shift arithmetic left)
算数左移
op r/m, 1/cl
1、左移CNT位 2、高位进CF 3、低位补0
OF,ZF,SF,PF,CF
SHL(shift logical left)
逻辑左移
op r/m, 1/cl
1、左移CNT位 2、高位进CF 3、低位补0
OF,ZF,SF,PF,CF
SAR(shift arithmeticright)
算数右移
op r/m, 1/cl
1、右移CNT位 2、高位保持不变(补自己) 3、低位进CF
OF,ZF,SF,PF,CF
SHR(shift logical right)
逻辑右移
op r/m, 1/cl
1、左移CNT位 2、高位补0 3、低位进CF
OF,ZF,SF,PF,CF
1 2 3 4 5 6 7 8 MOV AX, FEFE SHL AX, 1 MOV CL, 2 SHL AX, CL MOV WORD PTR [0], 5566 SHL WORD PTR [0], 1 SHL WORD PTR [0], CL AX, CL
循环移位
指令
名称
格式
意义
影响标志
ROL(rotate)
循环左移
op r/m, 1/cl
1、左移 2、高位进低位 3、高位进CF
OF、CF 其他标志无定义
ROR(rotate)
循环右移
op r/m, 1/cl
1、右移 2、低位进高位 3、低位进CF
OF、CF 其他标志无定义
RCL(carry)
带进位循环左移
op r/m, 1/cl
1、左移 2、高位进CF 3、CF进低位
OF、CF 其他标志无定义
RCR(carry)
带进位循环右移
op r/m, 1/cl
1、右移 2、高位进CF 3、CF进高位
OF、CF 其他标志无定义
1 2 3 4 5 mov ax, fefe rol ax, 1 mov ax, fefe rol ax, 2 rol ax, cl
ASM基础语法 配置Masm615
VSCode安装MASM/TASM插件
编译命令
1 2 ml /c xx.asm link xx.obj
编译+调试 脚本
1 2 3 ml /c %1.asm link %1.obj debug %1.exe
直接用插件,插件的环境是临时的
1 2 3 4 5 6 7 8 9 10 11 12 ml /c test.asm ;编译生成obj文件 link test.obj ;链接生成exe文件 debug test.exe data_seg segment mov ax, ax mov ax, ax ENRTY: mov ax, ax mov ax, ax data_seg ends end ENRTY
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 data segment ;数据段 string db 'zhuge shabi$' data ends code segment ;代码段 assume cs:code,ds:data start: mov ax,data ;获取段基址 mov ds,ax ;将段基址送入寄存器 mov dx,offset string mov ah,9 int 21h mov ah,4ch int 21h code ends end start
debug exe,用前边的-u,-a调试
入口
段
一个程序必须至少有一个段
一个程序中可以定义多个段
段不能嵌套
段可以重名,重名的段会被编译到同一块内存中
1 2 3 4 5 段名 segment start: xxx 段名 ends end start
数值 整数
整数可以支持多个进制
数值必须以数字开头,如果非数字前面必须加0
负数前面可以加减号(-)
1 2 3 4 十进制 mov ax, 1234 / mov ax, 1234d 二进制 mov ax, 1011b 八进制 mov ax, 76o 十六进制 mov ax, 76h / mov ax, 0abh
1 2 3 4 5 6 7 8 9 10 code segment start: mov ax, 256 mov ax, 256d mov ax, 10000000b mov cx, 100 mov dx, 1234h mov dx, -1h code ends end start
字符 1 2 3 4 mov al, 'a' mov bl, 'b' mov cl, 'c' mov dl, 'd'
变量 整数
整数可以支持多个类型
整数可以有多个初值,未初始化的值用问号(?)表示
变量一般定义在一个单独的段中
格式:变量名 类型 初始值 val dd 5566h
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 ;数据段和操作段分开 data_seg segment g_btVal db 55h ; db 字节 g_wVal dw 5566h ; dw 字 g_dwVal dd 55667788h ; dd 双字 g_qVal dq 1122334455667788h ; dq 8字节 g_tVal dt 11223344556677889900h ; dt 10字节 data_seg ends uinitdata_seg segment g_btVal1 db ? g_wVal1 dw ? g_tVal dt ? uinitdata_seg ends code_seg segment START: assume ds:data_seg ;ds:ip,此处指定下面g_btVal变量去data_seg找 mov ax, data_seg ;还需要重新指定ds的值,否则基地址错误 mov ds, ax mov al, g_btVal mov ax, g_wVal code_seg ends end START
-d 查看内存往前偏移
076D->0769为什么是偏移两行
-u 看基址,-d指定基地址看数据,mov是取地址的值,-u看要mov的值的地址
字符串
字符串都可以用单引号()或双引号(“”)
字符串一般以美元符$结尾
1 2 3 4 9_sz db "hello world$" ;16位汇编中以美元符结尾 data_seg segment g_szHello db "hello world$" ; db 字节 data_seg ends
数组 1 2 3 4 5 6 data_seg segment g_ary db 10h, 11h, 'a', 'b', 'c', 'd'; db 字节 g_ary1 db 10 dup(0cch); 重复10个字节,每个字节填充cc g_ary2 db 10 dup(?) g_ary3 db 45h, 10 dup(46h), 47h, 20 dup(48h) data_seg ends
属性 masm提供了很多伪指令,可以获取变量的大小和地址称之为变量的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 seg 取段基址 offset 取段偏移 type 取元素类型大小 length 取元素个数 size 取数据大小 data_seg segment g_btVal db 55h ; db 字节 g_wVal dw 5566h ; dw 字 g_dwVal dd 55667788h ; dd 双字 g_qVal dq 1122334455667788h ; dq 8字节 g_tVal dt 11223344556677889900h ; dt 10字节 data_seg ends code_seg segment START: assume ds:data_seg ;ds:ip,此处指定下面g_btVal变量去data_seg找 mov ax, data_seg ;还需要重新指定ds的值,否则基地址错误 mov ds, ax mov ax, seg g_wVal mov ax, offset g_dwVal code_seg ends end START
1 2 3 ;获取g_ary3的大小 mov ax, offset g_ary4 sub ax, offset g_ary3
堆栈 SS:存放栈的段地址; SP:堆栈寄存器SP(stack pointer)存放栈的偏移地址; BP: 基数指针寄存器BP(base pointer)是一个寄存器;
1 2 3 4 5 6 7 8 9 10 11 12 stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends push ax push bx push cx push dx pop ax pop bx pop cx pop dx
中断
dos系统提供的功能(API),通过21号中断 来调用
每个功能都有一个编号,通过AH指定功能号
每个功能的参数查看手册
INT指令,本指令将产生一个软中断,把控制转向一个类型号为n的软中断,该中断处理程序入口地址在中断向量表的n*4地 —- 址处的二个存储器字(4个单元)中.
1 2 3 4 API功能默认调用00:00的函数 -a ; 0000:0000 60 10 00 F0 0B 00 70 00-08 00 70 00 08 00 70 00 INT 00 ;表示调用 F000:1060
1 2 3 4 5 6 7 code_seg segment START: mov ah, 4ch mov al, 00h int code_seg ends end START ; -g或是.exe直接退出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 data_seg segment g_szHello db "hello world$" ; db 字节 data_seg ends code_seg segment START: assume ds:data_seg, ss:stack_seg mov ax, data_seg mov ds, ax mov ah, 09 mov dx, offset g_szHello int 21h code_seg ends end START ;-p参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .model small .data strs DB 'hello world',13,10,'$' .code start: mov ax,@data mov ds,ax mov dx,offset strs mov ah,09h int 21h mov ah,4ch int 21h end start
https://www.cnblogs.com/dgwblog/p/11865850.html
串操作
源操作数使用si,默认段为DS(以此为基址),可段超越
目的操作数使用di,默认段为ES(以此为基址),不可段超越
指令
功能
DF = 0(UP)
DF = 1(DN)
movsb (串传送)
1、mov string 2、将si地址的内容拷贝到di地址 3、(di) <- (si)
si <- si + 1 di <- di + 1
si <- si - 1 di <- di - 1
movsw
同 movsb
si <- si + 2 di <- di + 2
si <- si - 2 di <- di - 2
stosb (串存储)
1、store string 2、将al或者ax内存存储到di地址 3、(di) <- al或(di) <- ax
di <- di + 1
di <- di - 1
stosw
同 stosb
di <- di + 2
di <- di - 2
lodsb (串读取)
1、load string 2、将si地址内容读入al或者ax 3、al <- (si) 或 ax <- (si)
si <- si + 1
si <- si -1
lodsw
同 lodsb
si <- si + 2
si <- si - 2
cmpsb (串比较)
1、cmp string 2、si地址内容减去di地址内容,不存储结果,影响标志位 3、(si) - (di)
si <- si + 1 di <- di + 1
si <- si - 1 di <- di - 1
cmpsw
同 cmpsb
si <- si + 2 di <- di + 2
si <- si - 2 di <- di - 2
scasb (串扫描)
1、scan string 2、al或者ax减去di地址内容,不存结果,影响标志位 3、al - (di)或ax - (di) es:di
di <- di + 1
di <- di - 1
scasw
同 scasb
di <- di + 2
di <- di - 2
movsb & movsw 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 data_seg segment g_szSrc db "hello world", '$' g_szDst db 64 dup(0) data_seg ends code_seg segment START: assume ds:data_seg mov ax, data_seg ; 这三句是固定的基本上,不然会找错值 mov ds, ax mov ax, data_seg ; 这三句是固定的基本上,不然会找错值 mov es, ax ; 因为di用的是es lea si, g_szSrc mov di, offset g_szDst ; 相对起始位置 ;std -g 11 movsb ;movsw movsb movsb movsb movsb code_seg ends end START ;-p参数 ;-t ;-d 0 l 20 ;-t ;-d es l 20 可以把ds和es分开段写 ;movsb si bi默认是加一,根据DF标志位加一减一 ;STD DF<-1 自动减 ;CLD DF<-0 自动加
0E24:0010 = 0E25:0000??
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 data_seg segment g_szSrc db "hello world", '$' data_seg ends data_seg0 segment g_szDst0 db 64 dup(0) data_seg0 ends code_seg segment START: assume ds:data_seg, es:data_seg0 mov ax, data_seg ; 这三句是固定的基本上,不然会找错值 mov ds, ax mov ax, data_seg0 ; 这三句是固定的基本上,不然会找错值 mov es, ax ; 因为di用的是es lea si, g_szSrc mov di, offset g_szDst0 ; 相对起始位置 ;std -g 11 movsb ;movsw movsb movsb movsb movsb code_seg ends end START ;-p参数
stosb & stosw 1 2 3 4 5 6 7 8 mov di, offset g_szDst cld mov ax, 0cccch stowd stowd stowd stowd stowd
其他自行看
代码虚拟化、软件模拟硬件
重复前缀 串操作指令一般都配合重复前缀使用,实现内存的批量操作,
前缀
串操作
重复条件
rep
1、movs 2、stos 3、loads
cx != 0
repz / repe
1、cmps 2、scas
cx != 0 且 zf = 1
repnz / repne
1、cmps 2、scas
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 data_seg segment g_szSrc db "hello world", '$' g_szSrcEnd db ? data_seg ends buf_seg segment g_szDst db 16 dup(0) g_szDstEnd db ? buf_seg ends code_seg segment START: assume ds:data_seg mov ax, data_seg ; 这三句是固定的基本上,不然会找错值 mov ds, ax mov ax, buf_seg ; 这三句是固定的基本上,不然会找错值 mov es, ax ; 因为di用的是es lea si, g_szSrc lea di, g_szDst cld mov cx, offset g_szSrcEnd sub cx, offset g_szSrc rep movsb ;cx每次减一 code_seg ends end START ;-p参数
流程转移指令 无条件跳转 直接跳转
名称
修饰关键词(可选)
格式
功能
指令长度
示例
短跳(单字)
short
jmp short 标号
ip <- 标号偏移
2
0005:EB0B jmp 0012
近跳(双字)
near ptr
jmp near 标号
ip <- 标号偏移
3
0007:E90a01 jmp 0114
远跳(四字)
far ptr
jmp far ptr 标号 jmp 段名:标号
ip <- 标号偏移 cs <- 段地址
5
0000:EA00007c07 jmp 077C:0000
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 code_seg0 segment LABEL2: mov cx, cx code_seg0 ends code_seg segment START: jmp far ptr LABEL2 ; jmp code_seg0:LABEL2 jmp short LEABEL0 mov ax, ax LEABEL0: jmp near ptr LEABEL1 db 256 dup(0) mov bx, bx LEABEL1: mov ax, 4c00h int 21h code_seg ends end START
Jmp下条指令+偏移
1 2 3 4 5 6 7 ;计算机器码 ;短跳0xEB,近跳0XE9 0AE7:0102 jmp 0120 ; 短跳是一个字节,两个十六进制 120-104 = EB1C,主要是计算下条指令的偏移 0AE7:0102 jmp 0320 ; 近跳是两个字节,四个十六进制 320-105 = E91B02,因为三个位可以表示? 0AE7:0102 jmp 0020 ; E91BFF,补码E4用单字(-128-127)存不下,要用近跳 0AE7:0402 jmp 0100 0AE7:0122 jmp 0110
无指令跳转 使用寄存器间接转移
格式 jmp reg
reg为通用寄存器
功能 ip<-reg
只能用于段内转移
1 2 mov ax, offset LEABEL1 jmp ax
条件跳转 依据标志位判断,条件成立则跳转,条件不成立则不跳
单条件跳转
指令
英文单词
标志
说明
JZ/JE
zero,equal
ZF=1
相等/等于零
JNZ/JNE
not zero,not qual
ZF=0
不相等,不等于零
JCXZ
CX is zero
CX=0
cx为0
JS
sign
SF = 1
结果为负
JNS
not sign
SF = 0
结果为正
JP/JPE
parity, parity even
PF = 1
1为偶数个
JNP/JPO
not parity, parity odd
PF = 0
1为奇数个
JO
overflow
OF = 1
溢出
JNO
not overlow
OF = 0
不溢出
JC
carry
CF = 1
进位,小于
JNC
not carry
CF = 0
不进位,大于等于
1 2 3 4 5 6 7 8 FIND: cmp byte ptr [si], '$' je ENDFIND inc si jmp FIND ENDFIND: sub si, 1
无符号数判断
指令
英文
说明
标志
JB
below
小于
cf = 1
JNAE
not greater or equal
不大于等于
cf = 1
JAE
above or equal
大于等于
cf =0
JNB
not below
不小于
cf =0
JBE
below or equal
小于等于
cf = 1 或 zf = 1
JNA
not above
不大于
同 JBE
JA
above
大于
cf =0 或 zf =0
JNBE
not below or equal
不小于等于
同 JA
有符号数判断
指令
英文单词
说明
标志
JL
less
小于
SF != OF
JNGE
not greater or equal
不大于等于
SF != OF
JG
greater
大于
SF = OF 且 ZF = 0
JNLE
not less or equal
不小于等于
同 JG
JLE
less or equal
小于等于
ZF != OF 或 ZF = 1
JNG
not greator
不大于
同 JLE
JGE
greator orequal
大于等于
SF = OF
JNL
not less
不小于
同 JGE
LOOP 格式:loop标号
只能用于短转移
指令
重复条件
LOOP
cx != 0
LOOPZ/LOOPE
cx != 0 且 zf = 1
LOOPNZ/LOOPNE
cx != 0 且 zf = 0
函数 基础语法 call - push 下一条指令地址 jmp 目标地址
ret n - 弹出返回地址 jmp到返回地址 add sp, n
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends data_seg segment g_sz1 db "1hello world$" g_sz2 db "2hello world$" g_sz3 db "3hello world$" g_sz4 db "4hello world$" data_seg ends code_seg segment assume ds:data_seg, ss:stack_seg PUTS0: push bp mov bp, sp push dx mov dx, [bp+4] mov ah, 09h int 21h mov dl, 0ah mov ah, 02h int 21h pop dx pop bp ret 2 ;sp+2,一个参数+2 PUTS: push bp mov bp, sp sub sp, 8 ;申请局部变量空间 ;保存寄存器环境 push dx mov dx, [bp+4] mov ah, 09h int 21h mov word ptr [bp-4], 5566h mov word ptr [bp-8], 7788h ;多个函数调用要手动规划栈 mov dx, offset g_sz1 push dx call PUTS0 ;多个函数调用要手动规划栈 mov dl, 0ah mov ah, 02h int 21h ;恢复寄存器环境 pop dx add sp, 8 ;释放局部变量空间 mov sp, bp pop bp ;pop就直接更新bp的值吗 ret 2 ;sp+2,一个参数+2 StrLen: push bp mov bp, sp push si push bx mov bx, [bp+4] ;获取参数 mov si, bx FIND: cmp byte ptr [si], '$' je ENDFIND inc si jmp FIND ENDFIND: sub si, bx ;ax为函数默认返回值,默认不用入栈平栈 mov ax, si pop bx pop si mov sp, bp pop bp ret START: mov ax, data_seg mov ds, ax mov ax, offset g_sz1 push ax call StrLen add sp, 2 ;此处如果是 ret 2就可以不用在外面平 mov ax, offset g_sz2 push ax call StrLen add sp, 2 mov dx, offset g_sz1 push dx ;参数入栈 call PUTS ;返回地址入栈 mov dx, offset g_sz2 push dx call PUTS mov dx, offset g_sz3 push dx call PUTS mov dx, offset g_sz4 push dx call PUTS ;程序退出,退出码0 mov ax, 4c00h int 21h code_seg ends end START ;-p参数
bp用的是ss段,bx用的是dx段,栈是【局部变量】【bp】【返回地址】【入参】
函数执行流程
参数入栈
返回地址入栈,跳转到函数
保存栈帧(就是栈内给某个上下文环境临时规划的局部栈)
申请局部变量空间
保存寄存器环境
执行函数功能
恢复寄存器环境
恢复栈帧
弹出返回地址,返回[平栈]
[平栈]
指令(可选)
说明
功能
call (near ptr) 标号
段内直接调用
push 返回地址 jmp 标号
call REG call near ptr | word ptr [EA]
段内间接调用
push 返回地址 jmp 函数地址
call far ptr 标号 call dword ptr [EA]
段间调用
push
ret(n)
段内返回
pop ip add sp,n
retf(n)
段间返回
pop ip pop cs add sp, n
masm函数语法 自动算偏移,bp,sp入栈平栈
1 2 3 4 5 6 7 8 9 10 11 函数名 proc[距离][调用约定][uses reg1 reg2..][参数:word, 参数名:word..] ;uses保存寄存器环境 local 变量:word local 变量: word ret 函数名 endp 示例 TestProc PROC far stdcall uses bx dx si di arg1:word local btVal:byte ret TestProc ENDP
距离关键字
说明
near
函数只能段内调用 函数使用ret返回 调用时ip入栈
far
段内段间都可调用 函数使用retf返回 调用时都是ip和cs入栈
调用约定关键词
说明
C
调用方平栈
stdcall
被调用方平栈
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 include tool.inc stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends data_seg segment g_wVa11 dw 5566h g_wVa12 dw 7788 g_szFileName db "test.txt", 0 g_buf db 256 dup(0) g_hFile dw 0 data_seg ends code_seg segment assume ds:data_seg, ss:stack_seg START: mov ax, data_seg mov ds, ax invoke OpenFile, offset g_szFileName cmp ax,-1 ;打开文件成功返回句柄,不成功返回错误码 je ERROR mov g_hFile, ax invoke Readfile, g_hFile, offset g_buf, size g_buf cmp ax,0 je ERROR invoke CloseFile, g_hFile ERROR: ;宏汇编 invoke MySub, 45, 13 ;使用立即数时,会用ax中转 mov si, 9 mov di, 8 invoke MySub, si, di invoke MySub, g_wVa12, g_wVa11 mov ax, 45 push ax mov ax, 13 push ax call MySub add sp, 4 ;程序结束,退出码0 mov ax, 4C00H INT 21H code_seg ends end START
类型
局部变量类型
备注
db
byte
可以直接赋值使用
dw
word
可以直接赋值使用
dd
dword
不可以直接赋值使用
dq
qword
不可以直接赋值使用
dt
tbyte
不可以直接赋值使用
assume、invoke:没有对应的机器码,给编译器看的,伪指令
invoke 1 invoke 函数名, 参数1, 参数2, 参数3
说明:
会生成参数入栈代码
如果是c调用约定,会生成平栈代码
如果是局部变量取地址,需要使用addr伪指令
使用addr的时候,注意ax的使用
伪指令
说明
offset
取段内偏移
addr
取局部变量的地址,使用LEA指令
文件引用 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 ;tool.asm include tool.inc code_seg segment CloseFile proc far stdcall uses bx hFile:word mov bx, hFile mov ah, 3eh int 21h ret CloseFile ENDP ReadFile proc far stdcall uses bx dx cx hFile:word, pBuff:word, nBufSize:word mov bx, hFile mov ah, 3fh mov dx, pBuff mov cx, nBufSize int 21H jnc SUCESS ;失败 mov ax,0 ret SUCESS: ret ReadFile ENDP OpenFile proc far stdcall uses dx szFileName:word mov ah, 3dh mov al, 02h ;可读,可写 mov dx, szFileName int 21H jnc SUCESS ;失败 mov ax,-1 ret SUCESS: ;成功 ret OpenFile ENDP MySub PROC far c uses cx di es nVal1:word, nVal2:word local @btVar1:byte ;局部变量标识,非必须 local @wVa11:word local @dwVal1:dword local @buf[256]:byte xor ax, ax mov @btvar1,al mov @wVal1,ax ;mov @dwval1,ax ;lea bx, @buf invoke MyZeroMem,ss,addr @buf,255 ;add 取局部变量的地址,I专用于invoke ;去局部变量地址用 offset ? mov ax, nVal1 mov ax, nVal2 ret MySub ENDP MyZeroMem PROC far stdcall uses di cx es pSecBase:word, pBuff:word, nSize:word xor ax, ax mov es, pSecBase mov cx, nSize mov di, pBuff rep stosb ret MyZeroMem ENDP code_seg ends ends ;每个asm文件都要以ends结尾
声明头文件
1 2 3 4 5 6 7 8 9 10 11 12 13 ;tool.inc MySub PROTO far c :word, :word MyZeroMem PROTO far stdcall :word, :word, :word ;功能 - 创建文件,如果文件存在,则清空 ;参数 - ; szFileName - 文件名,0结尾字符串 ;返回 - ; 失败 - 返回 -1 ; 成功-返回文件句柄 CreateFile PROTO far stdcall szFileName:word ReadFile PROTO far stdcall hFile:Word, pBuff:word, nBufSize:word CloseFile PROTO far stdcall hFile:word OpenFile PROTO far stdcall szFileName:word
需要同时编译两个文件
1 2 ml /c test.asm tool.asm link test.obj tool.obj
PS:
调试小技巧,int 3 / db 0cch,直接 -g 会go到中断处,-p执行当前语句(不进入)
MASM扩展在这个扩展的设置中,有一个名为Working mode
。默认情况下,它被设置为单个文件。如果您更改为工作区,它将挂载整个dir。
宏汇编 表达式 $ org @@ 表达式需要能够在编译阶段计算结果
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 stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends data_seg segment g_dbVal0 db 11h org 10h g_dbVal1 db 22h org 20h g_dbVal3 db 33h g_wBuf dw 256 dup(0) ;相当于100个word,200个字节 ;g_wBufLen dw offset g_wBufLen - offset g_wBuf ;? g_wBufLen dw $ - offset g_wBuf org 400h ;下个段偏移位置从400开始 g_wVal0 dw 5566h data_seg ends code_seg segment assume ds:data_seg, ss:stack_seg START: mov ax, data_seg mov ds, ax mov ax, g_wBufLen mov dx, size g_wBuf / size word ;看值 mov ax, g_wVal0 / 2 ;报错,无法在编译阶段计算结果 mov ax, offset g_wVal0 - offset g_wBuf mov ax, 1122h and 5566h ;运算符 mov ax, 3344h or 7788h mov ax, not 0 and g_wVal0, 5566h ;指令 mov ax,6 1e 4 ;关系为假,结果为0 mov ax,7 gt 1 ;关系为真,结果为全F @@: xor ax, ax @@: mov ax, ax mov bx,bx jmp @f ;就近向下跳 mov si, si jmp @b ;就近向上跳 @@: mov cx, cx mov dx, dx @@: xor bx,bx mov ax, offset NEXT mov ax, $+3 NEXT: mov bx, $ mov ax, $ mov ax, $ ;程序结束,退出码0 mov ax, 4C00H INT 21H code_seg ends end START
结构体 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 stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends MyTestTag struc m_bval db θ m_wVal dw 0 m_buf db 10 dup (0) MyTestTag ends data_seg segment g_tag MyTestTag <55h, 7788h, "hello struct!"> data_seg ends code_seg segment MyTest proc far stdcall pTag:ptr MyTestTag assume si:ptr MyTestTag mov si, pTag mov ax, [si].m_wVal assume si:nothing mov bx, pTag mov ax, word ptr [bx+2] ret MyTest endp assume ds:data_seg, ss:stack_seg START: mov ax, data_seg mov ds, ax mov al, g_tag.m_bVal mov ax, g_tag.m_wVal lea ax, g_tag.m_buf mov g_tag.m_bval, 66h mov g_tag.m_wVal, 1234h invoke MyTest, offset g_tag ; local @stu:Students ;结构体局部变量 ; mov @stu.m_id, 6 ;使用结构体局部变量 ;程序结束,退出码0 mov ax, 4C00H INT 21H code_seg ends end START
宏 equ语句
不可以重命名
可用于常量和表达式
可用于字符串
可用于指令名,给指令取别名
可用于类型,给类型取别名
可用于操作数
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 stack_seg segment stack ;栈会自动加载 db 256 dup(0cch) ;为栈申请大小 stack_seg ends VERSION equ 10 VERSTR equ "hello wrold" MYMOV equ mov MYPUSHAX equ push ax data_seg segment g_wVal dw VERSION g_buf db VERSTR data_seg ends code_seg segment assume ds:data_seg, ss:stack_seg START: mov ax, data_seg mov ds, ax MYMOV ax, g_wVal MYPUSHAX ;程序结束,退出码0 mov ax, 4C00H INT 21H code_seg ends end START
=(只能整数的equ) macro语句 1 2 3 4 5 6 7 MYADD MACRO argl, arg2 mov ax, arg1 add ax, arg2 ENDM MYADD 4, 5 MYADD 7, 8
字符串拼接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 shift macro n, reg, d mov cl, n ro&d reg, cl endm shift 2,ax,1 shift 3,bx,r shift 1,cx,1 .if ax < θ mov ax, ax .elseif ax == 0 mov bx, bx .else mov cx, cx .endif
汇编手册中文(asm+masm).chm
补充 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 ;test.asm include tool.inc stack_seg segment stack ;栈会自动加载 db 500H dup(0cch) ;一个段(256)的栈 stack_seg ends data_seg segment g_buf db 256 dup(0) g_wBufLen dw $ - offset g_buf g_bufCnt db5 4 dup(0), 0ah, "$" data_seg ends code_seg segment assume ds:data_seg, ss:stack_seg GetwordsCount proc far stdcall uses bx pBuff:word local @bIsPreSapce:byte mov @bIsPreSapce, 0 mov ax, 1 mov bx, pBuff ;空字符串 .if byte ptr [bx] == "$" xor ax, ax ret .endif ;非空字符串 .while byte ptr[bx] != "$" .if byte ptr [bx] == " " .if @bIsPreSapce == 0 inc ax .endif mov @bIsPreSapce, 1 else mov @bIsPreSapce, 0 .endif inc bx .endw ret GetwordsCount endp START: mov ax, data_seg mov ds, ax xor cx, cx .while cx < 5 ;获取一行 invoke GetLine, offset g_buf, g_wBufLen ;获取此行的单词个数 invoke GetwordsCount, offset g_buf ;个数转字符串 invoke IntToStr, offset g_bufCnt, ax ;输出个数 invoke Puts, offset g_bufCnt inc cx .endw ;程序结束,退出码0 mov ax, 4C00H INT 21H code_seg ends end START
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 ;tool.asm include tool.inc data_seg segment g_bufHexChar db "0123456789abcdef" data_seg ends code_seg segment Puts proc far stdcall uses dx pBuff:word mov dx, pBuff mov ah, 09h int 21h mov dl, odh mov ah, 02h int 21H ret Puts endp IntToStr proc far stdcall uses si bx ds cx pBuff:word, n:word local @wCnt:word local @wOldDs:word assumeds:data_seg mov @wOldDs, ds mov @wCnt, θ .while @wCnt < 4 mov si, n and si, 0f000h mov cl, 0ch shr si, cl mov ax, data_seg mov ds, ax mov bx, offset g_bufHexChar mov al, [si + bx] mov ds, @wOldDs mov bx, pBuff add bx, @wCnt mov [bx], al mov cl, 4 shl n, cl inc @wCnt .endw GetChar proc far stdcall mov ah, 01h int 21H ret GetChar ENDP GetLine proc far stdcall uses bx PBuff:word, nBufSize:word mov bx,pBuff invoke Getchar .while al != 0dh mov [bx],al inc bx .if bx >= nBufSize .break .endif invoke Getchar .endw mov byte ptr[bx], '$' mov ax, bx sub ax, pBuff ret GetLine endp code_seg ends
1 2 3 4 ;tool.inc GetLine proto far stdcall PBuff:word, nBufSize:word IntToStr proto far stdcall pBuff:word, n:word Puts proto far stdcall pBuff:word
w32Dasm
机器码是十六进制,winHex打开的默认就是机器码
32ASM汇编 masm32.com
直接指定文件夹安装
把缺少的头文件路径添加在环境变量的include里
1 2 ml /c /coff test.asm link /subsystem:windows test.obj
1 2 3 4 5 6 7 8 9 10 11 12 13 ;测试代码 .386 .model flat, stdcall option casemap:NONE include windows.inc include user32.inc includelib user32.lib .data g_szTitle db "hello world", 0 .code ENTRY: invoke MessageBoxA, NULL, offset g_szTitle, NULL, MB_OK end ENTRY
与16位ASM区别 文件头三件套
1 2 3 .386 ;指令集 Processor .model flat, stdcall option casemap:NONE ;标识符是否大小写敏感,none是敏感
msdn宏汇编官网
https://docs.microsoft.com/en-us/cpp/assembler/masm/directives-reference?view=msvc-160
32位 = 4G
分段 32位汇编取消了分段,改用内存属性来划分,称作节(section)、内存区或内存块。
节
可读
可写
可执行
备注
.DATA
true
true
false
初始化的全局变量
.CONST
true
false
false
只读数据区
.DATA?
true
true
false
未初始化的全局变量
CODE
true
false
true
代码
1 2 3 4 5 6 7 8 9 10 .data g_dwVal dd θ g_dwVal1 dd 0, 0, 1 .data? g_dwVal2 dd ? .const g_dwVa13 dd 9 TestEntry: mov ax, ax end TestEntry
enter leave
寻址
32位没用中断,调用API
安装MASM
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 81 82 83 84 85 86 87 88 89 90 ;编码要用GBK .386 .model flat, stdcall option casemap:none include windows.inc include user32.inc include gdi32.inc include kernel32.inc includelib user32.lib includelib gdi32.lib includelib kernel32.lib .data g_szClassName db "Asmwindowclass", 0 g_szTitle db "32位汇编的第一个窗口", 0 g_szTip db "窗口创建失败", 0 .code MainWndProc proc hWnd:HWND, nMsg:UINT, wParam:WPARAM, lParam:LPARAM .IF nMsg == WM_DESTROY invoke PostQuitMessage, 0 .ENDIF invoke DefWindowProc, hWnd, nMsg, wParam, lParam ret MainWndProc endp WinMain proc hInstance:HINSTANCE local @wc:WNDCLASS local @hwnd:HWND local @msg:MSG ;设计注册窗口类 mov @wc.style, CS_HREDRAW or CS_VREDRAW mov @wc.lpfnWndProc, offset MainWndProc mov @wc.cbClsExtra, 0 mov @wc.cbWndExtra, 0 mov eax, hInstance mov @wc.hInstance, eax invoke LoadIcon, NULL, IDI_APPLICATION mov @wc.hIcon, eax invoke LoadCursor, NULL, IDC_ARROW mov @wc.hCursor, eax invoke GetStockObject, WHITE_BRUSH mov @wc.hbrBackground, eax mov @wc.lpszMenuName, NULL mov @wc.lpszClassName, offset g_szClassName invoke RegisterClass, addr @wc ;创建窗口 invoke CreateWindowEx, NULL, offset g_szClassName,offset g_szTitle, WS_OVERLAPPEDWINDOW,\ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,\ NULL, NULL, hInstance, NULL mov @hwnd, eax .if eax == NULL invoke MessageBox, NULL, offset g_szTip, offset g_szTitle, MB_OK ret .endif ;显示窗口 invoke ShowWindow, @hwnd, SW_SHOW ;更新窗口 invoke UpdateWindow, @hwnd ;消息循环 .WHILE TRUE invoke GetMessage, addr @msg, NULL, 0, 0 .IF eax == 0 .break .ENDIF invoke TranslateMessage, addr @msg invoke DispatchMessage, addr @msg .ENDW ;过程函数 ret WinMain endp Cr41Entry: invoke GetModuleHandle, NULL invoke WinMain, eax invoke ExitProcess, 0 end Cr41Entry
OD的简易使用
跳转指定地址,右键->跳转表达式 / ctrl+G / 回车 / 减号回退
右键汇编,右选可以撤销
右键快速返回栈顶
F2 断点
F7 单步步入;F8 单步步过;F9 运行;F12 暂停
删除OD/UDD文件夹下面的文件
资源和联合编译 C & 汇编的相互调用 VC + ml + link + rc
汇编调用C - OBJ
C调用汇编 - OBJ
建议编译成 DLL/lib 调用
查看 DLL 内部函数,Dependency walker
汇编在链接时可以导出 DLL 文件,但需要指定对应的 def 文件,不然 DLL 文件中没有函数,不会生成 lib 文件
1 link /subsystem:windows /Dll /Def:Asmdll.def asmdll.obj
内联汇编(c/c++写汇编) 内联汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ;不能使用宏汇编,比如invoke,.if等等可以使用size,type、length intnAry[10] = {}; __asm xor eax, eax __asm xor ebx, ebx __asm{ xor edi, edi xor esi, esi mov ebx, nRet mov nRet, eax mov nAry[0], eax mov nAry[5], eax push NULL push NULL push NULL push NULL call MessageBox ;invoke MessageBox,NULL,NULL,NULL,NULL }
1 2 裸函数 __declspec(naked)用于生成裸函数,编译器不会为这个函数生成任何代码,需要自已添加函数进入和返回的代码__LOCAL_SIZE - 内置宏,让编译器计算局部变量的大小
补丁 定位窗口的过程函数
1、根据窗口相关的 API 去跟踪调用
2、OD自带功能,查看->窗口->刷新
3、OD自带功能,查看->可执行模块(导入函数)->(搜函数)
条件断点功能
编程窗口程序思路
1、手动 create 窗口的
2、调用 dlgbox
搜索什么关键词要看具体什么应用,用什么语言开发(windows桌面程序)
1、查看->可执行模块 2、选择对应exe,[winmine.exe]右键查看名称,搜索 RegisterClass(导入函数),右键查看参考(调用),打断点 3、在输入函数内搜索 DIGLOGBOX ,可手动,可右键全部调用处打断点 4、执行,根据栈的跳转查看 pWndClass 栈结构内存,查手册可知,(pWndClass)的第二个参数为过程函数,内存跳转到汇编,打上断点,标注为主窗口的过程函数 5、pWndClass和RegisterClassW的关系是函数内部调用吗,此处是pWndClass已经调用,RegisterClassW还没调用吗 5、执行到过程函数处,可先禁用断点运行后再生效 6、寻找默认过程函数,点击Close时触发
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 01001BC9 . 55 push ebp ; 主函数默认过程函数 01001BCA . 8BEC mov ebp, esp 01001BCC . 83EC 40 sub esp, 40 01001BCF . 8B55 0C mov edx, dword ptr [ebp+C] ; 获取mMsg 01001BD2 . 8B4D 14 mov ecx, dword ptr [ebp+14] 01001BD5 . 53 push ebx 01001BD6 . 56 push esi 01001BD7 . 33DB xor ebx, ebx 01001BD9 . 57 push edi 01001BDA . BE 00020000 mov esi, 200 01001BDF . 43 inc ebx 01001BE0 . 33FF xor edi, edi 01001BE2 . 3BD6 cmp edx, esi ; mMsg跟状态码200比较 move 01001BE4 . 0F87 75030000 ja 01001F5F ; >200 01001BEA . 0F84 95040000 je 01002085 ; =200 01001BF0 . B8 00010000 mov eax, 100 01001BF5 . 3BD0 cmp edx, eax 01001BF7 . 0F87 5C010000 ja 01001D59 01001BFD . 0F84 97000000 je 01001C9A 01001C03 . 8BC2 mov eax, edx 01001C05 . 48 dec eax ; Switch (cases 2..47) 01001C06 . 48 dec eax 01001C07 . 74 76 je short 01001C7F 01001C09 . 83E8 04 sub eax, 4 01001C0C . 74 57 je short 01001C65 01001C0E . 83E8 09 sub eax, 9 01001C11 . 74 2B je short 01001C3E 01001C13 . 83E8 38 sub eax, 38 01001C16 . 0F85 742E0000 jnz 01004A90 ; 此处要走默认过程函数
1 2 3 4 5 6 7 8 9 10 01004A90 > \83FA 10 cmp edx, 10 ; Default case of switch 01001C05 01004A93 .^ 0F85 10D7FFFF jnz 010021A9 ; 01001C16修改前默认跳转 01004A99 . 6A 01 push 1 ; /Style = MB_OKCANCEL|MB_APPLMODAL 01004A9B . 6A 00 push 0 ; |Title = NULL 01004A9D . 68 5A4A0001 push 01004A5A ; |Text = "是",B7,"裥枰顺?",A1,"?,A8,"?3?",此处在01004A5A 编码写中文提示,push 只是作为参数入栈,可数据窗口编辑或汇编编辑 01004AA2 . FF75 08 push dword ptr [ebp+8] ; |hOwner 01004AA5 . FF15 B8100001 call dword ptr [<&USER32.MessageBoxW>>; \MessageBoxW 01004AAB . 83F8 01 cmp eax, 1 01004AAE .^ 0F84 F5D6FFFF je 010021A9 01004AB4 .^ E9 02D7FFFF jmp 010021BB
1 2 是否需要退出? \u662f\u5426\u9700\u8981\u9000\u51fa\uff1f
F9运行程序
1、右键->窗口->刷新,可直接跟踪 ClassProc,可直接下断点 111 ,这就是过程函数,消息断点
1 2 3 4 5 6 7 8 9 10 01004A90 > \83FA 10 cmp edx,0x10 ; Default case of switch 01001C05 01004A93 .^ 0F85 10D7FFFF jnz winmine3.010021A9 01004A99 . 6A 01 push 0x1 ; /Style = MB_OKCANCEL|MB_APPLMODAL 01004A9B . 6A 00 push 0x0 ; |Title = NULL 01004A9D . 68 5A4A0001 push winmine3.01004A5A ; |Text = "是否需要退出?£í?3?" 01004AA2 . FF75 08 push dword ptr ss:[ebp+0x8] ; |hOwner = NULL 01004AA5 . FF15 B8100001 call dword ptr ds:[<&USER32.MessageBoxW>>; \MessageBoxW 01004AAB . 83F8 01 cmp eax,0x1 01004AAE .^ 0F84 F5D6FFFF je winmine3.010021A9 01004AB4 .^ E9 02D7FFFF jmp winmine3.010021BB
激活码类型: 1、点击按钮获取指定弹框内容(bp GetDlgItemTextA、GetDlgItemTextW) 2、消息弹框(MessageBoxA、MessageBoxW) 执行到返回,跳过系统自带功能 直接搜索错误关键词,运行后 Ultra String Reference 查看对比String
点击验证,重点是获取点击事件,触发后续的逻辑
两个框校验key值: 1、直接搜索错误关键词,运行后 Ultra String Reference 2、F4跳到某地址 3、找到corrent前一条(00401642)
根据算法逻辑写注册机: 1、新建Win32 App
RadASM
重定位 重定位:重新定位代码的变量或地址
代码注入exe文件,申请一块内存
用汇编写一个执行文件,注入到扫描.exe里面,代码注入后地址会变化,要动态计算地址
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 .586 .model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib WinMain proto :DWORD,:DWORD,:DWORD,:DWORD .data g_szWinMineCap db "扫雷", 0 ;这里是窗口名 g_szUser32 db "user32.dll", 0 g_szMessageBox db "MessageBoxA", 0 .code CODE_BEG: jmp MSG_CODE g_szText db "我注入你了", 0 g_szCaption db "温情提示", 0 g_pfnMessageBox dd 0 MSG_CODE: int 3 call NEXT ;把pop ebx指令的地址压栈 NEXT: pop ebx ;pop ebx指令的地址弹栈入ebx sub ebx, offset NEXT push MB_OK mov eax, offset g_szCaption add eax, ebx ;计算变量g_szCaption在新内存中的地址 push eax mov eax, offset g_szText add eax, ebx ;计算变量e_szText在新内存中的地址 push eax push NULL mov eax, offset g_pfnMessageBox add eax, ebx call dword ptr [eax] ;call MessageBox ;invoke MessageBox, NULL, offset g_szText, offset g_szCaption, MB_OK CODE_END: g_dwCodeSize dd offset CODE_END - offset CODE_BEG WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL @hWndWinmine:HWND LOCAL @dwProcId:DWORD LOCAL @hProc:HANDLE LOCAL @pBuff:LPVOID LOCAL @dwBytesWrited:DWORD LOCAL @hUser32:HMODULE LOCAL @d01dProc:DWORD ;内存权限 invoke LoadLibrary, offset g_szUser32 mov @hUser32, eax invoke VirtualProtect, offset g_pfnMessageBox, size g_pfnMessageBox, PAGE_EXECUTE_READWRITE, addr @d01dProc invoke GetProcAddress, @hUser32, offset g_szMessageBox mov g_pfnMessageBox, eax invoke VirtualProtect, offset g_pfnMessageBox, size g_pfnMessageBox, @d01dProc, addr @d01dProc invoke FindWindow, NULL, offset g_szWinMineCap mov @hWndWinmine, eax invoke GetWindowThreadProcessId, @hWndWinmine, addr @dwProcId invoke OpenProcess, PROCESS_ALL_ACCESS, FALSE , @dwProcId mov @hProc, eax invoke VirtualAllocEx, @hProc, NULL, 1, MEM_COMMIT, PAGE_EXECUTE_READWRITE mov @pBuff, eax invoke WriteProcessMemory, @hProc, @pBuff, offset CODE_BEG, g_dwCodeSize, addr @dwBytesWrited invoke CreateRemoteThread, @hProc, NULL, 0, @pBuff, NULL, NULL, NULL xor eax, eax ret WinMain endp ; --------------------------------------------------------------------------- start: invoke WinMain, NULL,NULL,NULL, SW_SHOWDEFAULT invoke ExitProcess,eax end start
代码注入器
汇编库: oddisasm xedparse(x64) asmjit(1)
植物大战僵尸加方格
API hock comexplorer
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 81 82 83 84 85 86 87 88 .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include windows.inc include kernel32.inc include user32.inc includelib kernel32.lib includelib user32.lib .data g_szUser32 db "user32", 0 g_szMessageBoxA db "MessageBoxA", 0 g_szNewTitle db "这个标题被我占用了,哈哈", 0 g_pfnMessageBoxA dd 0 g_bIsSelfCall dd FALSE ;1、注入原生user.dll的messageboxA函数,注入后messagebox第一跳就会变成跳转指定地址,要修改的指令地址赋给ebx,再通过ptr修改指定地址的值 ;2、 .code ;要跳转的地址 HOOKCODE: .if g_bIsSelfCall == TRUE jmp OLDCODE .endif ;重入问题 api hook就是调用程序原有的API 再弹出原先窗口前再弹一个 钩子库??detours mov g_bIsSelfCall, TRUE invoke MessageBox, NULL, offset g_szUser32, offset g_szMessageBoxA, MB_OK mov g_bIsSelfCall, FALSE mov dword ptr [esp+0ch], offset g_szNewTitle OLDCODE: ;跳回去 mov eax, g_pfnMessageBoxA add eax, 5 ;下一条指令 ;调用被破坏掉的代码 push ebp mov ebp, esp jmp eax InstallHook proc uses ebx LOCAL @hUser32:HMODULE LOCAL @dw01dProc:DWORD ;int 3 ;获取User32 invoke GetModuleHandle, offset g_szUser32 mov @hUser32, eax ;获取User32的messagebox invoke GetProcAddress, @hUser32, offset g_szMessageBoxA mov g_pfnMessageBoxA, eax ;计算跳转偏移 mov eax, offset HOOKCODE sub eax, g_pfnMessageBoxA sub eax, 5 push eax invoke VirtualProtect,g_pfnMessageBoxA,1,PAGE_EXECUTE_READWRITE,addr @dw01dProc pop eax ;修改跳转,怎么指定修改的指令的messagebox的第一行 mov ebx, g_pfnMessageBoxA ; 保存基址信息 mov byte ptr[ebx], 0e9h ; 0xE9是JMP机器码 后面的四个字节是偏移 mov dword ptr[ebx+1], eax ; e9后面的四个字节是一个偏移地址 偏移地址=目的地址-跳转基地址(jmp的下一条指令的地址) invoke VirtualProtect,g_pfnMessageBoxA,1,@dw01dProc,addr @dw01dProc ret InstallHook endp DllMain proc hinstDLL:HINSTANCE, fdwReason:DWORD, lpvReserved:LPVOID .if fdwReason == DLL_PROCESS_ATTACH ;首次加载dll自动就执行installHook了 invoke InstallHook .endif mov eax, TRUE ret DllMain endp end DllMain ; remotedll
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 81 82 83 84 85 86 87 88 user.dll里面默认messagebox的实现 760C15F0 USER32.MessageBoxA 8BFF mov edi, edi 760C15F2 55 push ebp 760C15F3 8BEC mov ebp, esp 760C15F5 833D 946C0E76 0>cmp dword ptr [760E6C94], 0 760C15FC 74 22 je short 760C1620 760C15FE 64:A1 18000000 mov eax, dword ptr fs:[18] 760C1604 BA BC710E76 mov edx, 760E71BC 760C1609 8B48 24 mov ecx, dword ptr [eax+24] 760C160C 33C0 xor eax, eax 760C160E F0:0FB10A lock cmpxchg dword ptr [edx], ecx 760C1612 85C0 test eax, eax 760C1614 75 0A jnz short 760C1620 760C1616 C705 006D0E76 0>mov dword ptr [760E6D00], 1 760C1620 6A FF push -1 760C1622 6A 00 push 0 760C1624 FF75 14 push dword ptr [ebp+14] 760C1627 FF75 10 push dword ptr [ebp+10] 760C162A FF75 0C push dword ptr [ebp+C] 760C162D FF75 08 push dword ptr [ebp+8] 760C1630 E8 3B020000 call MessageBoxTimeoutA 760C1635 5D pop ebp 760C1636 C2 1000 retn 10 ;注入后变成 760C15F0 USER32.MessageBoxA - E9 0BFAF399 jmp 10001000 760C15F5 833D 946C0E76 0>cmp dword ptr [760E6C94], 0 760C15FC 74 22 je short 760C1620 760C15FE 64:A1 18000000 mov eax, dword ptr fs:[18] 760C1604 BA BC710E76 mov edx, 760E71BC 760C1609 8B48 24 mov ecx, dword ptr [eax+24] 760C160C 33C0 xor eax, eax 760C160E F0:0FB10A lock cmpxchg dword ptr [edx], ecx 760C1612 85C0 test eax, eax 760C1614 75 0A jnz short 760C1620 760C1616 C705 006D0E76 0>mov dword ptr [760E6D00], 1 760C1620 6A FF push -1 760C1622 6A 00 push 0 760C1624 FF75 14 push dword ptr [ebp+14] 760C1627 FF75 10 push dword ptr [ebp+10] 760C162A FF75 0C push dword ptr [ebp+C] 760C162D FF75 08 push dword ptr [ebp+8] 760C1630 E8 3B020000 call MessageBoxTimeoutA 760C1635 5D pop ebp 760C1636 C2 1000 retn 10 10001000 8BC0 mov eax, eax 10001002 C74424 0C 13300>mov dword ptr [esp+C], 10003013 1000100A A1 2C300010 mov eax, dword ptr [1000302C] 1000100F 83C0 05 add eax, 5 10001012 55 push ebp 10001013 8BEC mov ebp, esp 10001015 FFE0 jmp eax 10001017 55 push ebp 10001018 8BEC mov ebp, esp 1000101A 83C4 F8 add esp, -8 1000101D 53 push ebx 1000101E 68 00300010 push 10003000; ASCII "user32" 10001023 E8 72000000 call 1000109A; jmp 到 KERNEL32.GetModuleHandleA 10001028 8945 FC mov dword ptr [ebp-4], eax 1000102B 68 07300010 push 10003007; ASCII "MessageBoxA" 10001030 FF75 FC push dword ptr [ebp-4] 10001033 E8 68000000 call 100010A0; jmp 到 KERNEL32.GetProcAddress 10001038 A3 2C300010 mov dword ptr [1000302C], eax 1000103D B8 00100010 mov eax, 10001000 10001042 2B05 2C300010 sub eax, dword ptr [1000302C]; USER32.MessageBoxA 10001048 83E8 05 sub eax, 5 1000104B 50 push eax 1000104C 8D45 F8 lea eax, dword ptr [ebp-8] 1000104F 50 push eax 10001050 6A 40 push 40 10001052 6A 01 push 1 10001054 FF35 2C300010 push dword ptr [1000302C]; USER32.MessageBoxA 1000105A E8 47000000 call 100010A6; jmp 到 KERNEL32.VirtualProtect 1000105F 58 pop eax 10001060 8B1D 2C300010 mov ebx, dword ptr [1000302C]; USER32.MessageBoxA 10001066 C603 E9 mov byte ptr [ebx], 0E9 10001069 8943 01 mov dword ptr [ebx+1], eax 1000106C 8D45 F8 lea eax, dword ptr [ebp-8] 1000106F 50 push eax 10001070 FF75 F8 push dword ptr [ebp-8] 10001073 6A 01 push 1 10001075 FF35 2C300010 push dword ptr [1000302C]; USER32.MessageBoxA 1000107B E8 26000000 call 100010A6; jmp 到 KERNEL32.VirtualProtect 10001080 5B pop ebx 10001081 C9 leave 10001082 C3 retn
钢琴和筛选器异常 筛选器异常 / 最终异常
setUnhand
dump文件
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 .586 .model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib .data g_szMsg db "异常来了,是否跳过异常?", 0 g_szTxt db "没有发现CC断点", 0 g_szTip db "发现CC断点", 0 .code MyUnhandledExceptionFilter proc pEP:ptr EXCEPTION_POINTERS LOCAL @pEr:ptr EXCEPTION_RECORD LOCAL @pCtx:ptr CONTEXT mov ebx, pEP assume ebx:ptr EXCEPTION_POINTERS mov eax, [ebx].pExceptionRecord mov @pEr, eax mov eax, [ebx].ContextRecord mov @pCtx, eax mov ebx, @pEr assume ebx:ptr EXCEPTION_RECORD mov esi, @pCtx assume esi:ptr CONTEXT .if [ebx].ExceptionCode == EXCEPTION_ACCESS_VIOLATION add [esi].regEip, 2 ;设置TF标志位,继续执行 or [esi].regFlag,100h .elseif [ebx].ExceptionCode == EXCEPTION_SINGLE_STEP ;'判断是否有CC mov eax, [esi].regEip ;invoke MessageBox, NULL,addr byte ptr [eax] , NULL, MB_OK ;ret .if byte ptr [eax] == 033h ;0cch invoke MessageBox, NULL,offset g_szTip, NULL, MB_OK mov eax, EXCEPTION_EXECUTE_HANDLER ret .endif ;没有到达代码结束位置 .if[esi].regEip != offset CODE_END ;没有发现CC,继续单步 or [esi].regFlag, 100h .endif .endif assume esi:nothing assume ebx:nothing mov eax, EXCEPTION_CONTINUE_EXECUTION ;程序继续执行 ret MyUnhandledExceptionFilter endp start: ;28 invoke SetUnhandledExceptionFilter, offset MyUnhandledExceptionFilter xor eax, 8 mov eax, [eax] xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax xor eax, eax CODE_END: invoke MessageBox, NULL, offset g_szTxt, NULL, MB_OK invoke ExitProcess,eax end start
总结 exe程序 1、修改程序自身汇编代码(加个弹窗提示) 2、根据程序的算法逻辑写注册机(编写ASM程序) 3、编写汇编直接注入对应程序(代码/工具),汇编库就是写C程序调用汇编对应API 4、编写DLL(汇编),用DLL注入工具
windbg 一般断点,把打断点处的第一个字节替换成 cc
代码同上
TF标志位置1断点
异常错误码
配置环境变量
1 2 _NT_SYMBOL_PATH srv*C:\symbolslocal*https://msdl.microsoft.com/download/symbols
1、源码调试(汇编源码/C语言源码)必须有pdb文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bl 查看断点 bc 3-255 取消断点 bp user32!CreateWindowExA bm user32!Create*A bu 延迟断点,未加载的DLL函数 $exentry 程序入口点 teb thread environment block(结构体) dt _teb ;名称粉碎 dt 00338000 _teb dt _PEB !teb !peb
sehf
结构化异常处理
1 2 3 异常处理: -> 调试器 -> SEH -> 调试器 -> 筛选器 ->系统 SEH在筛选器前处理
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 .586 .model flat,stdcall option casemap:none include windows.inc include user32.inc include kernel32.inc includelib user32.lib includelib kernel32.lib Node struct next dd 0 handler dd 0 Node ends .data g_sz0 db " 0 异常来了", 0 g_sz1 db "1 异常来了", 0 .code Handler1 proc pER:ptr EXCEPTION_RECORD, pEstablisherFrame:DWORD, pCtx:ptr CONTEXT, pDispatcherContext:DWORD mov ebx, pER assume ebx:ptr EXCEPTION_RECORD mov esi,pCtx assume esi:ptr CONTEXT invoke MessageBox,NULL,offset g_sz1, NULL,MB_OK .if [ebx].ExceptionCode == EXCEPTION_INT_DIVIDE_BY_ZERO add[esi].regEip,2 .elseif[ebx].ExceptionCode == EXCEPTION_ACCESS_VIOLATION mov eax,ExceptionContinueSearch ret .endif assume ebx:nothing assume esi:nothing mov eax, ExceptionContinueExecution ret Handler1 endp Func1 proc LOCAL @node:Node mov @node.handler, offset Handler1 ;安装SEH assume fs:nothing mov eax,fs:[0] mov @node.next,eax lea eax,@node mov fs:[0], eax xor esi, esi div esi ;赋0异常 xor eax,eax mov [eax],eax ;内存访问异常 ;卸掉SEH mov eax, @node.next mov fs:[0], eax ret Func1 endp Handler0 proc pER:ptr EXCEPTION_RECORD, pEstablisherFrame:DWORD, pCtx:ptr CONTEXT, pDispatcherContext:DWORD mov ebx, pER assume ebx:ptr EXCEPTION_RECORD mov esi,pCtx assume esi:ptr CONTEXT invoke MessageBox,NULL, offset g_sz0,NULL,MB_OK .if [ebx].ExceptionCode == EXCEPTION_ACCESS_VIOLATION add [esi].regEip,2 .endif assume ebx:nothing assume esi:nothing mov eax,ExceptionContinueExecution ;mov eax,ExceptionContinueSearch:交给调试器或者筛选器处理 ret Handler0 endp Func0 proc LOCAL @node:Node mov @node.handler, offset Handler0 ;安装SEH assume fs:nothing mov eax, fs:[0] mov @node.next, eax lea eax, @node mov fs:[0],eax ;安装异常,安装在这个函数 invoke Func1 xor eax,eax mov [eax],eax ;内存访问异常 ;卸掉SEH mov eax, @node.next mov fs:[0], eax ret Func0 endp start: invoke Func0 invoke MessageBox,NULL,NULL,NULL,MB_OK xor eax, eax invoke ExitProcess,eax end start
RadASM 工程选项 -> 编译 /Zi -> 链接 /debug /pdb:”TestSEH.pdb”
生成pdb能在调试过程中更清楚地看函数
OD插件 OD插件都是DLL文件,创建动态链接库 比如:OD开启时加载插件DLL,修改某个异常判断的地址,从而修改异常处理机制 修改异常派发 OD插件都是DLL文件,创建动态链接库
异常处理插件 1 2 3 4 5 6 kernelbase -> UnhandledExceptionFilter (UnhandledExceptionFilter) 76F722A0 找到异常处理分支,查找跳,不抛给调试工具处理,返回给程序本身处理 (关键跳) 76F7235D /0F84 E6000000 je 76F72449
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 #include "pch.h" #include "Plugin.h" #pragma comment(lib,"ollydbg.lib" ) int ODBG_Plugindata (char * shortname) { strcpy_s(shortname, 31 , "od测试插件" ); return PLUGIN_VERSION; } int ODBG_Plugininit (int ollydbgversion, HWND hw, ulong* features) { return 0 ; } int ODBG_Paused (int reason, t_reg* reg) { if (reason == PP_EVENT) { HMODULE hKernel = GetModuleHandle("kernelbase.dll" ); LPBYTE pAddr = (LPBYTE)GetProcAddress(hKernel, "UnhandledExceptionFilter" ); pAddr += 0xBE ; BYTE btCode = 0x84 ; Writememory(&btCode, (ulong)pAddr, sizeof (btCode), MM_SILENT); } return 0 ; } BOOL APIENTRY DllMain ( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break ; } return TRUE; }
PS:右键分析代码
进程跟踪地址错误 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 #include "pch.h" #include "Plugin.h" #pragma comment(lib,"ollydbg.lib" ) DWORD WINAPI MyGetClassLongA (_In_ HWND hWnd, _In_ int nIndex) { if (IsWindowUnicode(hWnd)) { return GetClassLongW(hWnd, nIndex); } else { return GetClassLongA(hWnd, nIndex); } } int ODBG_Plugindata (char * shortname) { strcpy_s(shortname, 31 , "od测试插件2" ); return PLUGIN_VERSION; } int ODBG_Plugininit (int ollydbgversion, HWND hw, ulong* features) { DWORD dwOldProc = 0 ; LPDWORD pAddrToGetCL = (LPDWORD)0x0050D858 ; VirtualProtect(pAddrToGetCL,sizeof (DWORD),PAGE_EXECUTE_READWRITE, &dwOldProc); *pAddrToGetCL = (DWORD)MyGetClassLongA; VirtualProtect(pAddrToGetCL, sizeof (DWORD),dwOldProc,&dwOldProc); return 0 ; return 0 ; } int ODBG_Paused (int reason, t_reg* reg) { return 0 ; }
调试框架 为什么写汇编代码调用的是 win32的 API?(win32是桌面应用程序的底层接口吗)
事件驱动,消息响应
1、建立调试会话 CreateProcess DebugActiveProcess 2、循环接受调试事件 waitForDebugEvent 3、处理调试事件 4、提交处理结果 ContinueDebugEvent
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 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 .386 .model flat, stdcall ;32 bit memory model option casemap :none ;case sensitive include DBG.inc .data g_szExePath db "winmine.exe", 0 g_szEXCEPTION_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT", 0dh,0ah,0 g_szCREATE_THREAD_DEBUG_EVENT db "CREATE_THREAD_DEBUG_EVENT",0dh,0ah,0 g_szCREATE_PROCESS_DEBUG_EVENT db "CREATE_PROCESS_DEBUG_EVENT",0dh,0ah,0 g_szEXIT_THREAD_DEBUG_EVENT db "EXIT_THREAD_DEBUG_EVENT",0dh,0ah,0 g_szEXIT_PROCESS_DEBUG_EVENT db "EXCEPTION_DEBUG_EVENT",0dh,0ah,0 g_szLOAD_DLL_DEBUG_EVENT db "LOAD_DLL_DEBUG_EVENTO", 0dh,0ah,0 g_szUNLOAD_DLL_DEBUG_EVENT db "UNLOAD_DLL _DEBUG_EVENT",0dh,0ah,0 g_szOUTPUT_DEBUG_STRING_EVENT db "OUTPUT_DEBUG_STRING_EVENT",0dh,0ah,0 g_szFmtCreateProcess db "BaseOfImage:%08X, StartAddress:%08X", 0dh,0ah,"ImageName:%s",0dh, 0ah, 0 g_szFmtLoadDlls db "Base0fDll:%08X, ImageName:%s",0dh,0ah,0 g_szFmtLoadDllx db "Base0fDll:%08X, ImageName:%08X",0dh,0ah,0 .code OnLoadDll proc pDE:ptr DEBUG_EVENT LOCAL @dwAddrOfName:DWORD LOCAL @hProcess:HANDLE LOCAL @dwPid:DWORD LOCAL @dwBytesReaded:DWORD LOCAL @szwBuf[MAX_PATH]:word LOCAL @szBuf[MAX_PATH]:byte mov esi, pDE assume esi:ptr DEBUG_EVENT mov eax,[esi].dwProcessId mov @dwPid, eax lea esi,[esi].u.LoadDll assume esi:ptr LOAD_DLL_DEBUG_INFO invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,@dwPid .if eax == NULL mov eax,DBG_CONTINUE ret .endif mov @hProcess, eax invoke ReadProcessMemory, @hProcess,[esi]. lpImageName, addr @dwAddrOfName,sizeof @dwAddrOfName,addr @dwBytesReaded .if eax == FALSE invoke crt_printf, offset g_szFmtLoadDllx, [esi].lpBaseOfDll, [esi].lpImageName mov eax,DBG_CONTINUE ret .endif .if [esi].fUnicode invoke ReadProcessMemory, @hProcess, @dwAddrOfName, addr @szwBuf, MAX_PATH, addr @dwBytesReaded invoke WideCharToMultiByte, CP_ACP,0, addr @szwBuf, MAX_PATH, addr @szBuf,MAX_PATH, NULL, NULL .else invoke ReadProcessMemory,@hProcess, @dwAddrOfName, addr @szBuf, MAX_PATH, addr @dwBytesReaded .endif invoke crt_printf, offset g_szFmtLoadDlls, [esi].lpBaseOfDll, addr @szBuf assume esi:nothing mov eax,DBG_CONTINUE ret OnLoadDll endp OnCreateProcess proc pDE:ptr DEBUG_EVENT mov esi, pDE assume esi:ptr DEBUG_EVENT lea esi,[esi].u.CreateProcessInfo assume esi:ptr CREATE_PROCESS_DEBUG_INFO invoke crt_printf, offset g_szCREATE_PROCESS_DEBUG_EVENT invoke crt_printf,offset g_szFmtCreateProcess,[esi].lpBaseOfImage,[esi].lpStartAddress,[esi].lpImageName assume esi:nothing mov eax, DBG_CONTINUE ret OnCreateProcess endp main proc LOCAL @si:STARTUPINFO LOCAL @pi:PROCESS_INFORMATION LOCAL @de:DEBUG_EVENT LOCAL @dwContinueStatus:DWORD invoke RtlZeroMemory,addr @si,sizeof @si invoke RtlZeroMemory,addr @pi,sizeof @pi mov @si. cb, sizeof @si mov @dwContinueStatus, DBG_CONTINUE ;建立调试会话 invoke CreateProcess,offset g_szExePath, NULL, NULL, NULL, FALSE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, addr @si, addr @pi .if eax == FALSE ret .endif .while TRUE invoke WaitForDebugEvent,addr @de,INFINITE .if eax == FALSE .continue .endif .if @de.dwDebugEventCode == EXCEPTION_DEBUG_EVENT invoke crt_printf,offset g_szEXCEPTION_DEBUG_EVENT .elseif @de.dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT invoke crt_printf, offset g_szCREATE_THREAD_DEBUG_EVENT .elseif @de.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT invoke OnCreateProcess, addr @de mov @dwContinueStatus, eax .elseif @de.dwDebugEventCode == EXIT_THREAD_DEBUG_EVENT invoke crt_printf, offset g_szEXIT_THREAD_DEBUG_EVENT .elseif @de.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT invoke crt_printf, offset g_szEXIT_PROCESS_DEBUG_EVENT .elseif @de.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT ;invoke crt_printf, offset g_szLOAD_DLL_DEBUG_EVENT invoke OnLoadDll, addr @de mov @dwContinueStatus, eax .elseif @de.dwDebugEventCode == UNLOAD_DLL_DEBUG_EVENT invoke crt_printf, offset g_szUNLOAD_DLL_DEBUG_EVENT .elseif @de.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT invoke crt_printf, offset g_szOUTPUT_DEBUG_STRING_EVENT .endif ;没这行就GG invoke ContinueDebugEvent, @de.dwProcessId, @de.dwThreadId, @dwContinueStatus .endw xor eax, eax ret main endp start: invoke main xor eax,eax invoke ExitProcess,0 end start
PE 1 2 3 #修改对齐值 ml /c /coff pe.asm link /merge:.rdata=.text /align:16 /subsystem:windows user32.lib kernel32.lib pe.obj
其他都是16位残留
30 word + long = 64字节
IMAGE_SECTION_HEADER文件映射 选项头的地址 + 选项头的大小 = 节表的位置
从文件偏移(位置),映射数据(大小),到内存地址,在内存中所占大小
节表位置:NT的选项头之后,选项头的地址+选项头大小。就是.text之类的
节大小:两行半=16*2+8,union大小为最大元素
文件对齐和内存对齐不一致导致加载后地址偏移
winhex查看exe,od查看内存对比
规律:节与节是连续的
要求:PointerToRawData 和 sizeofRawData 都是跟 FileAlign 对齐,virtualAddress是跟 SectionAlign 对齐
sizeofImage:各节内存大小+PE头内存大小;最后一个节的virtualAddress+最后一个节的内存大小
地址转换 VA - virtual Address,内存中的虚拟地址,绝对地址
RVA - relative virtual address,相对虚拟地址,相对于模块基址的偏移
FA - file address,文件偏移,文件中所在的地址
virtualsize
virtualAddress
SizeofRawData
PointerToRawData
0x28
0x1000
0x200
0x400
0x92
0x2000
0x200
0x600
0x10
0x3000
0x200
0x800
0x10
0x4000
0x200
0xa00
00402145(VA) –> 2145(RVA) –> 145 + 600 –> 745(FA)
812(FA) –> 12 + 3000 -> 3012 + 00400000 –> 00403012(VA)
程序中的绝对地址映射到内存中的地址
修改winhex的745地址值,OD里的内存中00402145的改变
virtualsize
virtualAddress
SizeofRawData
PointerToRawData
0xBA
0x210
0xC0
0x210
0x10
0x2D0
0x10
0x2D0
从文件偏移 0x2D0 处复制 0x10 个字节的数据到内存 0x2D0处,占0x10
00400000 + 210 = 00400210
00400000 + 2D0 = 004002D0
dump文件的编写 PE头相同,把内存和文件偏移的赋值位置调换
dump是读取内存数据?运行时从文件内映射到内存,dump是从内存映射到文件?
默认还是按照从文件中拿数据映射到内存,把映射地址调换,exe运行机制又没改变? 运行程序,再用winhex打开,看此时的运行内存,即文件偏移复制到内存中的地址 把复制在内存中的节数据拷贝到新的exe
生成dump文件可能会因为全局变量初始化问题出问题
dump未初始化的文件
解答:
映射:按照节表的内容,将PE文件按照节表信息和节数据依次映射进入内存。
DUMP:将内存中的数据按节表数据将目标数据拷贝出来成为一个PE.exe文件
dump的先决条件 :在OEP处dump ,此时全局变量里的值未被初始化,不会存在全局变量的访问异常。
调试器默认都有dump功能
脱壳用dump
节表注入 添加节
节表添加一项
添加节数据
节表个数增1
修改SizeOfImage
扩展最后一个节
修改节表
添加节数据
修改SizeOfImage
CFF工具可以辅助
导入表
IMAGE_DATA_DIRECTORY
CFF Explorer,自带地址转换
Dependency Walker
DLL文件占20个字节,1行+4
节表往上8行就是导入表
PE往下7行半就是导出表、导入表
导入表20个字节
415c - 4000 + 5000 = 515c
https://www.cnblogs.com/iBinary/p/9740757.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; DWORD OriginalFirstThunk; } DUMMYUNIONNAME; DWORD TimeDateStamp; DWORD ForwarderChain; DWORD Name; DWORD FirstThunk; } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
可选头后的导入表(地址RVA、大小)跳转导入列表 IMAGE_IMPORT_DESCRIPTOR,每个导入表五个字节。
重点关注第一个(OriginalFirstThunk/INT)、第四个(Name)、最后一个(FirstThunk/IAT)
如果高位位1,说明是序号导入,低WORD 位导入函数的序号值。
如果最高位为 0,说明是名字导入,该DWORD 指向结构体 IMAGE_IMPORT_BY_NAME
1 2 3 4 5 6 7 8 9 typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; DWORD Function; DWORD Ordinal; DWORD AddressOfData; } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
IAT 和 INT都指向 IMAGE_THUNK_DATA32,分别指向 Ordinal 和 AddressOfData,它们再指向IMAGE_IMPORT_BY_NAME
1 2 3 4 typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; CHAR Name[1 ]; } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
PE加载到进程之前
PE加载到进程之后
415C(RVA) - 1000 + 400 = 355c(FOA)
4484(RVA) - 1000 + 400 = 3884(FOA)
直接内存里面看
PEload
https://www.52pojie.cn//thread-1077397-1-1.html
导出表 1 2 3 4 5 6 7 8 9 10 11 12 13 typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; DWORD Name; DWORD Base; DWORD NumberOfFunctions; DWORD NumberOfNames; DWORD AddressOfFunctions; DWORD AddressOfNames; DWORD AddressOfNameOrdinals; } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
https://www.cnblogs.com/iBinary/p/9739031.html
https://bbs.kanxue.com/thread-269947.htm
https://github.com/DonaldTrump0/LordPE/releases/tag/0.2Soblesky.2
基地重定向表 IMAGE_BASE_RELOCATION
分页地址,页面偏移 -8/2=word的个数
API模拟:把dll的接口复制在新内存,调用新内存里的接口
TLS表 thread local storage 线程局部存储
显示TLS
隐式TLS
资源表 函数转发
Import REConstructor