www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 芯聞號(hào) > 充電吧
[導(dǎo)讀]許多用戶都有過用Windows自帶的任務(wù)管理器查看所有進(jìn)程的經(jīng)驗(yàn),并且很多人都認(rèn)為在任務(wù)管理器中隱藏進(jìn)程是不可能的。而實(shí)際上,進(jìn)程隱藏是再簡(jiǎn)單不過的事情了。有許多可用的方法和參考源碼可以達(dá)到進(jìn)程隱藏的

許多用戶都有過用Windows自帶的任務(wù)管理器查看所有進(jìn)程的經(jīng)驗(yàn),并且很多人都認(rèn)為在任務(wù)管理器中隱藏進(jìn)程是不可能的。而實(shí)際上,進(jìn)程隱藏是再簡(jiǎn)單不過的事情了。有許多可用的方法和參考源碼可以達(dá)到進(jìn)程隱藏的目的。令我驚奇的是只有很少一部分的木馬使用了這種技術(shù)。估計(jì)1000個(gè)木馬中僅有1個(gè)是進(jìn)程隱藏的。我認(rèn)為木馬的作者太懶了,因?yàn)殡[藏進(jìn)程需要進(jìn)行的額外工作僅僅是對(duì)源代碼的拷貝-粘貼。所以我們應(yīng)該期待即將到來的會(huì)隱藏進(jìn)程的木馬。

自然地,也就有必要研究進(jìn)程隱藏的對(duì)抗技術(shù)。殺毒軟件和防火墻制造商就像他們的產(chǎn)品不能發(fā)現(xiàn)隱藏進(jìn)程一樣落后了。在少之又少的免費(fèi)工具中,能夠勝任的也只有Klister(僅運(yùn)行于Windows 2000平臺(tái))了。所有其他公司關(guān)注的只有金錢(俄文譯者kao注:不完全正確,F(xiàn)Secure的BlackLight Beta也是免費(fèi)的)。除此之外,所有的這些工具都可以很容易的anti掉。

用程序?qū)崿F(xiàn)隱藏進(jìn)程探測(cè)技術(shù),我們有兩種選擇:
* 基于某種探測(cè)原理找到一種隱藏的方法;
* 基于某個(gè)程序找到一種隱藏的方法,這個(gè)要簡(jiǎn)單一些。

購(gòu)買商業(yè)軟件產(chǎn)品的用戶不能修改程序,這樣可以保證其中綁定的程序的安全運(yùn)行。因此第2種方法提到的程序就是商業(yè)程序的后門(rootkits)(例如hxdef Golden edition)。唯一的解決方案是創(chuàng)建一個(gè)免費(fèi)的隱藏進(jìn)程檢測(cè)的開源項(xiàng)目,這個(gè)程序使用幾種不同的檢測(cè)方法,這樣可以發(fā)現(xiàn)使用某一種方法進(jìn)行隱藏的進(jìn)程。任何一個(gè)用戶都可以抵擋某程序的捆綁程序,當(dāng)然那要得到程序的源代碼并且按照自己的意愿進(jìn)行修改。

在這篇文章中我將討論探測(cè)隱藏進(jìn)程的基本方法,列出該方法的示例代碼,并創(chuàng)建一個(gè)能夠檢測(cè)上面我們提到的隱藏進(jìn)程的程序。

在用戶態(tài)(ring 3)檢測(cè)
我們從簡(jiǎn)單的用戶態(tài)(ring 3)檢測(cè)開始,不使用驅(qū)動(dòng)。事實(shí)上,每一個(gè)進(jìn)程都會(huì)留下某種活動(dòng)的痕跡,根據(jù)這些痕跡,我們就可以檢測(cè)到隱藏的進(jìn)程。這些痕跡包括進(jìn)程打開的句柄、窗口和創(chuàng)建的系統(tǒng)對(duì)象。要避開這種檢測(cè)技術(shù)是非常簡(jiǎn)單的,但是這樣做需要留意進(jìn)程留下所有痕跡,這種模式?jīng)]有被用在任何一個(gè)公開發(fā)行的后門(rootkits)上。(不幸的是內(nèi)部版本沒有對(duì)我開放)。用戶態(tài)方法容易實(shí)現(xiàn),使用安全,并且能夠得到很好的效果,因此這種方法不應(yīng)該被忽略。

首先我們定義一下用到的數(shù)據(jù),如下:

Code:

type
?PProcList = ^TProcList;
?TProcList = packed record
?? NextItem: pointer;
?? ProcName: array [0..MAX_PATH] of Char;
?? ProcId: dword;
?? ParrentId: dword;
?end;


使用ToolHelp API獲得所有進(jìn)程列表
定義一下獲得進(jìn)程列表的函數(shù)。我們要比較這個(gè)結(jié)果和通過其他途徑得到的結(jié)果:

Code:

{
?Acquiring list of processes by using ToolHelp API.
}
procedure GetToolHelpProcessList(var List: PListStruct);
var
?Snap: dword;
?Process: TPROCESSENTRY32;
?NewItem: PProcessRecord;
begin
? Snap := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
? if Snap <> INVALID_HANDLE_VALUE then
???? begin
????? Process.dwSize := SizeOf(TPROCESSENTRY32);
????? if Process32First(Snap, Process) then
???????? repeat
????????? GetMem(NewItem, SizeOf(TProcessRecord));
????????? ZeroMemory(NewItem, SizeOf(TProcessRecord));
????????? NewItem^.ProcessId? := Process.th32ProcessID;
????????? NewItem^.ParrentPID := Process.th32ParentProcessID;
????????? lstrcpy(@NewItem^.ProcessName, Process.szExeFile);
????????? AddItem(List, NewItem);
???????? until not Process32Next(Snap, Process);
????? CloseHandle(Snap);
???? end;
end;

很明顯,這不會(huì)發(fā)現(xiàn)任何隱藏進(jìn)程,所以這個(gè)函數(shù)只可以用來做探測(cè)隱藏進(jìn)程的參考。

?

通過使用Native API獲得進(jìn)程列表
再深一個(gè)層次的掃描我們要通過Native API ZwQuerySystemInformation獲得進(jìn)程列表。雖然在這個(gè)級(jí)別(ring 0)什么也發(fā)現(xiàn)不了,但是我們?nèi)匀粦?yīng)該檢查一下。(prince注:有點(diǎn)令人費(fèi)解,原文如下:The next scanning level will be acquisition a list of processes through

ZwQuerySystemInformation (Native API). It is improbable that something will be found out at this level but we should check it

anyway.)

Code:

