匯編相關(guān)

  • 匯編與機器代碼一一對應(yīng),但是匯編代碼卻與高級語言不是一一對應(yīng)的。
    struct Student
    {
        int num;
        int index;
        int score;
    };
    Student student = { 1,2,3 };

00FF1878 C7 45 F0 01 00 00 00 mov         dword ptr [ebp-10h],1  
00FF187F C7 45 F4 02 00 00 00 mov         dword ptr [ebp-0Ch],2  
00FF1886 C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3


    int array[] = { 1,2,3 };
002B1878 C7 45 F0 01 00 00 00 mov         dword ptr [ebp-10h],1  
002B187F C7 45 F4 02 00 00 00 mov         dword ptr [ebp-0Ch],2  
002B1886 C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3 

上述為兩串代碼所對應(yīng)的機器代碼和匯編語言,從其中可以看出,不同的代碼可以對應(yīng)同一串機器語言和匯編代碼。

VS常用的快捷鍵

  • 調(diào)試
    F9:切換斷點
    Shift +F11 :step out
    Ctr+J: 智能提示
    Tab:直接使用智能提示內(nèi)容

Ctr+M,M:折疊或者展開當(dāng)前方法
Ctr+M,O:折疊所有方法
Ctr+M,L:展開所有方法

項目中的重訂解決方案目標可以快速重新定義項目的SDK

函數(shù)重載

  • 注意:
    返回值類型與重載無關(guān)
    調(diào)用函數(shù)重載的時候,因為實參的隱式類型轉(zhuǎn)換可能會產(chǎn)生二義性

  • 本質(zhì):
    C++采用了name mangling或者叫name decoration技術(shù),即編譯的時候,會將函數(shù)名進行轉(zhuǎn)換,從而保證重載的進行

  • 利用IDA可以分析exe文件
    release模式下,編譯器會自動對代碼進行優(yōu)化,如將簡單函數(shù)中的代碼直接放到main函數(shù)中。當(dāng)然VS的編譯器也可以進制優(yōu)化,在屬性中可以修改將其優(yōu)化禁止。

默認參數(shù)

在使用默認參數(shù)的情況下,本質(zhì)還是push了多個參數(shù),然后再進一步運行。

extern “C”

被extern "C"修飾的代碼會按照C語言的方式去編譯

//方法1
extern "C" void test() {

}
//方法2
extern "C" {
    void mytest() {

    }
}

當(dāng)同時有函數(shù)的聲明和實現(xiàn)的時候,extern "C"需要加載聲明中,實現(xiàn)就沒必要放置了。

當(dāng)CPP調(diào)用C語言編寫的庫的時候,一般會在.h文件中聲明所有的函數(shù),然后自己的CPP文件中添加以下的代碼就可以輕易地進行調(diào)用

extern "C"{
#include "test.h" 
}

當(dāng)然,也可以在庫函數(shù)的.h文件中直接添加extern代碼進行簡化。
為了保證C語言的庫函數(shù)既可以在C中進行使用,也可以在CPP中進行使用,那么在需要判斷下CPP特有的宏cplusplus

#ifndef _文件名_h
#define _文件名_h
#ifdef _cplusplus
extern "C"{
#endif
//C語言函數(shù)的聲明

#ifdef _cplusplus
}
#endif
#endif

pragma once也可以用來防止頭文件的內(nèi)容被重復(fù)聲明,起到與上面一樣的效果,區(qū)別如下

  • #ifndef#define、#endif受到C\C++標準的支持,不受編譯器的任何限制。
  • 有些編譯器對#progma once(較老編譯器不支持,如GCC3.4之前)兼容性不夠好
  • #ifndef、#define、#endif可以針對文件中的部分代碼,但#progma once只能針對整個文件

內(nèi)聯(lián)函數(shù)

使用inline修飾函數(shù)的聲明或者實現(xiàn),編譯器會直接將函數(shù)調(diào)用轉(zhuǎn)為函數(shù)代碼,這樣會增加函數(shù)的體積,但是可以減少??臻g的利用。
內(nèi)聯(lián)函數(shù)和宏都可以減少函數(shù)調(diào)用的開銷,而對比宏,內(nèi)聯(lián)函數(shù)多了語法檢測和函數(shù)特性。而且宏會導(dǎo)致很多的問題

#define A(x) (x)+(x)
inline int sum(int x){ return x+x;}
int a=10;
A(++a);

const

struct Date{
int date;
int month;
int year;
}
Date d1={1,2,3};
const Date* p=&d1;
//在這樣的情況下,p指針所指地址的對應(yīng)結(jié)構(gòu)體的成員變量是不能改變的,即以下的代碼都是錯誤的,但是p可以指向另一個地址
p->year=2020;
*p=d2;
//但因為p不是常量,所以p的值可以改變,如下
p=&d2
 

