---
breaks: true
---
# 2020 程安 Reverse筆記2
###### tags: `程式安全`
## 如何恢復struct的member
### struct
- C用`malloc`來分配struct的空間
C++則是用`new`,差別是<font color='red'>`new`會自動幫你call constructor</font>
- 如果strip過後(指令`$ strip <binary>`),在c下面看起來就只是`malloc`了一個空間,但看不出來實際長甚麼樣子

- 可在`v3`上按右鍵`reset pointer type`之後,再右鍵`create new struct type`,就可以變成下面這樣

- 這時`shift + F1`可以看到多出一個struct
```c=
struct __attribute__((packed)) __attribute__((aligned(2))) struct_v3
{
char char0; //1byte
_BYTE gap1[3]; //下面dword 4byte對齊: +3byte
//把這個gap拔掉也不影響
_DWORD dword4; //4byte
_WORD word8; //2byte
//struct本身對齊2byte
}; //共佔0xc=12byte
```
## 基礎分析技巧
1. `file <binary>`
- base on `magic`
- `man file`

> The information identifying these files is read from /etc/magic and the compiled magic file usr/share/misc/magic.mgc, or the files in the directory /usr/share/misc/magic if the compiled file does not exist.
- `/usr/share/misc/magic.mgc`裡面定義了各種file的檢驗規則,用來判斷檔案型別
- `man magic` : 定義了怎麼檢查
- ex
```!
0 string MZ
>0x18 leshort <0x40
>>(4.s*512) leshort !0x014c MZ executable (MS-DOS)
# if it's not COFF, go back 512 bytes and add the offset taken
# from byte 2/3, which is yet another way of finding the start
# of the extended executable
>>>&(2.s-514) string LE LE executable (MS Windows VxD driver)
```
> 檢查offset 0,為MZ
> 繼續看offset 0x0 + 0x18 檢查一個short是否小於0x40,如果是繼續往下走,不是就找別的檢查規則
2. gdb
- `gcc -g`會帶上symbol,debug時會有行號

- 不小心`si`進到不想進的function,可以`fin`直接到結束的地方
- 逆symbol strip掉的binary,如果已經恢復struct,但需要動態debug,如果member有linklist會很可怕
- 可以先自己寫好struct,然後`gcc -g -c -o <name>.o`編譯起來
`Note: -c表不要link`
```c!=
#include <stdint.h>
struct ss{
char aChar; //1 byte
int aInt; //4 byte
int16_t aInt16;//2 byte 4+4+2= 10, true= 12
};
struct ss any; //隨便宣告即可,重點是要讓type留著
```
- 此時`objdump -t -M intel <file>.o`可以看到symbol table
`Note: -t表顯示symbol table, -T表顯示動態dynamic symbol table`

- gdb跑起來要逆的binary,在gdb cmd輸入`add-symbol-file <file>.o 0`
`Note: 0代表從offset 0開始載`
> `set $var1 = ...`可以自己設定值
> `p $var1`可以印出值
> - 假設在malloc之後`set $var1=$rax`,設定成malloc的空間ptr
> 然後繼續執行直到要Print出時想看一下struct的值
> 這時`p *(struct ss*)$var1`,
> 該struct的所有值就會以struct的方式顯示

-
3. x64dbg
- 想要監控某個memory位址甚麼時候會被存取
- `右鍵->中斷點`: 可以設定記憶體被讀/寫/執行時中斷
`Note: 原理是將該memory page設為不可讀/寫/執行,戳到時就會error,就可以被debugger抓到`

- 對應到gdb的 `watch(寫)`及`rwatch(讀)`
- ex.此時已`malloc`完,回傳值放在`rax=0x80052a0`,我們要監控這個記憶體位址何時被寫

`Note: Hardware point會斷在執行完之後`
## Anti-debug
### software斷點跟hardware斷點的差別
- Software斷點
- 用x64dbg隨意下幾個斷點,然後用<font color='red'>Cheat Engine</font>打開, Memory View
- 可以看到用x64dbg直接下software斷點的在memory上直接被改成`int3`

- Hardware斷點
- 若是在指令上`右鍵->中斷點->設定硬體執行中斷點`,則不會變成`int3`
- 但硬體斷點最多只能下4個
- x86提供DR0-DR7以及2個MSR暫存器用於hardware debug,其中只有DR0-DR3為硬體斷點暫存器

:::info
- 惡意程式利用檢查自己.text段的memory是否有被修改就可以判斷是否在被debug
- ex.把所有opcode相加,跟原本不一樣
- 因為硬體斷點不會修改,因此可以改用硬體斷點
:::
## 逆向的分析策略
1. 套用已知模型
- ex.知道malware有哪些分類(fileless, 銀行木馬, keylogger, etc)
- 就可以大概知道他可能會做什麼事
2. Magic Number
- 有些常用算法有固定Magic Number
- md5的狀態變量
> A=0x67452301,
> B=0xefcdab89,
> C=0x98badcfe,
> D=0x10325476
- AES的s-box

- DES的s-box

3. 預測程式碼 : 猜測程式可能在幹嘛
- ex. 前面做了`md5_init`,又有輸入的地方,後面可能就是拿輸入做md5 hash
4. 抽象化
## PE format
- Windows使用PE(Portable Executable)做為執行檔格式
Mac OS使用Mach-O
Unix及Linux使用ELF(Executable Linkable Format)
- 可以用`010 editor`或`PE Bear`等PE viewer查看PE格式
- PE file = `DOS Header + DOS stub + NT Header + Section Headers + Sections`


