---
# System prepended metadata

title: 2020 程安 Reverse筆記1 (逆向的各種trick)
tags: [程式安全]

---

# 2020 程安 Reverse筆記1 (逆向的各種trick)
###### tags: `程式安全`

## Compiler Walk Through
1. hello.c
```c=
#include <stdio.h>
#define A 'hello'

int main(){
    puts(A);
    return 0;
}
```

2. Preprocecssor把`define`展開
`gcc -E -o hello.i hello.c`
preprocessor會
```c=
//cat hello.i

...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 858 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 873 "/usr/include/stdio.h" 3 4

# 2 "hello.c" 2

# 4 "hello.c"
int main(){
 puts("hello");
 return 0;
}
```

3. Compiler將code編譯成assembly
`gcc -S hello.s hello.i`

```asm=
        .file   "hello.c"
        .text
        .section        .rodata
.LC0:
        .string "hello"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rdi
        movl    $0, %eax
        call    put@PLT
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:        .file   "hello.c"
        .text
        .section        .rodata
.LC0:
        .string "hello"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        endbr64
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        leaq    .LC0(%rip), %rdi
        call    puts@PLT
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:
        .long    0x3
3:
        .align 8
4:
```

4. Assembler組譯器將assembly轉成machine code
`gcc -c -o hello.o hello.s`
此時可以用`objdump`dump出來,但還無法執行,因為reloc的部分還沒有處理
ex.第11行應該是`call puts@plt`,但因為library還沒link進來,所以會直接跳到下一行
```asm=
hello.o:     file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
   0:   f3 0f 1e fa             endbr64
   4:   55                      push   rbp
   5:   48 89 e5                mov    rbp,rsp
   8:   48 8d 3d 00 00 00 00    lea    rdi,[rip+0x0]        # f <main+0xf>
   f:   e8 00 00 00 00          call   14 <main+0x14>
  14:   b8 00 00 00 00          mov    eax,0x0
  19:   5d                      pop    rbp
  1a:   c3
```

5. Linker負責link library function
`gcc -o hello hello.o`
這時才會變成真正可執行的binary

## Calling Convention
### cdecl (C預設的呼叫慣例,Linux全部都是這種) : caller清空stack
- 由<font color='red'>caller</font>清空stack
    - ex. `main`執行`myadd(1,2)`
        ```asm
        main:
            ...
            push 2
            push 1
            call myadd
            add esp, 0x8 ; 由main(caller)清空stack
            ...          ; 0x8 = 2個參數
        ``` 
- 優: 實作不定長度參數的function很方便(ex `printf`等format系列的)
- 缺: 浪費code空間,因為不用每次呼叫function後都要加上`add rsp,...`,這個指令佔3byte
### stdcall (Win32 API) : callee清空stack
- 聲明語法`int __stdcall function(arg,...)`
    - 如何用gcc編譯stdcall: 
        `int __attribute__((stdcall))function(arg,...)`
- 由<font color='red'>callee</font>清空stack
    - ex. `main`執行`myadd(1,2)`
        ```asm
        main:
            ...
            push 2
            push 1
            call myadd
            ...
        myadd:
            mov ..., [ebp+0x8]
            mov ..., [ebp+0xc]
            ...
            leave
            ret 0x8 ; 由myadd(callee)清空stack
                    ; 0x8 = 2個參數
        ```