{
?Acquiring list of processes by using ZwQuerySystemInformation.
}
procedure GetNativeProcessList(var List: PListStruct);
var
?Info: PSYSTEM_PROCESSES;
?NewItem: PProcessRecord;
?Mem: pointer;
begin
? Info := GetInfoTable(SystemProcessesAndThreadsInformation);
? Mem := Info;
? if Info = nil then Exit;
? repeat
?? GetMem(NewItem, SizeOf(TProcessRecord));
?? ZeroMemory(NewItem, SizeOf(TProcessRecord));
?? lstrcpy(@NewItem^.ProcessName,
?????????? PChar(WideCharToString(Info^.ProcessName.Buffer)));
?? NewItem^.ProcessId? := Info^.ProcessId;
?? NewItem^.ParrentPID := Info^.InheritedFromProcessId;
?? AddItem(List, NewItem);
?? Info := pointer(dword(info) + info^.NextEntryDelta);
? until Info^.NextEntryDelta = 0;
? VirtualFree(Mem, 0, MEM_RELEASE);
end;


通過進(jìn)程打開的句柄獲得進(jìn)程列表。
許多隱藏進(jìn)程無法隱藏他們打開的句柄,因此我們可以通過使用ZwQuerySystemInformation函數(shù)枚舉打開的句柄來構(gòu)建進(jìn)程列表。

Code:

{
? Acquiring the list of processes by using list of opened handles.
? Returns only ProcessId.
}
procedure GetHandlesProcessList(var List: PListStruct);
var
?Info: PSYSTEM_HANDLE_INFORMATION_EX;
?NewItem: PProcessRecord;
?r: dword;
?OldPid: dword;
begin
? OldPid := 0;
? Info := GetInfoTable(SystemHandleInformation);
? if Info = nil then Exit;
? for r := 0 to Info^.NumberOfHandles do
??? if Info^.Information[r].ProcessId <> OldPid then
???? begin
?????? OldPid := Info^.Information[r].ProcessId;
?????? GetMem(NewItem, SizeOf(TProcessRecord));
?????? ZeroMemory(NewItem, SizeOf(TProcessRecord));
?????? NewItem^.ProcessId?? := OldPid;
?????? AddItem(List, NewItem);
???? end;
? VirtualFree(Info, 0, MEM_RELEASE);
end;

到現(xiàn)在我們已經(jīng)可能發(fā)現(xiàn)一些東西了,但是我們不應(yīng)該依賴于像隱藏進(jìn)程一樣簡(jiǎn)單的隱藏句柄的檢查結(jié)果,盡管有些人甚至忘記隱藏他們。


通過列舉創(chuàng)建的窗口來得到進(jìn)程列表。
可以將那在系統(tǒng)中注冊(cè)窗口的進(jìn)程用GetWindowThreadProcessId構(gòu)建進(jìn)程列表。

Code:

{
? Acquiring the list of processes by using list of windows.
? Returns only ProcessId.
}
procedure GetWindowsProcessList(var List: PListStruct);

?function EnumWindowsProc(hwnd: dword; PList: PPListStruct): bool; stdcall;
?var
? ProcId: dword;
? NewItem: PProcessRecord;
?begin
? GetWindowThreadProcessId(hwnd, ProcId);
?? if not IsPidAdded(PList^, ProcId) then
??? begin
???? GetMem(NewItem, SizeOf(TProcessRecord));
???? ZeroMemory(NewItem, SizeOf(TProcessRecord));
???? NewItem^.ProcessId?? := ProcId;
???? AddItem(PList^, NewItem);
? end;
? Result := true;
?end;

begin
?EnumWindows(@EnumWindowsProc, dword(@List));
end;

幾乎沒有人會(huì)隱藏窗口,因此這種檢查可以檢測(cè)某些進(jìn)程,但是我們不應(yīng)該相信這種檢測(cè)。

直接通過系統(tǒng)調(diào)用得到進(jìn)程列表。
在用戶態(tài)隱藏進(jìn)程,一個(gè)普遍的做法是使用代碼注入(code-injection)技術(shù)和在所有進(jìn)程中攔截ntdll.dll中的ZwQuerySystemInformation函數(shù)。
ntdll中的函數(shù)實(shí)際上對(duì)應(yīng)著系統(tǒng)內(nèi)核中的函數(shù)和系統(tǒng)調(diào)用(Windows 2000 中的2Eh中斷或者Windows XP中的sysenter指令),因此大多數(shù)簡(jiǎn)單又有效的關(guān)于那些用戶級(jí)的隱藏進(jìn)程的檢測(cè)方法就是直接使用系統(tǒng)調(diào)用而不是使用API函數(shù)。

Windows XP中ZwQuerySystemInformation函數(shù)的替代函數(shù)看起來是這個(gè)樣子:

Code:

{
?ZwQuerySystemInformation for Windows XP.
}
Function XpZwQuerySystemInfoCall(ASystemInformationClass: dword;
???????????????????????????????? ASystemInformation: Pointer;
???????????????????????????????? ASystemInformationLength: dword;
???????????????????????????????? AReturnLength: pdword): dword; stdcall;
asm
?pop ebp
?mov eax, $AD
?call @SystemCall
?ret $10
?@SystemCall:
?mov edx, esp
?sysenter
end;


由于不同的系統(tǒng)調(diào)用機(jī)制,Windows 2000的這部分代碼看起來有些不同。

Code:

{
? Системный вызов ZwQuerySystemInformation для Windows 2000.
}
Function Win2kZwQuerySystemInfoCall(ASystemInformationClass: dword;
??????????????????????????????????? ASystemInformation: Pointer;
??????????????????????????????????? ASystemInformationLength: dword;
??????????????????????????????????? AReturnLength: pdword): dword; stdcall;
asm
?pop ebp
?mov eax, $97
?lea edx, [esp + $04]
?int $2E
?ret $10
end;


現(xiàn)在有必要使用上面提到的函數(shù)而不是ntdll來枚舉系統(tǒng)進(jìn)程了。實(shí)現(xiàn)的代碼如下:

Code:

{
? Acquiring the list of processes by use of a direct system call
? ZwQuerySystemInformation.
}
procedure GetSyscallProcessList(var List: PListStruct);
var
?Info: PSYSTEM_PROCESSES;
?NewItem: PProcessRecord;
?mPtr: pointer;
?mSize: dword;
?St: NTStatus;
begin
?mSize := $4000;
?repeat
? GetMem(mPtr, mSize);
? St := ZwQuerySystemInfoCall(SystemProcessesAndThreadsInformation,
????????????????????????????? mPtr, mSize, nil);
? if St = STATUS_INFO_LENGTH_MISMATCH then
??? begin
????? FreeMem(mPtr);
????? mSize := mSize * 2;
??? end;
?until St <> STATUS_INFO_LENGTH_MISMATCH;
?if St = STATUS_SUCCESS then
? begin
??? Info := mPtr;
??? repeat
???? GetMem(NewItem, SizeOf(TProcessRecord));
???? ZeroMemory(NewItem, SizeOf(TProcessRecord));
???? lstrcpy(@NewItem^.ProcessName,
???????????? PChar(WideCharToString(Info^.ProcessName.Buffer)));
???? NewItem^.ProcessId? := Info^.ProcessId;
???? NewItem^.ParrentPID := Info^.InheritedFromProcessId;
???? Info := pointer(dword(info) + info^.NextEntryDelta);
???? AddItem(List, NewItem);
??? until Info^.NextEntryDelta = 0;
? end;
?FreeMem(mPtr);
end;

