if结构
- C语言源码
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char** argv){
int a, b, c;
scanf("%d%d", &a, &b);
if(a > b){
c = a - b;
}
else {
c = b - a;
}
printf("%d", c);
return 0;
}
定位主函数,得到main函数的汇编代码
00401500 55 push ebp
00401501 89E5 mov ebp,esp
00401503 83E4 F0 and esp,-0x10 ; 使栈地址 16 字节对齐
00401506 83EC 20 sub esp,0x20 ; 开辟0x20字节的栈空间
00401509 E8 C2090000 call IF_struc.00401ED0
0040150E 8D4424 14 lea eax,dword ptr ss:[esp+0x14] ; 变量b的地址
00401512 894424 08 mov dword ptr ss:[esp+0x8],eax ; 第三个参数
00401516 8D4424 18 lea eax,dword ptr ss:[esp+0x18] ; 变量a的地址
0040151A 894424 04 mov dword ptr ss:[esp+0x4],eax ; 第二个参数
0040151E C70424 00404000 mov dword ptr ss:[esp],IF_struc.00404000 ; %d%d,第一个参数
00401525 E8 1E110000 call <jmp.&msvcrt.scanf>
0040152A 8B5424 18 mov edx,dword ptr ss:[esp+0x18] ; 变量a的值
0040152E 8B4424 14 mov eax,dword ptr ss:[esp+0x14] ; 变量b的值
00401532 39C2 cmp edx,eax
00401534 7E 12 jle short IF_struc.00401548 ; edx<=eax,即a<=b则跳转
00401536 8B5424 18 mov edx,dword ptr ss:[esp+0x18]
0040153A 8B4424 14 mov eax,dword ptr ss:[esp+0x14]
0040153E 29C2 sub edx,eax
00401540 89D0 mov eax,edx
00401542 894424 1C mov dword ptr ss:[esp+0x1C],eax
00401546 EB 10 jmp short IF_struc.00401558 ; 跳过else
00401548 8B5424 14 mov edx,dword ptr ss:[esp+0x14]
0040154C 8B4424 18 mov eax,dword ptr ss:[esp+0x18]
00401550 29C2 sub edx,eax
00401552 89D0 mov eax,edx
00401554 894424 1C mov dword ptr ss:[esp+0x1C],eax
00401558 8B4424 1C mov eax,dword ptr ss:[esp+0x1C]
0040155C 894424 04 mov dword ptr ss:[esp+0x4],eax
00401560 C70424 05404000 mov dword ptr ss:[esp],IF_struc.00404005 ; %d
00401567 E8 E4100000 call <jmp.&msvcrt.printf>
0040156C B8 00000000 mov eax,0x0
00401571 C9 leave
00401572 C3 retn
通过一个cmp、jle或者其他跳转语句和一个jmp来实现if结构;
jmp语句用来跳过else结构,直接执行if-else结构后的语句;
cmp用来比较是否满足if条件,满足则通过跳转语句跳过if,进而直接执行else结构;
for结构
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char** argv){
int n, sum=0;
scanf("%d", &n);
for(int i=1; i<=n; i++){
sum += i;
}
printf("%d", sum);
return 0;
}
定位到main函数
00401500 55 push ebp ; 开辟一个新的栈空间
00401501 89E5 mov ebp,esp
00401503 83E4 F0 and esp,-0x10 ; 使栈地址16字节对齐
00401506 83EC 20 sub esp,0x20 ; 开辟0x20字节大小的栈空间
00401509 E8 B2090000 call FOR_stru.00401EC0
0040150E C74424 1C 00000>mov dword ptr ss:[esp+0x1C],0x0 ; 变量sum的初始化赋值
00401516 8D4424 14 lea eax,dword ptr ss:[esp+0x14] ; 变量n的地址
0040151A 894424 04 mov dword ptr ss:[esp+0x4],eax ; 变量n,第二个参数
0040151E C70424 00404000 mov dword ptr ss:[esp],FOR_stru.00404000 ; %d,第一个参数
00401525 E8 0E110000 call <jmp.&msvcrt.scanf>
0040152A C74424 18 01000>mov dword ptr ss:[esp+0x18],0x1 ; 变量i初始化为1
00401532 EB 0D jmp short FOR_stru.00401541 ; 跳转到for循环的结束判断
00401534 8B4424 18 mov eax,dword ptr ss:[esp+0x18] ; 取出i的值放到eax
00401538 014424 1C add dword ptr ss:[esp+0x1C],eax ; sum = sum + i
0040153C 834424 18 01 add dword ptr ss:[esp+0x18],0x1 ; i++
00401541 8B4424 14 mov eax,dword ptr ss:[esp+0x14] ; for循环结束判断
00401545 394424 18 cmp dword ptr ss:[esp+0x18],eax ; 判断循环是否结束
00401549 ^ 7E E9 jle short FOR_stru.00401534 ; i<=n,则返回循环体内容继续执行
0040154B 8B4424 1C mov eax,dword ptr ss:[esp+0x1C] ; 取出sum值
0040154F 894424 04 mov dword ptr ss:[esp+0x4],eax
00401553 C70424 00404000 mov dword ptr ss:[esp],FOR_stru.00404000 ; %d
0040155A E8 E1100000 call <jmp.&msvcrt.printf> ; 输出sum值
0040155F B8 00000000 mov eax,0x0
00401564 C9 leave
00401565 C3 retn
对于for结构,汇编代码首先的for中的序号进行初始化
随之直接跳转到结束判断结构,判断for循环是否结束;
若未结束,则跳转回到for循环内容进行执行;
然后在此来到循环体结束判断,判断循环体是否结束;结束则不执行跳转语句jle跳回循环结构内容中,直接跳出循环,执行后续内容;
进而循环反复执行for循环体的内容
for结构也是通过一个jmp语句、跳转语句和一个判断结构完成的;
while结构
#include<stdio.h>
#include<stdlib.h>
void judge(int standard, int* end, int num){
if(standard == num){
*end = 0;
printf("恭喜你猜中了,数字以666的形式送给你!!");
}
else{
printf("猜错了呢!!真可惜!!!\n");
}
}
int main(int argc, char** argv){
int a = 30;
int end = 1;
int guessnum;
while(end){
printf("请输入你猜测的数字: ");
scanf("%d", &guessnum);
judge(a, &end, guessnum);
}
return 0;
}
00401533 55 push ebp
00401534 89E5 mov ebp,esp
00401536 83E4 F0 and esp,-0x10
00401539 83EC 20 sub esp,0x20
0040153C E8 AF090000 call WHILE_st.00401EF0
00401541 C74424 1C 1E000>mov dword ptr ss:[esp+0x1C],0x1E ; 第一个变量初始化为30
00401549 C74424 18 01000>mov dword ptr ss:[esp+0x18],0x1 ; 第二个变量初始化为1
00401551 EB 3C jmp short WHILE_st.0040158F
00401553 C70424 41404000 mov dword ptr ss:[esp],WHILE_st.00404041 ; 请输入你猜测的数字:
0040155A E8 09110000 call <jmp.&msvcrt.printf> ; 输出函数,这种输出地址值,一般为输出提示符
0040155F 8D4424 14 lea eax,dword ptr ss:[esp+0x14] ; 取出第三个变量的地址
00401563 894424 04 mov dword ptr ss:[esp+0x4],eax ; 作为scanf函数的第二个参数
00401567 C70424 56404000 mov dword ptr ss:[esp],WHILE_st.00404056 ; %d
0040156E E8 05110000 call <jmp.&msvcrt.scanf>
00401573 8B4424 14 mov eax,dword ptr ss:[esp+0x14] ; 第三个变量值取出放到eax
00401577 894424 08 mov dword ptr ss:[esp+0x8],eax ; 第三个变量是第三个参数
0040157B 8D4424 18 lea eax,dword ptr ss:[esp+0x18]
0040157F 894424 04 mov dword ptr ss:[esp+0x4],eax ; 第二个变量是第二个参数
00401583 8B4424 1C mov eax,dword ptr ss:[esp+0x1C]
00401587 890424 mov dword ptr ss:[esp],eax ; 第一个变量是第一个参数
0040158A E8 71FFFFFF call WHILE_st.00401500 ; 这个函数需要三个参数
0040158F 8B4424 18 mov eax,dword ptr ss:[esp+0x18]
00401593 85C0 test eax,eax ; 判断第二个变量是否为0
00401595 ^ 75 BC jnz short WHILE_st.00401553 ; 第二个变量作为结束循环的标准
00401597 B8 00000000 mov eax,0x0
0040159C C9 leave
0040159D C3 retn
while结构和for结构的区别就在于是否有初始化值的过程
当然while结构也同样可以转换成for结构,for结构也可以用while结构来表示
这两个结构都是用某一个变量作为循环结束评判的标准,但是在for结构中比较明显的就是在循环体的末尾,也就是在判断标准之前通常会有一个标准++的操作,也就是常见的i++操作
这里有点调皮了,还加了个函数调用的结构,也可以简单看看把,反正不太影响观察while结构;
do-while结构
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char** argv){
int a;
scanf("%d", &a);
do{
a--;
}while(a>1);
printf("%d", a);
return 0;
}
00401500 55 push ebp
00401501 89E5 mov ebp,esp
00401503 83E4 F0 and esp,-0x10
00401506 83EC 20 sub esp,0x20
00401509 E8 A2090000 call DO-WHILE.00401EB0
0040150E 8D4424 1C lea eax,dword ptr ss:[esp+0x1C]
00401512 894424 04 mov dword ptr ss:[esp+0x4],eax
00401516 C70424 00404000 mov dword ptr ss:[esp],DO-WHILE.00404000 ; %d
0040151D E8 06110000 call <jmp.&msvcrt.scanf>
00401522 8B4424 1C mov eax,dword ptr ss:[esp+0x1C]
00401526 83E8 01 sub eax,0x1
00401529 894424 1C mov dword ptr ss:[esp+0x1C],eax
0040152D 8B4424 1C mov eax,dword ptr ss:[esp+0x1C]
00401531 83F8 01 cmp eax,0x1
00401534 ^ 7F EC jg short DO-WHILE.00401522 ; a > b则跳转
00401536 8B4424 1C mov eax,dword ptr ss:[esp+0x1C] ; %d
0040153A 894424 04 mov dword ptr ss:[esp+0x4],eax
0040153E C70424 00404000 mov dword ptr ss:[esp],DO-WHILE.00404000 ; ASCII "%d"
00401545 E8 E6100000 call <jmp.&msvcrt.printf>
0040154A B8 00000000 mov eax,0x0
0040154F C9 leave
00401550 C3 retn
这个结构其实和while与for结构的区别就是是否一开始就先进行一次循环结束判断;
在do-while结构中是先执行一次循环体的内容,在进行循环结束的判断,这就是do-while结构与while和for的不同之处
也就是到时候由汇编转成高级语言的辨认之处;
还有一个就是jg跳转的条件是a>b,但是有时候用test作为判断条件,当test成立时跳转,即test判断的寄存器为0时;
switch结构
#include<stdio.h>
#include<stdlib.h>
int main(int argv, char** argc){
int sel;
scanf("%d", &sel);
switch(sel){
case 1:
printf("select the first one!");
break;
case 2:
printf("select the second!");
break;
case 3:
printf("select the third!");
break;
default:
printf("error selection!");
break;
}
return 0;
}
00401500 55 push ebp
00401501 89E5 mov ebp,esp
00401503 83E4 F0 and esp,-0x10
00401506 83EC 20 sub esp,0x20
00401509 E8 C2090000 call SWITCH_s.00401ED0
0040150E 8D4424 1C lea eax,dword ptr ss:[esp+0x1C]
00401512 894424 04 mov dword ptr ss:[esp+0x4],eax
00401516 C70424 00404000 mov dword ptr ss:[esp],SWITCH_s.00404000 ; %d
0040151D E8 26110000 call <jmp.&msvcrt.scanf>
00401522 8B4424 1C mov eax,dword ptr ss:[esp+0x1C]
00401526 83F8 02 cmp eax,0x2
00401529 74 18 je short SWITCH_s.00401543
0040152B 83F8 03 cmp eax,0x3
0040152E 74 21 je short SWITCH_s.00401551
00401530 83F8 01 cmp eax,0x1
00401533 75 2A jnz short SWITCH_s.0040155F
00401535 C70424 03404000 mov dword ptr ss:[esp],SWITCH_s.00404003 ; select the first one!
0040153C E8 0F110000 call <jmp.&msvcrt.printf>
00401541 EB 29 jmp short SWITCH_s.0040156C
00401543 C70424 19404000 mov dword ptr ss:[esp],SWITCH_s.00404019 ; select the second!
0040154A E8 01110000 call <jmp.&msvcrt.printf>
0040154F EB 1B jmp short SWITCH_s.0040156C
00401551 C70424 2C404000 mov dword ptr ss:[esp],SWITCH_s.0040402C ; select the third!
00401558 E8 F3100000 call <jmp.&msvcrt.printf>
0040155D EB 0D jmp short SWITCH_s.0040156C
0040155F C70424 3E404000 mov dword ptr ss:[esp],SWITCH_s.0040403E ; error selection!
00401566 E8 E5100000 call <jmp.&msvcrt.printf>
0040156B 90 nop
0040156C B8 00000000 mov eax,0x0
00401571 C9 leave
00401572 C3 retn
通过观察汇编代码可以知道,switch结构和其他结构的不同之处在于存在非常多的cmp和jmp以及条件跳转语句,这也是辨认出是switch结构的标志
当然了,这些switch结构也可以用if结构来表示,但是由于跳转指令jmp的存在,switch结构就变得相当明显了。
所以用高级语言重写的时候,直接根据cmp的比较进行选择即可;
struct结构
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student
{
long num;
char name[20];
char sex;
float score;
};
int main()
{
struct student stu_1;
struct student *p; //定义一个相同类型的指针,*同类型*
p=&stu_1;
stu_1.num=89101;
strcpy(stu_1.name, "Li Lin&");
stu_1.sex='M';
stu_1.score=89.5;
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n", stu_1.num, stu_1.name, stu_1.sex, stu_1.score);
printf("NO. :%ld\nname: %s\nsex:%c\nscore:%f\n", (*p).num, (*p).name, (*p).sex, (*p).score);
//对于指针其实可以换个方式
//(*p).score 等价于 p->score
return 0;
}
00401500 55 push ebp
00401501 89E5 mov ebp,esp
00401503 83E4 F0 and esp,-0x10 ; 使栈地址 16 字节对齐
00401506 83EC 50 sub esp,0x50 ; 开辟一个大小为0x50的栈空间
00401509 E8 020A0000 call STRUCT_s.00401F10
0040150E 8D4424 2C lea eax,dword ptr ss:[esp+0x2C] ; 取出一个地址
00401512 894424 4C mov dword ptr ss:[esp+0x4C],eax ; 将该地址放到最后一个变量f中
00401516 C74424 2C 0D5C0>mov dword ptr ss:[esp+0x2C],0x15C0D ; 将该地址第五个变量的第一个值修改为0x15c0d(89101)
0040151E 8D4424 2C lea eax,dword ptr ss:[esp+0x2C]
00401522 83C0 04 add eax,0x4
00401525 C700 4C69204C mov dword ptr ds:[eax],0x4C20694C ; 对[esp+0x30]开始定义字符串“Li Lin&”
0040152B C740 04 696E260>mov dword ptr ds:[eax+0x4],0x266E69
00401532 C64424 44 4D mov byte ptr ss:[esp+0x44],0x4D ; char类型变量赋值为0x4D(M)
00401537 A1 24404000 mov eax,dword ptr ds:[0x404024]
0040153C 894424 48 mov dword ptr ss:[esp+0x48],eax
00401540 D94424 48 fld dword ptr ss:[esp+0x48] ; 加载浮点数到堆栈中
00401544 0FB64424 44 movzx eax,byte ptr ss:[esp+0x44]
00401549 0FBED0 movsx edx,al
0040154C 8B4424 2C mov eax,dword ptr ss:[esp+0x2C]
00401550 DD5C24 10 fstp qword ptr ss:[esp+0x10] ; 放入浮点型参数
00401554 895424 0C mov dword ptr ss:[esp+0xC],edx ; 放入参数“M”
00401558 8D5424 2C lea edx,dword ptr ss:[esp+0x2C]
0040155C 83C2 04 add edx,0x4
0040155F 895424 08 mov dword ptr ss:[esp+0x8],edx ; 放入字符串地址
00401563 894424 04 mov dword ptr ss:[esp+0x4],eax ; 放入序号参数
00401567 C70424 00404000 mov dword ptr ss:[esp],STRUCT_s.00404000 ; NO. :%ld\nname: %s\nsex:%c\nscore:%f\n
0040156E E8 15110000 call <jmp.&msvcrt.printf>
00401573 8B4424 4C mov eax,dword ptr ss:[esp+0x4C]
00401577 D940 1C fld dword ptr ds:[eax+0x1C]
0040157A 8B4424 4C mov eax,dword ptr ss:[esp+0x4C]
0040157E 0FB640 18 movzx eax,byte ptr ds:[eax+0x18]
00401582 0FBED0 movsx edx,al
00401585 8B4424 4C mov eax,dword ptr ss:[esp+0x4C]
00401589 8D48 04 lea ecx,dword ptr ds:[eax+0x4]
0040158C 8B4424 4C mov eax,dword ptr ss:[esp+0x4C]
00401590 8B00 mov eax,dword ptr ds:[eax]
00401592 DD5C24 10 fstp qword ptr ss:[esp+0x10]
00401596 895424 0C mov dword ptr ss:[esp+0xC],edx
0040159A 894C24 08 mov dword ptr ss:[esp+0x8],ecx
0040159E 894424 04 mov dword ptr ss:[esp+0x4],eax
004015A2 C70424 00404000 mov dword ptr ss:[esp],STRUCT_s.00404000 ; NO. :%ld\nname: %s\nsex:%c\nscore:%f\n
004015A9 E8 DA100000 call <jmp.&msvcrt.printf>
004015AE B8 00000000 mov eax,0x0
004015B3 C9 leave
004015B4 C3 retn
这里主要有新的汇编指令fld和fstp,对应了压栈和出栈指令,只不过这两个指令是针对于浮点数的压栈和出栈指令。
主要用于调用函数的时候需要压入浮点数的参数。
结构体的所有使用的空间都是在栈中开辟;
Array结构
#include<stdio.h>
int main()
{
int array_1[2],array_2[2],array_3[2];
array_1={3,4};
array_2={8,9};
//array_3={21,45};
int i;
for(i=0;i<2;i++)
{
array_3[i]=array_1[i]*array_2[i];
Printf(“array_3[%d]=%d\n”,i,array_3[i]);
}
return 0;
}
PUSH EBP
MOV EBP,ESP
SUB ESP,5C
PUSH EBX
PUSH ESI
PUSH EDI
LEA EDI,DWORD PTR SS:[EBP-5C] ;栈顶位置地址
MOV ECX,17
MOV EAX,CCCCCCCC
REP STOS DWORD PTR ES:[EDI] ;对栈中空间复制中断指令
MOV DWORD PTR SS:[EBP-8],3 ; int array_1[2]={3,4};
MOV DWORD PTR SS:[EBP-4],4
MOV DWORD PTR SS:[EBP-10],8 ; int array_2[2]={8,9};
MOV DWORD PTR SS:[EBP-C],9
MOV DWORD PTR SS:[EBP-1C],0 ; for(i=0;i<2;i++),进行i的初始化复制
JMP SHORT array1.00401056
MOV EAX,DWORD PTR SS:[EBP-1C] ;第7个局部参数i
ADD EAX,1
MOV DWORD PTR SS:[EBP-1C],EAX
CMP DWORD PTR SS:[EBP-1C],2
JGE SHORT array1.0040108D
MOV ECX,DWORD PTR SS:[EBP-1C] ; array1的索引
MOV EDX,DWORD PTR SS:[EBP-1C] ; array2的索引
MOV EAX,DWORD PTR SS:[EBP+ECX*4-8] ;数组array_1,ecx不会大于2的
IMUL EAX,DWORD PTR SS:[EBP+EDX*4-10] ;乘法指令,EAX = EAX * DWORD PTR SS:[EBP+EDX*4-10],edx也不会大于2
MOV ECX,DWORD PTR SS:[EBP-1C] ; array3[2],将两数组相乘的结果放到array3[i]中
MOV DWORD PTR SS:[EBP+ECX*4-18],EAX
MOV EDX,DWORD PTR SS:[EBP-1C] ; printf("array_3[%d]=%d\n",i,array_3[i]);
MOV EAX,DWORD PTR SS:[EBP+EDX*4-18]
PUSH EAX
MOV ECX,DWORD PTR SS:[EBP-1C]
PUSH ECX
PUSH OFFSET array1.??_C@_0P@BAJC@array_>
CALL array1.printf
ADD ESP,0C
JMP SHORT array1.0040104D ; }
XOR EAX,EAX ; return 0;
POP EDI ; }
POP ESI
POP EBX
ADD ESP,5C
CMP EBP,ESP
CALL array1.__chkesp
Array结构其实比较难分辨,本次的汇编代码是由win7操作系统编译,然后反编译形成的;
比较容易辨认数组结构的是[EBP+ECX*4-8],其中ECX是数组的索引,EBP+ECX*4-8这个值当然是不会覆盖到下一个变量的地址;