- 缺: 不定長度參數的function無法處理
- 優: 節省code空間
### fastcall
- 聲明語法`int __fastcall function(arg,...)`
- 前兩個<font color='red'>DWORD以下size</font>的參數分別用`rcx`及`rdx`傳遞,後面的參數一樣用stack傳,由<font color='red'>callee</font>清空stack
- 優: 較快
### thiscall
- `thiscall`無法被明確聲明,因為`thiscall`是c++中class中function的預設呼叫慣例
- 如果參數個數確定,`this`指標通過<font color='red'>`ecx`</font>傳遞
    如果參數不定長度,`this`指標由<font color='red'>stack</font>傳遞(在參數都push上去之後)
    - ex myadd1
    ```clike=
    #include <iostream>
    #include <stdarg.h>
    using namespace std; //va_list

    class test{
        public:
            int myadd1(int a, int b){ //參數固定長度
                return a+b;
            }

            int myadd2(int num,...){ //參數不定長度
                va_list arg;
                va_start(arg, num);

                int res=0;
                for (int i = 0; i < num; i++)
                {
                    res += i;
                }

                return res;
            }
    };


    int main(){
        test A;
        int res1 = A.myadd1(1,2);
        int res2 = A.myadd2(3, 1, 2, 3);
        cout << res1 << endl;
        cout << res2 << endl;
        return 0;
    }
    ```
    - 轉成assembly後
    ```asm
    00401410 <_main>:
      401410:       8d 4c 24 04             lea    ecx,[esp+0x4]
      401414:       83 e4 f0                and    esp,0xfffffff0
      401417:       ff 71 fc                push   DWORD PTR [ecx-0x4]
      40141a:       55                      push   ebp
      40141b:       89 e5                   mov    ebp,esp
      40141d:       51                      push   ecx
      40141e:       83 ec 34                sub    esp,0x34
      401421:       e8 3a 06 00 00          call   401a60 <___main>
      401426:       8d 45 ef                lea    eax,[ebp-0x11]
      401429:       c7 44 24 04 02 00 00    mov    DWORD PTR [esp+0x4],0x2
      401430:       00
      401431:       c7 04 24 01 00 00 00    mov    DWORD PTR [esp],0x1
      401438:       89 c1                   mov    ecx,eax ; 參數固定,用ecx傳遞this指標
      40143a:       e8 89 28 00 00          call   403cc8 <__ZN4test6myadd1Eii>
      40143f:       83 ec 08                sub    esp,0x8
      401442:       89 45 f4                mov    DWORD PTR [ebp-0xc],eax
      401445:       c7 44 24 10 03 00 00    mov    DWORD PTR [esp+0x10],0x3
      40144c:       00
      40144d:       c7 44 24 0c 02 00 00    mov    DWORD PTR [esp+0xc],0x2
      401454:       00
      401455:       c7 44 24 08 01 00 00    mov    DWORD PTR [esp+0x8],0x1
      40145c:       00
      40145d:       c7 44 24 04 03 00 00    mov    DWORD PTR [esp+0x4],0x3
      401464:       00
      401465:       8d 45 ef                lea    eax,[ebp-0x11]
      401468:       89 04 24                mov    DWORD PTR [esp],eax ; 參數不固定,用stack傳遞指標
      40146b:       e8 70 28 00 00          call   403ce0 <__ZN4test6myadd2Eiz>
      401470:       89 45 f0                mov    DWORD PTR [ebp-0x10],eax
      401473:       8b 45 f4                mov    eax,DWORD PTR [ebp-0xc]
      401476:       89 04 24                mov    DWORD PTR [esp],eax
      401479:       b9                      .byte 0xb9
    ```
## Optimization最佳化
- `gcc --help=optimizers`看有哪些最佳化選項
`gcc -Q -O1 --help=optimizers`可以看在`O1`哪些optimizer是開啟或關閉,有`O0-O3`(`O0`表沒有最佳化)

