Thursday, August 18, 2011

Hook Specials 11 : Enumerate hidden process with hook SwapContext

Author:bzhkl
I wanted to detect hidden process, later used method of enum by force to be worked out. But can't find the code about hook SwapContext, So collect useful module on line and interrate the code to realize by oneself . its can run on XP3.

Attch full object's code

Hard question: Get address of SwapContext and some details ~
Principle: When KiSwapContext call SwapContext, Context of process or thread is saved on ESI, EDI save Context of swap-out thread, then intercept and capture esi that the mean entry ETHREAD to get EPROCESS for collect.

Fault: Very effect system performance,Because system thread dispatch will frequently, wrk's KiSwapContext with assembly can reflect efficiency requirements of high ~.

Has funny question on the sid
KiSwapContext is pattern of fastcall to be call ,That is through ECX or EDX to send spare , STDCALL pass on
WRK That's it comments
; BOOLEAN
; KiSwapContext (
; IN PKTHREAD OldThread
; IN PKTHREAD NewThread
; )
Watch WRK's code
mov edi, ecx ; set old thread address
mov esi, edx ; set next thread address
movzx ecx, byte ptr [edi].ThWaitirql ; set APC interrupt bypass disable
call SwapContext ; swap context
This is two parameter

Watch with WINDBG
80545975 8bf1 mov esi,ecx
80545977 8bbb24010000 mov edi,dword ptr [ebx+124h]
8054597d 89b324010000 mov dword ptr [ebx+124h],esi
80545983 8a4f58 mov cl,byte ptr [edi+58h]
80545986 e8f5000000 call nt!SwapContext (80545a80)
KiSwapContext has one parameter , esi is Context of replace to thread to through ECX, edi isn't it



WRK KiSwapContext 代码
cPublicFastCall KiSwapContext, 2
.fpo (0, 0, 0, 4, 1, 0)

;
; N.B. The following registers MUST be saved such that ebp is saved last.
; This is done so the debugger can find the saved ebp for a thread
; that is not currently in the running state.
;

sub esp, 4*4
mov [esp+12], ebx ; save registers
mov [esp+8], esi ;
mov [esp+4], edi ;
mov [esp+0], ebp ;
mov ebx, PCR[PcSelfPcr] ; set address of PCR
mov edi, ecx ; set old thread address
mov esi, edx ; set next thread address
movzx ecx, byte ptr [edi].ThWaitirql ; set APC interrupt bypass disable

call SwapContext ; swap context
mov ebp, [esp+0] ; restore registers
mov edi, [esp+4] ;
mov esi, [esp+8] ;
mov ebx, [esp+12] ;
add esp, 4*4 ;
fstRET KiSwapContext ;

fstENDP KiSwapContext




windbg得到的 KiSwapContext 代码
lkd> uf KiSwapContext
nt!KiSwapContext:
8054595c 83ec10 sub esp,10h
8054595f 895c240c mov dword ptr [esp+0Ch],ebx
80545963 89742408 mov dword ptr [esp+8],esi
80545967 897c2404 mov dword ptr [esp+4],edi
8054596b 892c24 mov dword ptr [esp],ebp
8054596e 648b1d1c000000 mov ebx,dword ptr fs:[1Ch]
80545975 8bf1 mov esi,ecx
80545977 8bbb24010000 mov edi,dword ptr [ebx+124h]
8054597d 89b324010000 mov dword ptr [ebx+124h],esi
80545983 8a4f58 mov cl,byte ptr [edi+58h]
80545986 e8f5000000 call nt!SwapContext (80545a80)
8054598b 8b2c24 mov ebp,dword ptr [esp]
8054598e 8b7c2404 mov edi,dword ptr [esp+4]
80545992 8b742408 mov esi,dword ptr [esp+8]
80545996 8b5c240c mov ebx,dword ptr [esp+0Ch]
8054599a 83c410 add esp,10h
8054599d c3 ret


The effect of drive after run:驱动运行后的效果是:

