zoukankan      html  css  js  c++  java
  • Win32汇编

    整理复习汇编语言的知识点,以前在学习《Intel汇编语言程序设计 - 第五版》时没有很认真的整理笔记,主要因为当时是以学习理解为目的没有整理的很详细,这次是我第三次阅读此书,每一次阅读都会有新的收获,这次复习,我想把书中的重点,再一次做一个归纳与总结(注:16位汇编部分跳过),并且继续尝试写一些有趣的案例,这些案例中所涉及的指令都是逆向中的重点,一些不重要的我就直接省略了,一来提高自己,二来分享知识,转载请加出处,敲代码备注挺难受的。

    汇编中常用的运算符,加减乘除等,另外包括了移位运算等,移位又分为,算数移位,逻辑移位,循环移位,双精度移位等。

    再次强调:该笔记主要学习的是汇编语言,不是研究编译特性的,不会涉及到编译器的优化与代码还原。


    ADD/SUB指令: ADD/SUB指令将将同尺寸的源操作数和目的操作数相加,且不改变原操作数,相加后的结果存入目的操作数中.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	MyList DWORD 10h,20h,30h,40h
    
    .code
    	main PROC
    		; 将eax于ebx 两数相加: 将相加后的结果放入eax
    		mov eax,1024
    		mov ebx,2048
    		add eax,ebx
    		
    		; 同样两数相减,将结果放到eax中
    		mov eax,1024
    		sub eax,512
    		
    		; 针对数组的相加同样可以
    		mov esi,offset MyList    ; 获取到首地址
    		mov eax,0
    		mov ebx,0
    		
    		mov eax,dword ptr ds:[esi]            ; 找到第一个元素
    		mov ebx,dword ptr ds:[esi + 1 * 4]    ; 找到第二个元素
    		add eax,ebx                           ; 相加操作
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    NEG 取反指令: 该指令通过将数字转换为对应的补码而求出其值的相反数,结合上面的加法与减法案例,我们来模拟编译器处理特定语句的写法Rval = -Xvar + (Yvar - Zvar)

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval SDWORD ?
    	Xval SDWORD 26
    	Yval SDWORD 30
    	Zval SDWORD 40
    .code
    	main PROC
    		; 1.写出: Rval = -Xvar + (Yvar - Zvar) 汇编格式
    		; 首先将Xval的值通过neg取反
    		mov eax,dword ptr ds:[Xval]
    		neg eax
    		
    		; 然后将Yval与Zval相减后复制到Yval
    		mov ebx,dword ptr ds:[Yval]
    		sub ebx,dword ptr ds:[Zval]
    		
    		;最后将两个子项相加后放入到Rval中
    		add eax,ebx
    		mov dword ptr ds:[Rval],eax
    		
    		; 2.写出: Rval = (Xval+Yval) - (Yval+Zval)
    		mov eax,dword ptr ds:[Xval]
    		add eax,dword ptr ds:[Yval]
    		mov dword ptr ds:[Rval],eax
    		
    		mov ebx,dword ptr ds:[Yval]
    		add ebx,dword ptr ds:[Zval]
    	
    		sub dword ptr ds:[Rval],ebx
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    AND/OR/XOR 布尔指令: AND指令是对数据进行与运算,OR指令则是对数据进行或运算,XOR则是异或,这三个指令都是将操作值保存在目的操作数中,需要注意的是这些运算符都是针对二进制数进行操作的.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    	String BYTE "hello LYSHARK",0dh,0h
    	
    .code
    	ToString proc
    		mov ecx,lengthof String
    		mov esi,offset String
    	s:	;and byte ptr [esi],11011111b  ; 清除第五位,小写变大写
    		or byte ptr [esi],00100000b    ; 设置第五位,大写变小写
    		inc esi
    		loop s
    		ret
    	ToString endp
    
    	main PROC
    		; and 逻辑与运算
    		xor eax,eax
    		mov al,00111011b
    		and al,00001111b  ; 运算后去除al中的0
    		
    		mov eax,00401024h
    		and eax,0ffh         ; eax = 00000024h
    		
    		mov eax,00401024h
    		and eax,0ffffh       ; eax = 00001024h
    		
    		mov eax,00401024h
    		and eax,0ffff0000h   ; eax = 00400000h
    		
    		; or 逻辑或运算
    		xor edx,edx
    		mov dl,5              ; 二进制值
    		or dl,30h             ; 转为有ASCII码
    		
    		; xor 异或运算
    		mov eax,0ffh
    		xor eax,0ffh          ; eax = 0
    		mov eax,4
    		xor eax,5             ; eax = 1
    
    		mov eax,0401000h
    		xor eax,0400000h      ; eax = 00001000h
    		mov eax,0401000h
    		xor eax,01000h        ; eax = 00400000h
    
    		; 异或可用于检查标志位
    		xor eax,eax
    		mov eax,00001111h
    		xor eax,0          ; 检查基偶标志
    		
    		mov eax,00100101h
    		xor eax,0          ; 影响PF标志
    		
    		call ToString
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    SHL/SHR 逻辑移位: SHL常用于对目标操作数执行逻辑左移(无符号数)操作,其左移后最低位以0填充,而移动出去的最高位则会送入CF(进位标志)中,而SHR则相反,对目标操作数执行逻辑右移(无符号数)操作,移出的数据位用0代替,最低位被复制到CF(进位标志)中,原来的进位标志位丢失.

    Intel处理器中定义,执行移位的源操作数的范围必须在0-255之间,在任何处理器上都可以使用CL寄存器存放移位位数,例如在下面的指令中,AL寄存器被左移一位,最高位被复制到了进位标志中,最低位被清零:

    01251006 | B3 8F                | mov al,10001111b                            | AL = 10001111b
    01251008 | D0E3                 | shl al,1                                    | CF = 1,AL = 00011110b
    
    01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
    01251008 | C0E0 02              | shl al,2                                    | CF = 0,AL = 00000000b
    
    01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
    01251008 | C0E0 01              | shl al,1                                    | CF = 1,AL = 00000000b
    
    01251006 | B0 01                | mov al,10100000b                            | AL = 10100000b
    01251008 | C0E0 03              | shl al,2                                    | CF = 0,AL = 10000000b
    

    另外使用SHL指令还可以进行2的次幂的高速乘法运算,任何操作数左移动N位,就相当于该操作数乘以2的N次方,如下例子:

    01311002 | B0 05                | mov al,5                                    | AL 左移动1位
    01311004 | D0E0                 | shl al,1                                    | al * 2 = 10
    
    01311007 | B0 05                | mov al,5                                    | AL左移2位
    01311009 | C0E0 02              | shl al,2                                    | al * 4 = 20
    
    01311007 | B0 05                | mov al,5                                    | AL左移3位
    01311009 | C0E0 03              | shl al,3                                    | al * 8 = 40
    

    下面是一个左移计算的案例,我们通过汇编来计算Rval = ((Xval + Yval) - (Yval + Zval)) * 8的结果.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval SDWORD ?
    	Xval SDWORD 50
    	Yval SDWORD 100
    	Zval SDWORD 10
    .code
    	main PROC
    		; Rval = ((Xval + Yval) - (Yval + Zval)) * 8
    		mov eax,dword ptr ds:[Xval]
    		add eax,dword ptr ds:[Yval]
    		
    		mov ebx,dword ptr ds:[Yval]
    		add ebx,dword ptr ds:[Zval]
    		
    		sub eax,ebx
    		
    		; 乘以8也就是左移3位 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
    		shl eax,3
    		mov dword ptr ds:[Rval],eax
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    对目标操作数执行SHR逻辑右移(无符号数)操作,移出的数据位用0代替,最低位被复制到CF进位标志中,原来的进位标志位丢失.

    0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
    0131100F | D0E8                 | shr al,1                                    | CF = 1,AL = 01000111b
    
    0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
    0131100F | D0E8                 | shr al,2                                    | CF = 1,AL = 00100011b
    

    另外任何无符号操作数逻辑右移N位,就相当于该操作数除以2的N次方,如下例子:

    01311012 | B2 20                | mov dl,20                                   | DL 右移1位 
    01311014 | D0EA                 | shr dl,1                                    | dl/2 = 10
    
    01311012 | B2 20                | mov dl,20                                   | DL 右移2位 
    01311014 | D0EA                 | shr dl,2                                    | dl/4 = 5
    

    下面是一个右移计算的案例,我们通过汇编来计算Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)的结果.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval SDWORD ?
    	Xval SDWORD 200
    	Yval SDWORD 50
    	Zval SDWORD 10
    .code
    	main PROC
    		; Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)
    		; 混合移位计算: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
    		; 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
    		
    		; 先通过右移3位,计算除法
    		mov eax,dword ptr ds:[Xval]   ; Xval / 8
    		shr eax,3
    		
    		; 再通过左移4位和2位分别计算乘法
    		mov ebx,dword ptr ds:[Yval]   ; Yval * 16
    		shl ebx,4
    		
    		mov ecx,dword ptr ds:[Zval]   ; Zval * 4
    		shl ecx,2
    		
    		add eax,ebx
    		sub eax,ecx
    		mov dword ptr ds:[Rval],eax
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    上面的这种计算方式属于乘数刚好是2的次幂,如果不是2的次幂则需要拆分后计算,如下案例,为了计算无符号乘以36,可以把36分解成2的5次方和2的2次方,然后利用移位命令高效计算.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval SDWORD ?
    .code
    	main PROC
    		; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
    		; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
    		
    		; 计算 123 * 36
    		; 等式拆分 EAX * 36 => EAX * (32 + 4) => (EAX * 32) + (EAX * 4)
    		mov eax,123
    		mov ebx,eax         ; 拷贝出一份
    		shl eax,5           ; 计算 (EAX * 32)
    		shl ebx,2           ; 计算 (EAX * 4)
    		add eax,ebx         ; 最后相加
    		mov dword ptr ds:[Rval],eax
    		
    		; 计算 123 * 24
    		; 等式拆分: EAX * 24 => EAX * (16 + 8) => (EAX * 16) + (EAX * 8)
    		mov eax,123
    		mov ebx,eax
    		shl eax,4           ; 计算 (EAX * 16)
    		shl ebx,3           ; 计算 (EAX * 8)
    		add eax,ebx
    		mov dword ptr ds:[Rval],eax
    		
    		; 计算 123 * 21
    		; 等式拆分: EAX * 21 => EAX * (16 + 4 + 1) => (EAX * 16) + (EAX * 4) + (EAX * 1)
    		
    		mov eax,123
    		mov ebx,eax
    		mov ecx,eax         ; 得到 (EAX * 1)
    		shl eax,4           ; 计算 (EAX * 16)
    		shl ebx,2           ; 计算 (EAX * 4)
    		add eax,ebx
    		add eax,ecx
    		mov dword ptr ds:[Rval],eax
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    下面是我通过寻找一些规律,能够在不查表的情况下逆推出其左移或者是右移中乘数或除数的具体值,如果比较复杂的话还是直接查表来的容易一些,此处只是一种思考方式.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    .code
    	main PROC
    	; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
    	; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
    		
    		; 乘法逆推: 28/7=4
    		mov eax,7                      ; eax=7
    		shl eax,2                      ; 求0x2是乘以几,乘以4
    		mov dword ptr ds:[Rval],eax    ;  eax = 28 计算出: 0x2 => 28/7=4
    		
    		; 乘法逆推: 96/6 = 16 => 4*4=16
    		mov eax,6                      ; eax = 6
    		shl eax,2                      ; 4
    		shl eax,2                      ; 4
    		mov dword ptr ds:[Rval],eax    ; eax = 96
    		
    		; 乘法逆推: 4*4*8
    		mov eax,4                      ; eax = 4
    		shl eax,2                      ; 运行到此处 eax=16 通过16/4 = 4 故乘以4
    		shl eax,3                      ; 运行到此处 eax  =128  通过 128/4=32 , 32/4=8 故乘以8
    		mov dword ptr ds:[Rval],eax
    		
    		; 除法逆推: 7/1.75 = 4
    		mov eax,7                      ; eax = 7
    		shr eax,2                      ; 此处乘以4
    		mov dword ptr ds:[Rval],eax    ; eax = 1.75 => 7/1.75=4
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    SAL/SAR 算数移位: SAL指令与SHL指令等价,SAR指令可以对有符号数进行快速除以2的次幂操作,也可以将一个AX寄存器中的值进行扩展,扩展成EAX.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .code
    	main PROC
    		; SAR => 算数右移
    		mov al,0f0h         ; AL = 11110000b (-16)
    		sar al,1            ; AL = 11111000b (-8)
    		
    		; SAR => 有符号除法 计算-128的,2的3次方
    		; 2次方 => -32 3次方 => -16 4次方 => -8 5次方 => -4
    		mov eax,-128        ; AL = 10000000b
    		sar eax,3           ; AL = 11110000b EAX = -16
    		
    		; SAR => 符号扩展AX扩展到EAX
    		; 先左移EAX 16位,然后算术右移EAX 16位
    		mov ax,-128         ; EAX = ????FF80h
    		shl eax,16          ; EAX = FF800000h
    		sar eax,16          ; EAX = FFFFFF80h
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    ROL/ROR 循环移位: ROL位循环左移,其左移1位后会把最高位同时复制到进位标志位和最低位中,而ROR则是循环右移,其右移1位后,把最低位同时复制到进位标志位和最高位中.

    循环移位和普通移位不同之处在于前者并不会丢失任何数据位,从一端走的数据位会从另一端出现,如循环左移会将高位复制到低位中,循环右移则将低位复制到高位中,但需要注意不论是左移/右移,都是对二进制格式进行操作的.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .code
    	main PROC
    		; ROL => 循环左移
    		mov al,40h        ; AL = 01000000b
    		rol al,1          ; AL = 10000000b , CF = 0
    		rol al,1          ; AL = 00000001b , CF = 1
    		rol al,1          ; AL = 00000010b , CF = 0
    		
    		mov al,00100000b
    		rol al,3          ; AL = 00000001b , CF = 1
    		
    		; ROR => 循环右移
    		mov al,01h        ; AL = 00000001b
    		ror al,1          ; AL = 10000000b , CF = 1
    		ror al,1          ; AL = 01000000b , CF = 0
    		
    		mov al,00000100b
    		ror al,3          ; AL = 10000000b , CF = 1
    		
    		; ROL => 循环左移,交换位组
    		; 交换 高半部分(位4-7) 低半部分(位0-3)
    		mov al,26h
    		rol al,4          ; rol 与 ror 结果一致
    		ror al,4
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    RCL/RCR 标志移位: RCL指令在每位左移1位后,把CF进位标志复制到最低有效位中,最高有效位复制到进位标志中, RCR则相反,右移后把CF进位标志复制到最高有效位中,并把最低有效位复制到进位标志中.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .code
    	main PROC
    		; RCL 左移
    		clc            ; 将CF进位标志置0
    		mov bl,88h     ; CF = 0 , BL = 10001000b
    		rcl bl,1       ; CF = 1 , BL = 00010000b
    		rcl bl,1       ; CF = 0 , BL = 00100001b
    		
    		; RCR 右移
    		stc            ; 将CF进位标志置1
    		mov ah,10h     ; CF = 1 , ah = 00010000h
    		rcr ah,1       ; CF = 1 , ah = 10001000h
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    让数组整体左移或右移.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval SDWORD ?
    	ArraySize = 3
    	Array DWORD ArraySize DUP(99999999h)
    	ByteArray BYTE 81h,20h,33h
    	WordArray WORD 810dh,0c064h,93abh
    .code
    	main PROC
    		; 多双字同时右移
    		mov esi,0
    		shr Array[esi + 8],1     ; 高双字
    		rcr Array[esi + 4],1     ; 中间双字
    		rcr Array[esi],1         ; 低双字
    		
    		; 多双字同时左移
    		mov esi,0
    		shl Array[esi+8],1
    		rcl Array[esi+4],1
    		rcl Array[esi],1
    		
    		; 让数组整体右移 (从高字节到低字节)
    		shr [ByteArray + 2],1
    		rcr [ByteArray + 1],1
    		rcr [ByteArray],1
    		
    		; 让数组整体左移 (从低字节到高字节)
    		shl [ByteArray],1
    		rcl [ByteArray + 2],1
    		rcl [ByteArray + 4],1
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    MUL/IMUL 乘法指令: MUL/IMUL分别可进行有符号与无符号的乘法运算,通常该指令都接受寄存器操作数,也接受内存操作数,但是不接受立即数,且乘数与被乘数大小必须相同,乘基尺寸是乘数/被乘数的两倍.

    MUL乘法指令有三种格式: 第一种将8位操作数与AL相乘,结果放入AX中,第二种将16位操作数与AX相乘,结果的高16位放入DX低16位放入AX,第三种将32位操作数与EAX相乘,结果高32位放入EDX第32位放入EAX中.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    	VarWordA WORD 2000h
    	VarWordB WORD 0100h
    .code
    	main PROC
    		; 执行8位乘法运算 al = al*bl
    		mov al,5h
    		mov bl,10h
    		mul bl
    		mov byte ptr ds:[Rval],al
    		
    		; 执行16位乘法运算
    		xor eax,eax
    		xor edx,edx
    		mov ax,word ptr ds:[VarWordA]
    		mul word ptr ds:[VarWordB]
    		mov word ptr ds:[Rval],ax     ; 低半部分
    		mov word ptr ds:[Rval],dx     ; 高半部分  DX:AX = 00000020h
    		
    		; 执行32位乘法运算
    		xor eax,eax
    		xor edx,edx
    		mov eax,12345h
    		mov ebx,1000h
    		mul ebx
    		mov dword ptr ds:[Rval],eax   ; 低半部分
    		mov dword ptr ds:[Rval],edx   ; 高半部分 EDX:EAX = 0000000012345000h
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    IMUL指令主要用于执行有符号整数的乘法运算,并保留乘积的符号位,且在32位汇编中有三种格式:但操作数格式,双操作数格式,三操作数格式,首先是单操作数模式,该模式把乘积存储在累加器AX中,或者将符号位放入EDX将结果放入EAX中.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	EDX_Rval DWORD ?
    	EAX_Rval DWORD ?
    .code
    	main PROC
    		
    		; 执行8位乘法运算: 48*8 得到的积+192 溢出
    		xor eax,eax
    		mov al,48
    		mov bl,4
    		imul bl          ; CF 进位 = 1 OF 溢出 = 1
    		
    		xor eax,eax
    		mov al,-4
    		mov bl,4
    		imul bl           ; AX=FFF0h OF=0
    		
    		; 执行16位乘法运算: 48*4 得到的积 +192
    		xor eax,eax
    		mov ax,48
    		mov bx,4
    		imul bx
    		mov word ptr ds:[EDX_Rval],dx
    		mov word ptr ds:[EAX_Rval],ax  ; DX:AX = 000000C0h OF=0
    		
    		; 执行32位乘法运算: +4823424 *(-423)
    		xor eax,eax
    		xor ebx,ebx
    		mov eax,+4823424
    		mov ebx,-423
    		imul ebx
    		mov dword ptr ds:[EDX_Rval],edx   ; EDX为符号位
    		mov dword ptr ds:[EAX_Rval],eax   ; EDX:EAX = FFFFFFFF86635D80h OF=0
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    接着就是乘法语句的双操作数与三操作数模式了,在双操作数中第一个操作数必须是寄存器,第二个操作数可以是寄存器或内存等,在三操作数模式中,把乘积存储在第一个操作数中,其他与双操作数类似.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	wordp   SWORD 4
    	dwordp  SDWORD 4
    	Rval DWORD ?
    .code
    	main PROC
    	
    		; 双操作数乘法运算
    		xor eax,eax
    		xor ebx,ebx
    		
    		mov ax,-16                   ; ax = -16
    		mov bx,2                     ; bx = 2
    		imul bx,ax                   ; bx = bx * ax
    		imul bx,2                    ; bx = bx * 2
    		imul bx,word ptr ds:[wordp]  ; bx = bx * wordp
    		mov word ptr ds:[Rval],bx    ; 放入变量中保存
    		
    		; 三操作数乘法运算
    		xor eax,eax
    		xor ebx,ebx
    		
    		imul bx,wordp,-16           ; bx = wordp * -16
    		imul ebx,dwordp,-16         ; ebx = dwordp * -16
    		imul ebx,dwordp,-20         ; ebx = dwordp * -20
    		mov dword ptr ds:[Rval],ebx ; 放入变量中
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    到此为止我们学会了通过移位的方式实现快速乘法运算,也学过使用MUL指令进行乘法计算,接下来我们可以编写两个案例分别通过移位和MUL计算EAX与36相乘的结果,看看哪一个效率更高一些.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .code
    	shl_proc proc
    		mov ecx,10
    	s:
    		push eax
    		mov ebx,eax
    		shl eax,5
    		shl eax,2
    		add eax,ebx
    		pop eax
    		loop s
    		ret
    	shl_proc endp
    	
    	mul_proc proc
    		mov ecx,10
    	s:
    		push eax
    		mov ebx,36
    		mul ebx
    		pop eax
    		loop s
    		ret
    	mul_proc endp
    
    	main PROC
    		mov eax,10
    		call shl_proc
    		
    		
    		mov eax,10
    		call mul_proc
    	
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    DIV/IDIV 除法指令: DIV是无符号除法指令,该指令支持8/16/32位无符号整数的除法运算,指令中唯的寄存器或内存操作数是除数,IDIV则是有符号除法指令,该指令与无符号除法几乎一致,唯一的不同在于有符号除法在进行相除操作时需要符号扩展.

    首先我们先来学习一下DIV无符号除法运算的使用技巧,分别演示8/16/32位无符号除法的使用方式.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    	DivIdend QWORD 0000000800300020h
    	DivIsor DWORD 00000100h
    	Div_Eax DWORD ?
    	Div_Edx DWORD ?
    .code
    	main PROC
    		
    		; 执行8位除法: 83/2 商al是41h 余数ah是1
    		xor eax,eax
    		mov ax,0083h                  ; 被除数
    		mov bl,2                      ; 除数
    		div bl                        ; ax = ax / bl
    		mov byte ptr ds:[Rval],ah     ; ah = 01h
    		mov byte ptr ds:[Rval],al     ; al = 41h
    		
    		; 执行16位除法: 8003h/100h 商是80h 余数是3
    		xor edx,edx                   ; 清除edx寄存器
    		mov ax,8003h                  ; 被除数
    		mov cx,100h                   ; 除数
    		div cx                        ; ax = ax / cx
    		mov word ptr ds:[Rval],ax     ; ax = 0080h
    		mov word ptr ds:[Rval],dx     ; dx = 0003h
    		 
    		; 执行32位除法
    		mov edx,dword ptr DivIdend + 4  ; 高双字
    		mov eax,dword ptr DivIdend      ; 低双字
    		div DivIsor                     ; 与被除数相除
    		
    		mov dword ptr ds:[Div_Eax],eax  ; eax = 08003000h
    		mov dword ptr ds:[Div_Edx],edx  ; edx = 00000020h
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    针对IDIV有符号数的除法运算,需要对被除数进行除法操作之前,对其进行符号扩展,汇编中有三条扩展命令.

    CBW 指令 将字节符号扩展至字,扩展AL的符号位至AH中,保留了数字的符号.
    CWD 指令 将字符号扩展至双字,指令扩展AX的符号位至DX中.
    CDQ 指令 双字符号扩展至八字节,指令扩展EAX的符号位至EDX中.

    当使用符号扩展指令扩展后,寄存器就可以被用来计算有符号除法了,代码如下所示:

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	ByteVal SBYTE -48
    	WordVal SWORD -5000
    	DworeVal SDWORD +50000
    	Rval DWORD ?
    .code
    	main PROC
    		; 字节扩展至字 -48/5
    		xor eax,eax
    		mov al,byte ptr ds:[ByteVal]  ; al = D0 取出 -48
    		cbw                           ; ax = FFD0 将al扩展至ax
    		mov bl,5                      ; bl = 05
    		idiv bl                       ; ax=ax/bl
    		mov word ptr ds:[Rval],ax     ; 结果: ax = FDF7
    		
    		mov byte ptr ds:[Rval],al     ; AL保存商 -9
    		mov byte ptr ds:[Rval],ah     ; AH保存余数 -3
    		
    		; 字扩展至双字
    		xor eax,eax
    		mov ax,word ptr ds:[WordVal]  ; 除数
    		cwd                           ; 扩展至双字(扩展AX至DX)
    		mov bx,+256                   ; 被除数
    		idiv bx                       ; ax = ax/bx
    		mov word ptr ds:[Rval],ax     ; 商AX=-19
    		mov word ptr ds:[Rval],dx     ; 余数DX=-136
    		
    		; 双字符号扩展至八字节
    		mov eax,dword ptr ds:[DworeVal]
    		cdq                             ; 扩展EAX到EDX
    		mov ebx,-256
    		idiv ebx
    		mov dword ptr ds:[Rval],eax     ; 商 EAX = -195
    		mov dword ptr ds:[Rval],edx     ; 余数 EDX = +80
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    学习了前面的这几种计算方式以后,我们就可以将其总结起来实现计算复杂的表达式了,先来三个练手的.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    	var1 DWORD 10
    	var2 DWORD 15
    	var3 DWORD 20
    	var4 DWORD 25
    .code
    	main PROC
    		; 实现计算: var4 = (var1 * 5) / (var2 - 3)
    		mov eax,dword ptr ds:[var1] ; 先计算左边 (var1 * 5)
    		mov ebx,5
    		mul dword ptr ds:[ebx]      ; EDX:EAX 乘积
    		
    		mov ebx,dword ptr ds:[var2] ; 计算右边 (var2 - 3)
    		sub ebx,3
    		
    		div dword ptr ds:[ebx]       ; 计算两者的除法
    		mov dword ptr ds:[var4],eax  ; 最后赋值操作
    		
    		; 实现计算: var4 = (var1+var2) * var3
    		mov eax,dword ptr ds:[var1]
    		add eax,dword ptr ds:[var2]   ; 计算前半部分
    		
    		mov ebx,dword ptr ds:[var3]   ; 计算后半部分
    		mul ebx
    		mov dword ptr ds:[var4],eax   ; 最后赋值操作
    		
    		; 实现计算: var1 = (var2/var3) * (var1+var2)
    		mov eax,var2
    		cdq                           ; 扩展为EDX:EAX
    		idiv dword ptr ds:[var3]      ; 计算除法,存入eax
    		
    		mov ebx,dword ptr ds:[var1]   ; 计算加法
    		add ebx,dword ptr ds:[var2]
    		
    		imul dword ptr ds:[ebx]       ; 最后计算乘法
    		mov dword ptr ds:[var1],eax   ; 在eax中取值
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    最后我们来实现一个相对复杂的案例,总体的复习一下,该案例计算var4 = (var1 * -5) / (-var2 % var3)返回值,我们可以从右边开始计算,并把右边的值存储到EBX中,然后把被除数符号扩展到EDX,最后使用IDIV计算除法.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    	var1 DWORD 54
    	var2 DWORD 56
    	var3 DWORD 5
    	var4 DWORD 52
    .code
    	main PROC
    		; 实现计算: var4 = (var1 * -5) / (-var2 % var3)
    		mov eax,dword ptr ds:[var2]
    		neg eax                      ; 将var2反转
    		
    		cdq                          ; 将被除数符号扩展
    		idiv var3                    ; 除以var3 则 EDX=余数
    		mov ebx,edx                  ; 将余数给EBX
    		
    		mov eax,-5
    		imul var1                    ; 计算 var1 * -5 结果给EAX
    		
    		idiv ebx                     ; eax = eax/ebx
    		mov dword ptr ds:[var4],eax  ; 最后将结果给var4
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    ADC/SBB 扩展加减法: 扩展加减法是指任意尺寸大小数字的加减法,其中ADC指令主要用户实现带进位加法,SBB指令则实现带进位减法,起作用都是将源操作数与目的操作数以及进位等相加减.

    以扩展加法为例,计算两个8位整数相加(FFh+FFh)产生的16位结果将被存放在DL:AL (01feh)中,如果是计算两个32位整数相加(FFFFFFFFh+FFFFFFFFh),则会在EDX和EAX中分别存放00000001h:FFFFFFFEh这两个值,扩展SBB同理.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include kernel32.inc
    includelib kernel32.lib
    
    .data
    	Rval DWORD ?
    .code
    	main PROC
    		; 计算8位加法操作
    		xor edx,edx
    		xor eax,eax
    		mov dl,0                      ; 清0
    		mov al,0ffh                   ; 设置加数
    		add al,0ffh                   ; al = al + 0ffh
    		adc dl,0                      ; 进位加法
    		mov byte ptr ds:[Rval],dl     ; 存放高位
    		mov byte ptr ds:[Rval],al     ; 存放低位
    		
    		; 计算32位加法操作
    		xor edx,edx
    		xor eax,eax
    		mov edx,0
    		mov eax,0ffffffffh
    		add eax,0ffffffffh
    		adc edx,0
    		mov dword ptr ds:[Rval],edx    ; 存放高位
    		mov dword ptr ds:[Rval],eax    ; 存放低位
    		
    		; 计算32位减法操作
    		xor edx,edx
    		xor eax,eax
    		mov edx,1       ; 设置高半部分
    		mov eax,0       ; 设置低半部分
    		sub eax,1       ; eax减去1
    		sbb edx,0       ; 减去1则高半部分为0
    		
    		mov dword ptr ds:[Rval],edx    ; 存放高位
    		mov dword ptr ds:[Rval],eax    ; 存放低位
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    LEA指令计算: Lea指令的使用初衷是取出某个内存的地址,但在汇编手册中可以发现其不止可以取地址同样可以实现算数运算,但这个运算与移位运算符一样只能计算2的次幂,当需要计算一个非次幂数字是,则需要对其进行分析与拆分,来实现对算数的计算.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include user32.inc
    includelib user32.lib
    include kernel32.inc
    includelib kernel32.lib
    
    include msvcrt.inc
    includelib msvcrt.lib
    
    .data
    	x DWORD ?
    	y DWORD ?
    	szFmt BYTE '计算结果: %d',0dh,0ah,0
    .code
    	main PROC
    		; 针对加法的lea指令优化
    		mov dword ptr ds:[x],5
    		mov dword ptr ds:[y],3
    		mov eax,dword ptr ds:[x]
    		mov ebx,dword ptr ds:[y]
    		
    		lea eax,dword ptr ds:[eax + 3]         ; eax = edx + 3
    		invoke crt_printf,addr szFmt,eax
    		
    		lea eax,dword ptr ds:[eax + ebx + 2]   ; eax = eax + ebx + 2
    		invoke crt_printf,addr szFmt,eax
    		
    		; 针对减法的lea指令优化
    		mov dword ptr ds:[x],6
    		mov eax,dword ptr ds:[x]
    		
    		lea eax,dword ptr ds:[eax - 2]         ; eax = eax - 2
    		invoke crt_printf,addr szFmt,eax
    		
    		; 针对乘法的lea指令优化
    		mov dword ptr ds:[x],5
    		mov dword ptr ds:[y],3
    		
    		mov eax,dword ptr ds:[x]
    		xor ebx,ebx
    		lea ebx,dword ptr ds:[eax * 8 + 2]     ; ebx = eax * 8 + 2
    		invoke crt_printf,addr szFmt,ebx
    		
    		; 如果使用lea计算乘法,则乘数必须是2/4/8
    		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 15 * eax
    		lea edx,dword ptr ds:[eax * 4 + eax]   ; edx = 4eax + eax => 5eax
    		lea edx,dword ptr ds:[edx * 2 + edx]   ; edx = 5eax * 2 + 5eax => 15eax
    		invoke crt_printf,addr szFmt,edx       ; edx = eax * 15 = 45
    		
    		; 如果计算乘法时乘数非2的次幂,则此时需要减
    		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 eax * 7 + 10
    		lea edx,dword ptr ds:[eax * 8]         ; edx = eax * 8
    		sub edx,eax                            ; edx = edx - 1eax
    		add edx,10                             ; edx = edx + 10
    		invoke crt_printf,addr szFmt,edx       ; edx = eax * 7 + 10
    		
    		mov eax,dword ptr ds:[y]               ; eax = 3 => 计算 eax * 3 - 7
    		lea edx,dword ptr ds:[eax * 2]         ; edx = eax * 2
    		add edx,eax                            ; edx = edx + eax
    		sub edx,7                              ; edx = edx - 7
    		invoke crt_printf,addr szFmt,edx       ; edx = eax * 3 - 7
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    除法变乘法: 相比较于乘法运算,除法运算则显得略微复杂些,当计算中被除数为正数时,则可以直接使用sar(算数右移)快速计算除法,如果被除数为负数,则需要使用cdq符号扩展后,然后and edx,xxx之后才能进行相除运算.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include user32.inc
    includelib user32.lib
    include kernel32.inc
    includelib kernel32.lib
    
    include msvcrt.inc
    includelib msvcrt.lib
    
    .data
    	x DWORD ?
    	y DWORD ?
    	z DWORD ?
    	szFmt BYTE '计算结果: %d',0dh,0ah,0
    .code
    	main PROC
    		mov dword ptr ds:[x],5
    		mov dword ptr ds:[y],-3
    		mov dword ptr ds:[z],-10
    
    		; 除数为2的优化方式
    		; 被除数为正数(无需扩展): eax => 5 / 2 = 2
    		mov eax,dword ptr ds:[x]   ; 被除数
    		sar eax,1                  ; 算数右移
    		invoke crt_printf,addr szFmt,eax
    		
    		; 被除数为负数(需要扩展): eax => -3 / 2 = -1
    		mov eax,dword ptr ds:[y]   ; 被除数
    		cdq                        ; 符号扩展
    		sub eax,edx                ; 被除数减去edx符号扩展
    		sar eax,1                  ; 算数右移
    		invoke crt_printf,addr szFmt,eax
    		
    		
    		; 除数为4的优化方式
    		; 被除数为正数(无需扩展): eax => 5 / 4 = 1
    		mov eax,dword ptr ds:[x]
    		sar eax,2
    		invoke crt_printf,addr szFmt,eax
    		
    		; 被除数为负数(需要扩展): eax => -10 / 4 = -2
    		; 抽取出公式来: (x + y - 1) / y
    		mov eax,dword ptr ds:[z]          ; eax = x
    		cdq
    		and edx,3                         ; edx = y-1
    		add eax,edx                       ; eax = eax + edx => x + (y - 1)
    		sar eax,2                         ; 2 => y
    		invoke crt_printf,addr szFmt,eax
    		
    		
    		; 除数为8的优化方式
    		; 被除数为正数(无需扩展): eax => 5 / 8 = 1
    		mov eax,dword ptr ds:[x]
    		sar eax,3
    		invoke crt_printf,addr szFmt,eax
    		
    		; 被除数为负数(需要扩展): eax => -10 / 8 = -1
    		mov eax,dword ptr ds:[z]
    		cdq
    		and edx,7
    		add eax,edx
    		sar eax,3
    		invoke crt_printf,addr szFmt,eax
    
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    除法中的除数与被除数都可以分为有符号与无符号,两种计算方式均有一定差异,其差异如下所示.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include user32.inc
    includelib user32.lib
    include kernel32.inc
    includelib kernel32.lib
    
    include msvcrt.inc
    includelib msvcrt.lib
    
    .data
    	x DWORD ?
    	y DWORD ?
    	z DWORD ?
    	szFmt BYTE '计算结果: %d',0dh,0ah,0
    .code
    	main PROC
    		mov dword ptr ds:[x],5
    		mov dword ptr ds:[y],10
    		mov dword ptr ds:[z],-10
    
    		; 除数为(无符号)正2的次幂的计算过程
    		mov eax,dword ptr ds:[x]
    		shr eax,1                  ; eax = 5 / 2
    		
    		mov eax,dword ptr ds:[x]
    		shr eax,2                  ; eax = 5 / 4
    		
    		mov eax,dword ptr ds:[x]
    		shr eax,3                  ; eax = 5 / 8
    		
    		
    		; 被除数为(有符号)的计算过程
    		mov eax,dword ptr ds:[z]
    		cdq
    		sub eax,edx
    		sar eax,1                  ; eax = -10 / 2
    		;neg eax                    ; 将eax取反
    		invoke crt_printf,addr szFmt,eax
    		
    		mov eax,dword ptr ds:[z]
    		cdq
    		and edx,3
    		add eax,edx
    		sar eax,2                  ; eax = -10 / 4
    		;neg eax
    		invoke crt_printf,addr szFmt,eax
    		
    		mov eax,dword ptr ds:[z]
    		cdq
    		and edx,7
    		add eax,edx
    		sar eax,3                   ; eax = -10 / 8
    		;neg eax
    		invoke crt_printf,addr szFmt,eax
    		
    		
    		; 除数为(有符号)负2的次幂的计算过程
    		mov eax,dword ptr ds:[y]    ; y = 10
    		cdq
    		sub eax,edx
    		sar eax,1                   ; eax = 10 / -2
    		neg eax                     ; 将正数 eax翻转为负数 = -5
    		invoke crt_printf,addr szFmt,eax
    		
    		mov eax,dword ptr ds:[y]    ; y = 10
    		cdq
    		and edx,3
    		add eax,edx
    		sar eax,2                   ; eax = 10 / -4
    		neg eax                     ; eax = -2
    		invoke crt_printf,addr szFmt,eax
    		
    		mov eax,dword ptr ds:[z]    ; z = -10 
    		cdq
    		and edx,7
    		add eax,edx
    		sar eax,3                   ; eax = -10 / -8 
    		neg eax                     ; eax = 1 (负负得正)
    		invoke crt_printf,addr szFmt,eax
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    上方的除法运算被除数均为2的次幂,除数的范围也被限定在了2/4/8这样的范围之内,如果是计算非2的次幂该怎么写呢,如下是计算非2的次幂的计算方式,通常情况下编译器会将除法运算转换为乘法,如果需要知道除数是多少则可以使用公式2^(32+n) / M计算后得出.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include user32.inc
    includelib user32.lib
    include kernel32.inc
    includelib kernel32.lib
    
    include msvcrt.inc
    includelib msvcrt.lib
    
    .data
    	x DWORD ?
    	y DWORD ?
    	z DWORD ?
    	szFmt BYTE '计算结果: %d',0dh,0ah,0
    .code
    	main PROC
    		mov dword ptr ds:[x],5
    		mov dword ptr ds:[y],10
    		mov dword ptr ds:[z],-10
    		
    		; 除法(有符号)非2的幂转换为乘法
    		
    		mov ecx,dword ptr ds:[y]      ; 被除数 ecx = 10 / 3 = 3
    		mov eax,055555556h            ; eax = M值 1431655766
    		imul ecx
    		mov eax,edx                   ; edx = n 计算: 2^(32+n) / M
    		shr eax,01fh                  ; 计算出除数为 2.9999 => 3
    		add edx,eax
    		invoke crt_printf,addr szFmt,edx
    		
    		
    		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 5 = 2
    		mov eax,066666667h             ; 此处的M模值是编译器计算后得到的
    		imul ecx
    		sar edx,1                      ; 想要知道除数是多少,只需要
    		mov eax,edx                    ; 2^(32 + edx) / M = 2^33 / 66666667 = 4.999
    		shr eax,01fh
    		add edx,eax
    		invoke crt_printf,addr szFmt,edx
    		
    		
    		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 6 = 1
    		mov eax,02AAAAAABh             ; eax = 715827883
    		imul ecx
    		mov eax,edx                    ; 2^(32 + edx) / M = 2^32 / 2AAAAAAB = 6
    		shr eax,01fh
    		add edx,eax
    		invoke crt_printf,addr szFmt,edx
    		
    		mov ecx,dword ptr ds:[y]       ; ecx = 10 / 9 = 1
    		mov eax,038E38E39h             ; eax = 954437177 
    		imul ecx
    		sar edx,1                      ; 2^(32 + edx) / M = 2^33 / 38E38E39 = 9
    		mov ecx,edx
    		shr ecx,01fh
    		add edx,ecx
    		invoke crt_printf,addr szFmt,edx
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    上方代码中的除法计算是针对有符号数进行的,如果是针对无符号数则需要以下方式计算.

    	.386p
    	.model flat,stdcall
    	option casemap:none
    
    include windows.inc
    include user32.inc
    includelib user32.lib
    include kernel32.inc
    includelib kernel32.lib
    
    include msvcrt.inc
    includelib msvcrt.lib
    
    .data
    	x DWORD ?
    	y DWORD ?
    	z DWORD ?
    	szFmt BYTE '计算结果: %d',0dh,0ah,0
    .code
    	main PROC
    		mov dword ptr ds:[x],-5
    		mov dword ptr ds:[y],10
    		mov dword ptr ds:[z],20
    		
    		; 除法(无符号)非2的次幂(正数)转换为乘法
    		
    		xor edx,edx
    		mov ecx,dword ptr ds:[y]    ; ecx = 10
    		mov eax,0AAAAAAABh          ; ecx / 3 = 3
    		mul ecx
    		shr edx,1
    		invoke crt_printf,addr szFmt,edx
    		
    		; 还原除数: 2 ^(32 + n) / M => 2 ^ (32+2) / 0CCCCCCCDh = 5
    		xor edx,edx
    		mov ecx,dword ptr ds:[y]    ; ecx = 10 => 计算: 10/5
    		mov eax,0CCCCCCCDh          ; eax = M
    		mul ecx
    		shr edx,2                   ; edx= n
    		invoke crt_printf,addr szFmt,edx
    		
    		; 还原除数: 2 ^(32 + n) / M => 2 ^ (32+2) / 0AAAAAAABh = 6
    		xor edx,edx
    		mov ecx,dword ptr ds:[y]     ; ecx = 10 => 计算:10/6
    		mov eax,0AAAAAAABh           ; eax = M
    		mul ecx
    		shr edx,2                    ; edx = n
    		invoke crt_printf,addr szFmt,edx
    		
    		
    		;还原除数: 2 ^(32 + n) / M => 2 ^ 33 / 038E38E39h = 9
    		xor edx,edx
    		mov ecx,dword ptr ds:[z]     ; ecx = 20  => 计算: 20/9
    		mov eax,038E38E39h           ; eax = M
    		mul ecx
    		shr edx,1                    ; edx = n
    		invoke crt_printf,addr szFmt,edx
    		
    		
    		; 除法(无符号)非2的次幂(负数)转换为乘法
    		; 还原除数: 2 ^(32 + n) / M => 2 ^ 33 / 0AAAAAAABh = nge(3) => -3
    		xor edx,edx
    		mov ecx,dword ptr ds:[z]      ; ecx = 20  => 计算: 20/-3
    		mov eax,0AAAAAAABh            ; eax = M
    		mul ecx
    		shr edx,1                     ; edx = n 
    		neg edx                       ; edx=6 结果neg取反
    		invoke crt_printf,addr szFmt,edx
    		
    		; 还原除数: 2 ^(32 + n) / M => 2 ^ 63 / 080000003h = nge(5) => -5
    		xor edx,edx
    		mov ecx,dword ptr ds:[x]       ; ecx = -5 => 计算: -5 / -5
    		mov eax,080000003h             ; eax = M
    		mul ecx
    		shr edx,01fh                   ; edx = n
    		invoke crt_printf,addr szFmt,edx
    		
    		invoke ExitProcess,0
    	main ENDP
    END main
    

    版权声明: 本博客,文章与代码均为学习时整理的笔记,博客中除去明确标注有参考文献的文章,其他文章【均为原创】作品,转载请务必【添加出处】,您添加出处是我创作的动力!

    警告:如果您恶意转载本人文章,则您的整站文章,将会变为我的原创作品,请相互尊重!
  • 相关阅读:
    移动互联网实战--Apple的APNS桩推送服务的实现(1)
    移动互联网实战--社交游戏的排行榜设计和实现(2)
    移动互联网实战--社交游戏的排行榜设计和实现(1)
    Linux 线程--那一年, 我们一起忽视的pthread_join
    移动互联网实战--资源类APP的数据存储处理和优化
    Thrift 个人实战--RPC服务的发布订阅实现(基于Zookeeper服务)
    移动互联网实战--Web Restful API设计和基础架构
    Thrift 个人实战--Thrift RPC服务框架日志的优化
    Thrift 个人实战--Thrift 服务化 Client的改造
    Kafka集群副本分配算法解析
  • 原文地址:https://www.cnblogs.com/LyShark/p/13566385.html
Copyright © 2011-2022 走看看