• VS反汇编分析


    X86和X87汇编指令

    数据传输指令

    • 它们在存贮器和寄存器、寄存器和输入输出端口之间传送数据.

    • 通用数据传送指令.

    	MOV		传送字或字节.
    	MOVSX	先符号扩展,再传送.
    	MOVZX	先零扩展,再传送.
    	PUSH	把字压入堆栈.
    	POP		把字弹出堆栈.
    	PUSHA	把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.
    	POPA	把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.
    	PUSHAD	把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.
    	POPAD	把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.
    	BSWAP	交换32位寄存器里字节的顺序
    	XCHG	交换字或字节.(至少有一个操作数为寄存器,段寄存器不可作为操作数)
    	CMPXCHG	比较并交换操作数.(第二个操作数必须为累加器AL/AX/EAX)
    	XADD	先交换再累加.(结果在第一个操作数里)
    	XLAT	字节查表转换.----BX指向一张256字节的表的起点,AL为表的索引值(0-255,即0-FFH);返回AL为查表结果.([BX+AL]->AL)
    
    • 输入输出端口传送指令.
      • IN I/O端口输入. ( 语法: IN 累加器, {端口号│DX} )
      • OUT I/O端口输出. ( 语法: OUT {端口号│DX},累加器 )输入输出端口由立即方式指定时, 其范围是 0-255; 由寄存器 DX 指定时,其范围是 0-65535.
    • 目的地址传送指令.
    	LEA		装入有效地址.例: LEA DX,string ;把偏移地址存到DX.
    	LDS		传送目标指针,把指针内容装入DS.例: LDS SI,string	;把段地址:偏移地址存到DS:SI.
    	LES		传送目标指针,把指针内容装入ES.例: LES DI,string	;把段地址:偏移地址存到ES:DI.
    	LFS		传送目标指针,把指针内容装入FS.例: LFS DI,string	;把段地址:偏移地址存到FS:DI.
    	LGS		传送目标指针,把指针内容装入GS.例: LGS DI,string	;把段地址:偏移地址存到GS:DI.
    	LSS		传送目标指针,把指针内容装入SS.例: LSS DI,string	;把段地址:偏移地址存到SS:DI.
    
    • 标志传送指令.
      • LAHF 标志寄存器传送,把标志装入AH.
      • SAHF 标志寄存器传送,把AH内容装入标志寄存器.
      • PUSHF 标志入栈.
      • POPF 标志出栈.
      • PUSHD 32位标志入栈.
      • POPD 32位标志出栈.

    算术运算指令

    	ADD		加法.
    	ADC		带进位加法.
    	INC		加 1.
    	AAA		加法的ASCII码调整.
    	DAA		加法的十进制调整.
    	SUB		减法.
    	SBB		带借位减法.
    	DEC		减 1.
    	NEG		求反(以	0 减之).
    	CMP		比较.(两操作数作减法,仅修改标志位,不回送结果).
    	AAS		减法的ASCII码调整.
    	DAS		减法的十进制调整.
    	MUL		无符号乘法.结果回送AH和AL(字节运算),或DX和AX(字运算),
    	IMUL	整数乘法.结果回送AH和AL(字节运算),或DX和AX(字运算),
    	AAM		乘法的ASCII码调整.
    	DIV		无符号除法.结果回送:商回送AL,余数回送AH, (字节运算);或 商回送AX,余数回送DX,	(字运算).
    	IDIV	整数除法.结果回送:商回送AL,余数回送AH, (字节运算);或 商回送AX,余数回送DX, (字运算).
    	AAD		除法的ASCII码调整.
    	CBW		字节转换为字. (把AL中字节的符号扩展到AH中去)
    	CWD		字转换为双字. (把AX中的字的符号扩展到DX中去)
    	CWDE	字转换为双字. (把AX中的字符号扩展到EAX中去)
    	CDQ		双字扩展. (把EAX中的字的符号扩展到EDX中去)
    
    

    逻辑运算指令

    	AND		与运算.
    	OR		或运算.
    	XOR		异或运算.
    	NOT		取反.
    	TEST	测试.(两操作数作与运算,仅修改标志位,不回送结果).
    	SHL		逻辑左移.
    	SAL		算术左移.(=SHL)
    	SHR		逻辑右移.
    	SAR		算术右移.(=SHR)
    	ROL		循环左移.
    	ROR		循环右移.
    	RCL		通过进位的循环左移.
    	RCR		通过进位的循环右移.
    	以上八种移位指令,其移位次数可达255次.
    	移位一次时, 可直接用操作码. 如 SHL AX,1.
    	移位>1次时, 则由寄存器CL给出移位次数.
    	如 MOV CL,04	SHL	AX,CL
    

    串指令

    	DS:SI	源串段寄存器 :源串变址.
    	ES:DI	目标串段寄存器:目标串变址.
    	CX 重复次数计数器.
    	AL/AX	扫描值.
    	D标志	0表示重复操作中SI和DI应自动增量; 1表示应自动减量.
    	Z标志	用来控制扫描或比较操作的结束.
    	MOVS	串传送.( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
    	CMPS	串比较.( CMPSB 比较字符. CMPSW 比较字. )
    	SCAS	串扫描.把AL或AX的内容与目标串作比较,比较结果反映在标志位.
    	LODS	装入串.把源串中的元素(字或字节)逐一装入AL或AX中.( LODSB	传送字符. LODSW	传送字.	LODSD 传送双字.	)
    	STOS	保存串.是LODS的逆过程.
    	REP			当CX/ECX<>0时重复.
    	REPE/REPZ	当ZF=1或比较结果相等,且CX/ECX<>0时重复.
    	REPNE/REPNZ	当ZF=0或比较结果不相等,且CX/ECX<>0时重复.
    	REPC		当CF=1且CX/ECX<>0时重复.
    	REPNC		当CF=0且CX/ECX<>0时重复.
    

    程序转移指令

    • 无条件转移指令 (长转移)
      • JMP 无条件转移指令
      • CALL 过程调用
      • RET/RETF 过程返回.
    • 条件转移指令 (短转移,-128到+127的距离内)( 当且仅当(SF XOR OF)=1时,OP1<OP2 )
    	JA/JNBE		不小于或不等于时转移.
    	JAE/JNB		大于或等于转移.
    	JB/JNAE		小于转移.
    	JBE/JNA		小于或等于转移.
    		以上四条,测试无符号整数运算的结果(标志C和Z).
    	JG/JNLE		大于转移.
    	JGE/JNL		大于或等于转移.
    	JL/JNGE		小于转移.
    	JLE/JNG		小于或等于转移.
    		以上四条,测试带符号整数运算的结果(标志S,O和Z).
    	JE/JZ		等于转移.
    	JNE/JNZ		不等于时转移.
    	JC			有进位时转移.
    	JNC			无进位时转移.
    	JNO			不溢出时转移.
    	JNP/JPO		奇偶性为奇数时转移.
    	JNS			符号位为 "0" 时转移.
    	JO			溢出转移.
    	JP/JPE		奇偶性为偶数时转移.
    	JS			符号位为 "1" 时转移.
    
    • 循环控制指令(短转移)
      • LOOP CX不为零时循环.
      • LOOPE/LOOPZ CX不为零且标志Z=1时循环.
      • LOOPNE/LOOPNZ CX不为零且标志Z=0时循环.
      • JCXZ CX为零时转移.
      • JECXZ ECX为零时转移.
    • 中断指令
      • INT 中断指令
      • INTO 溢出中断
      • IRET 中断返回
    • 处理器控制指令
      HLT 处理器暂停, 直到出现中断或复位信号才继续.
      WAIT 当芯片引线TEST为高电平时使CPU进入等待状态.
      ESC 转换到外处理器.
      LOCK 封锁总线.
      NOP 空操作.
      STC 置进位标志位.
      CLC 清进位标志位.
      CMC 进位标志取反.
      STD 置方向标志位.
      CLD 清方向标志位.
      STI 置中断允许位.
      CLI 清中断允许位.

    伪指令

    DW			定义字(2字节).
    PROC		定义过程.
    ENDP		过程结束.
    SEGMENT		定义段.
    ASSUME		建立段寄存器寻址.
    ENDS		段结束.
    END			程序结束.
    

    处理机控制指令:标志处理指令

    CLC		进位位置0指令
    CMC		进位位求反指令
    STC		进位位置为1指令
    CLD		方向标志置1指令
    STD		方向标志位置1指令
    CLI		中断标志置0指令
    STI		中断标志置1指令
    NOP		无操作
    HLT		停机
    WAIT	等待
    ESC		换码
    LOCK	封锁
    

    程序内存

    • 代码段CS:保存程序代码文本

    • 数据段DS:保存初始化的全局变量和静态变量

    • BSS(Block Started by Symbol): 保存未初始化的全局变量和静态变量

    • 堆(heap):动态分配内存,向地址增大的方向增长

    • 栈(stack):存放局部变量,向地址减小的方向增长

    常用指令

    • add:加法指令,第一个是目标操作数,第二个是源操作数,格式为:目标操作数 = 目标操作数 + 源操作数;

    • sub:减法指令,格式同 add;

    • call:调用函数,一般函数的参数放在寄存器中;

    • ret:跳转会调用函数的地方。对应于call,返回到对应的call调用的下一条指令,若有返回值,则放入eax中;

    • push:把一个32位的操作数压入堆栈中,这个操作在32位机中会使得esp被减4(字节),esp通常是指向栈顶的

    • pop:与push相反,esp每次加4(字节),一个数据出栈。pop的参数一般是一个寄存器,栈顶的数据被弹出到这个寄存器中;

    • mov:数据传送。第一个参数是目的操作数,第二个参数是源操作数,就是把源操作数拷贝到目的一份。

    • xor:异或指令,这本身是一个逻辑运算指令,但在汇编指令中通常会见到它被用来实现清零功能。用 xor eax,eax这种操作来实现 mov eax,0,可以使速度更快,占用字节数更少。

    • lea:取得第二个参数地址后放入到前面的寄存器(第一个参数)中。然而lea也同样可以实现mov的操作,例如:lea edi,[ebx-0ch],方括号表示存储单元,也就是提取方括号中的数据所指向的内容,然而lea提取内容的地址,这样就实现了把(ebx-0ch)放入到了edi中,但是mov指令是不支持第二个操作数是一个寄存器减去一个数值的。

    • stos:串行存储指令,它实现把eax中的数据放入到edi所指的地址中,同时edi后移4个字节,这里的stos实际上对应的是stosd,其他的还有stosb,stosw分别对应1,2个字节。

    • jmp:无条件跳转指令,对应于大量的条件跳转指令。

    • jg:条件跳转,大于时成立,进行跳转,通常条件跳转之前会有一条比较指令(用于设置标志位)。

    • jl:小于时跳转

    • cmp:比较大小指令,,CMP OPR1 , OPR2.相当于(OPR1)-(OPR2),它并不保存运算结果,只是根据结果设置相关的条件标志位(SF、ZF、CF、OF)。CMP指令后往往跟着条件转移指令,实现根据比较的结果产生不同的程序分支的功能。

    • rep 根据ECX寄存器的值进行重复循环操作

    寄存器

    • EAX:累加(Accumulator)寄存器,常用于函数返回值,它是很多加法乘法指令的缺省寄存器。
    • EBX:基址(Base)寄存器,以它为基址访问内存
    • ECX:计数器(Counter)寄存器,常用作字符串和循环操作中的计数器
    • EDX:数据(Data)寄存器,常用于乘除法和I/O指针
    • ESI:源变址寄存器
    • DSI:目的变址寄存器
    • ESP:堆栈(Stack)指针寄存器,指向堆栈顶部
    • EBP:基址指针寄存器,指向当前堆栈底部
    • EIP:指令寄存器,指向下一条指令的地址
    • ESI,EDI,分别是16位寄存器SI和DI的32位扩展,它们是源变址寄存器,和目的变址寄存器,用于串操作指令中。同时,它们也可以作为通用寄存器使用。

    #include "pch.h"
    #include <iostream>
    using namespace std;
    int Add(int x, int y)
    {
    00A117E0 55                   push        ebp  //栈基址寄存器内容送栈顶
    00A117E1 8B EC                mov         ebp,esp  //栈顶寄存器内容为当前栈基址 (函数调用压栈后更新栈基址为原先的栈顶,原先的栈基址已经保存在栈顶,故而能够在函数调用返回后找到上一层函数的起始地址)
    00A117E3 81 EC CC 00 00 00    sub         esp,0CCh  //esp-0cch,相当于栈往下长了0CCH(51*4)个单位,/*用于预留空间给函数临时变量*/
    00A117E9 53                   push        ebx  //基址寄存器压栈
    00A117EA 56                   push        esi  //源变址寄存器压栈
    00A117EB 57                   push        edi  //目的变址寄存器压栈
    00A117EC 8D BD 34 FF FF FF    lea         edi,[ebp-0CCh]  //ebp-0cch这个值送到edi中,也就是说把目的地址寄存器的值设置成了栈基址往下0cch个字节处
    00A117F2 B9 33 00 00 00       mov         ecx,33h  //计数器设置为33H,用于循环计数(51)
    00A117F7 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  //eax设置成0CCCCCCCCH
    00A117FC F3 AB                rep stos    dword ptr es:[edi]  //rep是循环,stos是串行存储指令,它实现把eax中的数据放入到edi所指的地址中,同时edi后移4个字节
    00A117FE B9 09 C0 A1 00       mov         ecx,offset _3B624A16_test.cpp (0A1C009h)  
    00A11803 E8 19 FA FF FF       call        @__CheckForDebuggerJustMyCode@4 (0A11221h)  
    	int z = 0;
    00A11808 C7 45 F8 00 00 00 00 mov         dword ptr [z],0  //ptr指针指向变量z的地址,在这个地址上写入双字大小表示的0
    	z = x + y;
    00A1180F 8B 45 08             mov         eax,dword ptr [x]  //ptr指针指向变量x的地址,取这个地址双字内容送入eax,相当于就是把变量x的内容送eax
    00A11812 03 45 0C             add         eax,dword ptr [y]  //ptr指针指向变量y的地址,取这个地址的双字内容与eax内容相加,结果还在eax中
    00A11815 89 45 F8             mov         dword ptr [z],eax  //把eax内容送入z中
    
    	return z;
    00A11818 8B 45 F8             mov         eax,dword ptr [z]  //z内容送入eax作为返回值
    }
    00A1181B 5F                   pop         edi   //edi出栈
    00A1181C 5E                   pop         esi  //esi出栈
    00A1181D 5B                   pop         ebx  //ebx出栈
    00A1181E 81 C4 CC 00 00 00    add         esp,0CCh  //栈顶指针减去0CCH,相当于退栈清除填充内容
    00A11824 3B EC                cmp         ebp,esp  //检查ebp是否小于esp,调用__RTC_CheckEsp检查
    00A11826 E8 00 FA FF FF       call        __RTC_CheckEsp (0A1122Bh)  
    00A1182B 8B E5                mov         esp,ebp  //该函数返回后,其基地址变成当前栈中函数的栈顶地址
    00A1182D 5D                   pop         ebp  //该函数的基地址已经没用了,弹出该函数的基地址
    00A1182E C3                   ret  //返回指令
    --- 无源文件 -----------------------------------------------------------------------
    00A1182F CC                   int         3  //断点
    00A11830 CC                   int         3  
    00A11831 CC                   int         3  
    00A11832 CC                   int         3  
    00A11833 CC                   int         3  
    00A11834 CC                   int         3  
    00A11835 CC                   int         3  
    00A11836 CC                   int         3  
    00A11837 CC                   int         3  
    00A11838 CC                   int         3  
    00A11839 CC                   int         3  
    00A1183A CC                   int         3  
    00A1183B CC                   int         3  
    00A1183C CC                   int         3  
    00A1183D CC                   int         3  
    00A1183E CC                   int         3  
    00A1183F CC                   int         3  
    00A11840 CC                   int         3  
    00A11841 CC                   int         3  
    00A11842 CC                   int         3  
    00A11843 CC                   int         3  
    00A11844 CC                   int         3  
    00A11845 CC                   int         3  
    00A11846 CC                   int         3  
    00A11847 CC                   int         3  
    00A11848 CC                   int         3  
    00A11849 CC                   int         3  
    00A1184A CC                   int         3  
    00A1184B CC                   int         3  
    00A1184C CC                   int         3  
    00A1184D CC                   int         3  
    00A1184E CC                   int         3  
    00A1184F CC                   int         3  
    
    
    int main()
    {
    00A118F0 55                   push        ebp  //栈基址寄存器内容压栈
    00A118F1 8B EC                mov         ebp,esp  //esp称为新的栈基址
    00A118F3 81 EC E4 00 00 00    sub         esp,0E4h  //栈下移0E4H个字节的空间
    00A118F9 53                   push        ebx  //ebx esi edi入栈
    00A118FA 56                   push        esi  
    00A118FB 57                   push        edi  
    00A118FC 8D BD 1C FF FF FF    lea         edi,[ebp-0E4h]  //edi设定
    00A11902 B9 39 00 00 00       mov         ecx,39h  //循环计数器设定
    00A11907 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  //eax赋值
    00A1190C F3 AB                rep stos    dword ptr es:[edi]  //填充0CCCCCCCCH
    00A1190E B9 09 C0 A1 00       mov         ecx,offset _3B624A16_test.cpp (0A1C009h)  
    00A11913 E8 09 F9 FF FF       call        @__CheckForDebuggerJustMyCode@4 (0A11221h)  
    	int a = 3;
    00A11918 C7 45 F8 03 00 00 00 mov         dword ptr [a],3  //a = 3
    	int b = 5;
    00A1191F C7 45 EC 05 00 00 00 mov         dword ptr [b],5  // b= 5
    	int ret = 0;
    00A11926 C7 45 E0 00 00 00 00 mov         dword ptr [ret],0  // ret = 0
    
    	ret = Add(a, b);
    00A1192D 8B 45 EC             mov         eax,dword ptr [b]  //eax累加寄存器赋值b中内容eax = b = 5
    00A11930 50                   push        eax  //eax入栈
    00A11931 8B 4D F8             mov         ecx,dword ptr [a] //a中内容送ecx ,ecx = a = 3 
    00A11934 51                   push        ecx  //ecx入栈
    00A11935 E8 60 F8 FF FF       call        Add (0A1119Ah)//调用Add函数  
    00A1193A 83 C4 08             add         esp,8  //esp栈顶寄存器+8,因为一个参数+4,这里有两个参数传入,并且函数参数是向栈上方涨的
    00A1193D 89 45 E0             mov         dword ptr [ret],eax  //eax送入ret变量所处内存地址。注意,此处eax是存储了Add函数的返回值,故而ret = 8
    	cout << ret << endl;
    00A11940 8B F4                mov         esi,esp  //栈顶寄存器内容送esi
    00A11942 68 4E 12 A1 00       push        offset std::endl<char,std::char_traits<char> > (0A1124Eh)  // "push variable" means push the value of the variable and "push offset variable" means push the offset of the variable.
    00A11947 8B FC                mov         edi,esp  //栈顶指针送edi
    00A11949 8B 45 E0             mov         eax,dword ptr [ret]  //ret内容送eax
    00A1194C 50                   push        eax  //eax入栈
    00A1194D 8B 0D A8 B0 A1 00    mov         ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0A1B0A8h)]  //ecx赋值,打印长度确定,循环打印字符,接下来调用打印函数打印字符
    00A11953 FF 15 9C B0 A1 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0A1B09Ch)]  //调用打印函数,不断打印栈顶的值,栈顶是eax,其值等于ret
    00A11959 3B FC                cmp         edi,esp    //检查edi<esp  每个过程函数调用结束后都要比较
    00A1195B E8 CB F8 FF FF       call        __RTC_CheckEsp (0A1122Bh)  
    00A11960 8B C8                mov         ecx,eax  //eax送ecx
    00A11962 FF 15 A0 B0 A1 00    call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0A1B0A0h)]  //调用打印函数 打印endl
    00A11968 3B F4                cmp         esi,esp //检查esi<esp  
    00A1196A E8 BC F8 FF FF       call        __RTC_CheckEsp (0A1122Bh)  
    	return 0;
    00A1196F 33 C0                xor         eax,eax  //eax清零
    }
    //退栈函数返回
    00A11971 5F                   pop         edi  
    00A11972 5E                   pop         esi  
    00A11973 5B                   pop         ebx  
    00A11974 81 C4 E4 00 00 00    add         esp,0E4h  
    00A1197A 3B EC                cmp         ebp,esp  
    00A1197C E8 AA F8 FF FF       call        __RTC_CheckEsp (0A1122Bh)  
    00A11981 8B E5                mov         esp,ebp  
    00A11983 5D                   pop         ebp  
    00A11984 C3                   ret  
    
  • 相关阅读:
    Integer to Roman leetcode java
    Reverse Integer leetcode java
    Binary Tree Maximum Path Sum leetcode java
    公司来了一个奇葩需求pppoe client+server+EOIP+vlan
    魔兽数据库-自然
    windows默认dns解析走ipv4而不走ipv6
    ROS支持BCP桥接(基于PPP隧道)
    几款比较好用的电动理发器推荐
    centos 拨号pptp在拨号成功和拨号失败的时候脚本处理!!!非常重要
    ros routeros 脚本命令script
  • 原文地址:https://www.cnblogs.com/czsharecode/p/12303096.html
Copyright © 2020-2023  润新知