Collect process
0x81FE7768 svchost.exe
0x81EC6478 alg.exe
0x81F5E990 svchost.exe
0x81FE83E0 DrvLoader.exe
0x81CA7020 taskmgr.exe
0x81595D70 VMwareService.e
0x815C6608 VMwareTray.exe
0x815D9C08 svchost.exe
0x8159C528 ctfmon.exe
0x8158EB60 VisualTaskTips.
0x815E5DA0 winlogon.exe
0x81E96418 DrvLoader.exe
0x815F8BD8 lsass.exe
0x81FE5020 MSDEV.EXE
0x81EC0B28 mdm.exe
0x81EE0500 explorer.exe
0x81F13020 MSDEV.EXE
0x815523A8 DBGVIEW.EXE
0x81F74020 csrss.exe
0x821507C0 System
0x82009830 VMwareUser.exe
0x81F66DA0 services.exe
0x81F07B88 svchost.exe


Under is main code


DWORD gThreadsProcessOffset =0x220; // ETHREAD on offset of EPROCESS
/*

+0x218 TopLevelIrp : Uint4B
+0x21c DeviceToVerify : Ptr32 _DEVICE_OBJECT
+0x220 ThreadsProcess : Ptr32 _EPROCESS
*/

ULONG ProcessNameOffset = 0x174; // 进程对应的文件名 在 EPROCESS偏移
/*
+0x170 Session : Ptr32 Void
+0x174 ImageFileName : [16] UChar
+0x184 JobLinks : _LIST_ENTRY
*/

PProcessList wLastItem = NULL;
int BeTerminate = 0; //1 signify stop of thread 3 signify state of non-PENDING 0 signify normal operation of thread

void _stdcall CollectProcess(PEPROCESS pEPROCESS) // collect EPROCESS
{
if (!IsAdded(wLastItem, pEPROCESS)) AddItem(&wLastItem, pEPROCESS);
return;
}

void __stdcall ThreadCollect(PUCHAR pEthread) //根据ETHREAD得到EPROCESS 并调用CollectProcess来搜集
{
PEPROCESS pEprocess = *(PEPROCESS *)(pEthread + gThreadsProcessOffset);
if (pEprocess) CollectProcess(pEprocess);
return;
}


DWORD outPEthread = 0;
void __stdcall ProcessData(DWORD pInEthread, DWORD pOutEthread)
{
DWORD pid, eprocess;
char * pname;
if (MmIsAddressValid(PVOID(pInEthread+0x220)) ) // 这里以及下面要判断是不是一个真正的 Ethread 结构体 有时好像调用SwapContext传进来的不是 Ethread 结构体 然后就蓝屏 具体没有深究 加个判断就不蓝了~
{
eprocess = *(DWORD*)(pInEthread+0x220);

if (MmIsAddressValid(PVOID(eprocess) ) )
{
ThreadCollect((PUCHAR)pInEthread);
}

}
}



PBYTE GoBackAddr = NULL;
PBYTE ChangAddr = NULL;

DWORD CallContextOffset = 0;

__declspec(naked) VOID HookSwap()
{

_asm
{
pushad
pushfd
cli
}

_asm
{
// EDI 是换出的线程上下文
push edi
//ESI 是换入的线程上下文
push esi
call ProcessData //搜集进程
}

_asm
{
sti
popfd
popad
}
_asm jmp DWORD PTR[GoBackAddr]
}

/*
得到SwapContext地址的原理是
用PsLookupThreadByThreadId得到Idle System的KTHREAD
res=(PCHAR)(Thread->Tcb.KernelStack);
SwapAddr=*(DWORD *)(res+0x08);
*/
PCHAR GetSwapAddr()
{
PCHAR res = 0;
NTSTATUS Status;
PETHREAD Thread;

if (*NtBuildNumber <= 2195)
Status = PsLookupThreadByThreadId((PVOID)4, &(PETHREAD)Thread);
else
Status = PsLookupThreadByThreadId((PVOID)8, &(PETHREAD)Thread);

if (NT_SUCCESS(Status))
{
if (MmIsAddressValid(Thread))
{
res = (PCHAR)(Thread->Tcb.KernelStack);

}
if (MmIsAddressValid(res+8))
{
_asm
{
mov eax,res
add eax,8
mov eax,[eax]
mov res,eax
}
}
else
{
res = 0;
return NULL;
}
}
_asm
{
mov eax,res
sub eax,5
mov ChangAddr,eax
mov edx,[eax+1]
mov CallContextOffset,edx
add eax,edx
add eax,5
mov GoBackAddr,eax
mov res,eax
}
return res;
}



