前段時間包同學到一家公司去面試,面試官問他如果不用TerminateProcess如何實現關閉進程,作為一個快要畢業的人來說對這個問題很敏感,畢竟也要面對這關. 現在問題擺在面前,如何解決這個問題,想了想最好的方法就是直接看操作系統怎麼實現 TerminateProcess 的自己實現個就好了,在一定程度上就可以阻止別人通過hook技術來攔截. 這段代碼很早就寫好了,但是帖子什麼的一直沒什麼時間寫(薛老師今天講的殼還沒有脫),寫完這篇帖子去脫殼了
另外: 本人小菜,大神們輕點噴
開始吧!下面截圖的代碼都是win2000的代碼,在看源碼的同時有時候需要用ida打開win7的ntoskrnl.exe對比著看,所幸的是雖然經過很多版本更迭,但是這塊的基本原理沒啥變化,所以就儘量不貼ida反彙編的圖了(可讀性差)
NtTerminateProcess的函數實現,其中最關鍵的 如圖:
這個函數主要乾的事情就是遍歷進程的線程,然後對每個線程執行PspTerminateThreadByPointer
再分析下 PspTerminateThreadByPointer 函數的實現
這個函數是分兩種情況的:
情況一,是線程自己關閉自己:直接執行PspExitThread
PspExitThread這個函數太龐雜簡單說下它的作用:
1. 執行了一大堆清理代碼,主要清理當前線程Ethread的資源
2. 從調度鏈表和等待鏈表中去掉它
3. 如果是進程的最後一個線程,直接清理進程空間
4. 執行KiSwapThread切換到一個新線程去
情況二,關閉掉別的線程:在對方線線程中插入一個內核apc,這個內核apc最後會調用PspExitThread函數
PspTerminateThreadByPointer 和NtTerminateProcess分析總結:
1. 所謂殺死進程,其實只要把每個線程殺死就好了,最後一個線程會負責收屍的
2. 線程不能被殺死,只能自殺.所以如果想殺掉線程最好讓它自己執行自殺代碼,內核apc(後面會簡單講下內核apc,沒法深入再講就錯題了)是個不錯的選擇
內核Apc執行的時機(講的不對請指正哈):
1. 中斷和異常返回,下面是ReatOs的代碼(貼代碼為證,避免別人說我瞎嗶嗶O(∩_∩)O哈哈~)
2. 高irql轉到第irql,這塊直接看win732逆向的代碼:KfLowerIrql
主要關注: HalpCheckForSoftwareInterrrupt
執行apc的地方就是KiDeliverApc
3. 線程切換的時候,還是直接貼win2000的代碼
原理講完了,現在直接貼效果圖了:
殺掉之前,寫好進程id:
執行完殺掉的代碼:
另外直接附上代碼:
Entry.c
#include
#include
//需要殺死的進程id
#define PCHUNTER_ID 3232
VOID DriverUnload(PDRIVER_OBJECT pDriver);
PEPROCESS LookupProcess(HANDLE hPid);
PETHREAD LookupThread(HANDLE hTid);
VOID KillProcess(PEPROCESS pEProcess);
ULONG GetPspTerminateThreadByPointer();
ULONG GetPspExitThread(ULONG PspTerminateThreadByPointer);
VOID SelfTerminateThread(
KAPC *Apc,
PKNORMAL_ROUTINE *NormalRoutine,
PVOID *NormalContext,
PVOID *SystemArgument1,
PVOID *SystemArgument2);
fpTypePspExitThread g_fpPspExitThreadAddr = NULL;
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath)
{
DbgBreakPoint();
pDriver->DriverUnload = DriverUnload;
//提前把函數查找出來
ULONG uPspTerminateThreadByPointerAddr = GetPspTerminateThreadByPointer();
if (0 == uPspTerminateThreadByPointerAddr)
{
KdPrint(("查找PspTerminateThreadByPointerAddr地址出錯\n"));
return STATUS_SUCCESS;
}
g_fpPspExitThreadAddr = (fpTypePspExitThread)GetPspExitThread(uPspTerminateThreadByPointerAddr);
if (NULL == g_fpPspExitThreadAddr)
{
KdPrint(("查找PspExitThread地址出錯\n"));
return STATUS_SUCCESS;
}
//
PEPROCESS pProcess = LookupProcess((HANDLE)PCHUNTER_ID);
if (NULL == pProcess)
{
KdPrint((("沒有在PsCidTable中找到進程,尼瑪不會隱藏了吧\n")));
}
else
{
KillProcess(pProcess);
}
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
KdPrint(("驅動退出\n"));
}
PEPROCESS LookupProcess(HANDLE hPid)
{
PEPROCESS pEProcess = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId(hPid, &pEProcess)))
return pEProcess;
return NULL;
}
VOID KillProcess(PEPROCESS pEProcess)
{
PEPROCESS pEProc = NULL;
PETHREAD pEThrd = NULL;
ULONG i = 0;
for (i = 4; i < 0x25600; i += 4)
{
pEThrd = LookupThread((HANDLE)i);
if (!pEThrd) continue;
pEProc = IoThreadToProcess(pEThrd);
if (pEProc == pEProcess)
{
PKAPC pApc = NULL;
pApc = (PKAPC)ExAllocatePool(NonPagedPool, sizeof(KAPC));
if (NULL == pApc) return;
//插入內核apc
KeInitializeApc(pApc, (PKTHREAD)pEThrd, OriginalApcEnvironment, (PKKERNEL_ROUTINE)&SelfTerminateThread, NULL, NULL, 0, NULL);
KeInsertQueueApc(pApc, NULL, 0, 2);
}
ObDereferenceObject(pEThrd);
}
}
PETHREAD LookupThread(HANDLE hTid)
{
PETHREAD pEThread = NULL;
if (NT_SUCCESS(PsLookupThreadByThreadId(hTid, &pEThread)))
return pEThread;
return NULL;
}
VOID SelfTerminateThread(
KAPC *Apc,
PKNORMAL_ROUTINE *NormalRoutine,
PVOID *NormalContext,
PVOID *SystemArgument1,
PVOID *SystemArgument2)
{
ExFreePool(Apc);
g_fpPspExitThreadAddr(STATUS_SUCCESS);
}
ULONG GetPspTerminateThreadByPointer()
{
UNICODE_STRING funcName;
RtlInitUnicodeString(&funcName, L"PsTerminateSystemThread");
ULONG step = 0;
ULONG targetFunAddr = 0;
ULONG baseFunAddr = (ULONG)MmGetSystemRoutineAddress(&funcName);
for (step = baseFunAddr; step < (baseFunAddr + 1024); step++)
{
//searching for 0x50,0xe8
if (((*(PUCHAR)(UCHAR*)(step - 1)) == 0x50) && ((*(PUCHAR)(UCHAR*)(step)) == 0xe8))
{
ULONG offset = *(PULONG)(step + 1);
targetFunAddr = step + 5 + offset;
break;
}
}
return targetFunAddr;
} //PspExitThread stamp code:0x0c 0xe8
ULONG GetPspExitThread(ULONG PspTerminateThreadByPointer)
{
ULONG step = 0;
ULONG targetFunAddr = 0;
ULONG baseFunc = PspTerminateThreadByPointer;
for (step = baseFunc; step < (baseFunc + 1024); step++)
{
//searching for 0x0c,0xe8
if (((*(PUCHAR)(UCHAR*)(step - 1)) == 0x0c) && ((*(PUCHAR)(UCHAR*)(step)) == 0xe8))
{
ULONG m_offset = *(PULONG)(step + 1);
targetFunAddr = step + 5 + m_offset;
break;
}
}
return targetFunAddr;
}
KrTypeDef.h#pragma once
#include
#include
#pragma warning(disable:4189 4100)
typedef enum _KAPC_ENVIRONMENT
{
OriginalApcEnvironment,
AttachedApcEnvironment,
CurrentApcEnvironment,
InsertApcEnvironment
} KAPC_ENVIRONMENT;
typedef VOID (*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef VOID(*PKKERNEL_ROUTINE) (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
);
typedef VOID(*PKRUNDOWN_ROUTINE) (
IN struct _KAPC *Apc
);
VOID NTAPI KeInitializeApc(__in PKAPC Apc,
__in PKTHREAD Thread,
__in KAPC_ENVIRONMENT TargetEnvironment,
__in PKKERNEL_ROUTINE KernelRoutine,
__in_opt PKRUNDOWN_ROUTINE RundownRoutine,
__in PKNORMAL_ROUTINE NormalRoutine,
__in KPROCESSOR_MODE Mode,
__in PVOID Context
);
BOOLEAN NTAPI KeInsertQueueApc(IN PKAPC Apc,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2,
IN KPRIORITY PriorityBoost);
typedef VOID(NTAPI *fpTypePspExitThread)(
IN NTSTATUS ExitStatus
);
#define OFFSET(type, f) ((SIZE_T) \
((char *)&((type *)0)->f - (char *)(type *)0))
ps:代碼有處bug,就是假設在殺線程的同時創建線程怎麼辦?
最後謝謝大家!
更多幹貨文章,關注看雪學院公眾號 ikanxue
閱讀更多 看雪學院 的文章