const的位置問題,關(guān)鍵在于const右邊修飾的是p還是*p

    int age = 10;
    //p1不是常量 *p1是常量
    const int * p1 = &age;
    //p2不是常量 *p2是常量
    int const  * p2 = &age;
    //p3是常量,*p3不是常量
    int * const p3 = &age;
    //都是常量
    const int * const p4 = &age;
    int const * const p5 = &age;

引用

注意點

  • 引用相當(dāng)于變量的別名
  • 對引用做計算,就是對引用所指的變量做計算
  • 在定義的時候就必須初始化,一定指定了某個對象們就不能再改變
  • 可以利用引用初始化另一個,相當(dāng)于一個變量有多個別名
  • 相比較于指針,引用更加安全

引用的本質(zhì)

引用的本質(zhì)是指針,一個引用在64操作系統(tǒng)下與指針的占用大小是一樣的,都是8字節(jié)。
而且與實際的指針相比,引用是在變量前面加了const的指針,因為引用一旦定義就不能再引用其他的變量。
如果引用聲明成常引用,那么該引用可以直接用10這樣的常量來賦值

  • const引用相關(guān)
    當(dāng)函數(shù)參數(shù)的值必須使用引用,但是希望可以將常量傳入時,函數(shù)的形參可以聲明成const int &test這樣。同時聲明成這樣的好處是調(diào)用時,實參可以是一般的變量,也可以是常引用,也可以是常量
  • const修飾的引用可以指向不同的數(shù)據(jù)類型,但是不同的數(shù)據(jù)類型在匯編層其實就是新建了一個臨時變量
int age=18;
const long &refAge=age;
age=24;
//age=24;refAge=18。相當(dāng)于refAge指向的是一個臨時的數(shù)據(jù)

匯編語言

通用寄存器
64bit
RAX、RBX、RCX、RDX
32bit
EAX、EBX、ECX、EDX
16bit
AX、BX、CX、DX
8bit
AH、AL、CH、CL、DH、DL、BH、BL
上述所有的低字節(jié)寄存器其實就是高寄存器的低端位
一般來說,R開頭的都是64bit。E開頭的是32bit

匯編要點總結(jié)

  • mov dest, src 相當(dāng)于dest=src
  • [地址值] 。中括號里面放的都是內(nèi)存地址
  • word是2字節(jié),dword是4字節(jié),qword是8字節(jié)
// 
mov         dword ptr [ebp-8],3 
  • call:函數(shù)調(diào)用

  • lea dest,[地址值]
    直接將地址值給到dest,(load effect address)

  • ret
    函數(shù)返回

  • xor op1,op2
    將op1和op2異或的值賦值給op1

  • add op1,op2

  • sub op1,op2

  • inc op

  • dec op

  • jmp 內(nèi)存地址
    跳轉(zhuǎn)到某個內(nèi)存地址去執(zhí)行代碼。一般j開頭的都是跳轉(zhuǎn),大多數(shù)是帶條件的跳轉(zhuǎn)

  • jne jump not equal
    比較結(jié)果不相等才進行跳轉(zhuǎn)

    int a = 3;
00A62028 C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3  
    int b = 4;
00A6202F C7 45 EC 04 00 00 00 mov         dword ptr [ebp-14h],4  
    if (a == b) {
00A62036 8B 45 F8             mov         eax,dword ptr [ebp-8]  
00A62039 3B 45 EC             cmp         eax,dword ptr [ebp-14h]  
00A6203C 75 0F                jne         00A6204D  
        printf("1");
00A6203E 68 CC 7B A6 00       push        0A67BCCh  
00A62043 E8 69 F3 FF FF       call        00A613B1  
00A62048 83 C4 04             add         esp,4  
    }
    else
00A6204B EB 0D                jmp         00A6205A  
    {
        printf("2");
00A6204D 68 D0 7B A6 00       push        0A67BD0h  
00A62052 E8 5A F3 FF FF       call        00A613B1  
00A62057 83 C4 04             add         esp,4  
    }
    getchar();
00A6205A 8B F4                mov         esi,esp  
00A6205C FF 15 94 B1 A6 00    call        dword ptr ds:[00A6B194h]  
00A62062 3B F4                cmp         esi,esp  
00A62064 E8 C2 F1 FF FF       call        00A6122B  
  • 引用和指針
//int *p=&a;
lea eax,[ebp-0CH]
mov dword ptr[0DH],eax
  • 注意點
    指針數(shù)組(存儲指針的數(shù)組)int * p[3]
    數(shù)組指針(指向數(shù)組的指針)int(*p)[3]

軟件破解

利用OD工具可以查看.exe文件的字節(jié)碼。其中F2可以設(shè)置斷點,然后進行軟件的調(diào)試工作

暴力破解

分析代碼中的邏輯,查找到關(guān)鍵的判斷匯編的位置,將判斷不相等的匯編改成空指令,即nop(90),將相應(yīng)的字節(jié)換成90就可以跳過關(guān)鍵的判斷,操作如下:右擊相應(yīng)的指令,然后選擇二進制,用nop來填充。修改了代碼之后右擊選擇復(fù)制到可執(zhí)行文件,然后右擊保存

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容