BOOL HookSwapFunction(BOOL flag)
{
if (flag == TRUE)
{
KIRQL OldIrql=0;
DWORD NewOffset;//HookSwap-ChangAddr-5;
_asm
{
mov eax,HookSwap
mov edx,ChangAddr
sub eax,edx
sub eax,5
mov NewOffset,eax
}

PAGED_CODE()
ASSERT(KeGetCurrentIrql()<=DISPATCH_LEVEL);
KeRaiseIrql(2,&OldIrql);//HIGH_LEVEL
__asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H //disable WP bit
MOV CR0, EAX
}
_asm
{
mov eax,ChangAddr
push NewOffset
pop dword ptr[eax+1]

}

__asm
{
MOV EAX, CR0
OR EAX, 10000H //enable WP bit
MOV CR0, EAX
STI
}


KeLowerIrql(OldIrql);

}
//Bug Check 0xD1: DRIVER_IRQL_NOT_LESS_OR_EQUAL

else
{
KIRQL OldIrql=0;
KeRaiseIrql(2,&OldIrql);///HIGH_LEVEL
__asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H //disable WP bit
MOV CR0, EAX
}

_asm
{
mov eax,ChangAddr
push CallContextOffset
pop dword ptr[eax+1]
}


__asm
{
MOV EAX, CR0
OR EAX, 10000H //enable WP bit
MOV CR0, EAX
STI
}
KeLowerIrql(OldIrql);
// DbgPrint("HookSwapFunctionFALSE");//jution
}

}

PEPROCESS processObject (PETHREAD ethread) {
return (PEPROCESS)(ethread->Tcb.ApcState.Process);
}



void klisterUnload(IN PDRIVER_OBJECT pDriverObject)
{
BeTerminate = 1;
while(BeTerminate != 3) // = 3时说明创建的线程不是pending状态且马上会结束 这时候可以UNLOAD 否则线程在PENDING状态UNLOADE 会直接蓝
{

}

if (GoBackAddr)//PBYTE GoBackAddr = NULL;
HookSwapFunction(FALSE);
}

void showProcess()
{

PProcessList temp;
DWORD count = 0;
PUCHAR pFileName;
temp = wLastItem;


while (temp) //遍历链表
{
if (temp->pEPROCESS)
{
count++;
pFileName = (PUCHAR)((unsigned int)(temp->pEPROCESS) + 0x174);
DbgPrint("0x%08X %s \n",(unsigned int)(temp->pEPROCESS), pFileName);
}
temp = PProcessList(temp->NextItem);
}

DbgPrint("共有%d个进程", count);
}


void WorkThread(IN PVOID pContext)
{
LARGE_INTEGER timeout;

while(true)
{
if (MmIsAddressValid(&BeTerminate) ) // 因为BeTerminate是在UNLOAD中设置的 可能驱动卸载后 这个变量不能访问 所以用MmIsAddressValid判断下
{
if(BeTerminate == 0)
{

//等待单位是 100ns //-10作用是转换成微秒 //2000000微秒=2秒
timeout = RtlConvertLongToLargeInteger(-10 * 2000000);

KeDelayExecutionThread(KernelMode, FALSE, &timeout);
DbgPrint("搜集到的进程是");
showProcess();
}
else
{
BeTerminate = 3;
PsTerminateSystemThread(STATUS_SUCCESS);
goto __end;
}
}
else
{
BeTerminate = 3;
PsTerminateSystemThread(STATUS_SUCCESS);
goto __end;
}
}
__end:;
}


// 驱动程序加载时调用DriverEntry例程
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath
)
{
NTSTATUS dwStAtus;
HANDLE hThread;

pDriverObject->DriverUnload=klisterUnload;

dwStAtus = PsCreateSystemThread(&hThread,
(ACCESS_MASK)0,
NULL,
(HANDLE)0,
NULL,
WorkThread,
NULL
);


GetSwapAddr();
if (GoBackAddr){
HookSwapFunction(TRUE);
}
return STATUS_SUCCESS;
}


No comments:

Post a Comment