- 常用
    - 常數摺疊(constant folding): 把結果直接算出來
        - ex. `a = 1*2*3*4;` $\Rightarrow$ `a = 24;`
   
    - 常數傳播(constant propogate)  
        - ex.
            ```c
            int x = 14;
            int y = x - 5;
            return x+y;
            ```
            $\Rightarrow$ propogate
            ```c
            int x = 14;
            int y = 14 - 5;
            return 14 + y ;
            ```
            $\Rightarrow$ folding
            ```c
            int x = 14;
            int y = 9;
            return 14 + y ;
            ```
            $\Rightarrow$ propogate
            ```c
            int x = 14;
            int y = 9;
            return 14 + 9 ;
            ```
            $\Rightarrow$ folding
            ```c
            int x = 14; //被拔掉
            int y = 9;  //被拔掉
            return 23 ; //不會用到x跟y,所以optimization會拔掉x跟y的宣告
            ```
    - Tail call: 返回值是call某function後的結果
     Tail Recursion: 返回值為遞迴呼叫的結果
        - ex.
            ```c
            int search(int data){
                return advance_search(data); 
            }
            ```
        - 為了減少stack的使用
            - ex.
            ```c=
            #include <stdio.h>

            int sum(int n) {
                if (n == 0)
                    return 0;
                else
                    return sum(n - 1) + n;
            }

            int main(){
                    printf('%d',sum(10));
                    return 0;
            }

            //gcc -o add add.c
            //gcc -o add2 add.c -O2 //optimize
            ```
            - add
            ```asm=
            sum:
                push   rbp
                mov    rbp,rsp
                sub    rsp,0x10
                mov    DWORD PTR [rbp-0x4],edi
                cmp    DWORD PTR [rbp-0x4],0x0
                jne    1165 <sum+0x1c>
                mov    eax,0x0
                jmp    1177 <sum+0x2e>
                mov    eax,DWORD PTR [rbp-0x4]
                sub    eax,0x1
                mov    edi,eax
                call   1149 <sum>
                mov    edx,DWORD PTR [rbp-0x4]
                add    eax,edx
                leave
                ret
            ```
            - add2
            ```asm=
            sum:
                xor    eax,eax 
                test   edi,edi             ;檢查edi是否=0
                je     1197 <sum+0x17>     ; =0跳到第9行
                nop    WORD PTR [rax+rax*1+0x0] 
                add    eax,edi             
                sub    edi,0x1             ; 若edi - 1 = 0會設定ZF
                jne    1190 <sum+0x10>     ; if ZF=0,跳第2行
                ret
            ```
    - magic trick : 減少除法(div)的使用
        - 想法
            1. 用乘法處理
            2. 如果是2的冪次方更好,用shift的即可
        - ex. 計算`x / 9`
            $\frac{x}{9} = \frac{x}{2^n} \cdot \frac{2^n}{9}$
            
            `n`可由編譯器決定,因此compile時就可以事先將$\frac{2^n}{9}$算好
            (ex. `n`取`33`, `hex(ceil(2^33/9)) = 0x38e38e39`)
            
            $\Rightarrow  \frac{x}{9} = \frac{x \cdot 0x38e38e39}{2^{33}}$
            $\rightarrow$ 即將$x\cdot0x38e38e39$向右移33bit = 右移1bit
            
        - 如果是`idiv`還要多考慮sign的問題
            - 因為c的除法是<font color='red'>向下取整</font>, i.e $-3/2 = -2$,  $3/2 = 1$
                - 除數為負
                    $\lfloor\frac{-3}{2}\rfloor + 1 = \lceil\frac{-3}{2}\rceil$
                - 除數為正
                    $\lfloor\frac{3}{2}\rfloor + 0 = \lceil\frac{3}{2}\rceil$
        
            - `0x38e38e39`必為整數,因此判斷乘數的正負即可
                - 第31bit即sign bit
                    - sign bit = 0: 算數右移31bit得`0`
                        - `結果  = (x*0x38e38e39 >> 1) - 0`
                    - sign bit = 1: 算術右移31bit得`11...1 = -1`
                        - `結果 = (x*0x38e38e39 >> 1) - (-1)`
                              
            
## Class and Struct
### struct內對齊: `min(datatype_size, pack)`
- ex 計算struct佔的記憶體空間
```c=
#include <stdio.h>

typedef struct {
  unsigned char      v1;    //1byte
  unsigned int       v2;    //4byte
  unsigned short     v3;    //2byte
  unsigned char      v4[3]; //3byte
  unsigned long long v5;    //8byte
} myStrA;

void main() {
  // 輸出struct大小
  printf("Size of myStrA = %d\n", sizeof(myStrA));

  // 建立方便辨識的測試資料
  myStrA a;
  a.v1 = 0x11;
  a.v2 = 0x22334455;
  a.v3 = 0x6677;
  a.v4[0] = 0x12;
  a.v4[1] = 0x34;
  a.v4[2] = 0x56;
  a.v5 = 0x8899AABBCCDDEEFF;

  // 輸出 myStrA 內部實際的資料
  unsigned char *ptr = (unsigned char *) &a;
  int i, s = sizeof(myStrA);
  printf("a = ");
  for (i = 0; i < s; ++i) {
    printf("%02X ", ptr[i]);
  }
  printf("\n");
}

/*
結果:
    Size of myStrA = 24
    a = 11 00 00 00 55 44 33 22 77 66 12 34 56 00 00 00 FF EE DD CC BB AA 99 88
       |v1|        |     v2    | v3  |   v4   |        |           v5          |
```
:::info
可以發現雖然v1只有1byte,但在記憶體上還是保留了4byte,
而short佔2byte卻保留了8byte
原因是因為preprocessor在處理時預設會以<font color='red'>該datatype長度的倍數</font>對其進行對齊
- v1為char,char以1byte的倍數對齊,此時offset = 0x0,放上v1後offset = 0x1
- v2為int,int以4byte的倍數對齊,因此compiler跳過3byte,讓offset = 0x4再放上v2,offset = 0x8
- v3為short,short以2byte得倍數對齊,8為2的倍數,因此直接放上v3,offset = 0xa
- v4為一個char array,char以1byte的倍數對齊,因此直接把三個element放上去,offset 0xd
- v5為long int,以8byte的倍數對齊,因此跳過3byte,讓offset為0x10再放上v5
:::

