整理復習匯編語言的知識點,以前在學習《Intel匯編語言程序設計 - 第五版》時沒有很認真的整理筆記,主要因為當時是以學習理解為目的沒有整理的很詳細,這次是我第三次閱讀此書,每一次閱讀都會有新的收獲,這次復習,我想把書中的重點,再一次做一個歸納與總結(注:16位匯編部分跳過),並且繼續嘗試寫一些有趣的案例,這些案例中所涉及的指令都是逆向中的重點,一些不重要的我就直接省略了,一來提高自己,二來分享知識,轉載請加出處,敲代碼備注挺難受的。
這次復習的重點就是高級語言,各種語句的底層實現邏輯,我們手工的來實現一些常用的表達式,逐級遞增難度,本文中所仿寫的匯編流程,風格,參考自VS2013編譯器的Debug實現,由於不是研究編譯特性的文章,故此處不考慮編譯器對代碼實施的各種優化措施,只注重C語言代碼的匯編化。
IF/AND/OR 語句
IF中的AND語句的構造: and語句為等式兩邊只要一邊返回假,則整個等式就不需要繼續下去了,只有等式1成立的情況下才會繼續判斷等式2是否成立。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1 >= 20 and var2 <= 100 and var3 == 50)
{
printf("xor eax,eax");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
flag DWORD ?
.code
main PROC
; if(var1 >= 20 and var2 <= 100 and var3 == 50)
cmp dword ptr ds:[var1],20 ; 判斷是否大於20
jl L1 ; 不大於則跳轉
cmp dword ptr ds:[var2],100 ; 判斷是否小於100
jg L1 ; 不小於則跳轉
cmp dword ptr ds:[var3],50 ; 判斷是否等於50
jne L1 ; 不等於則跳轉
mov dword ptr ds:[flag],1 ; 說明等式成立 flag=1
jmp L2
L1: mov dword ptr ds:[flag],0
L2: cmp dword ptr ds:[flag],0
je lop_end ; 為0則跳轉,不為0則繼續執行
xor eax,eax ; 此處是執行if語句內部
xor ebx,ebx
xor ecx,ecx
jmp lop_end
lop_end:
nop ; 直接結束
invoke ExitProcess,0
main ENDP
END main
IF中OR語句的構造: OR語句的判斷則是只要等式兩邊一邊的結果返回為真,則整個表達式的后半部分直接跳過。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1 > var2 || var2 <= var3)
{
printf("xor eax,eax");
}
else if(var3 == 50 || var2 > 10)
{
printf("xor ebx,ebx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; if (var1 > var2 || var2 <= var3)
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 > var2
jg L1
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg L2 ; 條件是 var2 > var3 則跳轉
L1:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L2:
; else if(var3 == 50 || var2 > 10)
cmp dword ptr ds:[var3],50
je L3
cmp dword ptr ds:[var2],10 ; var2 > 10
jle lop_end
L3:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
nop
int 3
invoke ExitProcess,0
main ENDP
END main
IF中AND/OR混合構造:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20;
int var2 = 10;
int var3 = 50;
if ((var1 >= 10 && var2 <= 20) || (var2 == 10 && var3 >= 40))
{
printf("xor eax,eax");
}
else
{
printf("xor ebx,ebx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; if ((var1 >= 10 && var2 <= 20) && (var2 == 10 || var3 >= 40))
cmp dword ptr ds:[var1],10 ; var1 >= 10
jl L1
cmp dword ptr ds:[var2],20 ; var2 <= 20
jg L1
cmp dword ptr ds:[var2],10 ; var2 == 10
je L2
cmp dword ptr ds:[var3],40 ; var3 >= 40
jl L1
jmp L2
L1:
xor ebx,ebx ; else
jmp lop_end
L2:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF語句嵌套調用: 在編寫這樣子的嵌套語句時,應該由外到內逐層解析,這樣能更容易寫出優美的表達式。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
if (var1 >= var2)
{
if ((x<y) && (z>y))
{
printf("xor eax,eax");
}
else
{
printf("xor ebx,ebx");
}
}
else if (var2 > var3)
{
printf("xor ecx,ecx");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; if(var1 >= var2) ?
jl L1
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[y] ; if((x<y)) ?
jge L2
mov eax,dword ptr ds:[z] ; if((z>y)) ?
cmp eax,dword ptr ds:[y]
jle L2
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L1:
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jle lop_end
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
L2:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF 判斷平年閏年: 閏年時年份對400取余等於0的數,或者對4取余等於0並且對100取余不等於0的數.
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int year = 2017;
if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
{
printf("%d 閏年 \n", year);
}
{
printf("%d 平年 \n", year);
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Year DWORD 2017
szFmtR BYTE '%d 是閏年',0dh,0ah,0
szFmtP BYTE '%d 是平年',0dh,0ah,0
.code
main PROC
mov eax,dword ptr ds:[Year] ; year = 2017;
cdq
mov ecx,400
idiv ecx ; year % 400 == 0
test edx,edx
je L1
mov eax,dword ptr ds:[Year]
and eax,080000003h ; year % 4
test eax,eax
jne L2
mov eax,dword ptr ds:[Year]
cdq
mov ecx,100
idiv ecx ; year % 100 != 0
test edx,edx ; 比較余數
je L2 ; 跳轉則是平年
L1: mov eax,dword ptr ds:[Year]
invoke crt_printf,addr szFmtR,eax ; 是閏年
jmp lop_end
L2: mov eax,dword ptr ds:[Year]
invoke crt_printf,addr szFmtP,eax ; 是平年
jmp lop_end
lop_end:
int 3
main ENDP
END main
IF語句三層嵌套:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
int result = 1;
if ((var1 >= var2) && (var2 <= var3) || (var3 > var1))
{
if ((x % 2 == 0) || (y % 2 != 0))
{
if (result == 1)
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; and var1 >= var2
jl lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; and var2 <= var3
jle L1
mov eax,dword ptr ds:[var3]
cmp eax,dword ptr ds:[var1] ; or var3 > var1
jle lop_end
L1:
mov eax,dword ptr ds:[x]
and eax,080000001h ; eax = eax % 2 = 0
jns L2 ; eax = 0 則跳轉
dec eax
or eax,0fffffffeh ; eax = eax % 2 != 0
inc eax
L2:
mov eax,dword ptr ds:[result]
test eax,eax ; if(result == 1)
jne L3
jmp lop_end
L3:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF語句中的TEST: 這里有多種寫法,第一種是比較好的寫法,不需要增加太多編號,第二種是正常人的思維方式.
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int x = 100, y = 200, z = 300;
int var1 = 20,var2 = 10,var3 = 50;
int result = 1;
if (var1 >= var2 && var2 <= var3)
{
if (x == 100 || y == 200 || z == 300)
{
if (result == 1)
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 >= var2
jl lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg lop_end
mov eax,dword ptr ds:[x]
cmp eax,100 ; x == 100
jne lop_end
mov eax,dword ptr ds:[y]
cmp eax,200 ; y == 200
jne lop_end
mov eax,dword ptr ds:[z]
cmp eax,300 ; z = 300
jne lop_end
mov eax,dword ptr ds:[result]
test eax,eax ; eax = 0 ?
jz lop_end
xor eax,eax
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
以下是人的邏輯方式.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x DWORD 100
y DWORD 200
z DWORD 300
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
result DWORD 1
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 >= var2
jge L1
jmp lop_end
L1:
mov eax,dword ptr ds:[var2] ; var2 <= var3
cmp eax,dword ptr ds:[var3]
jle L2
L2:
mov eax,dword ptr ds:[x]
cmp eax,100 ; x == 100 ?
je L3
mov eax,dword ptr ds:[y] ; y == 200 ?
cmp eax,200
je L3
mov eax,dword ptr ds:[y]
cmp eax,300 ; z == 300 ?
je L3
jmp lop_end
L3:
mov eax,dword ptr ds:[result] ; result == 1 ?
test eax,eax ; eax && eax != 0
jz lop_end
xor eax,eax
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF-ELSEIF-ELSE: 多層循環從何治,看我的,給我寫。
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (var1 > 20)
printf("xor eax,eax");
else if (var2 > 10)
printf("xor ebx,ebx");
else if (var2 < var3)
printf("xor ecx,ecx");
else
printf("xor edx,edx");
return 0;
}
正常寫法
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20 ; var1 > 20
jg L1
mov eax,dword ptr ds:[var2]
cmp eax,10 ; var2 > 10
jg L2
cmp eax,dword ptr ds:[var3]
jl L3 ; var2 < var3
xor edx,edx ; printf("xor edx,edx")
jmp lop_end
L1:
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L2:
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
L3:
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
編譯器是這樣干的,我把他的思路寫一下。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20
jle L1
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
L1:
mov eax,dword ptr ds:[var2]
cmp eax,10
jle L2
xor ebx,ebx ; printf("xor ebx,ebx")
jmp lop_end
L2:
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jge L3
xor ecx,ecx ; printf("xor ecx,ecx")
jmp lop_end
L3:
xor edx,edx ; printf("xor edx,edx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
編譯器對於if-elseif-else是這樣處理的.
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1 > 20)
printf("xor eax,eax");
else if (var2 >= 20)
printf("xor ebx,ebx");
else if (var3 <= 20)
printf("xor ecx,ecx");
else
printf("xor edx,edx");
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,20 ; var1 > 20 ?
jle L1 ; 不大於則跳到L1繼續判斷
xor eax,eax
jmp lop_end ; 最后都要跳向結束
L1: mov eax,dword ptr ds:[var2]
cmp eax,20 ; var1 >= 20 ?
jl L2 ; 不大於則繼續判斷L2
xor ebx,ebx
jmp lop_end
L2: mov eax,dword ptr ds:[var3]
cmp eax,20 ; var3 <= 20 ?
jg L3 ; 大於則跳到L3
xor ecx,ecx
jmp lop_end
L3: xor edx,edx
jmp lop_end
lop_end:
xor esi,esi
invoke ExitProcess,0
main ENDP
END main
IF的前期腦殘寫法: 寫的爛,沒編譯器生成的代碼有趣,垃圾保存。
腦殘1
if(var1 > var2) and (var2 < var3)
{
xor eax,eax
}else if(var1 > var3)
{
xor ebx,ebx
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; if(var1 > var2)
jg L1
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var3] ; else if(var1 > var3)
jg L3
L1:
mov eax,dword ptr ds:[var2] ; if(var2 < var3)
cmp eax,dword ptr ds:[var3]
jl L2
L2:
xor eax,eax
jmp lop
L3:
xor ebx,ebx
jmp lop
lop:
nop
invoke ExitProcess,0
main ENDP
END main
腦殘2
if var1 == var2
{
if x > y
{
xchg x,y
}
else
{
x=10
y=20
}
}else
{
var1 = 0
var2 = 0
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
x BYTE 6
y BYTE 5
var1 DWORD 10
var2 DWORD 10
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 == var2 ?
jne L1 ; 不等於跳轉到L1
mov al,byte ptr ds:[x]
cmp al,byte ptr ds:[y] ; x > y ?
jg L2 ; 大於跳到L2
mov byte ptr ds:[x],0 ; 不大於則執行x=10 y=20
mov byte ptr ds:[y],0
jmp lop
L1:
mov dword ptr ds:[var1],0 ; var1 != var2 則執行
mov dword ptr ds:[var2],0
L2:
mov al,byte ptr ds:[x]
mov bl,byte ptr ds:[y]
xchg al,bl ; x y 數值交換
mov byte ptr ds:[x],al
mov byte ptr ds:[y],bl
jmp lop
lop:
nop
invoke ExitProcess,0
main ENDP
END main
if 雙層嵌套結構: 包含有and,or運算符的連用處理.
int var1 = 20;
int var2 = 10;
int var3 = 50;
if (var1++ > 5 && var2++ >= 10)
{
var3 = var3 + 10;
var3 << 2;
if (var3 <= 100 or var3 <= 1000)
xor eax,eax
else
xor ebx,ebx
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
inc dword ptr ds:[var1] ; var1++
mov eax,dword ptr ds:[var1]
cmp eax,5 ; var1 > 5 ?
jg L1
jmp lop_end
L1:
inc dword ptr ds:[var2] ; var2++
mov eax,dword ptr ds:[var2] ; var2 >=10 ?
cmp eax,10
jge L2
jmp lop_end
L2:
mov eax,dword ptr ds:[var3] ; 獲取 var3
add eax,10 ; var3 = var3 + 10
shl eax,2 ; var3 << 2
cmp eax,100
jle L3 ; var3 <= 100 ?
cmp eax,1000 ; eax or
jle L3 ; var3 <= 1000 ?
jmp L4 ; else
L3:
xor eax,eax
jmp lop_end
L4:
xor ebx,ebx
jmp lop_end
lop_end:
nop
invoke ExitProcess,0
main ENDP
END main
編譯器對於此類嵌套出處理結果是這樣的,由於and指令左面如果成立則繼續執行右面的判斷,如果不成立右面的直接掠過,這樣的話就比較有趣了,如下是我根據匯編代碼推測的一段片段,。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
tmp DWORD ?
flag DWORD ?
.code
main PROC
mov eax,dword ptr ds:[var1]
mov dword ptr ds:[tmp],eax ; 將var1原值備份到tmp
mov ecx,dword ptr ds:[var1]
add ecx,1 ; 遞增var1並會寫到變量中
mov dword ptr ds:[var1],ecx
cmp dword ptr ds:[tmp],5 ; 用原值與5做比較
jle L1 ; 如果 var1 < var2
mov dword ptr ds:[flag],1
jmp L2
L1: mov dword ptr ds:[flag],0 ; 判斷的是and的第一個等式
L2: cmp dword ptr ds:[flag],0
je lop_end
mov eax,dword ptr ds:[var2]
mov dword ptr ds:[tmp],eax ; 備份var2
mov ecx,dword ptr ds:[var2]
add ecx,1 ; 遞增運算++
mov dword ptr ds:[var2],ecx
cmp dword ptr ds:[tmp],10 ; 判斷 var2>=10 ?
jl L3 ; 不大於則跳到L3
mov dword dword ptr ds:[flag],1 ; 大於則標志flag=1
jmp L4
L3: mov dword ptr ds:[flag],0
L4: cmp dword ptr ds:[flag],0
je lop_end ; 不跳轉則執行內部if
mov eax,dword ptr ds:[var3]
add eax,10
mov dword ptr ds:[var3],eax ; 遞增var3
mov eax,dword ptr ds:[var3]
shl eax,2
mov dword ptr ds:[var3],eax ; var3 = var3 << 2
cmp dword ptr ds:[var3],100 ; var3 <= 100
jle L5
cmp dword ptr ds:[var3],1000 ; var3<=1000
jg L6 ; 跳轉到內層else
L5:
xor eax,eax
nop
jmp lop_end
L6:
xor ebx,ebx
nop
jmp lop_end
lop_end:
xor eax,eax
invoke ExitProcess,0
main ENDP
END main
IF中的自增自減處理: 執行自增自減運算需要找一個臨時區域來存放自增后的數據,所以首先要開辟局部空間,多數情況下開辟空間可在棧上,例如使用sub esp,12
來分配棧空間,並初始化后即可使用,最后需要將該空間恢復.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.code
main PROC
push ebp
mov ebp,esp
sub esp,12 ; 開辟 3*4 =12 的空間
lea edi,dword ptr ss:[ebp-12] ; 指向棧中基址
mov ecx,3 ; 填充次數 12/4 = 3
mov eax,0cccccccch ; 填充物
rep stosd ; 初始化開始
mov dword ptr ss:[ebp-12],1
mov dword ptr ss:[ebp-8],2 ; 給每個地址賦值
mov dword ptr ss:[ebp-4],3
mov eax,dword ptr ss:[ebp-12] ; 取第一個數據1
mov ebx,dword ptr ss:[ebp-4] ; 取第二個數據3
add eax,ebx ; 執行遞增
mov dword ptr ss:[ebp-8],eax ; 寫回棧
add esp,12 ; 平棧
mov esp,ebp
pop ebp
invoke ExitProcess,0
main ENDP
END main
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (var1++ >= 20 && ++var2 > 10)
{
printf("xor eax,eax");
}
return 0;
}
以下代碼中需要注意,當我們使用var1++
時程序是將++后的結果賦值到了棧中存放,並讓var1變量遞增,而判斷則使用的是棧中的原值,相反++var1
則是在原值上直接進行操作,將操作結果賦值給原值后在進行判斷.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
push ebp
mov ebp,esp
sub esp,8 ; 開辟 2*4 =8 的空間
lea edi,dword ptr ss:[ebp-8] ; 指向棧中基址
mov ecx,2 ; 填充次數 8/4 = 2
mov eax,0cccccccch ; 填充物
rep stosd ; 初始化開始
mov eax,dword ptr ds:[var1]
mov dword ptr ss:[ebp-8],eax ; 將var1存入臨時變量中
add eax,1
mov dword ptr ds:[var1],eax ; 將相加后的結果寫回到var1
cmp dword ptr ss:[ebp-8],20 ; 用原值與20對比
jl L1
mov dword ptr ss:[ebp-4],1 ; 局部變量存放標志=1
jmp L2
L1: mov dword ptr ss:[ebp-4],0
L2: cmp dword ptr ss:[ebp-4],0
je lop_end
mov eax,dword ptr ds:[var2] ; 繼續執行 ++var2
add eax,1
mov dword ptr ds:[var2],eax
cmp dword ptr ds:[var2],10 ; var2 > 10
jle lop_end
xor eax,eax ; printf("xor eax,eax")
lop_end:
add esp,8 ; 平棧
mov esp,ebp
pop ebp
invoke ExitProcess,0
main ENDP
END main
IF嵌套中的移位1:
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (((var1 << 2) ^ (var2 << 3)) || ((var2 << 1) ^ (var3 << 3)))
{
if ((var1 >= var2) || (var2 <= var3) && (var3 == 50))
{
printf("xor eax,eax");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; ((var1 << 2) ^ (var2 << 3))
mov eax,dword ptr ds:[var1]
shl eax,2
mov ecx,dword ptr ds:[var2]
shl ecx,3
xor eax,ecx
je L1
; ((var2 << 1) ^ (var3 << 3))
mov eax,dword ptr ds:[var2]
shl eax,1
mov eax,dword ptr ds:[var3]
shl ecx,3
xor eax,ecx
je lop_end
; (var1 >= var2)
L1: mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2]
jge L2
; (var2 <= var3)
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3]
jg lop_end
L2:
; (var3 == 50)
cmp dword ptr ds:[var3],50
jnz lop_end
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
第二種如果判斷
#include <stdio.h>
#include <windows.h>
int main(int argc,char * argv[])
{
int var1 = 20,var2 = 10,var3 = 50;
if (((var1 << 2) % 2) || (var3 >> 1) % 3)
{
if (((var1 << 2) + 10) > 50)
{
printf("xor ebx,ebx");
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
.code
main PROC
; ((var1 << 2) % 2)
mov eax,dword ptr ds:[var1]
shl eax,2
and eax,080000001h ; var1 % 2
jns L2 ; 非負數則跳轉
; (var3 >> 1) % 3 ; 為負數執行第二個表達式
L1: mov eax,dword ptr ds:[var3]
sar eax,1 ; var3 >> 1
cdq ; 擴展為8字節
mov ecx,3 ; 除以3
idiv ecx
test edx,edx ; 比較余數是否為0
je lop_end
; ((var1 << 2) + 10) > 50
L2: mov eax,dword ptr ds:[var1]
shl eax,2
add eax,10
cmp eax,50
jle lop_end
xor eax,eax ; printf("xor ebx,ebx")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
IF中的三目運算符:
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int var1 = 20, var2 = 10, var3 = 50;
if ((var1 > var2 ? 1 : 0) && (var2 <= var3 ? 1 : 0))
{
printf("xor eax,eax");
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
var1 DWORD 20
var2 DWORD 10
var3 DWORD 50
flag DWORD ?
.code
main PROC
mov eax,dword ptr ds:[var1]
cmp eax,dword ptr ds:[var2] ; var1 > var2 ?
jle L1
mov dword ptr ds:[flag],1 ; 表達式1成立
jmp L2
L1: mov dword ptr ds:[flag],0
L2: cmp dword ptr ds:[flag],0
je lop_end
mov eax,dword ptr ds:[var2]
cmp eax,dword ptr ds:[var3] ; var2 <= var3
jg L3
mov dword ptr ds:[flag],1 ; 表達式2成立
jmp L4
L3: mov dword ptr ds:[flag],0
L4: cmp dword ptr ds:[flag],0
je lop_end
xor eax,eax ; printf("xor eax,eax")
jmp lop_end
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
While /For 語句構建
While/FOr 循環框架: while循環,for循環的簡單框架,后期會逐步提高難度,最終實現一個循環鏈表結構。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 設置while初始化
S1: cmp dword ptr ds:[count],10 ; 設置最大循環數
jge loop_end ; 判斷是否循環結束
xor eax,eax ; 執行循環體
mov eax,dword ptr ds:[count] ; 取出循環條件
add eax,1 ; 遞增
mov dword ptr ds:[count],eax ; 寫回
jmp S1
loop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
再看一下他的好基友,do-while是如何構造的,相比於while,該語句是先執行在判斷,從效率上來說這個效率要高於while.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 初始化循環次數
S1: xor eax,eax ; 執行循環體
mov eax,dword ptr ds:[count] ; 取出計數器
add eax,1 ; 遞增
mov dword ptr ds:[count],eax ; 回寫
cmp dword ptr ds:[count],10 ; 與10做對比
jl S1 ; 小於則繼續循環
int 3
invoke ExitProcess,0
main ENDP
END main
最后看一個for語句的實現流程,該語句的構建方式相對於While來說略顯復雜些,效率遠不及While,反匯編后發現,編譯器是這樣構建的.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
count DWORD ?
.code
main PROC
mov dword ptr ds:[count],0 ; 設置 int x = 0;
jmp L2
L1: mov eax,dword ptr ds:[count] ; x = x++
add eax,1
mov dword ptr ds:[count],eax
L2: cmp dword ptr ds:[count],10 ; 比較 x < 10
jge lop_end
xor eax,eax ; 執行循環體
jmp L1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
在Python中for循環是for x in range(2,10)
可以指定一個范圍,我們接着嘗試構建一下.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
.data
start_count DWORD ?
end_count DWORD ?
.code
main PROC
mov dword ptr ds:[start_count],2 ; 指定開始循環編號
mov dword ptr ds:[end_count],5 ; 指定結束循環編號
mov ecx,dword ptr ds:[start_count]
L1: cmp dword ptr ds:[end_count],ecx
jle lop_end
xor eax,eax ; 循環體內部
add ecx,1 ; 每次遞增
mov dword ptr ds:[start_count],ecx
jmp L1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
While遍歷數組: 以下案例主要通過仿寫While循環結構並通過比例因子尋址,實現對一個DWORD數組的遍歷.
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };
int count = 0;
while (count < sizeof(Array) / sizeof(int))
{
printf("value = %d \n", Array[count]);
count = count + 1;
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 1,2,3,4,5,6,7,8,9,10
count DWORD ?
szFmt BYTE 'value = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; 初始化循環
mov ecx,0 ; 設置循環計數(比例因子)
S1: cmp dword ptr ds:[count],lengthof MyArray ; 與數組總長度對比
jge lop_end ; 是否結束
lea esi,dword ptr ds:[MyArray] ; 獲取數組基地址
mov ebx,dword ptr ds:[esi + ecx * 4] ; 比例因子尋址
invoke crt_printf,addr szFmt,ebx ; 調用系統crt
mov ecx,dword ptr ds:[count]
add ecx,1 ; 計次循環遞增
mov dword ptr ds:[count],ecx
jmp S1
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
For循環嘗試判斷: 這次使用For循環,首先仿寫For循環語句,然后在內部判斷指定數值是否合格,合格輸出.
#include <stdio.h>
#include <Windows.h>
int main(int argc,char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };
for (int x = 0; x < 10; x++)
{
if (Array[x] >= 50)
{
printf("out -> %d \n", Array[x]);
}
}
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
szFmt BYTE 'out -> %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count] ; 獲取循環次數,當作因子
lea esi,dword ptr ds:[MyArray] ; 取數組基地址
mov ebx,dword ptr ds:[esi + eax * 4] ; 因子尋址
cmp ebx,50
jl L2 ; 如果小於50則跳轉到下一次循環
invoke crt_printf,addr szFmt,ebx ; 調用系統crt
jmp L2
lop_end:
int 3
invoke ExitProcess,0
main ENDP
END main
繼續增加難度,求最大最小平均值的代碼,嘗試用匯編實現.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,44,56,67 };
int max_result = 0,min_result = 100,sum_result = 0,avg_result = 0;
for (int x = 0; x < 10; x++)
{
if (Array[x] >= max_result)
{
max_result = Array[x];
}
if (Array[x] <= min_result)
{
min_result = Array[x];
}
sum_result = sum_result + Array[x];
avg_result = sum_result / 10;
}
printf("max = %d min = %d sum = %d avg = %d \n", max_result,min_result,sum_result,avg_result);
system("pause");
return 0;
}
以下這段代碼,寫的有點小問題,但大體完善,先思考一下哪里的問題,后期我在發答案!
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
max_result DWORD 0
min_result DWORD 100
sum_result DWORD 0
avg_result DWORD 0
szFmt BYTE 'max = %d min = %d sum = %d avg = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count]
lea esi,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[max_result] ; Array[x] >= max_result
jl L3
mov dword ptr ds:[max_result],ebx ; max_result = Array[x];
L3:
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[min_result] ; Array[x] <= min_result
jg L4
L4:
mov ebx,dword ptr ds:[esi + eax * 4]
mov edx,dword ptr ds:[sum_result] ; sum_result + Array[x];
add ebx,edx
mov dword ptr ds:[sum_result],ebx ; sum_result
mov eax,dword ptr ds:[sum_result]
cdq
mov ecx,10
idiv ecx ; sum_result / 10;
mov dword ptr ds:[sum_result],eax ; avg_result
jmp L2
lop_end:
mov eax,dword ptr ds:[max_result]
mov ebx,dword ptr ds:[min_result]
mov ecx,dword ptr ds:[sum_result]
mov edx,dword ptr ds:[avg_result]
invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
int 3
invoke ExitProcess,0
main ENDP
END main
問題顯而易見,相信大家都看出來了,我就直接公布正確代碼了
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,44,56,67
count DWORD ?
max_result DWORD 0
min_result DWORD 100
sum_result DWORD 0
avg_result DWORD 0
szFmt BYTE 'max = %d min= %d sum= %d avg = %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int x = 0
jmp L1
L2: mov eax,dword ptr ds:[count]
add eax,1 ; x ++
mov dword ptr ds:[count],eax
L1:
cmp dword ptr ds:[count],10 ; x < 10
jge lop_end
mov eax,dword ptr ds:[count]
lea esi,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[max_result] ; Array[x] >= max_result
jl L3
mov dword ptr ds:[max_result],ebx ; max_result = Array[x];
L3:
mov ebx,dword ptr ds:[esi + eax * 4]
cmp ebx,dword ptr ds:[min_result] ; Array[x] <= min_result
jg L4
mov dword ptr ds:[min_result],ebx
L4:
mov ebx,dword ptr ds:[esi + eax * 4] ; Array[x]
add dword ptr ds:[sum_result],ebx ; sum_result = sum_result + Array[x];
mov eax,dword ptr ds:[sum_result]
cdq ; 符號擴展
mov ecx,10 ; / 10
idiv ecx ; sum_result / 10;
mov dword ptr ds:[avg_result],eax ; avg_result
jmp L2
lop_end:
mov eax,dword ptr ds:[max_result]
mov ebx,dword ptr ds:[min_result]
mov ecx,dword ptr ds:[sum_result]
mov edx,dword ptr ds:[avg_result]
invoke crt_printf,addr szFmt,eax,ebx,ecx,edx
int 3
main ENDP
END main
Do-While 與跳出循環: 要說continue與break語句的唯一區別,就在於一個是跳轉到了本次循環的結束位置,另一個則是條向了總循環結束位置.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,78,33,45,78,90,32,15,56,67 };
int index = 0;
do
{
if (Array[index] > 10 && Array[index + 1] <= 20)
{
printf("array[1] => %d array[2] => %d \n", Array[index], Array[index + 1]);
break;
}
index = index + 1;
} while (index < 10);
return 0;
}
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 56,78,33,45,78,90,32,15,56,67
count DWORD ?
szFmt BYTE 'array[1] => %d array[2] => %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[count],0 ; int index = 0;
L1:
mov eax,dword ptr ds:[count]
cmp dword ptr ds:[MyArray + eax * 4],10 ; Array[index] > 10
jle L2
mov eax,dword ptr ds:[count]
add eax,1
cmp dword ptr ds:[MyArray + eax * 4],20 ; Array[index + 1] <= 20
jg L2
mov esi,dword ptr ds:[MyArray + eax * 4 - 4] ; esi = Array[index]
mov edi,dword ptr ds:[MyArray + eax * 4] ; edi = Array[index+1]
invoke crt_printf,addr szFmt,esi,edi
jmp lop_end ; break
L2: mov eax,dword ptr ds:[count]
add eax,1 ; index = index + 1;
mov dword ptr ds:[count],eax
cmp dword ptr ds:[count],10 ; index < 10
jl L1
lop_end: ; break
int 3
main ENDP
END main
For循環多重IF判斷: 在循環中我們首先判斷兩個數組中元素是否大於0,大於則執行加法運算,然后輸出基數或偶數.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int SrcArray[10] = { 56,78,33,45,78,90,32,15,56,67 };
int DstArray[10] = { 59,77,89,23,11,45,67,88,93,27 };
int index = 0;
for (index = 0; index < 10; index++)
{
if (SrcArray[index] != 0 && DstArray[index] != 0)
{
int sum = SrcArray[index] + DstArray[index];
if (sum % 2 == 0)
printf("偶數: %d \n", sum);
else
printf("基數: %d \n", sum);
}
}
system("pause");
return 0;
}
思考了一會,花費了一些時間,但還是使用匯編完成了.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
SrcArray DWORD 56,78,33,45,78,90,32,15,56,67
DstArray DWORD 59,77,89,23,11,45,67,88,93,27
index DWORD 0
sum DWORD 0
szFmt1 BYTE '基數: %d ',0dh,0ah,0
szFmt2 BYTE '偶數: %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[index],0 ; index = 0
jmp L1
L2: mov eax,dword ptr ds:[index]
add eax,1 ; index++
mov dword ptr ds:[index],eax
L1:
cmp dword ptr ds:[index],10 ; index < 10
jge lop_end
mov eax,dword ptr ds:[index];
cmp dword ptr ds:[SrcArray + eax * 4],0
je L2 ; SrcArray[index] != 0
mov eax,dword ptr ds:[index]
cmp dword ptr ds:[DstArray + eax * 4],0 ; DstArray[index] != 0
je L2
; ------------------------------------------
; 另類加法,通過一個SrcArray定位DstArray完成加法
mov eax,dword ptr ds:[index] ; 獲取因子
lea esi,dword ptr ds:[SrcArray] ; 取數組首地址
mov ebx,dword ptr ds:[esi + eax * 4] ; 獲取 SrcArray[index]
mov ecx,dword ptr ds:[esi + eax * 4 + 40] ; 獲取 DstArray[index]
add ebx,ecx ; SrcArray[index] + DstArray[index]
mov dword ptr ds:[sum],ebx ; sum = SrcArray[index] + DstArray[index]
mov eax,dword ptr ds:[sum]
and eax,080000001h ; sum % 2 == 0
test eax,eax
jne L3
invoke crt_printf,addr szFmt2,dword ptr ds:[sum] ; 偶數輸出
jmp L2
L3:
invoke crt_printf,addr szFmt1,dword ptr ds:[sum] ; 基數輸出
jmp L2
lop_end:
int 3
main ENDP
END main
For語句嵌套(乘法口訣表): 首先我們來接觸一下For循環的嵌套實現方法,以打印99表為例,嘗試使用匯編實現.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
for (int x = 1; x < 10; x++)
{
for (int y = 1; y <= x; y++)
{
int result = x*y;
printf("%d*%d=%-3d", y, x, result);
}
printf("\n");
}
system("pause");
return 0;
}
執行雙層循環需要嵌套For語句,先來寫一個簡單的雙層For循環的匯編版.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '內層循環: %d 外層循環: %d ',0dh,0ah,0
szPr BYTE '----->',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; int x = 1
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1:
cmp dword ptr ds:[x],10 ; x < 10
jge lop_end
mov dword ptr ds:[y],1 ; y = 1
jmp L3
L5: mov eax,dword ptr ds:[y]
add eax,1 ; y++
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y <= x
jg L4
; 執行的是循環體內部
mov eax,dword ptr ds:[x]
mov ebx,dword ptr ds:[y]
invoke crt_printf,addr szFmt,eax,ebx
jmp L5
L4:
; 執行外層循環
invoke crt_printf,addr szPr
jmp L2
lop_end:
int 3
main ENDP
END main
最終實現只是相應的做一個替換即可.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '%d * %d = %d ',0
szPr BYTE ' ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; int x = 1
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1:
cmp dword ptr ds:[x],10 ; x < 10
jge lop_end
mov dword ptr ds:[y],1 ; y = 1
jmp L3
L5: mov eax,dword ptr ds:[y]
add eax,1 ; y++
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y <= x
jg L4
; 執行的是循環體內部
mov eax,dword ptr ds:[x]
imul eax,dword ptr ds:[y]
invoke crt_printf,addr szFmt,dword ptr ds:[y],dword ptr ds:[x],eax
jmp L5
L4:
; 執行外層循環
invoke crt_printf,addr szPr
jmp L2
lop_end:
int 3
main ENDP
END main
For簡單循環(水仙花數): 所謂水仙花數是指一個三位數,其各位數字立方和等於該數本身.
例如: 153是一個水仙花數,因為153=1的三次方+5的三次方+3的三次方.
分析: 利用for循環控制100-999個數,每個數分解出個位,十位,百位.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int x, y, z, n;
for (n = 100; n < 1000; n++)
{
x = n / 100;
y = n / 10 % 10;
z = n % 10;
if (x * 100 + y * 10 + z == x*x*x + y*y*y + z*z*z)
{
printf("水仙花: %-5d \n", n);
}
}
system("pause");
return 0;
}
嘗試使用匯編實現計算邏輯.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
n DWORD ?
szFmt BYTE '水仙花: %-5d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[n],100 ; n = 100
jmp L1
L2: mov eax,dword ptr ds:[n]
add eax,1 ; n++
mov dword ptr ds:[n],eax
L1: mov eax,dword ptr ds:[n]
cmp eax,1000 ; n < 1000
jge lop_end
mov eax,dword ptr ds:[n]
cdq
mov ecx,100 ; x = n / 100;
idiv ecx
mov dword ptr ds:[x],eax
mov eax,dword ptr ds:[n]
cdq
mov ecx,10
idiv ecx ; y = n / 10;
cdq
mov ecx,10
idiv ecx ; y = y % 10;
mov dword ptr ds:[y],edx
mov eax,dword ptr ds:[n]
cdq
mov ecx,10
idiv ecx ; z = n % 10;
mov dword ptr ds:[z],edx
; 開始執行if()比較語句
imul eax,dword ptr ds:[x],100 ; x * 100
imul ecx,dword ptr ds:[y],10 ; y * 10
add eax,dword ptr ds:[z] ; + z
add ecx,eax
mov edx,dword ptr ds:[x]
imul edx,dword ptr ds:[x] ; x*x*x
imul edx,dword ptr ds:[x]
mov eax,dword ptr ds:[y]
imul eax,dword ptr ds:[y] ; y*y*y
imul eax,dword ptr ds:[y]
add edx,eax
mov eax,dword ptr ds:[z]
imul eax,dword ptr ds:[z] ; z*z*z
imul eax,dword ptr ds:[z]
add edx,eax
cmp ecx,edx ; (x * 100 + y * 10 + z) == (x*x*x + y*y*y + z*z*z)
jne L2
mov eax,dword ptr ds:[n]
invoke crt_printf,addr szFmt,eax
jmp L2
lop_end:
int 3
main ENDP
END main
For語句嵌套(冒泡排序): 冒泡排序實現思路從后向前,大的數下沉小的數上移,C代碼如下,嘗試使用匯編實現.
#include <stdio.h>
#include <Windows.h>
int main(int argc, char *argv[])
{
int Array[10] = { 34,78,65,77,89,43,23,55,67,8 };
int x, y, temporary, ArraySize=10;
for (x = 0; x < ArraySize - 1; x++)
{
for (y = ArraySize - 1; y > x; y--)
{
if (Array[y - 1] > Array[y])
{
temporary = Array[y - 1];
Array[y - 1] = Array[y];
Array[y] = temporary;
}
}
}
for (int x = 0; x < 10; x++)
{
printf("%d \n", Array[x]);
system("pause");
return 0;
}
未完待續
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
.code
main PROC
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; x < ArraySize - 1
cmp dword ptr ds:[x],eax
jge lop_end
; 內層循環體內容
L4: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; y = ArraySize - 1
mov dword ptr ds:[y],eax
jmp L3
mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3:
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; y > x
jle L2
mov ecx,dword ptr ds:[y]
mov eax,dword ptr ds:[Array + ecx * 4] ; y
sub ecx,1
mov ebx,dword ptr ds:[Array + ecx * 4] ; x
xchg eax,ebx
mov dword ptr ds:[Array + ecx * 4],eax
add ecx,1
mov dword ptr ds:[Array + ecx * 4],ebx
jmp L4
jmp L2
lop_end:
int 3
main ENDP
END main
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
main PROC
; 初始化的部分
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
; 外層循環體
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; ArraySize - 1
cmp dword ptr ds:[x],eax ; x < ArraySize
jge lop_end
; 內層循環體內容
mov eax,dword ptr ds:[ArraySize]
sub eax,1
mov dword ptr ds:[y],eax
jmp L3
L4: mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3: mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x]
jle L2
mov esi,dword ptr ds:[y]
mov ebx,dword ptr ds:[Array + esi * 4] ; Array[y]
mov edx,dword ptr ds:[Array + esi * 4 - 4] ; Array[y - 1]
cmp edx,ebx
jle L4
mov dword ptr ds:[Array + esi * 4],edx
mov dword ptr ds:[Array + esi * 4 - 4],ebx
; invoke crt_printf,addr szFmt,ebx,edx
jmp L4
jmp L2
lop_end:
int 3
main ENDP
END main
排序完成
完整代碼已經寫出來了,如下所示.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Array DWORD 34,78,65,77,89,43,23,55,67,8
x DWORD ?
y DWORD ?
Temporary DWORD ?
ArraySize DWORD ?
szFmt BYTE '%d --> %d ',0dh,0ah,0
.code
main PROC
; 初始化的部分
mov dword ptr ds:[x],0 ; x=0
mov dword ptr ds:[ArraySize],10 ; ArraySize=10
; 外層循環體
jmp L1
L2: mov eax,dword ptr ds:[x]
add eax,1 ; x++
mov dword ptr ds:[x],eax
L1: mov eax,dword ptr ds:[ArraySize]
sub eax,1 ; ArraySize - 1
cmp dword ptr ds:[x],eax ; x < ArraySize
jge lop_end
; 內層循環體內容
mov eax,dword ptr ds:[ArraySize]
sub eax,1
mov dword ptr ds:[y],eax
jmp L3
L4: mov eax,dword ptr ds:[y]
sub eax,1 ; y--
mov dword ptr ds:[y],eax
L3: mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[x] ; Array[y - 1] > Array[y]
jle L2
mov esi,dword ptr ds:[y]
mov ebx,dword ptr ds:[Array + esi * 4] ; Array[y]
mov edx,dword ptr ds:[Array + esi * 4 - 4] ; Array[y - 1]
cmp edx,ebx
jle L4
mov dword ptr ds:[Array + esi * 4],edx ; Array[y] = Array[y - 1]
mov dword ptr ds:[Array + esi * 4 - 4],ebx ; Array[y - 1] = Array[y]
; invoke crt_printf,addr szFmt,ebx,edx
jmp L4
jmp L2
lop_end:
nop
; 執行打印函數
mov dword ptr ds:[Temporary],0
jmp L5
L7: mov eax,dword ptr ds:[Temporary]
add eax,1
mov dword ptr ds:[Temporary],eax
L5:
mov eax,dword ptr ds:[Temporary]
cmp eax,10
jge L6
lea esi,dword ptr ds:[Array] ; 取數組基地址
mov esi,dword ptr ds:[Array + eax * 4] ; 比例因子尋址
invoke crt_printf,addr szFmt,esi,esi
jmp L7
L6:
int 3
main ENDP
END main
先看排序部分
接着是輸出部分
While 三層嵌套: 在寫三層嵌套時,你需要注意了,在內層循環時需要清空變量,不然會導致循環一次整個程序結束掉了,就像下面的這種寫法,乍一看沒啥問題,可是一旦運行就會發現,程序每次都只運行外層一次循環就意外終止了,經過反匯編調試發現,是粗心導致沒有讓內層循環及時的置空。
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int x=1, y=1, z=1;
while (x < 5)
{
while (y < 5)
{
while (z < 5)
{
z = z + 1;
}
y = y + 1;
}
x = x + 1;
}
return 0;
}
來一個案例看看,使用三層嵌套完成案例,有1,2,3,4個數字,能組成多少個互補相同且不重復的三位數,嘗試使用匯編實現以下這個邏輯。
#include <windows.h>
#include <stdio.h>
int main(int argc,char * argv[])
{
int x=1, y=1, z=1;
while (x < 5)
{
while (y < 5)
{
while (z < 5)
{
if (x != z && x != y && y != z)
{
printf("%d,%d,%d \n", x, y, z);
}
z = z + 1;
}
z = 1;
y = y + 1;
}
y = 1;
x = x + 1;
}
return 0;
}
首先我們先來構建一個雙層循環,然后再構建三層的.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
szFmt BYTE '外層循環: %d ---> 內層循環:%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外層循環
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 內層循環
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3
; 循環過程執行
mov esi,dword ptr ds:[x]
mov edi,dword ptr ds:[y]
invoke crt_printf,addr szFmt,esi,edi
mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
接着是構建一個三層循環體,三層循環體就像漢堡一樣,前面初始化部分時面包,中間時肉,后面也是面包,放在一起,很有食欲。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
szFmt BYTE '外層循環: %d ---> 中間層循環: %d ---> 內層循環: %d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外層循環
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 中間循環
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3 ; 大於跳到最外層
; 內層循環
mov dword ptr ds:[z],1 ; z = 1
L5: mov ecx,dword ptr ds:[z]
cmp ecx,5 ; z < 5
jge L4 ; 大於跳到中間層
; 三層循環框架
mov eax,dword ptr ds:[x]
mov ebx,dword ptr ds:[y]
mov ecx,dword ptr ds:[z]
invoke crt_printf,addr szFmt,eax,ebx,ecx
mov ecx,dword ptr ds:[z]
add ecx,1 ; z = z + 1
mov dword ptr ds:[z],ecx
jmp L5
L4: mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
中間的IF語句,就是漢堡包的佐料部分,肉質絲滑,入口即化,實在是美妙至極,如下時肉質部分。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
x DWORD ?
y DWORD ?
z DWORD ?
szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[x],1 ; x = 1
; 外層循環
L1: mov ecx,dword ptr ds:[x]
cmp ecx,5 ; x < 5
jge lop_end
; 中間循環
mov dword ptr ds:[y],1 ; y = 1
L2: mov ecx,dword ptr ds:[y] ; ecx = y
cmp ecx,5 ; y < 5
jge L3 ; 大於跳到最外層
; 內層循環
mov dword ptr ds:[z],1 ; z = 1
L5: mov ecx,dword ptr ds:[z]
cmp ecx,5 ; z < 5
jge L4 ; 大於跳到中間層
; 三層循環框架
;mov eax,dword ptr ds:[x]
;mov ebx,dword ptr ds:[y]
;mov ecx,dword ptr ds:[z]
;invoke crt_printf,addr szFmt,eax,ebx,ecx
; 開始在框架中搞事情
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[z]
je L6
mov eax,dword ptr ds:[x]
cmp eax,dword ptr ds:[y]
je L6
mov eax,dword ptr ds:[y]
cmp eax,dword ptr ds:[z]
je L6
invoke crt_printf,addr szFmt,dword ptr ds:[x],dword ptr ds:[y],dword ptr ds:[z]
L6: mov ecx,dword ptr ds:[z]
add ecx,1 ; z = z + 1
mov dword ptr ds:[z],ecx
jmp L5
L4: mov ecx,dword ptr ds:[y]
add ecx,1 ; y = y + 1
mov dword ptr ds:[y],ecx
jmp L2
L3: mov ecx,dword ptr ds:[x]
add ecx,1 ; x = x + 1
mov dword ptr ds:[x],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
Switch與循環: Switch語句與IF語句類似,不同之處就在於Switch是將跳轉地址保存在數組中,需要時去數組中通過比例因子尋找到指定的內存然后,使用一條Jmp指令跳轉過去,實在美妙!
先給大家看一下,我是怎嗎保存這些地址的吧,匯編代碼如下所示,直接取出標號,放入數組中,也可以使用堆棧存儲,隨意。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MemArray DWORD ?
szFmt BYTE '%d,%d,%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[MemArray],offset lop_end
mov dword ptr ds:[MemArray+4],offset L2
lop_end:
int 3
L2:
xor eax,eax
xor ebx,ebx
main ENDP
END main
嘗試構建Switch語句。
#include <windows.h>
#include <stdio.h>
int main(int argc, char * argv[])
{
int x = 0;
while (x < 6)
{
switch (x)
{
case 0:
printf("1"); break;
case 1:
printf("2"); break;
case 2:
printf("3"); break;
case 3:
printf("4"); break;
case 4:
printf("5"); break;
default:
printf("0"); break;
}
x = x + 1;
}
return 0;
}
理解了Switch的查表法,然后再配合匯編中的語法規范就可以巧妙地構造出Switch結構.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MemArray DWORD 0,0,0,0,0,0,0,0,0,0
Count DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
; 將指定標號的地址取出,並復制到數組
mov dword ptr ds:[MemArray],offset S0
mov dword ptr ds:[MemArray + 4],offset S1
mov dword ptr ds:[MemArray + 8],offset S2
mov dword ptr ds:[MemArray + 12],offset S3
mov dword ptr ds:[MemArray + 16],offset S4
mov dword ptr ds:[MemArray + 20],offset S_END
mov dword ptr ds:[Count],0
L1: mov ecx,dword ptr ds:[Count]
cmp ecx,6
jg lop_end
; 通過循環次數尋找指令地址並跳轉
mov ecx,dword ptr ds:[Count]
cmp ecx,6
jg S_END
jmp dword ptr ds:[MemArray + ecx * 4]
S0: mov eax,1
invoke crt_printf,addr szFmt,eax
jmp L2
S1: mov eax,2
invoke crt_printf,addr szFmt,eax
jmp L2
S2: mov eax,3
invoke crt_printf,addr szFmt,eax
jmp L2
S3: mov eax,4
invoke crt_printf,addr szFmt,eax
jmp L2
S4: mov eax,5
invoke crt_printf,addr szFmt,eax
jmp L2
S_END: mov eax,0
invoke crt_printf,addr szFmt,eax
jmp L2
L2: mov ecx,dword ptr ds:[Count]
add ecx,1 ; ecx ++
mov dword ptr ds:[Count],ecx
jmp L1
lop_end:
int 3
main ENDP
END main
Loop實現排序: 如果不自己構建排序循環,使用loop實現,則冒泡排序將變得香。
先來看一個匯編案例,我想說,觀察下面的代碼,你說 這是不是一個死循環呢?思考一下。
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
Count DWORD 10
.code
main PROC
mov ecx,dword ptr ds:[Count]
dec ecx
L1: push ecx
invoke crt_printf,addr szFmt,ecx
pop ecx
loop L1
main ENDP
END main
不是,loop人家執行的時候,會自動的將ecx中的值減去1,所以他不是死循環,來實現一下這個需求。
#include <windows.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
int Array[10] = { 56,88,34,67,98,25,67,10,87,43 };
int x=10, y, temporary;
while (x - 1 > 0)
{
y = x;
while (y > 0)
{
if (Array[y] > Array[y - 1])
{
temporary = Array[y - 1];
Array[y - 1] = Array[y];
Array[y] = temporary;
}
y--;
}
x--;
}
for (int x = 0; x < 10; x++)
printf("%d ", Array[x]);
return 0;
}
然后使用loop實現雙層夾心漢堡,口感同樣一級棒.
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 25,74,89,33,24,67,93,15,78,92
Count DWORD 10
PrCount DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
; 數組排序
mov ecx,dword ptr ds:[Count] ; 獲取到數組元素數
dec ecx ; 數組減1
L1: push ecx ; 入棧保存
lea esi,dword ptr ds:[MyArray] ; 得到數組基地址
L2: mov eax,dword ptr ds:[esi]
cmp eax,dword ptr ds:[esi + 4] ; 比較第一個數組與第二個
jg L3
xchg eax,[esi + 4] ; 交換數據
mov [esi],eax
L3: add esi,4
loop L2
pop ecx ; 彈出數據
loop L1
; for循環輸出元素
mov dword ptr ds:[PrCount],0
jmp LL1
mov ecx,dword ptr ds:[PrCount]
add ecx,1
mov dword ptr ds:[PrCount],ecx
LL1:
mov ecx,dword ptr ds:[PrCount]
cmp ecx,10
jg lop_end
lea eax,dword ptr ds:[MyArray]
mov ebx,dword ptr ds:[eax + ecx * 4]
invoke crt_printf,addr szFmt,ebx
mov ecx,dword ptr ds:[PrCount]
add ecx,1
mov dword ptr ds:[PrCount],ecx
jmp LL1
lop_end:
int 3
main ENDP
END main
While 實現(二分法): 二分查找法也是常用查找結構,主要思想是對半分,如果中位數大於則說明元素在前半部分,如果小於則說明在后半部分,該排序唯一需要注意的是,數組必須是一個有序集合.
#include <windows.h>
#include <stdio.h>
int BinSearch(int value[], const int searchVal, int Count)
{
int first = 0;
int last = Count - 1;
while (first <= last)
{
int mid = (last + first) / 2; // 取中位數
if (value[mid] < searchVal) // 中位數小於searchVal
{ // 說明元素在后面
first = mid + 1;
}
else if (value[mid] > searchVal)
{ // 否則說明元素在前
last = mid - 1;
}
else
{ // 找到后返回中位數
return mid;
}
}
return -1;
}
int main(int argc, char *argv[])
{
// 二分查找法,必須針對的是有序數組
int Array[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 查找數組Array中索引7所在的下標
int ret = BinSearch(Array, 7, 10);
printf("數組下標: %d \n", ret);
system("pause");
return 0;
}
接着是嘗試使用匯編實現這個查找邏輯,完整版代碼已經寫好了
.386p
.model flat,stdcall
option casemap:none
include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib
.data
MyArray DWORD 1,2,3,4,5,6,7,8,9,10
SearchVal DWORD 7
Count DWORD 10
first DWORD ?
last DWORD ?
mid DWORD ?
szFmt BYTE '%d ',0dh,0ah,0
.code
main PROC
mov dword ptr ds:[first],0 ; first = 0;
mov edi,dword ptr ds:[SearchVal] ; 得到要查找的數
lea ebx,dword ptr ds:[MyArray] ; 得到數組基地址
; int last = Count - 1;
mov eax,dword ptr ds:[Count]
sub eax,1
mov dword ptr ds:[last],eax
; while(first <=last)
L1: mov ecx,dword ptr ds:[first]
cmp ecx,dword ptr ds:[last]
jg lop_end
; int mid = (last + first) / 2;
mov eax,dword ptr ds:[last]
add eax,dword ptr ds:[first]
shr eax,1
mov dword ptr ds:[mid],eax
; edx = value[mid]
mov esi,dword ptr ds:[mid]
shl esi,2
mov edx,[ebx + esi]
;invoke crt_printf,addr szFmt,edx
; if(edx < SearchVal(edi))
cmp edx,edi
jge L2
; first = mid + 1;
mov eax,dword ptr ds:[mid]
add eax,1
mov dword ptr ds:[first],eax
jmp L1
L2:
; else if (value[mid] > searchVal)
cmp edx,edi
jle L3
; last = mid - 1;
mov eax,dword ptr ds:[mid]
sub eax,1
mov dword ptr ds:[last],eax
jmp L1
L3: ; else
mov eax,dword ptr ds:[mid]
invoke crt_printf,addr szFmt,eax
jmp lop_end
jmp L1
lop_end:
mov eax,-1
int 3
main ENDP
END main