這種方法能檢測(cè)幾乎100%的用戶態(tài)的后門(rootkits),例如hxdef的所有版本(包括黃金版)。

通過分析相關(guān)的句柄得到進(jìn)程列表。
基于枚舉句柄的方法。這個(gè)方法的實(shí)質(zhì)并不是查找進(jìn)程打開的句柄,而是查找同該進(jìn)程相關(guān)的其他進(jìn)程的句柄。這些句柄可以是進(jìn)程句柄也可以是線程句柄。當(dāng)找到進(jìn)程句柄,我們就可以用ZwQueryInformationProcess函數(shù)得到進(jìn)程的PID。對(duì)于線程句柄,我們可以通過ZwQueryInformationThread得到進(jìn)程ID。存在于系統(tǒng)中的所有進(jìn)程都是由某些進(jìn)程產(chǎn)生的,因此父進(jìn)程擁有他們的句柄(除了那些已經(jīng)被關(guān)閉的句柄),對(duì)于Win32子系統(tǒng)服務(wù)器(csrss.exe)來說所有存在的進(jìn)程的句柄都是可以訪問的。另外,Windows NT大量使用Job objects(prince: 任務(wù)對(duì)象?姑且這么翻譯吧,有不妥的地方請(qǐng)指教),任務(wù)對(duì)象可以關(guān)聯(lián)進(jìn)程(比如屬于某用戶或服務(wù)的所有進(jìn)程),因此當(dāng)找到任務(wù)對(duì)象的句柄,我們就可以利用它得到與之關(guān)聯(lián)的所有進(jìn)程的ID。使用QueryInformationJobObject和信息類的函數(shù)JobObjectBasicProcessIdList就可以實(shí)現(xiàn)上述功能。利用分析進(jìn)程相關(guān)的句柄得到進(jìn)程列表的實(shí)現(xiàn)代碼如下:

Code:

{
?Acquiring the list of processes by analyzing handles in other processes.
}
procedure GetProcessesFromHandles(var List: PListStruct; Processes, Jobs, Threads: boolean);
var
?HandlesInfo: PSYSTEM_HANDLE_INFORMATION_EX;
?ProcessInfo: PROCESS_BASIC_INFORMATION;
?hProcess : dword;
?tHandle: dword;
?r, l???? : integer;
?NewItem: PProcessRecord;
?Info: PJOBOBJECT_BASIC_PROCESS_ID_LIST;
?Size: dword;
?THRInfo: THREAD_BASIC_INFORMATION;
begin
?HandlesInfo := GetInfoTable(SystemHandleInformation);
?if HandlesInfo <> nil then
?for r := 0 to HandlesInfo^.NumberOfHandles do
?? if HandlesInfo^.Information[r].ObjectTypeNumber in [OB_TYPE_PROCESS, OB_TYPE_JOB, OB_TYPE_THREAD] then
??? begin
????? hProcess? := OpenProcess(PROCESS_DUP_HANDLE, false,
?????????????????????????????? HandlesInfo^.Information[r].ProcessId);
??????????????????????????????
????? if DuplicateHandle(hProcess, HandlesInfo^.Information[r].Handle,
???????????????????????? INVALID_HANDLE_VALUE, @tHandle, 0, false,
???????????????????????? DUPLICATE_SAME_ACCESS) then
??????????? begin
???????????? case HandlesInfo^.Information[r].ObjectTypeNumber of
?????????????? OB_TYPE_PROCESS : begin
???????????????????? if Processes and (HandlesInfo^.Information[r].ProcessId = CsrPid) then
???????????????????? if ZwQueryInformationProcess(tHandle, ProcessBasicInformation,
??????????????????????????????????????????? @ProcessInfo,
??????????????????????????????????????????? SizeOf(PROCESS_BASIC_INFORMATION),
??????????????????????????????????????????? nil) = STATUS_SUCCESS then
???????????????????? if not IsPidAdded(List, ProcessInfo.UniqueProcessId) then
??????????????????????? begin
??????????????????????? GetMem(NewItem, SizeOf(TProcessRecord));
??????????????????????? ZeroMemory(NewItem, SizeOf(TProcessRecord));
??????????????????????? NewItem^.ProcessId?? := ProcessInfo.UniqueProcessId;
??????????????????????? NewItem^.ParrentPID? := ProcessInfo.InheritedFromUniqueProcessId;
??????????????????????? AddItem(List, NewItem);
??????????????????????? end;
???????????????????? end;

?????????????? OB_TYPE_JOB???? : begin
????????????????????????????????? if Jobs then
?????????????????????????????????? begin
??????????????????????????????????? Size := SizeOf(JOBOBJECT_BASIC_PROCESS_ID_LIST) + 4 * 1000;
??????????????????????????????????? GetMem(Info, Size);
??????????????????????????????????? Info^.NumberOfAssignedProcesses := 1000;
??????????????????????????????????? if QueryInformationJobObject(tHandle, JobObjectBasicProcessIdList,
???????????????????????????????????????????????????????????????? Info, Size, nil) then
?????????????????????????????????????? for l := 0 to Info^.NumberOfProcessIdsInList - 1 do
???????????????????????????????????????? if not IsPidAdded(List, Info^.ProcessIdList[l]) then
?????????????????????????????????????????? begin
??????????????????????????????????????????? GetMem(NewItem, SizeOf(TProcessRecord));
??????????????????????????????????????????? ZeroMemory(NewItem, SizeOf(TProcessRecord));
??????????????????????????????????????????? NewItem^.ProcessId?? := Info^.ProcessIdList[l];
??????????????????????????????????????????? AddItem(List, NewItem);
?????????????????????????????????????????? end;
??????????????????????????????????? FreeMem(Info);
?????????????????????????????????? end;
????????????????????????????????? end;