- 此外,可以用`#pragma pack()`指定對齊的長度(1,2,4,16,沒指定預設為8),compiler會<font color='blue'>拿指定的長度跟datatype預設的長度</font>做比較,並<font color='red'>取小的那個</font>
    - ex. code前面加上`#pragma pack(2)`之後結果變為
        ```
        Size of myStrA = 20
        a = 11 00 55 44 33 22 77 66 12 34 56 00 FF EE DD CC BB AA 99 88
           |v1|  |     v2    | v3  |   v4   |  |           v5          |
        ```
        :::danger
        - v1放完後offset = 0x1,
         v2為int,因為指定2byte對齊
         故取`min(2,4) = 2`對齊,所以v1放完只跳過1byte
         - v4放完後offset = 0xb
         v5取`min(2,8) = 2`對齊,因此只跳過1byte
        :::
        
### struct本身也要對齊: `min(max_size_member, pack)`
- ex
```c=
#include <stdio.h>

typedef struct {
  unsigned char      v1;    //1byte
  unsigned int       v2;    //4byte
  unsigned short     v3;    //2byte
} myStrA
    
void main() {
  // 輸出struct大小
  printf("Size of myStrA = %d\n", sizeof(myStrA));
  
  // 建立方便辨識的測試資料
  myStrA a;
  a.v1 = 0x11;
  a.v2 = 0x22334455;
  a.v3 = 0x6677;
    
  // 輸出 myStrA 內部實際的資料
  unsigned char *ptr = (unsigned char *) &a;
  int i, s = sizeof(myStrA);
  printf("a = ");
  for (i = 0; i < s; ++i) {
    printf("%02X ", ptr[i]);
  }
  printf("\n");
}
    
/*
結果
    Size of myStrA = 12
    a = 11 00 00 00 55 44 33 22 77 66 00 00
       |v1|        |     v2    |  v3 |
```

:::info
- struct內應該佔 1 + 3 + 4 + 2 = 10byte,卻佔了12byte
- 因為struct本身也要對齊,
取`min(max_size_member, pack) = min(sizeof(int), pack=8) = min(4,8) = 4`對齊,
因此把struct補到12byte
:::

### C++ name mangle
- 為了解決不能使用跟c library一樣函數名稱的問題,C++加入了namespace來解決
- C++的繼承後Override或Overloading(多載)可能造成多個函數共用一個函數名稱(差別在<font color='blue'>參數個數或型態不同</font>)
`Note: override要父class在該函數前面宣告為virtual(虛函式),繼承的class才可以改寫`
    - 為了讓compiler可以區分多個不同的同名函式,必須要對產生的符號動一點手腳,這就是<font color='red'>Name Mangling</font>
    
