汇编代码段落

计算输入内容长度

00402865  |> /8A06          /mov al,byte ptr ds:[esi]                ;  计算长度(esi)
00402867  |. |46            |inc esi                                 ;esi每次+1,直到末端
00402868  |. |84C0          |test al,al   ;当两个值不同时,al遇到0了,所以会不同,也就跳出循环
0040286A  |.^\75 F9         \jnz short WannaFla.00402865

函数调用

;------------------------
;1.通过栈传递参数
;2.通过寄存器传递参数:快
;3.名称修饰约定
;
;下边的是通过栈传递参数的代码
;------------------------


;未优化的汇编代码
push par2                           ;压入参数,根据调用约定不同,会改变push的参数的先后顺序
push par1        
call test                           ;调用函数,执行call指令保存返回地址,即该处的下一条指令的地址
{
    push ebp                        ;保护现场原来的函数指针?
    mov  ebp, esp                   ;将此时的栈顶指针保存在ebp中,在之后可以通过[ebp+offset]的方式调用各个参数
    mov  eax, dword ptr [ebp+0c]    ;调用参数2,该调用约定下参数2先push,所以高地址(在下边的)加的offset更大。
    mov  ebx, dword ptr [ebp+08]    ;调用参数1,为什么差的是0xc-0x8=4?因为push的都是4字节
    sub  esp, 8                     ;因为有局部变量,局部变量压入栈,所以esp要往上,指到栈顶
    ...
    add  esp, 8                     ;与sub esp,8相对应:栈顶指针往下走,即弹出局部变量
    pop  ebp                        ;恢复现场的ebp指针?
    ret  8                          ;ret后的指=参数个数*4h 平衡栈
}

;几种局部变量分配与清除栈的形式
|           形式1            |          形式2          |          形式3           |
|sub esp, n                  |add esp,-n              |push reg                  |
|add esp, n                  |sub esp,-n              |pop  reg                  |

;优化的汇编代码, 优化选项为"Maximize Speed"
;会直接使用esp取各种参数
mov eax, dword ptr [esp+04]         ;调用参数1
mov ecx, dword ptr [esp+08]         ;调用参数2

全局变量 

由于全局变量作用于整个程序,所以跟局部变量相比,全局变量会直接被放在一块内存区里。一般放在.data块。

mov eax, dword ptr [4084C0h]         ;一个固定的硬编码地址,直接调用全局变量,位于该地址

数组

/*C语言代码*/
int main(void)
{
    static int a[3] = {0x11,0x22,0x33};
    int i,s=0,b[3];

    for(i=0;i<3;i++)
    {
        s = s + a[i];
        b[i] = s;    
    }

    for(i=0;i<3;i++)
    {
        printf("%d\n",b[i]);
    }

}

;对应的汇编代码
00401000        sub  esp, 0c        ; 给局部变量留空间
00401003        xor  ecx, ecx       ; s = 0
00401005        xor  eax, eax       ; i = 0
00401007        push esi            
00401008        push edi
00401009        /mov edi, dword ptr[eax+407030]   ;[eax+407030]-数组的起始位置 放入 edi中
0040100F        |add eax, 4                       ;数组的索引放在eax中,在最后与0xc比较,每次加4,共3:a[3]
                                                  ;eax为什么是0x4,因为数组的每个值占0x4,[eax+407030]依次取到数组值            
00401012        |add ecx, edi                     ;s=s+a[i]   
00401014        |cmp eax, 0C                      ;i?>3
00401017        |mov dword ptr [esp+eax+4], ecx   ;b[i]=s 
0040101B        \jl  short 00401009
0040101D        lea esi, dword ptr [esp+8]        
00401021        mov edi,3                         ;第二个i,计数
00401026        /mov eax, dword ptr [esi]         ;[esi]指向数组b[]
00401028        |push eax        
00401029        |push 40703C
0040102E        |call 00401050                    ;printf("%d\n",b[i])
00401033        |add  esp, 8
00401036        |add  esi, 4                      ;同上,取下一个
00401039        |dec  edi
0040103A        \jnz  short 00401026


;可知,数组寻址一般使用"基址+偏移量"的方式实现
;mov eax, [407030h + eax]

IF-THEN-ELSE

 

;if-else

;====================
cmp  a, b
jz(jnz)  xxxx
;====================
;z寄存器是是否为zero的标志为zero 则z为真 为1
;cmp指令有时会用 or test 指令代替


#include<stdio.h>
int main(void)
{
    int a,b=5;
    scanf("%d",&a);
    
    if(a==0)
        a=8;
    
    return a+b;
}


00401000    push  ecx                    ; 分配内存<=>sub esp,4
00401001    lea   eax, dword ptr [esp]   ;eax指向局部变量空间
00401005    push  eax                    
00401006    push  00407030               ;push "%d"
0040100B    call  00401030               ;调用scanf函数
00401010    mov   eax,dword ptr[esp+8]   ;输入的字符串放在[esp+8]这个地址,转出到eax
00401014    add   esp,00000008           ;因为是_cdel调用约定,所以在函数外作栈平衡操作
00401017    test  eax,eax                ;test:若eax=0,则zf置1,否则zf置0
00401019    jne   00401020               ;jne:若zf=1就不跳转,否则跳转
0040101B    mov   eax,00000008           ;a = 8
00401020    add   eax,00000005           ;a+b
00401023    pop   ecx                    ;与push ecx相对应
00401024    ret

