kd> u PspUserThreadStartup
nt!PspUserThreadStartup:
805c7050 6a20 push 20h
[COLOR=red]805c7052 6870ae4d80 push offset nt!ObWatchHandles+0x61c (804dae70)[/COLOR]
805c7057 e8c41ef7ff call nt!_SEH_prolog (80538f20)
805c705c 64a124010000 mov eax,dword ptr fs:[00000124h]
805c7062 8bf0 mov esi,eax
805c7064 8975e0 mov dword ptr [ebp-20h],esi
805c7067 8b7e44 mov edi,dword ptr [esi+44h]
805c706a 897ddc mov dword ptr [ebp-24h],edi
kd> dt _ETHREAD
ntdll!_ETHREAD
...
+0x224 StartAddress : Ptr32 Void
...
/************************************************************************/
/* 获取信息,并根据用户的选择结束进程或者允许运行 */
/* arg[0] 和 arg[1] 分别为PspUserThreadStartup 的两个参数 */
/* arg[0] = StartRoutine, arg[1] = StartContext */
/************************************************************************/
void DoSomethingIWant(PULONG arg)
{
ULONG dwEProcess = (ULONG)PsGetCurrentProcess();
ANSI_STRING ansiCurrentProcessName,ansiPerentProcessName;
ULONG uRet = 0;
ULONG Pid, PerentPid;
KSPIN_LOCK SpinLock;
KIRQL kirql;
PLIST_ENTRY head,curr;
ULONG n;
if (g_uStartSign == 0) //没准备好?
{
return;
}
Pid = (ULONG)PsGetCurrentProcessId();
PerentPid = *(PULONG)(dwEProcess + OFFSET_PERENT_PID_EPROCESS);//通过偏移从EPROCESS中获取父进程ID,偏移为硬编码
KeInitializeSpinLock(&SpinLock);
if (g_uStartSign != arg[1]) //是否为进程的第一个线程
{
//添加PID到PidTable中
KeAcquireSpinLock(&SpinLock,&kirql);
SetPidTable(Pid, 1);
KeReleaseSpinLock(&SpinLock,kirql);
//取当前进程名
GetProcessPathName(dwEProcess,&ansiCurrentProcessName);
//取父进程名
GetProcessPathNameByPID(PerentPid, &ansiPerentProcessName);
//打印信息~~
DbgPrint("[ProcessMon]Process Creating, Process Name = %s ,PID = %d ,PerentName = %s PID = %d\r\n",
ansiCurrentProcessName.Buffer,Pid, ansiPerentProcessName.Buffer,PerentPid);
//让用户选择
uRet = GetUserChoose(&ansiPerentProcessName,&ansiCurrentProcessName, PerentPid, Pid);
//从PidTable中删除PID
KeAcquireSpinLock(&SpinLock,&kirql);
SetPidTable(Pid, 0);
KeReleaseSpinLock(&SpinLock,kirql);
//释放内存了.
if(ansiCurrentProcessName.Length) RtlFreeAnsiString(&ansiCurrentProcessName);
if(ansiPerentProcessName.Length) RtlFreeAnsiString(&ansiPerentProcessName);
//根据用户的选择允许运行或者结束进程
if(uRet)
ZwTerminateProcess(NtCurrentProcess(), 0);
KeSetEvent(&g_eventNotify, 0, 0); //通知那些等待的线程---------------------
KeClearEvent(&g_eventNotify); //置为非信号----------------------------|
} // |
else // |
{ // |
//不是第一个线程 |
while (IsPidInTable(Pid)) //如果线程所属进程在等待用户判断,则让其等待 |
{ // |
KeWaitForSingleObject(&g_eventNotify,Executive,KernelMode,0,0); // <-----
}
//通过EPROCESS->ThreadListHead遍历线程,ThreadListHead的偏移量为硬编码
for (n = 0, curr = head = (PLIST_ENTRY)(dwEProcess + OFFSET_THREAD_LIST_HEAD_EPROCESS);
curr->Blink != head ;
curr = curr->Blink, n++){} //计算当前进程的线程数目
DbgPrint("[ProcessMon]Pid: %.4d ,Thread count = %d\r\n",Pid, n);
}
return;
}
/************************************************************************/
/* 自己假冒的函数 保存现场后CALL DoSomethingIWant */
/************************************************************************/
__declspec(naked) void FakePspUserThreadStartup()
{
__asm{
pushfd
pushad
mov ebx,esp
add ebx,44
push ebx
call DoSomethingIWant
popad
popfd
jmp g_Orig
}
}
/************************************************************************/
/* 挂钩PspUserThreadStartup */
/* 挂钩的地址是PspUserThreadStartup+2 */
/************************************************************************/
void HookPspUserThreadStartup()
{
PUCHAR pHookAddr = (PUCHAR)FindPspUserThreadStartupAddress();
UCHAR JMPCode[5] = {0xe9,0,0,0,0};
UCHAR JMPBackCode[5] = {0xe9,0,0,0,0};
g_Orig = ExAllocatePool(NonPagedPool,10);
if (!g_Orig)
{
DbgPrint("[ProcessMon]Failed with Allocate Pool\r\n");
return ;
}
if (!pHookAddr) return;
pHookAddr += 2; //挂钩的地址是PspUserThreadStartup+2
*((PULONG)(JMPCode+1)) = (ULONG) FakePspUserThreadStartup - ((ULONG)pHookAddr + 5);
*((PULONG)(JMPBackCode+1)) = (ULONG)pHookAddr + 5 - ((ULONG)g_Orig + 10);
memcpy(
g_Orig,
(PVOID)pHookAddr,
5);
memcpy(
(PVOID)((ULONG)g_Orig+5),
(PVOID)JMPBackCode,
5);
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
memcpy((PVOID)pHookAddr,JMPCode,5);
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
}
[培训]科锐逆向工程师培训第53期2025年7月8日开班!