### Dos Header
- 0x0 : 'MZ'
- 最尾端4byte(`e_lfanew`) : 指向'PE'所在的offset

### Dos Stub
- 裡面有一個'This program cannot run in DOS mode'的字串

- 如果用dosbox開debug mode執行PE檔,會執行這邊的code,裡面真的是assembly code
### NT Header('PE' Signature + File Hdr + Optional Hdr)
- 從這邊開始才是給現在電腦在看的東西
- 如果有在Linux上裝`mingw-w64`,可以在`/usr/i686-w64-mingw32/include`下的`winnt.h`中看到`_IMAGE_NT_HEADER`的struct
#### File Header

- `Machine`: 記錄這個file在什麼樣的機器上跑
- `NumberOfSection`: 有幾個section
- `SizeOfOptionalHeader`: 紀錄Optional Header的size,
- 正常來說,Optiona Header是一個c的struct,應該已經知道size,所以這個entry的存在意義(?
- 結果變成可以利用的點,有些惡意程式會把Optional Header改大,然後在Optional Header的後面塞惡意code,但是這些code<font color='red'>並不會在parse header時被執行到</font>,還是要透過控制執行流程才能跳到這邊的code
#### Optional Header: 雖然是option但還是必選RRR
- `Magic`: 標誌位,但這邊指的是他是32還是64bit
- `AddressOfEntryPoint`: 程式進入點在文件中的offset
- `ImageBase`: mapping到記憶體上要從哪裡開始放
- `ImageBase` + `AddressOfEntryPoint`才是程式執行起來之後的entry point
- `FileAlignment`跟`SectionAlignment`
1. `FileAlignment`:代表在file中,每個section要以`FileAlignment`對齊,不夠的地方會用`00 = NULL`補滿
- ex 通常`FileAlignment` = 0x200

2. `SectionAlignment`:mapping到記憶體上之後,每個section要`SectionAlignment`對齊
- 為了方便設定每個section的`rwx`權限,通常會把同一個section放在一個page上,而一個page通常為4096byte = 0x1000,因此`SectionAlignment`通常設為0x1000

- `SizeOfHeader`: Headers+NULL的大小,通常是0x400
- `NumberOfRvaAndSizes`: 通常是固定值 = 0x10
- RVA是一個array,總共有0x10=16個值(其中第16個是reserved, 第15個是.NET header)

- ex. 想要找<font color='red'>import table</font>,在file offset 0x100的地方找到Import Directory的RVA=0x9000, <font color='blue'>加上`ImageBase`</font>之後就是memory中import table的位置
### Section Headers
有幾個section就有幾個section header,這些section header用一個array串起來

- `Raw Address` = `PointerToRawData`: section在file中的offset,為`FileAlignment`的倍數
- `Raw Size` = `SizeOfRawData`: 該section在file中的大小(`FileAlignment的倍數`)
- `Virtual Address`: 加上`ImageBase`就是section在memory中的address,為`SectionAligmnment`的倍數
- `Virtual Size`: 不包含NULL的section size

### Section
:::info
1. `.text`
2. `.data`
3. `.rdata` : read only data
- ex. print出的字串
4. `.bss` : 沒初始化的全域或靜態變數
- ex. `int i;`
5. `.idata` : import相關的
6. `.edata` : export相關的
`Note: 如果沒有.idata或.edata,可能被併在.data中`
7. `.rsrc` : 存放GUI相關的資源,定義在`winuser.h`中
8. `.reloc` : 重定位相關
9. `.pdata` : 跟例外處理相關
`Note: PE(32bit)沒有,PE+(64bit)才有`
:::
- 找Import在file中的位置
- ex. 找KERNEL32.dll的位置
`NameRVA` - `.idata`的`Virtual Addr` + `.idata`的`
PointerToRawData`
= 9994 - 9000 + 4E00 = 5794


可以`ctrl+G`跳到0x5794

#### IAT(Import Address Table)
- 每個DLL都是一個`IMAGE_IMPORT_DESCRIPTOR`結構
`Note: 64bit的DLL放在System32, 32bit的DLL放在SysWOW64`
```c=
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
__C89_NAMELESS union {
DWORD Characteristics;
DWORD OriginalFirstThunk; //指向INT
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;
DWORD FirstThunk; //指向IAT
} IMAGE_IMPORT_DESCRIPTOR;
```
- `OriginalFirstThunk`: 指向`INT`,是一個array
每個entry是一個`IMAGE_THUNK_DATA`結構
其中的`AddressOfData`再指向一個`IMAGE_IMPORT_BY_NAME`結構
- `FirstThunk`: 指向`IAT`,每個entry也是`IMAGE_THUNK_DATA`結構,
<font color='blue'>初始化</font>時,`IAT`跟`INT`中的`AddressOfData`指向相同的`IMAGE_IMPORT_BY_NAME`結構
```c=
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString;
DWORD Function;
DWORD Ordinal;
DWORD AddressOfData; //指向IMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; //函數編號
CHAR Name[1]; //函數名稱
} IMAGE_IMPORT_BY_NAME,*PIMAGE_IMPORT_BY_NAME;
```
