HITCON Training 2019
揣摩軟體安全 - 學習系統攻防
===
* 課程附件:[Download link](https://drive.google.com/open?id=1FuZxvwXXkAQZsYV6JbNj2Ke-KcJLu3Lz)
* password:HitconTraining@@2019
# use debugger
## 靜態分析-IDApro 與他們的小夥伴
### 基本功能
- F5大法:一鍵還原source code
- space:shift view
- shift+f12:string search
- Hex <-> Ord 進位轉換 快捷鍵:H
- 重命名變數 快捷鍵:N
- 找尋關鍵點引用 快捷鍵:X
- 快速跳到指定位置: G
- 回到上一步操作: ESC
- 點選 API 按 Y 改函數 type "MACRO_PROCESS"
- 可以辨認出有 bitmask (openprocess函數)的參數 (不一定準確)
- 在imports

- 按 Y 改函數 type "MACRO_PROCESS"

- 原本的變數就變成可讀性高的變數名稱
### ray式IDA
- IDA 佈景主題 plugin
- https://github.com/zyantific/IDASkins/releases/
- Unzip and copy “plugins”dir To C:\Program Files\IDA 7.0\
### YARA
- 很棒的內容搜索引擎
#### demo
- rule.txt:

- command : yara + rule.txt + target_file
```
C:\Users\terry>C:\Users\terry\Downloads\HITCON2019\Tools\Yara\yara32.exe C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt
test_yara_rule C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt
# 看出現在哪
C:\Users\terry>C:\Users\terry\Downloads\HITCON2019\Tools\Yara\yara32.exe C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt -s
test_yara_rule C:\Users\terry\Downloads\HITCON2019\Tools\Yara\rule.txt
0xd7:$b: AABBCCDD
```
#### mkyara
- 一鍵產生yara rule
- install:
1. 先灌pip

2. command line
```
install pip
cd c:\python27-x64>cd Scripts
pip install mkyara
```
3. 把

4. 放到

5. 選取一段直接產生該段的rule

- 自動辨認可能變動的值成為?

### FireEye stack string parse
- 自動化找尋隱藏字串
- install(cmd)
```
# copy the “~\HITCON2019\IDA Plugins\flare-ida-master\python,~\HITCON2019\IDA Plugins\flare-ida-masterplugins” dir to C: Program Files IDA 7.0
cd C:\python27-x64\Scripts>
pip install https://github.com/williballenthin/vivisect/zipball/master
```
- use


## 萬能F5(?
- demo_1(Anti IDA Trick1 - F5 Error)
- IDA 非常重視 Stack 平衡 , 因此自行修改 Stack平衡就可能讓 IDA 解析錯誤. (__declspec function)
- IDA 也會把 jmp 強制跳躍指令視為跳到另一個 function 執行 , 多加一個 label 達到欺騙的目的效果
- F5 按下不能 QQ
```cpp=
//#include "pch.h"
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <stdio.h>
//example 1
//IDA 非常重視 Stack 平衡 , 因此自行修改 Stack平衡就可能讓 IDA 解析錯誤. (__declspec function)
void __declspec(naked) SecretFunc()
{
printf("IDA~ are you see me!?");
getchar();
_asm
{
add esp, 0x4
ret
}
}
//example 2
//IDA 也會把 jmp 強制跳躍指令視為跳到另一個 function 執行 , 多加一個 label 達到欺騙的目的
void destroy_func()
{
__asm
{
lea eax, lab1 //eax = lab1;
jmp eax //goto lab1;
_emit 0x90 //0x90 == nop
lab1:
}
printf("destroy_func\n");
}
int main(int argc, char *argv[])
{
//example 1
_asm
{
jmp dword ptr[SecretFunc]
}
//example 2
//destroy_func();
return 0;
}
```
- demo_2(Anti IDA Trick2)
- IDA 對 SEH 解析有問題 , 不會顯示 except 後的結果 , 因此可自行觸發異常達成欺騙目的.
```cpp=
#include "pch.h"
#include <iostream>
int main()
{
__try
{
printf("IDA what are you doing?\n");
_asm
{
mov eax, 0 //eax = 0;
mov eax, [eax] //eax = *0; access zero address to trigger exception
}
}
__except (1)
{
printf("Evil things you want. \n");
}
getchar();
return 0;
}
```

## 動態分析(ollydgb/x64dbg)

> EIP 現在正準備要執行的指令
- 基礎介面操作
- 開啟目標的兩種方式
- 直接開啟(會以Debug Mode 開啟)
- 影響某些記憶體行為,並更容易被偵測到有debugger存在
- 直接拖進視窗裡面
- 附加式開啟
- 可繞過許多剛開始針對debugger 的檢測
- 先執行程式
- 開dbg (檔案>:heart: 附加)

- 分析目標確認(投影片等等再加)
> 先F9一次
- 快捷鍵們
- F2 - 斷點 放置軟體斷點 , 像是陷阱的概念 , 程序執行遇到斷點後會暫停 , 將控制權交給 debugger (x64dbg)
- F7 - 單步步進 執行一行指令,**會進入function (call)** 中執行
- F8 - 單步步過 執行一行指令,**不會進入function (call)** 中執行
- F9 - 直接讓程式流程執行, 直到觸發斷點停下
- F4 - 跳到滑鼠點選的記憶體位置 快速跳到指定的記憶體位置執行, 通常用在跳出多個for 迴圈,取代人力慢慢按F8
- Ctrl + G : 跳到指定記憶體位置
# debugger 破解技巧& 軟體實戰
## 快快樂樂學組語
- 參數傳遞:由上到下,由左到右

- mov aaa , bbb (等價於 C 語言中 aaa = bbb;)
- mov aaa , [bbb] (等價於 C 語言中 aaa = *bbb;)
- push xxx (將 xxx 參數放入 stack 中) ,通常配合 call 指令使用
- call xxx ( 等價於 C 語言中 執行 xxx() 函數) •
- jmp 0xaaaabbbb (無條件強制流程跳躍到記憶體0xaaaabbbb 執行)
- (等價於C語言中的goto 0xaaaabbbb;)
- 其他 J 開頭的指令都是根據某些條件,跳躍到其他記憶體執行
- Nop •
- Nothing to do. 不做任何事
- cmp aaa , bbb (比較 aaa , bbb 的數值)
- 通常配合 J 開頭的條件跳躍指令
- ret (返回上一個函數流程執行) (等價於 C 語言中的 return)
## Pdb File 的應用
- 主要用途:方便開發者 debug 使用
- 進階用途:跟蹤 Project & 作者痕跡 (丟到hex editor)
- VS 開發設定:編譯去除選項(屬性 -> 連結器 -> 產生偵錯資訊 -> 否)
- 拿掉後逆向難度變高
## 指令暫存器的通用概念
- eip •
- 一定存放目前執行指令的記憶體位置
- eax •
- 函數返回值 (通常放這)
- 在一個 call 完成後退出時,要特別關心 eax (返回值)
- ecx •
- 存放物件指標 (通常)
- for 的 i 計數器大小 (通常) , 通常會有一個累加的動作 , 類似 c 語言中的 i++
- esp •
- 一定存放 Stack (堆疊)記憶體位置頂部
- ebp
- 一定存放 Stack (堆疊)記憶體位置底部
### demo
- 基本驗證機制 Source Code
```cpp=
#include "pch.h"
//#include "stdafx.h"
#include "windows.h" //Windows API Library
#include <iostream>
int main()
{
DWORD input_passwd = NULL;
printf("Input password\n");
std::cin >> input_passwd;
if (input_passwd != 12345678)
{
MessageBoxA(0,"Regist Fail!!!" , "Notify" , MB_OK);
return 0;
}
```
#### 註冊/特殊字串搜索
- 任意地方右鍵>搜尋>目前模組>字串引用


#### 堆疊(Stack) 回逤法
- 跳出錯誤提示時 , 快速按下暫停 , 並從堆疊中找線索 • <3> API 斷點法 • 常見的錯誤提示 API 斷點 • MessageBoxA / MessageBoxW • GetWindowTextA / GetWindowTextW
- step
- 等跳出messagebox的時候點擊彈出視窗按F12

- 然後進dbg按F9>再按暫停>進呼叫堆疊

#### API 斷點法
- step
- 先執行程式,輸入隨便的數字,按下enter會跳到messagebox,設斷點

- 按F8繼續跑,跑到call messagebox會跳出fail

- 按下確定後按F8繼續跑會跳回判斷正確的函數,往上滑可以找到跳轉的地方(74 18 je -> jmp

#### 工具法(API monitor)
- 進入工具攔截(左側選擇項目)

- 攔截到的進入點

- 去gdb(ctrl+G)跳記憶體,找到程式進入點

## 軟體驗證基本流程與破解練習
- 破解初探
- 定位關鍵點
- Binary Patch
- 破解軟體授權(010editor)
- 先把程式搞到一定要註冊完才能使用(方便查找)

- 隨便打個帳號密碼讓他噴訊息,然後搜尋字串"invalid name"

- 找到程式判斷購買成功的地方,用二進位Nope填充不要跳到錯誤,通常找je等,會跳一大段的指令,中間通常有判斷或是讓驗證通過的地方,只要把開頭跳走的地方弄掉,就能卡進去

## 軟體破解實戰 - 某知名的16進位編輯器的註冊破解
記在腦子裡 <- ??
剛剛那個完全破解
步驟太多了
# anti-debugger
## TEB / PEB 介紹
* TEB(Thread Environment Block)
* 存放許多和 Thread 有關的資訊 , ex:Image base / Path..
* 透過組合語言指令取得: fs:[0]
* PEB(Process Environment Block)
* 存放許多和 Process 有關的資訊 , ex:Image base / Path..
* 透過組合語言指令取得: fs:[0x30]
* 
```
typedef struct _PEB {
+0x000 BOOLEAN InheritedAddressSpace;
+0x001 BOOLEAN ReadImageFileExecOptions;
+0x002 BOOLEAN BeingDebugged; 標誌當前行程是否被Debugger <===== 目標
+0x003 BOOLEAN Spare;
+0x004 HANDLE Mutant;
+0x008 PVOID ImageBaseAddress; 行程的映像地址,預設 exe:0x400000 , dll:0x1000000
+0x00c PPEB_LDR_DATA ldr; 由PE Loader填充,包含很多pe中包含的信息,用來列舉用戶加載的 dll
+0x010 PRTL_USER_PROCESS_PARAMETERS ProcessParameters; 指向 RTL_USER_PROCESS_PARAMETERS 的指標,RTL_USER_PROCESS_PARAMETERS 中是一些行程的參數。
+0x014 PVOID SubSystemData;
PVOID ProcessHeap; 指向行程堆積地址,每個程式新建時預設堆積使用
PVOID FastPebLock; 存放的是PEBLOCKROUTINE這個例程函數需要用到的參數。
PPEBLOCKROUTINE FastPebLockRoutine; PEB加鎖/解鎖回調例程
PPEBLOCKROUTINE FastPebUnlockRoutine;
ULONG EnvironmentUpdateCount; 行程的環境變數更改的次數
PPVOID KernelCallbackTable; 從內核 “回呼” 用戶空間的函數
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;
ULONG TlsExpansionCounter;
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
...
```
* reference
* [Peb(Process Environment Block)简单学习及分析](http://www.youngroe.com/2015/08/01/Debug/peb-analysis/)
## Anti Debugger Trick
- Trick1:PEB->BeingDebugged / PEB->NtGlobalFlag •
- Trick2:Enum Process Name
- x32dbg.exe / x64dbg.exe
- Trick3:Enum Process Windows Name
- FindWindow(“x64dbg_windows_name”)
- Trick4:透過時間差來判別
- GetTickCount、GetSystemTime、GetLocalTime …
- Trick5:錯誤的API call 卻回傳正確結果
[自動偵測debuger code](https://hackmd.io/xE5l25jPQs-7oyHKMpk2GA)
## Anti Debugger plugins
- Sharp OD Plugin
- https://goo.gl/juzdQ5
**防止偵測debug,過濾掉API上偵測回傳的數值**
- ScyllaHide Plugin
- https://github.com/x64dbg/ScyllaHide/releases
**防止偵測debug,過濾掉API上偵測回傳的數值**
> 打一下這兩個是幹嘛的 我沒聽到
> 還有上面那個010 editor回去有空弄成一個一個步驟 乾蝦
>
## Anti VM Trick
- Trick1: Vmware IO API
- Trick2: Vm Mac Address(固定)
- Trick3: Vm Process Name
- Vmtools.exe …
- Trick4: Vm File Directory
- C:\ProgramFile\vmware …
- Trick5: 奇技淫巧 (anti sandbox)
- 桌面檔案數量
- 瀏覽器網站數量
- 最近訪問檔案數量
- 記憶體是否小於或等於 1G
- 硬碟容量 <= 100 GB
- Anti-vm
```cpp=
//#include "stdafx.h"
#include "windows.h"
#include <iostream> //for c++ string
#include <tlhelp32.h>//for eunmProcess API
#include <tchar.h>
#include "shlwapi.h"
//For Query Mac Address
#include <httpext.h>
#include <windef.h>
#include <nb30.h>
#pragma comment(lib,"netapi32")
#pragma comment(lib,"shlwapi")
using namespace std;
bool Check_VM_MacAddress()
{
wstring strMac = L"";
TCHAR mac[20] = { 0 };
TCHAR check_mac[20] = { 0 };
NCB ncb;
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff[30];
}ASTAT, *PASTAT;
ASTAT Adapter;
typedef struct _LANA_ENUM
{
UCHAR length;
UCHAR lana[MAX_LANA];
}LANA_ENUM;
LANA_ENUM lana_enum;
UCHAR uRetCode;
memset(&ncb, 0, sizeof(ncb));
memset(&lana_enum, 0, sizeof(lana_enum));
ncb.ncb_command = NCBENUM;
ncb.ncb_buffer = (unsigned char *)&lana_enum;
ncb.ncb_length = sizeof(LANA_ENUM);
uRetCode = Netbios(&ncb);
if (uRetCode != NRC_GOODRET)
return false;
for (int lana = 0; lana < lana_enum.length; lana++)
{
ncb.ncb_command = NCBRESET;
ncb.ncb_lana_num = lana_enum.lana[lana];
uRetCode = Netbios(&ncb);
if (uRetCode == NRC_GOODRET)
break;
}
if (uRetCode != NRC_GOODRET)
return false;
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT;
ncb.ncb_lana_num = lana_enum.lana[0];
strcpy_s((char*)ncb.ncb_callname, sizeof("*"), "*");
ncb.ncb_buffer = (unsigned char *)&Adapter;
ncb.ncb_length = sizeof(Adapter);
uRetCode = Netbios(&ncb);
if (uRetCode != NRC_GOODRET)
return false;
_stprintf_s(mac, 20, _T("%02x:%02x:%02x:%02x:%02x:%02x"),
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2],
Adapter.adapt.adapter_address[3],
Adapter.adapt.adapter_address[4],
Adapter.adapt.adapter_address[5]);
_stprintf_s(check_mac, 20, _T("%02x-%02x-%02x"), //for check.
Adapter.adapt.adapter_address[0],
Adapter.adapt.adapter_address[1],
Adapter.adapt.adapter_address[2]);
strMac = check_mac;
printf("Get local mac address:%ws\n", mac);
if (strMac == L"00-05-69" || strMac == L"00-0c-29" || strMac == L"00-50-56")//Vmware
{
return TRUE;
}
if (strMac == L"00-03-ff")//VirtualPC
{
return TRUE;
}
if (strMac == L"08-00-27")//VirtualBox
{
return TRUE;
}
return FALSE;
}
BOOL check_VM_EnumprocessName() //https://www.tenouk.com/cpluscodesnippet/listheprocessmodulesinfo.html
{
HANDLE hProcessSnap;
PROCESSENTRY32 pe32;
// Take a snapshot of all processes in the system.
hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE)
{
return(FALSE);
}
// Set the size of the structure before using it.
pe32.dwSize = sizeof(PROCESSENTRY32);
// Retrieve information about the first process, and exit if unsuccessful
printf("\n\n*********************************");
printf("\nList of process & their info...\n");
printf("*********************************");
if (!Process32First(hProcessSnap, &pe32))
{
printf("Process32First()"); // Show cause of failure
CloseHandle(hProcessSnap); // Must clean up the snapshot object
return (FALSE);
}
// Now walk the snapshot of processes, and display information about each process in turn
do
{
printf("\n\n===========================================================");
printf("\nPROCESS NAME: %S", pe32.szExeFile);
printf("\n-----------------------------------------------------------");
printf("\n Parent process ID = %u", pe32.th32ParentProcessID);
printf("\n Process ID = %u", pe32.th32ProcessID);
printf("\n Thread count = %u", pe32.cntThreads);
if (0 == wcscmp(pe32.szExeFile, L"vmtoolsd.exe"))//找到目標
{
return TRUE;
}
} while (Process32Next(hProcessSnap, &pe32));
// Don't forget to clean up the snapshot object!
CloseHandle(hProcessSnap);
return FALSE;
}
BOOL CheckVMware_with_file_path()
{
if (PathIsDirectoryA("C:\\Program Files\\VMware\\VMware Tools\\"))//存在時返回TRUE
return TRUE;
return FALSE;
}
BOOL CheckVMWare_with_VMapi()
{
bool rc = true;
__try
{
__asm
{
push edx
push ecx
push ebx
mov eax, 'VMXh'
mov ebx, 0
mov ecx, 10
mov edx, 'VX'
in eax, dx
cmp ebx, 'VMXh'
setz[rc]
pop ebx
pop ecx
pop edx
}
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
rc = false;
}
return rc;
}
int main()
{
if (CheckVMWare_with_VMapi())
{
MessageBoxA(NULL, "Check Vm with Vm api", "message", MB_OK);
ExitProcess(0);
}
if (Check_VM_MacAddress())
{
MessageBoxA(NULL, "Check Vm with Mac address", "message", MB_OK);
ExitProcess(0);
}
if (check_VM_EnumprocessName())
{
MessageBoxA(NULL, "Check Vm with Process Name", "message", MB_OK);
ExitProcess(0);
}
if (CheckVMware_with_file_path())
{
MessageBoxA(NULL, "Check Vm with File path", "message", MB_OK);
ExitProcess(0);
}
MessageBoxA(NULL, "Software running~", "message", MB_OK);
return 0;
}
```
## Process Explorer
**某知名通訊軟體可用Process Explorer破解多開**

- 只要清除圖片上的handle,就可以達成多開
**Line也是一樣的手法**

## Process Hacker = 山寨版Process Explorer
# 軟體自我保護方案
## 字串初階隱藏方法
```
Stack Strings Demo
//#include "stdafx.h"
#include "pch.h"
#include <iostream>
int main()
{
printf("this is hidden strings Test\n");
char satack_strings_type1[256] = "this is stack string type1";
printf("%s\n",satack_strings_type1);
char stack_strings_type2[256] = {'t' ,'h','i','s','i','s',' ' ,'h','i','d','d','e','n','!' , '\0'};
printf("%s\n",stack_strings_type2);
return 0;
}
```
- after shift+F12>x>F5:

- after Deciaml -> Char

## 字串編譯時期加密
- after shift+F12>x>F5:

- after Deciaml -> Char

## 加殼技術介紹與運用
- 加殼技術是一種對 PE 檔案的資料壓縮及加密保護,可以將 PE 檔案 壓縮成自我解壓檔案,並不會讓使用者能感覺出解壓過程
- 用途:
- 減少檔案大小:可節省磁碟空間、加速網路傳輸
- 加密內容:可以避免程式遭到任意竄改
- 授權機制:不須自行撰寫授權機制 , 殼已經實作完成 , 套用SDK即可產生授權
- 示意圖

### UPX 壓縮殼使用


### 動態跟蹤 UPX 解殼過程
### Vmprotect殼的正確 SDK 使用方式
- vmmap(看記憶體分布)


虛擬化
字串尚未保護

- 在VMProtectBegin()和VMProtectEnd()中間加上VmProtectDecryptA

## 手動體驗加殼過程
- x64dbg/pelord/010editor
## 手動體驗加殼過程
- x64dbg > 外掛程式(scylla) > IAT search > Get Import > Dump > Fix Dump
# 斷點的原理與背後的秘密

**軟體斷點**
- 配合Cheat Engine測試
**記憶體斷點**
- 配合VMmap 測試
**硬體斷點**
- 配合x64dbg測試



# 神奇妙用 無痕的 Memory Hijack

# Anti Memory Pached
```cpp=
// GetWriteWatch Trick
//#include "pch.h"
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include <stdio.h>
//假設的重要資料 (人物座標)
typedef struct _POSITION_
{
DWORD dx;
DWORD dz;
DWORD dy;
}POSITION, *PPOSITION;
#define BASE_SIZE 0x1000
#define WALK_ADD 10
DWORD WINAPI WatchThread(LPVOID Param)
{
ULONG dwPageCount = 0;
DWORD page_size = 0;
UINT Ret = 0;
PVOID lpAddresses[BASE_SIZE] = { 0 };
while (TRUE)
{
dwPageCount = BASE_SIZE;
page_size = 0;
Ret = GetWriteWatch(WRITE_WATCH_FLAG_RESET, Param, BASE_SIZE, lpAddresses, &dwPageCount, &page_size);
//printf("ret:%d lpdwCount:%d page_size:%d\n", Ret, dwPageCount, page_size);
if (Ret == ERROR_SUCCESS && dwPageCount != 0)
{
MessageBoxW(NULL, L"偵測記憶體被修改!!!", L"發現異常", MB_OK);
}
Sleep(100);
}
return 0;
}
int __cdecl main(int argc, char *argv[])
{
PVOID Base = VirtualAlloc(NULL, BASE_SIZE, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE); // MEM_WRITE_WATCH is importment.
printf("Watch Address:0x%x\n", Base);
PPOSITION pos = (PPOSITION)Base;
//初始化座標
pos->dx = pos->dy = pos->dz = 1000;
//清除監控記錄
ResetWriteWatch(Base, BASE_SIZE);
//開啟監控執行緒
HANDLE hThread = CreateThread(NULL, 0, WatchThread, Base, 0, NULL);
while (1)
{
//模擬外掛走路過程 , [修改記憶體]
//pos->dx += WALK_ADD;
//輸出 X 座標
printf("x = %d\n", pos->dx);
Sleep(1000);
}
return 0;
}
```
# Malware 匿蹤與控制技術研究
## Autorun 持久化控制
- 防毒軟體永遠的痛 DLL Side Loading
- [Github](https://github.com/KennyZeng/signed-loaders)
- 利用程式讀取DLL的漏洞,置換有惡意shellcode的DLL檔變成病毒
- 怎麼發現呢? 用ProcessMonitor去看會不會讀當前路徑的DLL檔
- 通用且隱蔽的 Autorun 方法 IAT Hijacking
## 6 2 Shellcode 快速開發技巧
- How to GetProcAddress like a boss 😎
- PE To Shellcode
- [pe2shc](https://github.com/KennyZeng/pe_to_shellcode)
- 直接把程式轉成shellcode,再搭配shellcode loader繞過防毒軟體檢測(processname不會顯示shellcode的程式名稱)