```c=
#include <iostream>
using namespace std;

int func(int a){
    return a;
}
float func(float b){
    return b;
}

class C {
    public:
        int func(int c){
            return c;
        };
        
        class C2 {
            public:
                int func(int d){
                    return d;
                }
        };
        
        C2* do_something(){
            C2 *c2 = new C2;
            return c2;
        }
};

namespace N {
    int func(int e){
        return e;
    };

    class C {
        public:
            int func(int f){
                return f;
            };
    };
}

class C1:public C{  //C1繼承C
    public:
        float func(float g){  //override
            return g;
        }
};

int main(){
    int a = func(1);               //func(int)
    float b = func(2.5f);          //func(float)
    
    //class C
    C *c = new C;
    int c_ = c->func(3);           //C::func(int)
    //class C::C2
    C::C2 *d = c->do_something();  
    int d_ = d->func(4);           //C::C2::func(int)

    //namespace N
    int e = N::func(5);            //N::func(int)
    N::C *f = new N::C;
    //class N::C
    int f_ = f->func(6);           //N::C::func(int)
    
    C1 *g = new C1;                
	float g_ = g->func(7.5f);  //C1::func(float)
    
    cout << a << endl;
    cout << b << endl;
    cout << c_ << endl;
    cout << d_ << endl;
    cout << e << endl;
    cout << f_ << endl;
    cout << g_ << endl;

    return 0;
}
```
:::info
objdump出來之後,每個function的name變成
a. `func(int)`  $\Rightarrow$ `_Z4funci`
b. `func(float)`  $\Rightarrow$ `_Z4funcf`
c. `C::func(int)`  $\Rightarrow$ `_ZN1C4funcEi`
d. `C::C2::func(int)`  $\Rightarrow$ `_ZN1C2C24funcEi`
e. `N::func(int)`  $\Rightarrow$ `_ZN1N4funcEi`
f. `N::C::func(int)`  $\Rightarrow$ `_ZN1N1C4funcEi`
g. `C1::func(float)`  $\Rightarrow$ `_ZN2C14funcEf`
<br>
- 可以用`c++filt <mangling_name>`來還原
![](https://i.imgur.com/fceF9av.png)
:::

### C++ virtual table
- 當new一個class,會去找該class的constructor(或其父class的constructor)
    - 會把this(struct的第一個member)設成一個function(該類別的第一個virtual function)

- ex
```clike=
#include <string>
#include <algorithm> //for std::reverse
#include <iostream>

using namespace std;





class human{
        protected:
        string nation;

        public:
        virtual string getNation(){
                return this->nation;
        }

        virtual string ReverseNation(){
                reverse(nation.begin(), nation.end());
        } 
};

class Taiwanese:public human{
        public:
        Taiwanese(){
                nation = "Taiwan";
        }

        string getNation(){
                return "You should be a Chinese!!";
        }
};

int main(){
        Taiwanese* MinYeon = new Taiwanese();
        cout << *(string *)(((char*)MinYeon)+8) << endl; // origin nation
        //MinYeon->nation is not work, since nation is protected
        cout << MinYeon->getNation() << endl;
}

/*
output:
    Taiwan
    You should be a Chinese!!
```
開ida看之後
- `human`的constructor
![](https://i.imgur.com/EEMaAcn.png =400x)

```=
03CB0 ; `vtable for'Taiwanese
03CB0 _ZTV9Taiwanese  dq 0                    ; offset to this
03CB8                 dq offset _ZTI9Taiwanese ; `typeinfo for'Taiwanese
03CC0 off_3CC0        dq offset _ZN9Taiwanese9getNationB5cxx11Ev
03CC0                                         ; DATA XREF: Taiwanese::Taiwanese(void)+1D↑o
03CC8                 dq offset _ZN5human13ReverseNationB5cxx11Ev
03CD0                 public _ZTV5human ; weak
03CD0 ; `vtable for'human
03CD0 _ZTV5human      dq 0                    ; offset to this
03CD8                 dq offset _ZTI5human    ; `typeinfo for'human
03CE0 off_3CE0        dq offset _ZN5human9getNationB5cxx11Ev
03CE0                                         ; DATA XREF: human::human(void)+10↑o
03CE0                                         ; human::~human()+10↑o
03CE8                 dq offset _ZN5human13ReverseNationB5cxx11Ev
```

- 每個類別的struct最前面(this)都指向自己的vtable,vtable的每個entry分別指向該類別中的virtual function
    - 一個繼承父類別的子類別,也會繼承父類別的vtable,如果有override某個函式,則對應的vtable entry會指向override的function(ex.第4行),否則指向父類別的function(ex.第6行)
    - ex. `myDerived`繼承`myBase`,並override`func2`
    ![](https://i.imgur.com/zZpUGlO.png =400x)
    
:::warning
IDA tricks
`shift + F1`可以開啟Local types,可以右鍵edit修改struct member
:::

### C++ Polymorphism(多態/多型)