?????????????? OB_TYPE_THREAD? : begin
????????????????????????????????? if Threads then
????????????????????????????????? if ZwQueryInformationThread(tHandle, THREAD_BASIC_INFO,
????????????????????????????????????????????????????????????? @THRInfo,
????????????????????????????????????????????????????????????? SizeOf(THREAD_BASIC_INFORMATION),
????????????????????????????????????????????????????????????? nil) = STATUS_SUCCESS then
??????????????????????????????????? if not IsPidAdded(List, THRInfo.ClientId.UniqueProcess) then
???????????????????????????????????? begin
?????????????????????????????????????? GetMem(NewItem, SizeOf(TProcessRecord));
?????????????????????????????????????? ZeroMemory(NewItem, SizeOf(TProcessRecord));
?????????????????????????????????????? NewItem^.ProcessId?? := THRInfo.ClientId.UniqueProcess;
?????????????????????????????????????? AddItem(List, NewItem);
???????????????????????????????????? end;
???????????????????????????????? end;

???????????? end;
???????????? CloseHandle(tHandle);
??????????? end;
????????? CloseHandle(hProcess);
??????? end;
?VirtualFree(HandlesInfo, 0, MEM_RELEASE);
end;

不幸的是,上面提到的這些方法有些只能得到進(jìn)程ID,而不能得到進(jìn)程名字。因此,我們還需要通過進(jìn)程ID得到進(jìn)程的名稱。當(dāng)然,當(dāng)這些進(jìn)程是隱藏進(jìn)程的時(shí)候我們就不能使用ToolHelp API來實(shí)現(xiàn)。所以我們應(yīng)該訪問進(jìn)程內(nèi)存通過讀取該進(jìn)程的PEB得到進(jìn)程名稱。PEB地址可以用ZwQueryInformationProcess函數(shù)獲得。以上所說的功能實(shí)現(xiàn)代碼如下:

Code:

function GetNameByPid(Pid: dword): string;
var
?hProcess, Bytes: dword;
?Info: PROCESS_BASIC_INFORMATION;
?ProcessParametres: pointer;
?ImagePath: TUnicodeString;
?ImgPath: array[0..MAX_PATH] of WideChar;
begin
?Result := ';
?ZeroMemory(@ImgPath, MAX_PATH * SizeOf(WideChar));
?hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, Pid);
?if ZwQueryInformationProcess(hProcess, ProcessBasicInformation, @Info,
????????????????????????????? SizeOf(PROCESS_BASIC_INFORMATION), nil) = STATUS_SUCCESS then
? begin
?? if ReadProcessMemory(hProcess, pointer(dword(Info.PebBaseAddress) + $10),
??????????????????????? @ProcessParametres, SizeOf(pointer), Bytes) and
????? ReadProcessMemory(hProcess, pointer(dword(ProcessParametres) + $38),
??????????????????????? @ImagePath, SizeOf(TUnicodeString), Bytes)? and
????? ReadProcessMemory(hProcess, ImagePath.Buffer, @ImgPath,
??????????????????????? ImagePath.Length, Bytes) then
??????? begin
????????? Result := ExtractFileName(WideCharToString(ImgPath));
??????? end;
?? end;
?CloseHandle(hProcess);
end;


當(dāng)然,用戶態(tài)隱藏進(jìn)程的檢測(cè)方法不止這些,還可以想一些稍微復(fù)雜一點(diǎn)的新方法(比如,用SetWindowsHookEx函數(shù)對(duì)可訪問進(jìn)程的注入和當(dāng)我們的DLL并成功加載后對(duì)進(jìn)程列表的分析),但是現(xiàn)在我們將用上面提到的方法來解決問題。這些方法的優(yōu)點(diǎn)是他們可以簡(jiǎn)單地編程實(shí)現(xiàn),并且除了可以檢測(cè)到用戶態(tài)的隱藏進(jìn)程,還可以檢測(cè)到少數(shù)的在內(nèi)核態(tài)實(shí)現(xiàn)的隱藏進(jìn)程... 要實(shí)現(xiàn)真正可靠的進(jìn)程隱藏工具我們應(yīng)該使用Windows未公開的內(nèi)核數(shù)據(jù)結(jié)構(gòu)編寫內(nèi)核驅(qū)動(dòng)程序。


內(nèi)核態(tài)(Ring 0)的檢測(cè)
恭喜你,我們終于開始進(jìn)行內(nèi)核態(tài)隱藏進(jìn)程的分析。內(nèi)核態(tài)的檢測(cè)方法同用戶態(tài)的檢測(cè)方法的主要區(qū)別是所有的進(jìn)程列表都沒有使用API調(diào)用而是直接來自系統(tǒng)內(nèi)部數(shù)據(jù)結(jié)構(gòu)。在這些檢測(cè)方法下隱藏進(jìn)程要困難得多,因?yàn)樗鼈兌际腔谕琖indows內(nèi)核相同的原理實(shí)現(xiàn)的,并且從這些內(nèi)核數(shù)據(jù)結(jié)構(gòu)中刪除進(jìn)程將導(dǎo)致該進(jìn)程完全失效。

內(nèi)核中的進(jìn)程是什么?每一個(gè)進(jìn)程都有自己的地址空間,描述符,線程等,內(nèi)核的數(shù)據(jù)結(jié)構(gòu)就涉及這些東西。每一個(gè)進(jìn)程都是由EPROCESS結(jié)構(gòu)描述,而所有進(jìn)程的結(jié)構(gòu)都被一個(gè)雙向循環(huán)鏈表維護(hù)。進(jìn)程隱藏的一個(gè)方法就是改變進(jìn)程結(jié)構(gòu)鏈表的指針,使得鏈表枚舉跳過自身達(dá)到進(jìn)程隱藏的目的。避開進(jìn)程枚舉并不影響進(jìn)程的任何功能。無論怎樣,EPROCESS結(jié)構(gòu)總是存在的,對(duì)一個(gè)進(jìn)程的正常功能來說它是必要的。在內(nèi)核態(tài)檢測(cè)隱藏進(jìn)程的主要方法就是對(duì)這個(gè)結(jié)構(gòu)的檢查。

我們應(yīng)該定義一下將要儲(chǔ)存的進(jìn)程信息的變量格式。這個(gè)變量格式應(yīng)該很方便地存儲(chǔ)來自驅(qū)動(dòng)的數(shù)據(jù)(附錄)。結(jié)構(gòu)定義如下:

Code:

typedef struct _ProcessRecord
{
?? ULONG?????? Visibles;
?? ULONG?????? SignalState;
?? BOOLEAN???? Present;
?? ULONG?????? ProcessId;
?? ULONG?????? ParrentPID;
?? PEPROCESS?? pEPROCESS;
?? CHAR??????? ProcessName[256];
} TProcessRecord, *PProcessRecord;

