--- 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`了一個空間,但看不出來實際長甚麼樣子 ![](https://i.imgur.com/pFuqwxI.png =500x) - 可在`v3`上按右鍵`reset pointer type`之後,再右鍵`create new struct type`,就可以變成下面這樣 ![](https://i.imgur.com/6Mil92C.png =500x) - 這時`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` ![](https://i.imgur.com/yf7OiMf.png) > 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時會有行號 ![](https://i.imgur.com/qN2PcE8.png =500x) - 不小心`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` ![](https://i.imgur.com/T3QAbyh.png =500x) - 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的方式顯示 ![](https://i.imgur.com/0F9FRCz.png =300x) - 3. x64dbg - 想要監控某個memory位址甚麼時候會被存取 - `右鍵->中斷點`: 可以設定記憶體被讀/寫/執行時中斷 `Note: 原理是將該memory page設為不可讀/寫/執行,戳到時就會error,就可以被debugger抓到` ![](https://i.imgur.com/z19YPL6.png =500x) - 對應到gdb的 `watch(寫)`及`rwatch(讀)` - ex.此時已`malloc`完,回傳值放在`rax=0x80052a0`,我們要監控這個記憶體位址何時被寫 ![](https://i.imgur.com/ZyZ1qbZ.png) `Note: Hardware point會斷在執行完之後` ## Anti-debug ### software斷點跟hardware斷點的差別 - Software斷點 - 用x64dbg隨意下幾個斷點,然後用<font color='red'>Cheat Engine</font>打開, Memory View - 可以看到用x64dbg直接下software斷點的在memory上直接被改成`int3` ![](https://i.imgur.com/MIQFPJe.png) - Hardware斷點 - 若是在指令上`右鍵->中斷點->設定硬體執行中斷點`,則不會變成`int3` - 但硬體斷點最多只能下4個 - x86提供DR0-DR7以及2個MSR暫存器用於hardware debug,其中只有DR0-DR3為硬體斷點暫存器 ![](https://i.imgur.com/rUYk2wU.png) :::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 ![](https://i.imgur.com/KWQONkS.png) - DES的s-box ![](https://i.imgur.com/qioaPJu.png) 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` ![](https://i.imgur.com/a66hK4O.png) ![](https://i.imgur.com/qE4snIP.png =300x) ### Dos Header - 0x0 : 'MZ' - 最尾端4byte(`e_lfanew`) : 指向'PE'所在的offset ![](https://i.imgur.com/hxL9H8t.png) ### Dos Stub - 裡面有一個'This program cannot run in DOS mode'的字串 ![](https://i.imgur.com/KqRoGFW.png) - 如果用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 ![](https://i.imgur.com/4bDAdZN.png =500x) - `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 ![](https://i.imgur.com/lqBrhWb.png =200x) 2. `SectionAlignment`:mapping到記憶體上之後,每個section要`SectionAlignment`對齊 - 為了方便設定每個section的`rwx`權限,通常會把同一個section放在一個page上,而一個page通常為4096byte = 0x1000,因此`SectionAlignment`通常設為0x1000 ![](https://i.imgur.com/At91uaR.png) - `SizeOfHeader`: Headers+NULL的大小,通常是0x400 - `NumberOfRvaAndSizes`: 通常是固定值 = 0x10 - RVA是一個array,總共有0x10=16個值(其中第16個是reserved, 第15個是.NET header) ![](https://i.imgur.com/SnjgXYx.png =400x) - 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串起來 ![](https://i.imgur.com/y5Utlb8.png =500x) - `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 ![](https://i.imgur.com/tBhCj3e.png =600x) ### 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 ![](https://i.imgur.com/1qq5Oz1.png =400x) ![](https://i.imgur.com/TGWOXY9.png =400x) 可以`ctrl+G`跳到0x5794 ![](https://i.imgur.com/lbsdBwH.png =500x) #### 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; ``` ![](https://i.imgur.com/zL65a1Z.png =500x)