switch-case

 

#include<stdio.h>

int main(void)
{
	int a;
	scanf("%d", &a);
	switch (a)
	{
	case 1:
		printf("a=1");
		break;
	case 2:
		printf("a=2");
		break;
	case 10:
		printf("a=10");
		break;
	default:printf("a=default");
		break;
	}
	return 0;
}



;汇编代码
00401000    push  ebp
00401001    mov   ebp, esp
00401003    sub   esp, 00000008       ;局部变量分配内存
00401006    lea   esp, [ebp-04]
00401009    push  eax
0040100A    push  00408030            ;指向字符"%d"
0040100F    call  004010A2            /;scanf("%d",&a)
00401014    add   esp,00000008        |scanf的特点,输入后在[ebp-xxx]处又开始拿出来
00401017    mov   ecx,[ebp-04]        \;将输入的结果传给ecx
0040101A    mov   [ebp-08],ecx        ;再放入[ebp-08]
0040101D    cmp   [ebp-08],01         ;case 1
00401021    je    00401031            ;跳转处会调用printf(a=1)
00401023    cmp   [ebp-08],02         ;case 2
00401027    je    00401040            ;跳转处会调用printf(a=2)            
00401029    cmp   [ebp-08],0A         ;case 10
0040102D    je    0040104F            ;同上
0040102F    jmp   0040104F            ;同上
00401031    push  00408034
00401036    call  00401071            ;各种printf
0040103B    add   esp, 00000004
0040103E    jmp   0040106B
00401040    push  00408038
00401045    call  00401071
0040104A    add   esp,00000004
0040104D    jmp   0040106B
0040104F    push  00408044
00401054    call  00401071
00401059    add   esp,00000004
0040105C    jmp   0040106B
0040105E    push  00408044
00401063    call  00401071
00401068    add   esp,00000004         ;与sub平衡
0040105C    xor   eax,eax
0040105E    mov   esp, ebp
00401063    pop   ebp
00401000    ret

其实代码来看,都是各种逻辑判断和条件跳转语句。switch何尝不是ifesle(不优化的前提下)。

但是 继续看switch的代码

switch(a)
{
    case 1:printf("a=1");break;
    case 2:printf("a=2");break;
    case 3:printf("a=2");break;
    case 4:printf("a=2");break;
    case 5:printf("a=2");break;
    case 6:printf("a=2");break;
    case 7:printf("a=2");break;
    default:printf("a=default");break;

}

;如果!取值表示一个算数级数!编译器会使用一个跳转表来实现
jmp dword ptr [4*eax+004010B0]  ;跳转表,eax的值影响取到的是具体那个

循环(包括while 和 for)

int main(void)
{
    int sum=0,i=0;
    for(i=0;i<=100;i++)
    {
        sum = sum + i;    
    }
    return 0;
}


;汇编未优化代码
00401000    push ebp                    ;保护现场
00401001    mov  ebp,esp                ;栈顶放入ebp中
00401003    sub  esp,00000008           ;局部变量的空间
00401006    mov  [ebp-04],00000000      ;sum = 0
0040100D    mov  [ebp-08],00000000      ;i = 0
00401014    jmp  0040101F               ;i<100的循环
00401016    mov  eax,dword ptr [ebp-08] ;
00401019    add  eax,00000001           ;与上条一起,构成i++
0040101C    mov  dword ptr [ebp-08],eax ;再放入[ebp-08]中,并在下条比较,看是否大于100
0040101F    cmp  dword ptr [ebp-08],64  ;如上
00401023    jg   00401030               ;如果大于100,跳到retrn 0
00401025    mov  ecx,dword ptr [ebp-04]
00401028    add  ecx,dword ptr [ebp-08] ;sum的计算
0040102B    mov  dword ptr [ebp-04],ecx 
0040102E    jmp  00401016
00401030    xor  eax,eax
00401032    mov  esp,ebp
00401034    pop  ebp
00401035    ret

;for循环相对熟悉且易看。特征:从高地址往低地址返回再次执行这些指令 有计数器
;在优化后 +1会成 inc 且寄存器用得多了

滥用返回指针(花指令)

004011C0 sub_4011C0    pro near
004011C0 
004011C0
004011C0 var_4         = byte ptr -4
004011C0               
004011C0               call  $+5             ;该指令push了该地址+5的地址,也就是4011C5,同时esp也等于该值
004011C5               add   [esp+4+var_4],5 ;可以看到var_4=-4,所以该指令简化为 add [esp],5 
                                               将esp指向栈顶加5,因此esp在4011CA,栈顶变了
004011C9               retn                  ;pop出栈顶,即值4011CA,然后jmp到他
004011C9 sub_4011C0    endp ; sp-analysis failed   ;因此这么一波花指令下来,其实就是调用4011CA处的代码,
                                                造成了ida识别错误。
                                                将这三行代码nop了,再把下边的创建函数。完成去花。
004011C9 
004011CA ; ----------------------------------------------
004011CA               push   ebp
004011CB               mov    ebp,esp
004011CD               mov    eax,[ebp+8]
004011D0               imul   eax,2Ah
004011D3               mov    esp,ebp
004011D5               pop    ebp
004011D6               retn

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值