應(yīng)該為這些結(jié)構(gòu)分配連續(xù)的大塊的內(nèi)存,并且不設(shè)置最后一個(gè)結(jié)構(gòu)的Present標(biāo)志。

在內(nèi)核中使用ZwQuerySystemInformation函數(shù)得到進(jìn)程列表。

我們先從最簡(jiǎn)單的方式開始,通過ZwQuerySystemInformation函數(shù)得到進(jìn)程列表:

Code:

PVOID GetNativeProcessList(ULONG *MemSize)
{
?? ULONG PsCount = 0;
?? PVOID Info = GetInfoTable(SystemProcessesAndThreadsInformation);
?? PSYSTEM_PROCESSES Proc;
?? PVOID Mem = NULL;
?? PProcessRecord Data;

?? if (!Info) return NULL; else Proc = Info;

?? do
?? {
????? Proc = (PSYSTEM_PROCESSES)((ULONG)Proc + Proc->NextEntryDelta);??
????? PsCount++;
?? } while (Proc->NextEntryDelta);

?? *MemSize = (PsCount + 1) * sizeof(TProcessRecord);

?? Mem = ExAllocatePool(PagedPool, *MemSize);

?? if (!Mem) return NULL; else Data = Mem;
??
?? Proc = Info;
?? do
?? {
????? Proc = (PSYSTEM_PROCESSES)((ULONG)Proc + Proc->NextEntryDelta);
????? wcstombs(Data->ProcessName, Proc->ProcessName.Buffer, 255);
????? Data->Present??? = TRUE;
????? Data->ProcessId? = Proc->ProcessId;
????? Data->ParrentPID = Proc->InheritedFromProcessId;
????? PsLookupProcessByProcessId((HANDLE)Proc->ProcessId, &Data->pEPROCESS);
????? ObDereferenceObject(Data->pEPROCESS);
????? Data++;
?? } while (Proc->NextEntryDelta);

?? Data->Present = FALSE;

?? ExFreePool(Info);

?? return Mem;
}

以這個(gè)函數(shù)做參考,任何內(nèi)核態(tài)的隱藏進(jìn)程都不會(huì)被檢測(cè)出來,但是所有的用戶態(tài)隱藏進(jìn)程如hxdef是絕對(duì)逃不掉的。

在下面的代碼中我們可以簡(jiǎn)單地用GetInfoTable函數(shù)來得到信息。為了防止有人問那是什么東西,下面列出完整的函數(shù)代碼。

Code:

/*
? Receiving buffer with results from ZwQuerySystemInformation.
*/
PVOID GetInfoTable(ULONG ATableType)
{
?? ULONG mSize = 0x4000;
?? PVOID mPtr = NULL;
?? NTSTATUS St;
?? do
?? {
????? mPtr = ExAllocatePool(PagedPool, mSize);
????? memset(mPtr, 0, mSize);
????? if (mPtr)
????? {
???????? St = ZwQuerySystemInformation(ATableType, mPtr, mSize, NULL);
????? } else return NULL;
????? if (St == STATUS_INFO_LENGTH_MISMATCH)
????? {
???????? ExFreePool(mPtr);
???????? mSize = mSize * 2;
????? }
?? } while (St == STATUS_INFO_LENGTH_MISMATCH);
?? if (St == STATUS_SUCCESS) return mPtr;
?? ExFreePool(mPtr);
?? return NULL;
}

我認(rèn)為這段代碼是很容易理解的...

利用EPROCESS結(jié)構(gòu)的雙向鏈表得到進(jìn)程列表。
我們又進(jìn)了一步。接下來我們將通過遍歷EPROCESS結(jié)構(gòu)的雙向鏈表來得到進(jìn)程列表。鏈表的表頭是PsActiveProcessHead,因此要想正確地枚舉進(jìn)程我們需要找到這個(gè)并沒有被導(dǎo)出的符號(hào)。在這之前我們應(yīng)該知道System進(jìn)程是所有進(jìn)程列表中的第一個(gè)進(jìn)程。在DriverEntry例程開始時(shí)我們需要用PsGetCurrentProcess函數(shù)得到當(dāng)前進(jìn)程的指針(使用SC管理器的API或者ZwLoadDriver函數(shù)加載的驅(qū)動(dòng)始終都是加載到System進(jìn)程的上下文中的),BLink在ActiveProcessLinks中的偏移將指向PsActiveProcessHead。像這樣:

Code:

PsActiveProcessHead = *(PVOID *)((PUCHAR)PsGetCurrentProcess + ActiveProcessLinksOffset + 4);


現(xiàn)在就可以遍歷這個(gè)雙向鏈表來創(chuàng)建進(jìn)程列表了:

Code:

PVOID GetEprocessProcessList(ULONG *MemSize)
{
?? PLIST_ENTRY Process;
?? ULONG PsCount = 0;
?? PVOID Mem = NULL;
?? PProcessRecord Data;

?? if (!PsActiveProcessHead) return NULL;

?? Process = PsActiveProcessHead->Flink;

?? while (Process != PsActiveProcessHead)
?? {
????? PsCount++;
????? Process = Process->Flink;
?? }

?? PsCount++;

?? *MemSize = PsCount * sizeof(TProcessRecord);

?? Mem = ExAllocatePool(PagedPool, *MemSize);
?? memset(Mem, 0, *MemSize);

?? if (!Mem) return NULL; else Data = Mem;

?? Process = PsActiveProcessHead->Flink;

?? while (Process != PsActiveProcessHead)
?? {
????? Data->Present???? = TRUE;
????? Data->ProcessId?? = *(PULONG)((ULONG)Process - ActPsLink + pIdOffset);
????? Data->ParrentPID? = *(PULONG)((ULONG)Process - ActPsLink + ppIdOffset);
????? Data->SignalState = *(PULONG)((ULONG)Process - ActPsLink + 4);
????? Data->pEPROCESS?? = (PEPROCESS)((ULONG)Process - ActPsLink);
????? strncpy(Data->ProcessName, (PVOID)((ULONG)Process - ActPsLink + NameOffset), 16);?????
????? Data++;
?????? Process = Process->Flink;
??
?? }

?? return Mem;
}

為了得到進(jìn)程名稱、ID和父進(jìn)程ID,我們利用它們?cè)贓PROCESS結(jié)構(gòu)中的偏移地址(pIdOffset, ppIdOffset, NameOffset, ActPsLink)。這些偏移隨著Windows系統(tǒng)版本的不同而不同,因此我們要在進(jìn)程檢測(cè)程序的代碼中進(jìn)行區(qū)分后得到他們正確的值(附錄)。

任何一個(gè)通過API截取方式隱藏的進(jìn)程都將被上面這個(gè)方法檢測(cè)出來。但是如果進(jìn)程是通過DKOM(直接處理內(nèi)核對(duì)象 - Direct Kernel Object Manipulation)方式隱藏,那這個(gè)方法就失效了,因?yàn)檫@種進(jìn)程都被從進(jìn)程鏈表中刪掉了。

通過列舉調(diào)度程序(scheduler)中的線程得到進(jìn)程列表。

對(duì)付這種隱藏進(jìn)程(俄文翻譯kao注:這個(gè)地方原文寫的比較模糊,作者大概的意思應(yīng)該是“使用DKOM的方式檢測(cè)隱藏進(jìn)程”)的其中一種檢測(cè)方式是通過調(diào)度程序(scheduler)中的線程列表來得到進(jìn)程列表。Windows 2000有三個(gè)維護(hù)線程的雙向鏈表(KiWaitInListHead, KiWaitOutListHead, KiDispatcherReadyListHead)。前面兩個(gè)鏈表包含等待某種事件的線程,最后面的鏈表包含的是等待執(zhí)行的線程。我們處理這些鏈表,根據(jù)線程鏈表結(jié)構(gòu)ETHREAD中的偏移就可以得到一個(gè)線程的ETHREAD指針(俄文翻譯kao注:原文中這句話實(shí)在是太難懂了,希望我翻譯的正確)。這個(gè)結(jié)構(gòu)包括了很多進(jìn)程相關(guān)指針,也就是結(jié)構(gòu)_KPROCESS *Process(0x44, 0x150)和結(jié)構(gòu)_EPROCESS *ThreadsProcess(0x22C, 這僅是Windows 2000中的偏移量)。前面兩個(gè)指針對(duì)于一個(gè)線程的功能性沒有任何影響,因此可以很容易修改它們來隱藏進(jìn)程。相反,第三個(gè)指針是當(dāng)切換地址空間時(shí)調(diào)度程序(schedler)使用的指針,所以這個(gè)指針是不能修改的。我們就用它來找到擁有某個(gè)線程的進(jìn)程。

Klister就是使用了這種檢測(cè)方法,它的最大的缺點(diǎn)就是只能在Windows 2000平臺(tái)上工作(但是在這個(gè)平臺(tái)上某個(gè)補(bǔ)丁包也會(huì)讓它失效)。導(dǎo)致這個(gè)情況發(fā)生的原因就是這種程序使用了硬編碼的線程鏈表地址,而在每個(gè)補(bǔ)丁包中這些地址可能都是不同的。

在程序中使用硬編碼地址是很糟糕的解決方案,操作系統(tǒng)的升級(jí)就會(huì)使你的程序無法正常工作,要盡量避免使用這種檢測(cè)方法。所以應(yīng)該通過分析那些使用了這些鏈表的內(nèi)核函數(shù)來動(dòng)態(tài)地得到它們的地址。

首先我們?cè)囋嚳丛赪indows 2000平臺(tái)上找出KiWaitInListHead和KiWaitOutListHead.使用鏈表地址的函數(shù)KeWaitForSingleObject代碼如下:

Code:

.text:0042DE56???????????????? mov???? ecx, offset KiWaitInListHead
.text:0042DE5B???????????????? test??? al, al
.text:0042DE5D???????????????? jz????? short loc_42DE6E
.text:0042DE5F???????????????? cmp???? byte ptr [esi+135h], 0
.text:0042DE66???????????????? jz????? short loc_42DE6E
.text:0042DE68???????????????? cmp???? byte ptr [esi+33h], 19h
.text:0042DE6C???????????????? jl????? short loc_42DE73
.text:0042DE6E???????????????? mov???? ecx, offset KiWaitOutListHead

我們使用反匯編器(用我寫的LDasm)反匯編KeWaitForSingleObject函數(shù)來獲得這些地址。當(dāng)索引(pOpcode)指向指令“mov ecx,

KiWaitInListHead”,(pOpcode + 5)指向的就是指令“test al, al”,(pOpcode + 24)指向的就是“mov ecx, KiWaitOutListHead”。

這樣我們就可以通過索引(pOpcode + 1)和(pOpcode + 25)正確地得到KiWaitInListHead和KiWaitOutListHead的地址了。搜索地址的代碼

如下:

Code:

void Win2KGetKiWaitInOutListHeads()
{
?? PUCHAR cPtr, pOpcode;
?? ULONG Length;
??
?? for (cPtr = (PUCHAR)KeWaitForSingleObject;
??????? cPtr < (PUCHAR)KeWaitForSingleObject + PAGE_SIZE;
???????????? cPtr += Length)
?? {
????? Length = SizeOfCode(cPtr, &pOpcode);

????? if (!Length) break;
?????
????? if (*pOpcode == 0xB9 && *(pOpcode + 5) == 0x84 && *(pOpcode + 24) == 0xB9)
????? {
???????? KiWaitInListHead? = *(PLIST_ENTRY *)(pOpcode + 1);
???????? KiWaitOutListHead = *(PLIST_ENTRY *)(pOpcode + 25);
???????? break;
????? }
?? }

?? return;
}


在Windows 2000平臺(tái)下我們可以用同樣的方法得到KiDispatcherReadyListHead, 搜索KeSetAffinityThread函數(shù):

Code:

.text:0042FAAA???????????????? lea???? eax, KiDispatcherReadyListHead[ecx*8]
.text:0042FAB1???????????????? cmp???? [eax], eax


搜索KiDispatcherReadyListHead函數(shù)的代碼:

Code:

void Win2KGetKiDispatcherReadyListHead()
{
?? PUCHAR cPtr, pOpcode;
?? ULONG Length;
??
?? for (cPtr = (PUCHAR)KeSetAffinityThread;
??????? cPtr < (PUCHAR)KeSetAffinityThread + PAGE_SIZE;
???????????? cPtr += Length)
?? {
????? Length = SizeOfCode(cPtr, &pOpcode);

????? if (!Length) break;?????

????? if (*(PUSHORT)pOpcode == 0x048D && *(pOpcode + 2) == 0xCD && *(pOpcode + 7) == 0x39)
????? {
???????? KiDispatcherReadyListHead = *(PVOID *)(pOpcode + 3);
???????? break;
????? }
?? }

?? return;
}

不幸的是,Windows XP內(nèi)核完全不同于Windows 2000內(nèi)核。XP下的調(diào)度程序(scheduler)只有兩個(gè)線程鏈表:KiWaitListHead和

KiDispatcherReadyListHead。我們可以通過搜索KeDelayExecutionThread函數(shù)來查找KeWaitListHead:

Code:

.text:004055B5???????????????? mov???? dword ptr [ebx], offset KiWaitListHead
.text:004055BB???????????????? mov???? [ebx+4], eax


搜索代碼如下:

Code:

void XPGetKiWaitListHead()
{
?? PUCHAR cPtr, pOpcode;
?? ULONG Length;

?? for (cPtr = (PUCHAR)KeDelayExecutionThread;
??????? cPtr < (PUCHAR)KeDelayExecutionThread + PAGE_SIZE;
???????????? cPtr += Length)
?? {
????? Length = SizeOfCode(cPtr, &pOpcode);

????? if (!Length) break;

????? if (*(PUSHORT)cPtr == 0x03C7 && *(PUSHORT)(pOpcode + 6) == 0x4389)
????? {
???????? KiWaitInListHead = *(PLIST_ENTRY *)(pOpcode + 2);
???????? break;
????? }
?? }

?? return;
}

最困難的是查找KiDispatcherReadyListHead。主要的問題是KiDispatcherReadyListHead的地址并沒有被任何一個(gè)導(dǎo)出的函數(shù)使用。因此就要

用更加復(fù)雜的搜索算法搞定它。就從KiDispatchInterrupt函數(shù)開始,我們感興趣的地方只有這里:

Code:

.text:00404E72???????????????? mov???? byte ptr [edi+50h], 1
.text:00404E76???????????????? call??? sub_404C5A
.text:00404E7B???????????????? mov???? cl, 1
.text:00404E7D???????????????? call??? sub_404EB9


這段代碼中的第一個(gè)函數(shù)調(diào)用指向的就是包含KiDispatcherReadyListHead引用的函數(shù)。盡管如此,搜索KiDispatcherReadyListHead的地址卻

變的更加復(fù)雜,因?yàn)檫@個(gè)函數(shù)的相關(guān)代碼在Windows XP SP1和SP2中是不同的。在SP2中它是這個(gè)樣子:

Code:

.text:00404CCD???????????????? add???? eax, 60h
.text:00404CD0???????????????? test??? bl, bl
.text:00404CD2???????????????? lea???? edx, KiDispatcherReadyListHead[ecx*8]
.text:00404CD9???????????????? jnz???? loc_401F12
.text:00404CDF???????????????? mov???? esi, [edx+4]


And in SP1:
SP1中是這樣的:

Code:

.text:004180FE???????????????? add???? eax, 60h
.text:00418101???????????????? cmp???? [ebp+var_1], bl
.text:00418104???????????????? lea???? edx, KiDispatcherReadyListHead[ecx*8]
.text:0041810B???????????????? jz????? loc_418760
.text:00418111???????????????? mov???? esi, [edx]

僅僅查找一個(gè)“l(fā)ea”指令是不可靠的,因此我們也應(yīng)該檢查“l(fā)ea”后面的指令(LDasm中的IsRelativeCmd函數(shù))。搜索

KiDispatcherReadyListHead的全部代碼如下:

Code:

void XPGetKiDispatcherReadyListHead()
{
?? PUCHAR cPtr, pOpcode;
?? PUCHAR CallAddr = NULL;
?? ULONG Length;

?? for (cPtr = (PUCHAR)KiDispatchInterrupt;
??????? cPtr < (PUCHAR)KiDispatchInterrupt + PAGE_SIZE;
???????????? cPtr += Length)
?? {
????? Length = SizeOfCode(cPtr, &pOpcode);

????? if (!Length) return;

????? if (*pOpcode == 0xE8 && *(PUSHORT)(pOpcode + 5) == 0x01B1)
????? {
???????? CallAddr = (PUCHAR)(*(PULONG)(pOpcode + 1) + (ULONG)cPtr + Length);
???????? break;
????? }
?? }

?? if (!CallAddr || !MmIsAddressValid(CallAddr)) return;

?? for (cPtr = CallAddr; cPtr < CallAddr + PAGE_SIZE; cPtr += Length)
?? {
????? Length = SizeOfCode(cPtr, &pOpcode);

????? if (!Length) return;

????? if (*(PUSHORT)pOpcode == 0x148D && *(pOpcode + 2) == 0xCD && IsRelativeCmd(pOpcode + 7))
????? {
???????? KiDispatcherReadyListHead = *(PLIST_ENTRY *)(pOpcode + 3);
???????? break;
????? }
?? }

?? return;
}


找到線程鏈表地址之后我們就可以非常簡(jiǎn)單地枚舉出那些進(jìn)程了,代碼如下:

Code:

void ProcessListHead(PLIST_ENTRY ListHead)
{
?? PLIST_ENTRY Item;

?? if (ListHead)
?? {
????? Item = ListHead->Flink;

????? while (Item != ListHead)
????? {
???????? CollectProcess(*(PEPROCESS *)((ULONG)Item + WaitProcOffset));
???????? Item = Item->Flink;
????? }
?? }

?? return;
}

CollectProcess是一個(gè)非常有用的函數(shù),它可以增加一個(gè)進(jìn)程到進(jìn)程列表中去。


通過攔截系統(tǒng)調(diào)用得到進(jìn)程列表。

任何一個(gè)進(jìn)程都要通過API來和系統(tǒng)進(jìn)行交互,而大多數(shù)交互都通過系統(tǒng)調(diào)用傳遞給了內(nèi)核。當(dāng)然,進(jìn)程也可以不使用任何API而存在,但是這

樣一來它也就不能做任何有用(或有害)的事情。一般而言,我們的思路是使用系統(tǒng)調(diào)用管理器攔截系統(tǒng)調(diào)用,然后得到管理器中當(dāng)前進(jìn)程的

EPROCESS指針。應(yīng)該在某段時(shí)間收集指針列表,這個(gè)表不會(huì)包含信息收集時(shí)沒有使用任何系統(tǒng)調(diào)用的進(jìn)程(比如,進(jìn)程的線程都處于等待狀態(tài)

)。

Windows 2000平臺(tái)使用2Eh中斷進(jìn)行系統(tǒng)調(diào)用,因此我們需要修改IDT中的相應(yīng)的中斷描述符來攔截系統(tǒng)調(diào)用,這就要用sidt指令得到IDT在內(nèi)存

中的位置。該指令返回這樣一個(gè)結(jié)構(gòu):

Code:

typedef struct _Idt
{
?? USHORT Size;
?? ULONG? Base;
} TIdt;


修改2Eh中斷向量的代碼如下:

Code:

void Set2kSyscallHook()
{
?? TIdt Idt;
?? __asm
?? {
????? pushad
????? cli
????? sidt [Idt]
????? mov esi, NewSyscall
????? mov ebx, Idt.Base
????? xchg [ebx + 0x170], si
????? rol esi, 0x10
????? xchg [ebx + 0x176], si
????? ror esi, 0x10
????? mov OldSyscall, esi
????? sti
????? popad
?? }
}


當(dāng)然在卸載驅(qū)動(dòng)之前還要保存原始狀態(tài)的信息:

Code:

void Win2kSyscallUnhook()
{
?? TIdt Idt;
?? __asm
?? {
????? pushad
????? cli
????? sidt [Idt]
????? mov esi, OldSyscall
????? mov ebx, Idt.Base
????? mov [ebx + 0x170], si
????? rol esi, 0x10
????? mov [ebx + 0x176], si
????? sti
????? xor eax, eax
????? mov OldSyscall, eax
????? popad
?? }
}


Windows XP使用sysenter/sysexit指令(出現(xiàn)在Pentium 2處理器中)實(shí)現(xiàn)系統(tǒng)調(diào)用。這些指令的功能由model-specific registers(MSR)控制

。系統(tǒng)調(diào)用管理器的地址保存在MSR寄存器,SYSENTER_EIP_MSR(0x176)中。用rdmsr指令讀取MSR寄存器,同時(shí)設(shè)置ECX = 要讀取的寄存器的號(hào)

碼,結(jié)果保存在兩個(gè)積存器EDX:EAX中。在我們這里,SYSENTER_EIP_MSR積存器是32位積存器,所以EDX為0,EAX內(nèi)是系統(tǒng)調(diào)用管理器的地址。

同樣地,我們也可以用wrmsr指令寫MSR積存器。有一個(gè)地方需要注意:當(dāng)寫32位MSR積存器的時(shí)候,EDX應(yīng)該被清空,否則將引起異常并且導(dǎo)致

系統(tǒng)立即崩潰。

考慮到所有的事情之后,替代系統(tǒng)調(diào)用管理器的代碼如下:

Code:

void SetXpSyscallHook()
{
?? __asm
?? {
????? pushad
????? mov ecx, 0x176
????? rdmsr
????? mov OldSyscall, eax
????? mov eax, NewSyscall
????? xor edx, edx
????? wrmsr
????? popad
?? }
}


恢復(fù)原始的系統(tǒng)調(diào)用管理器代碼:

Code:

void XpSyscallUnhook()
{
?? __asm
?? {
????? pushad
????? mov ecx, 0x176
????? mov eax, OldSyscall
????? xor edx, edx
????? wrmsr
????? xor eax, eax
????? mov OldSyscall, eax
????? popad
?? }
}


Windows XP的另外一個(gè)特性是它既可以使用sysenter也可以使用int 2Eh來進(jìn)行系統(tǒng)調(diào)用,所以我們要替換這兩種情況下的系統(tǒng)調(diào)用管理器。

我們的新的系統(tǒng)調(diào)用管理器應(yīng)該得到當(dāng)前進(jìn)程的EPROCESS指針,并且如果是一個(gè)新的進(jìn)程,我們要把這個(gè)新的進(jìn)程加到我們的進(jìn)程列表中。
新的系統(tǒng)調(diào)用管理器代碼如下:

Code:

void __declspec(naked) NewSyscall()
{
?? __asm
?? {
????? pushad
????? pushfd
????? push fs
????? mov di, 0x30
????? mov fs, di
????? mov eax, fs:[0x124]
????? mov eax, [eax + 0x44]
????? push eax
????? call CollectProcess
????? pop fs
????? popfd
????? popad
????? jmp OldSyscall
?? }
}


得到進(jìn)程列表的這段代碼應(yīng)該在某個(gè)時(shí)間段內(nèi)工作,所以我們有這樣的問題:如果在列表中的進(jìn)程被刪除掉,在隨后的時(shí)間內(nèi)我們將保留一些

無效指針,結(jié)果就是檢測(cè)隱藏進(jìn)程失敗或者導(dǎo)致系統(tǒng)BSOD。解決這個(gè)問題的辦法是,用PsSetCreateProcessNotifyRoutine函數(shù)注冊(cè)我們的回調(diào)

函數(shù),這個(gè)回調(diào)函數(shù)將會(huì)在系統(tǒng)創(chuàng)建或者銷毀一個(gè)進(jìn)程的時(shí)候被調(diào)用。當(dāng)進(jìn)程被銷毀時(shí),我們也應(yīng)該把它從我們的表中刪除掉。
回調(diào)函數(shù)的原型如下:

Code:

VOID
(*PCREATE_PROCESS_NOTIFY_ROUTINE) (
??? IN HANDLE? ParentId,
??? IN HANDLE? ProcessId,
??? IN BOOLEAN? Create
??? );

安裝回調(diào)函數(shù)的代碼如下:

Code:

?PsSetCreateProcessNotifyRoutine (NotifyRoutine, FALSE);

取消回調(diào)函數(shù)的代碼:

Code:

?PsSetCreateProcessNotifyRoutine (NotifyRoutine, TRUE);


這里有一個(gè)問題,回調(diào)函數(shù)總是在系統(tǒng)被銷毀的時(shí)候創(chuàng)建,因此我們不可能直接在這個(gè)回調(diào)函數(shù)中刪除進(jìn)程列表中的相應(yīng)進(jìn)程。這樣我們就要

用系統(tǒng)的work items,首先調(diào)用IoAllocateWorkItem函數(shù)為work item分配內(nèi)存,然后調(diào)用IoQueueWorkItem函數(shù)(俄文翻譯者kao注:這一句我

不太確定...)將任務(wù)放置到工作線程隊(duì)列中。在處理過程中我們不僅僅從進(jìn)程列表中刪除掉已經(jīng)終止的進(jìn)程,而且還要加入新創(chuàng)建的線程。處

理代碼如下:

Code:

void WorkItemProc(PDEVICE_OBJECT DeviceObject, PWorkItemStruct Data)
{
?? KeWaitForSingleObject(Data->pEPROCESS, Executive, KernelMode, FALSE, NULL);
?
?? DelItem(&wLastItem, Data->pEPROCESS);

?? ObDereferenceObject(Data->pEPROCESS);

?? IoFreeWorkItem(Data->IoWorkItem);

?? ExFreePool(Data);

?? return;
}


void NotifyRoutine(IN HANDLE? ParentId,
?????????????????? IN HANDLE? ProcessId,
?????????????????? IN BOOLEAN Create)
{
?? PEPROCESS?????? process;
?? PWorkItemStruct Data;

?? if (Create)
?? {
????? PsLookupProcessByProcessId(ProcessId, &process);

????? if (!IsAdded(wLastItem, process)) AddItem(&wLastItem, process);

????? ObDereferenceObject(process);

?? } else
?? {
????? process = PsGetCurrentProcess();
??????
????? ObReferenceObject(process);

????? Data = ExAllocatePool(NonPagedPool, sizeof(TWorkItemStruct));

????? Data->IoWorkItem = IoAllocate

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