Saturday, August 20, 2011

Hook Specials 13 : Inline hook of Non-export function - PspTerminateProcess

Author:Sysnap

The code is include ....That isn't modified.When search feature code , Not search on non-paging memory. if you moderate modify the code isn't show BSOD.

When some functions is hook on inline hook ssdt, I feel easy.But It is difficult about hook to non-export function ,Check n times, It is succeed finally.

This is define.

NTSTATUS
PspTerminateProcess(
PEPROCESS Process,
NTSTATUS ExitStatus
)

Because there is not export function, Memory seach to position,Watch to used windbg

lkd> u PspTerminateProcess
nt!PspTerminateProcess:
805d23a0 8bff mov edi,edi
805d23a2 55 push ebp
805d23a3 8bec mov ebp,esp
805d23a5 56 push esi
805d23a6 64a124010000 mov eax,dword ptr fs:[00000124h]
805d23ac 8b7508 mov esi,dword ptr [ebp+8]
805d23af 3b7044 cmp esi,dword ptr [eax+44h]
805d23b2 7507 jne nt!PspTerminateProcess+0x1b (805d23bb)

Use dd command
lkd> dd PspTerminateProcess
805d23a0 8b55ff8b a16456ec 00000124 3b08758b
805d23b0 07754470 00000db8 575aebc0 0248be8d
805d23c0 47f60000 12742001 0174868d 56500000
805d23d0 5d237268 ef50e880 086affff 0709f058
805d23e0 e856006a 00004f78 ff85f88b 75ff1e74
805d23f0 07e8570c 57fffffd 4f62e856 f88b0000
805d2400 ea75ff85 00bc8639 06740000 ff04e856
805d2410 c033fffe c25d5e5f cccc0008 cccccccc

The feature code is 8b55ff8b a16456ec 00000124 3b08758b,The feature code is modified front five bytes when we find it on memory.

mov edi,edi
push ebp
mov ebp,esp


ZwQuerySystemInformation枚举内核模块,第一个模块就是我们要

的,PspTerminateThreadByPointer就是在里面

#include "ntddk.h"
#include "InlineHook.h"

PVOID GetUndocumentFunctionAdress()
{

ULONG size,index;
PULONG buf;
ULONG i;
PSYSTEM_MODULE_INFORMATION module;
PVOID driverAddress=0;
ULONG ntosknlBase;
ULONG ntosknlEndAddr;
ULONG curAddr;
NTSTATUS status;
ULONG retAddr;
ULONG code1_sp2=0x8b55ff8b,code2_sp2=0xa16456ec,code3_sp2=0x00000124,code4_sp2=0x3b08758b;

ZwQuerySystemInformation(SystemModuleInformation,&size, 0, &size);
if(NULL==(buf = (PULONG)ExAllocatePool(PagedPool, size)))
{
DbgPrint("failed alloc memory failed \n");
return 0;
}

status=ZwQuerySystemInformation(SystemModuleInformation,buf, size , 0);
if(!NT_SUCCESS( status ))
{
DbgPrint("failed query\n");
return 0;
}

module = (PSYSTEM_MODULE_INFORMATION)(( PULONG )buf + 1);
ntosknlEndAddr=(ULONG)module->Base+(ULONG)module->Size;
ntosknlBase=(ULONG)module->Base;
curAddr=ntosknlBase;
ExFreePool(buf);

for (i=curAddr;i<=ntosknlEndAddr;i++)
{
if ((*((ULONG *)i)==code1_sp2)&&(*((ULONG *)(i+4))==code2_sp2)&&(*((ULONG *)(i+8))==code3_sp2)&&(((ULONG*)(i+12))==code4_sp2))

{

retAddr=i;
DbgPrint("adress is:%x",retAddr);
return retAddr;

}
}
}

VOID Unload(PDRIVER_OBJECT DriverObject)
{
DbgPrint("Unload Called \r\n");

}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING str)
{

DriverObject->DriverUnload = Unload;
PspTerminateProcess = GetUndocumentFunctionAdress();
return STATUS_SUCCESS;
}

编译一下,加载运行,嗯,DebugView输出了:adress is:805c8620
那就是我们的函数地址找到了,找到地址当然是引用拉
只需这样声明一下:
typedef NTSTATUS (*PSPTERMINATETPROCESS)(
PEPROCESS Process,
NTSTATUS ExitStatus
);
PSPTERMINATETPROCESS PspTerminateProcess;
PspTerminateProcess=(PSPTERMINATETPROCESS)PspTerminateThreadByPointerAdrr;
好了,至此我们就获得了PspTerminateThreadByPointer在内存中的地址,也做了一些工作,

现在PspTerminateThreadByPointer就可以像正常的导出函数一样直接使用了,不过我们是

要它的地址就可以,如果你想用PspTerminateProcess就可以按上面的声明,就可以用.
好了,解决了地址问题,接着就是修改这个地址开始的5个字节
805d23a0 8bff mov edi,edi
805d23a2 55 push ebp
805d23a3 8bec mov ebp,esp
在修改之前应该做一下检查,看我们要HOOK的函数有没有被别人先HOOK了,这是必要的,否

则有可能导致系统崩溃
检查只需要添加一个函数就可以
NTSTATUS CheckPspTerminateProcessIsHook()
{
int i=0;
char *addr = (char *)PspTerminateProcess;

char code[] = { 0x8b, 0xff, 0x55, 0x8b, 0xec};

while(i<5)
{
DbgPrint(" - 0x%02X ", (unsigned char)addr[i]);
if(addr[i] != code[i])
{
return STATUS_UNSUCCESSFUL;
}
i++;
}
return STATUS_SUCCESS;
}

嗯,DriverEntry添加
if(STATUS_SUCCESS != CheckPspTerminateProcessIsHook())
{
DbgPrint("PspTerminateProcess Match Failed !");
return STATUS_UNSUCCESSFUL;
}
编译测试一下,嗯正常没问题,既然没什么问题,那下面我们就开始对这个函数进行inline

hook了,
接着开始核心部分
添加函数
_declspec(naked) T_PspTerminateProcess(
PEPROCESS Process,
NTSTATUS ExitStatus
)
{


_asm
{
mov edi, edi
push ebp
mov ebp ,esp
push [ebp+0ch]
push [ebp+8]
call MyPspTerminateProcess
cmp eax,1
jz end
mov eax,PspTerminateProcess
add eax,5
jmp eax

end:
pop ebp
retn 8
}
}
}

我们就是要修改PspTerminateProcess的前5个字节,代替为一条近jmp指令,跳转到

我们的这个函数里来,在T_PspTerminateProcess里call MyPspTerminateProcess ,MyPspTerminateProcess 是我们的功能函数,就是你hook后想做什么事情,这里我们简单的输出点字符就好

那怎样改PspTerminateProcess的前5个字节,代替为一条jmp

指令,跳到T_PspTerminateProcess里面来呢????????带着问题开始吧

VOID InlineHookPspTerminateProcess()
{


int JmpOffSet;
unsigned char JmpCode[5] = { 0xe9, 0x00, 0x00, 0x00, 0x00 };
KIRQL oldIrql;

if (PspTerminateProcess == 0)
{
DbgPrint("PspTerminateProcess NOT FOUND\n");
return;
}

DbgPrint( "PspTerminateProcess is found at:0x%08x\n", (ULONG)PspTerminateProcess );

DbgPrint("T_PspTerminateProcess is:%x\n",T_PspTerminateProcess);
JmpOffSet= (char*)T_PspTerminateProcess - (char*)PspTerminateProcess - 5;
DbgPrint("JmpOffSet is:%x\n",JmpOffSet);
RtlCopyMemory ( JmpCode+1, &JmpOffSet, 4 );

_asm
{
CLI
MOV EAX, CR0
AND EAX, NOT 10000H
MOV CR0, EAX
}
oldIrql = KeRaiseIrqlToDpcLevel();
RtlCopyMemory ( PspTerminateProcess, JmpCode, 5 );
DbgPrint("PspTerminateProcess is hook now \n");
KeLowerIrql(oldIrql);

_asm
{
MOV EAX, CR0
OR EAX, 10000H
MOV CR0, EAX
STI
}

}
好了,测试一下,只需要在DriverEntry添加
InlineHookPspTerminateThreadByPointer();嗯,运行正常,没有蓝屏,呵呵,那就继续
既然在T_PspTerminateProcess里面调用了MyPspTerminateProcess ,那就写这个函数吧,这里只是简单的输出PspTerminateProcess hello,你可以根据需要重写这个函数
int MyPspTerminateProcess(
PEPROCESS Process,
NTSTATUS ExitStatus
)
{
DbgPrint("PspTerminateProcess hello\n");

return 1;
}
返回了1,那cmp eax,1 ,呵呵放行原来的函数执行
好了,现在基本工作都完成了,就写卸载驱动部分
VOID Unload(PDRIVER_OBJECT DriverObject)
{

unsigned char Code[5]={0x8b,0xff,0x55,0x8b,0xec};

_asm
{
CLI
MOV eax, CR0
AND eax, NOT 10000H
MOV CR0, eax

pushad
mov edi, PspTerminateProcess
mov eax, dword ptr Code[0]
mov [edi], eax
mov al, byte ptr Code[4]
mov [edi+4], al
popad

MOV eax, CR0
OR eax, 10000H
MOV CR0, eax
STI
}

DbgPrint("Unload Called \r\n");

}
到此,我们的工作基本完成了,不过这只是一个测试的,还有待加强,运行后就是下面这样



Friday, August 19, 2011

Hook Specials 12 : Inline Hook MessageBox on ring3(demo)

Author:sding

The code is lead inline hook on ring,The part code refer to code of "wofeiwo",Thanks .

Two error is showed on original text

The code is used to vc++ 6.0,

Exe is builded in vc++ compiler, Functions proceed among jump to like API

as follows
0040100F $ /E9 CC020000 jmp MyFunc
00401014 $ |E9 37000000 jmp main
00401019 $ |E9 B2000000 jmp Hook


code:
#include "stdafx.h"
#include
#include
#include
//using namespace std;

DWORD head;//Save return address of API
int nRet;
BYTE orig_code[5] = {0x90, 0x90, 0x90, 0x90, 0x90};//Save original command
BYTE hook_code[5] = {0xe9, 0, 0, 0, 0};//Save to command of jump toMyMessageBoxA
BYTE jmp_org_code[5] = {0xe9, 0, 0, 0, 0};//Save five bytes command of original front address

int MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
int MyFunc();
void Hook();
int jmp_back();

ULONG OldFuncAddr;
ULONG MyFuncAddr;
ULONG jmp_backAddr;

//在修改前几个字节时,注意:取出的指令为完整的

int main()
{
Hook();

int rt = MessageBoxA(NULL, "Hello World", "Title", MB_OK);
// cout << rt << endl;//查看返回值是否已修改成功

system("pause");

return 0;
}

void Hook()
{
DWORD dwOldProtect;
OldFuncAddr = (ULONG)MessageBoxA;

// MyFuncAddr = MyMessageBoxA的实际地址
MyFuncAddr = *(ULONG *)((BYTE *)MyMessageBoxA+1) + (ULONG)MyMessageBoxA + 5;
// jmp_backAddr = jmp_back的实际地址
jmp_backAddr = *(ULONG *)((BYTE *)jmp_back+1) + (ULONG)jmp_back + 5;
//修改内存为PAGE_EXECUTE_READWRITE
VirtualProtect((LPVOID)jmp_backAddr, 10, PAGE_EXECUTE_READWRITE, &dwOldProtect);

VirtualProtect((LPVOID)OldFuncAddr, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//计算跳转地址
*((ULONG*)(hook_code+1)) = (ULONG)MyFuncAddr - (ULONG)OldFuncAddr - 5;

memcpy(orig_code,(BYTE *)OldFuncAddr, 5);

memcpy((BYTE*)OldFuncAddr, hook_code, 5);
//计算返回地址
*((ULONG*)(jmp_org_code+1)) = (ULONG)OldFuncAddr - (ULONG)jmp_backAddr - 5;

memcpy((BYTE *)jmp_backAddr, orig_code, 5);

memcpy((BYTE *)jmp_backAddr + 5, jmp_org_code, 5);
}

__declspec(naked) int jmp_back()
{
__asm
{
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
_emit 0x90
}
}

//MyMessageBoxA:在函数执行前进行自己的处理
__declspec(naked) int MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("MyMessageBoxA is called\r\n");
__asm
{
pop head
pop hWnd
pop lpText
pop lpCaption
pop uType
}
MyFunc();////可以加入函数过程
__asm
{
//压栈过程
push uType
push lpCaption
push lpText
push hWnd
push head
//跳回MessageBoxA入口点
jmp jmp_back;
ret;
}
}


//MyMessageBoxA:在函数执行后进行自己的处理
__declspec(naked) int MyMessageBoxAA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("MyMessageBoxAA is called\r\n");
__asm
{
pop head
push offset s1;//返回地址为S1:
//跳回MessageBoxA入口点
jmp jmp_back;
s1: nop
}

MyFunc();

__asm
{
;//将原返回地址压栈
mov eax, 0;////演示:将返回结果改为0,也可由MyFunc返回
push head
ret;
}
}

int MyFunc()
{
printf("Hello World\r\n");
return 1;
}


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;
}


Wednesday, August 17, 2011

Hook Specials 10 :General inline hook on ring3 or ring0

Author:cooldiyer
code:
#include
#include "InlineHook.h"

typedef void (__stdcall *__Sleep)(DWORD);
__Sleep realSleep = NULL;

VOID
__stdcall
MySleep(
IN DWORD dwMilliseconds
)
{
printf("Sleep(%d) Called\n", dwMilliseconds);

return realSleep(dwMilliseconds);
}

int main(int argc, char* argv[])
{
InlineHook(Sleep, MySleep, &realSleep);
Sleep(10);
UnInlineHook(Sleep, realSleep);
return 0;
}
Main file's code


code:
/*++

Copyright (c) 2008/08/27 By CoolDiyer

Abstract:

Ring3 all-purpose inline hook module

--*/

#if !defined(AFX_INLINEHOOK_H_INCLUDED)
#define AFX_INLINEHOOK_H_INCLUDED

#ifdef WIN32
#define RING3
#endif

#ifdef RING3
#include
#else
#include
#endif

#include "LDasm.h"

#ifdef RING3
#define __malloc(_s) VirtualAlloc(NULL, _s, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
#define __free(_p) VirtualFree(_p, 0, MEM_RELEASE)
#define JMP_SIZE 5
#else
#define __malloc(_s) ExAllocatePool(NonPagedPool, _s)
#define __free(_p) ExFreePool(_p)
#define JMP_SIZE 7
#endif

#ifdef RING3

BOOL
WriteReadOnlyMemory(
LPBYTE lpDest,
LPBYTE lpSource,
ULONG Length
)
/*++
Wirte only-read-memory, dont't lock on ring3
--*/
{
BOOL bRet;
DWORD dwOldProtect;
bRet = FALSE;

// Make a front few bytes of memory can be written
if (!VirtualProtect(lpDest, Length, PAGE_READWRITE, &dwOldProtect))
{
return bRet;
}

memcpy(lpDest, lpSource, Length);

bRet = VirtualProtect(lpDest, Length, dwOldProtect, &dwOldProtect);

return bRet;
}

#else

NTSTATUS
WriteReadOnlyMemory(
LPBYTE lpDest,
LPBYTE lpSource,
ULONG Length
)
/*++
写只读内存(源于Mark代码)
--*/
{
NTSTATUS status;
KSPIN_LOCK spinLock;
KIRQL oldIrql;
PMDL pMdlMemory;
LPBYTE lpWritableAddress;

status = STATUS_UNSUCCESSFUL;

pMdlMemory = IoAllocateMdl(lpDest, Length, FALSE, FALSE, NULL);

if (NULL == pMdlMemory) return status;

MmBuildMdlForNonPagedPool(pMdlMemory);
MmProbeAndLockPages(pMdlMemory, KernelMode, IoWriteAccess);
lpWritableAddress = MmMapLockedPages(pMdlMemory, KernelMode);
if (NULL != lpWritableAddress)
{
oldIrql = 0;
KeInitializeSpinLock(&spinLock);
KeAcquireSpinLock(&spinLock, &oldIrql);

memcpy(lpWritableAddress, lpSource, Length);

KeReleaseSpinLock(&spinLock, oldIrql);
MmUnmapLockedPages(lpWritableAddress, pMdlMemory);

status = STATUS_SUCCESS;
}

MmUnlockPages(pMdlMemory);
IoFreeMdl(pMdlMemory);

return status;
}

#endif

BOOL
GetPatchSize(
IN void *Proc, /* 需要Hook的函数地址 */
IN DWORD dwNeedSize, /* Hook函数头部占用的字节大小 */
OUT LPDWORD lpPatchSize /* 返回根据函数头分析需要修补的大小 */
)
/*++
计算函数头需要Patch的大小
--*/
{
DWORD Length;
PUCHAR pOpcode;
DWORD PatchSize = 0;

if (!Proc || !lpPatchSize)
{
return FALSE;
}

do
{
Length = SizeOfCode(Proc, &pOpcode);
if ((Length == 1) && (*pOpcode == 0xC3)) break;
if ((Length == 3) && (*pOpcode == 0xC2)) break;
Proc = (PVOID)((DWORD)Proc + Length);

PatchSize += Length;
if (PatchSize >= dwNeedSize)
{
break;
}

} while (Length);

*lpPatchSize = PatchSize;

return TRUE;
}

BOOL
InlineHook(
IN void *OrgProc, /* 需要Hook的函数地址 */
IN void *NewProc, /* 代替被Hook函数的地址 */
OUT void **RealProc /* 返回原始函数的入口地址 */
)
/*++
对函数进行Inline Hook
--*/
{
DWORD dwPatchSize; // 得到需要patch的字节大小
DWORD dwOldProtect;
LPVOID lpHookFunc; // 分配的Hook函数的内存
DWORD dwBytesNeed; // 分配的Hook函数的大小
LPBYTE lpPatchBuffer; // jmp 指令的临时缓冲区

if (!OrgProc || !NewProc || !RealProc)
{
return FALSE;
}
// 得到需要patch的字节大小
if (!GetPatchSize(OrgProc, JMP_SIZE, &dwPatchSize))
{
return FALSE;
}

/*
0x00000800 0x00000800 sizeof(DWORD) // dwPatchSize
JMP / FAR 0xAABBCCDD E9 DDCCBBAA JMP_SIZE
... ... dwPatchSize // Backup instruction
JMP / FAR 0xAABBCCDD E9 DDCCBBAA JMP_SIZE
*/

dwBytesNeed = sizeof(DWORD) + JMP_SIZE + dwPatchSize + JMP_SIZE;

lpHookFunc = __malloc(dwBytesNeed);

// 备份dwPatchSize到lpHookFunc
*(DWORD *)lpHookFunc = dwPatchSize;

// 跳过开头的4个字节
lpHookFunc = (LPVOID)((DWORD)lpHookFunc + sizeof(DWORD));


// 开始backup函数开头的字
memcpy((BYTE *)lpHookFunc + JMP_SIZE, OrgProc, dwPatchSize);

lpPatchBuffer = __malloc(dwPatchSize);

// NOP填充
memset(lpPatchBuffer, 0x90, dwPatchSize);

#ifdef RING3
// jmp到Hook
*(BYTE *)lpHookFunc = 0xE9;
*(DWORD*)((DWORD)lpHookFunc + 1) = (DWORD)NewProc - (DWORD)lpHookFunc - JMP_SIZE;

// 跳回原始
*(BYTE *)((DWORD)lpHookFunc + 5 + dwPatchSize) = 0xE9;
*(DWORD*)((DWORD)lpHookFunc + 5 + dwPatchSize + 1) = ((DWORD)OrgProc + dwPatchSize) - ((DWORD)lpHookFunc + JMP_SIZE + dwPatchSize) - JMP_SIZE;


// jmp
*(BYTE *)lpPatchBuffer = 0xE9;
// 注意计算长度的时候得用OrgProc
*(DWORD*)(lpPatchBuffer + 1) = (DWORD)lpHookFunc - (DWORD)OrgProc - JMP_SIZE;

#else

// jmp到Hook
*(BYTE *)lpHookFunc = 0xEA;
*(DWORD*)((DWORD)lpHookFunc + 1) = (DWORD)NewProc;
*(WORD*)((DWORD)lpHookFunc + 5) = 0x08;

// 跳回原始
*(BYTE *)((DWORD)lpHookFunc + JMP_SIZE + dwPatchSize) = 0xEA;
*(DWORD*)((DWORD)lpHookFunc + JMP_SIZE + dwPatchSize + 1) = ((DWORD)OrgProc + dwPatchSize);
*(WORD*)((DWORD)lpHookFunc + JMP_SIZE + dwPatchSize + 5) = 0x08;

// jmp far
*(BYTE *)lpPatchBuffer = 0xEA;

// 跳到lpHookFunc函数
*(DWORD*)(lpPatchBuffer + 1) = (DWORD)lpHookFunc;
*(WORD*)(lpPatchBuffer + 5) = 0x08;
#endif

WriteReadOnlyMemory(OrgProc, lpPatchBuffer, dwPatchSize);

__free(lpPatchBuffer);


*RealProc = (DWORD)lpHookFunc + JMP_SIZE;

return TRUE;
}

void UnInlineHook(
void *OrgProc, /* 需要恢复Hook的函数地址 */
void *RealProc /* 原始函数的入口地址 */
)
/*++
恢复对函数进行的Inline Hook
--*/
{
DWORD dwPatchSize;
DWORD dwOldProtect;
LPBYTE lpBuffer;

// 找到分配的空间
lpBuffer = (DWORD)RealProc - (sizeof(DWORD) + JMP_SIZE);
// 得到dwPatchSize
dwPatchSize = *(DWORD *)lpBuffer;

WriteReadOnlyMemory(OrgProc, RealProc, dwPatchSize);

// 释放分配的跳转函数的空间
__free(lpBuffer);
}
#endif // !defined(AFX_INLINEHOOK_H_INCLUDED)

Hook Specials 9 : Talk out inline hook of kernel on three fronts

Hook Specials 9 : Talk out inline hook of kernel on three fronts
Step 1: principle of Inline hook
This is common statement of inline hooke about modify flow of function is executed,And conquer the field that control function and filter to operate.We can replace anyplace of original commands to our jump commands in theory,Some peoples  really do it to hide for check inline hook ,Do it before flow of function and command is very famillar, even this inline hook don't have generality and stability. The article talk to realized two general inline

principle of Inline hook: Analyse a few command of function's start and They is copied to saved, After our command  is called to original command be replaced. If execute original function, must after our function is finished and executed a few command of be saved front on address of return our command later.
The whole process of the inline hook in this,Check function and Get address fucntion is amonged to only call function.
The article talk two inline hook at these point.
Explain three-point:
1、Balance stack is top,Parameter popdown yet.
2、Whether WP bit control processor of CR0 allow to memory's page of only read is writed. When value is zero ,Protection mechanism is forbidden.
3、Lifting interrupt level to DISPATCH_LEVEL, Prohibit interrupt of thread change-over generated.
                   
(Two) Use inline hook
Inline hook divide into two types:
(1)inline export function, ObReferenceObjectByHandle is selected for example。
(2)inline not export function,KiInsertQueueApc is selected for example.
First a few bytes of export function can be viewed with windbg.However Not-export function need confirm to a few bytes oneself,during many questions is noticed.When we understand the article ,You feel very simple about inline hook.
Next through two examples to explain that how use inline hook.
1、inline hook ObReferenceObjectByHandle,Proctect process
ObReferenceObjectByHandle is included export function of ntoskrnl.exe and frequently be used on kernel.
NtCreateProcess need call ObReferenceObjectByHandle for create process, NtTerminateProcess need call ObReferenceObjectByHandle,Because of it that we protect and disabled create process with hook.
Effect : Already ran Notepad can't finish to use task management
Flow:
HookObReferenceObjectByHandle------DetourMyObReferenceObjectByHa ndle----------UnHookObReferenceObjectByHandle
Core code:
//=======================================inline HOOK ObReferenceObjectByHandle===========================
//ObReferenceObjectByHandle is export function of ntoskrnl.exe,First five bytes is hooked.
//Bytes type data  unsigned char
ULONG  CR0VALUE;
BYTE  OriginalBytes[5]={0};             //Save first five bytes of original function        
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //Jump's address of hook
extern POBJECT_TYPE *PsProcessType;
NTKERNELAPI NTSTATUS ObReferenceObjectByHandle(
                       
                         IN HANDLE  Handle,
                         IN ACCESS_MASK  DesiredAccess,
                         IN POBJECT_TYPE  ObjectType  OPTIONAL,
                         IN KPROCESSOR_MODE  AccessMode,
                         OUT PVOID  *Object,
                         OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
                       
                         );
//HOOK function
NTSTATUS DetourMyObReferenceObjectByHandle(
                     
                       IN HANDLE  Handle,         
                       IN ACCESS_MASK  DesiredAccess
                       IN POBJECT_TYPE  ObjectType  OPTIONAL,
                       IN KPROCESSOR_MODE  AccessMode,
                       OUT PVOID  *Object,
                       OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL);
//
//hook flow HookObReferenceObjectByHandle---DetourMyObReferenceObjectByHandle---UnHookObReferenceObjectByHandle
void  HookObReferenceObjectByHandle()
{
 
  //Evaluation front defined array
  KIRQL Irql;
  KdPrint(("[ObReferenceObjectByHandle] :0x%x",ObReferenceObjectByHandle));  //address confirm
  //Save first five bypes of function
  RtlCopyMemory(OriginalBytes,(BYTE *)ObReferenceObjectByHandle,5);
  //Save first five bytes's offset of new function
  *(ULONG *)(JmpAddress+1)=(ULONG)DetourMyObReferenceObjectByHandle-((ULONG)ObReferenceObjectByHandle+5);
  //Start inline hook
  //Close to write-protect of memory
  _asm
  
  {
    push eax
    
      mov eax, cr0
      mov CR0VALUE, eax
      and eax, 0fffeffffh
      mov cr0, eax
      pop eax
  }
 
  //Lifting interrupt level of IRQL
  Irql=KeRaiseIrqlToDpcLevel();
  //Write Jmp to five bytes of function
  RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,JmpAddress,5);
  //Recover Irql
  KeLowerIrql(Irql);
  //Open write-protect of memory
 
  __asm
  
  {     
  
    push eax
    
      mov eax, CR0VALUE
    
      mov cr0, eax
    
      pop eax
    
  }
 
}
 
_declspec (naked) NTSTATUS OriginalObReferenceObjectByHandle(IN HANDLE  Handle,
                             
                               IN ACCESS_MASK  DesiredAccess,
                             
                               IN POBJECT_TYPE  ObjectType  OPTIONAL,
                             
                               IN KPROCESSOR_MODE  AccessMode,
                             
                               OUT PVOID  *Object,
                             
                               OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                             
{
 
  _asm
  
  { 
  
    mov edi,edi
      push ebp
      mov ebp,esp
      mov eax,ObReferenceObjectByHandle
      add eax,5
      jmp eax              
    
  }
 
}

NTSTATUS DetourMyObReferenceObjectByHandle(
                     
                       IN HANDLE  Handle,
                     
                       IN ACCESS_MASK  DesiredAccess,
                     
                       IN POBJECT_TYPE  ObjectType  OPTIONAL,
                
                       IN KPROCESSOR_MODE  AccessMode,
                     
                       OUT PVOID  *Object,
                     
                       OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL)
                     
{
 
  NTSTATUS status;
 
  //Call original
 
  status=OriginalObReferenceObjectByHandle(Handle,DesiredAccess,ObjectType,AccessMode,Object,HandleInformation);
 
  if((status==STATUS_SUCCESS)&&(DesiredAccess==1))
  
  { 
  
    if(ObjectType== *PsProcessType)
    
    {
    
      if( _stricmp((char *)((ULONG)(*Object)+0x174),"notepad.exe")==0)
      
      { 
      
        ObDereferenceObject(*Object);
      
        return STATUS_INVALID_HANDLE;
      
      }
    
    }
  
  }
 
  return status;
 
}
 
void UnHookObReferenceObjectByHandle()
{
 
  //Write back five bytes to original again
 
  KIRQL Irql;
 
    //Close write-protect
 
  _asm
  
  {
  
    push eax
    
      mov eax, cr0
    
      mov CR0VALUE, eax
    
      and eax, 0fffeffffh
    
      mov cr0, eax
    
      pop eax
    
  }
 
    //Lifting IRQL to Dpc
 
    Irql=KeRaiseIrqlToDpcLevel();
 
  RtlCopyMemory((BYTE *)ObReferenceObjectByHandle,OriginalBytes,5);
 
  KeLowerIrql(Irql);
 
    //Open write-protect
 
  __asm
  
  {     
  
        push eax
      mov eax, CR0VALUE
      mov cr0, eax
    
      pop eax
    
  }
}
After driver is loaded, program of finished notepad as follow:
    (image one)
In more detail:
1、ObReferenceObjectByHandle analyse
NTSTATUS
  ObReferenceObjectByHandle(
    IN HANDLE  Handle,
    IN ACCESS_MASK  DesiredAccess,
    IN POBJECT_TYPE  ObjectType  OPTIONAL,
    IN KPROCESSOR_MODE  AccessMode,
    OUT PVOID  *Object,
    OUT POBJECT_HANDLE_INFORMATION  HandleInformation  OPTIONAL
    );
Function prototype as above,Get pointer of object by handle,return:
STATUS_SUCCESS                        succed call
STATUS_OBJECT_TYPE_MISMATCH      
STATUS_ACCESS_DENIED                 limite not enough
STATUS_INVALID_HANDLE                invalid handle       
Call NtTerminateProcess need call ObReferenceObjectByHandle,So we protect process through return value of function is modified.But NtCreateProcess call same this functiong . If don't distinguish,Create process prohibit yet.How distinguish who call it, we can reference WRK, I found to through the second paramter - DesiredAccess can be judged, Create and finish process is different about the second paramter ,PROCESS_CREATE_PROCESS和PROCESS_TERMINATE,Question away.
PspCreateProcess on WRK-v1.2\base\ntos\ps\create.c
Call ObReferenceObjectByHandle code:
Status = ObReferenceObjectByHandle (ParentProcess,
                                            PROCESS_CREATE_PROCESS,
                                            PsProcessType,
                                            PreviousMode,
                                            &Parent,
                                            NULL);
NtTerminateProcess on WRK-v1.2\base\ntos\ps\psdelete.c
Call ObReferenceObjectByHandle code:
st = ObReferenceObjectByHandle (ProcessHandle,
                                    PROCESS_TERMINATE,
                                    PsProcessType,
                                    KeGetPreviousModeByThread(&Self->Tcb),
                                    &Process,
                                    NULL);
DesiredAccess description:
#define PROCESS_TERMINATE         (0x0001) // winnt
#define PROCESS_CREATE_THREAD     (0x0002) // winnt
#define PROCESS_SET_SESSIONID     (0x0004) // winnt
#define PROCESS_VM_OPERATION      (0x0008) // winnt
#define PROCESS_VM_READ           (0x0010) // winnt
#define PROCESS_VM_WRITE          (0x0020) // winnt
// begin_ntddk begin_wdm begin_ntifs
#define PROCESS_DUP_HANDLE        (0x0040) // winnt
// end_ntddk end_wdm end_ntifs
#define PROCESS_CREATE_PROCESS    (0x0080) // winnt
#define PROCESS_SET_QUOTA         (0x0100) // winnt
#define PROCESS_SET_INFORMATION   (0x0200) // winnt
#define PROCESS_QUERY_INFORMATION (0x0400) // winnt
#define PROCESS_SET_PORT          (0x0800)
#define PROCESS_SUSPEND_RESUME    (0x0800) // winnt
2、Call Function's description
We call a function only to write function's name on C language,But infact do it:
Push paramter of fuction to stack and return address of function, call function , open up space of stack and register local variable for new function,
Recover stack keep stack balance
(_stdcall)assembly code:
Push paramter 4
Push paramter 3
Push paramter 2
Push paramter 1
Call  function, call command finish two operate in the same time,one is return push stack,two jump to entry address of be called function
Push  ebp
Mov ebp,esp
Sub  esp, XX  ;Open stack fram space
……
Add  esp ,XX
Pop ebp
Retn          ;recove stack balance
stack details :
ESP
Local variable
 
 
EBP
return address
paramter 1
paramter 2
paramter 3
paramter 4
The stack is mad from high to low address.
Paramter pass to EBP,aligned use four bytes
paramter 4----------------------EBP+0x14
paramter 3----------------------EBP+0x10
paramter 2----------------------EBP+0xc
paramter 1--------------------- EBP+0x8
Get local variable through Ebp-XX
So inline hook notice stack balance , brokn stack will breakdown function.
My thinking usual has three steps about inline hook:
HOOK function ----- DetourMy handle function ---------- UnHook function
Modify result of function return or handle among data, then call original function. When we handle original function that already hook, So i construction a original function, Paramter alreday press-in stack before hook in the side, Announce function type is _declspec (naked).。
It is hard to understand about function call stack frame, I is hard to explain everything , Welcome authority talk with me.
2、inline hook KiInsertQueueApc cope kill process for APC
KiInsertQueueAPc is non-export on kernel,Under code may is used to universally template of non-export function inline, Everybody can modify it by need, Principle already is analysed because of inline ObReferenceObject, This part don't analyse by me, Use this yet - Hook function ---DetourMy function --- UnHook function
Croe code:
//===================inline hook KiInsertQueueApc====================
//KiInsertQueueApc is non-export on kernel, Position it from KeInsertQueueApc
//Modify front five bytes of KiInsertQueueApc
//Thinking of handle function: apc-->kthread---apc_state--eprocess--process's name
//HookKiInsertQueueApc---DetourMyKiInsertQueueApc---UnHookKiInsertQueueApc
ULONG CR0VALUE;
ULONG g_KiInsertQueueApc;
         
BYTE JmpAddress[5]={0xE9,0,0,0,0};       //Jump HOOK function's address
BYTE  OriginalBytes[5]={0};             //Save front five bytes of original function
VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment);
VOID WPOFF()
{
  _asm
  
  {
  
    push eax
    
      mov eax, cr0
    
      mov CR0VALUE, eax
    
      and eax, 0fffeffffh
    
      mov cr0, eax
    
      pop eax
      cli
    
  };
 
}
VOID WPON()
{
    __asm
  
  {     
    sti
    push eax
    
      mov eax, CR0VALUE
    
      mov cr0, eax
    
      pop eax
    
  };
}
//1、Get KiInsertQueueApc address
ULONG GetFunctionAddr( IN PCWSTR FunctionName)     //PCWSTR const pointer , point 16 bit UNICODE
{
  UNICODE_STRING UniCodeFunctionName;
  RtlInitUnicodeString( &UniCodeFunctionName, FunctionName );
  return (ULONG)MmGetSystemRoutineAddress( &UniCodeFunctionName ); 
}
ULONG GetKiInsertQueueApcAddr()
{
  ULONG sp_code1=0x28,sp_code2=0xe8,sp_code3=0xd88a;  //Signature, sp_code3 windbg show error, That is d88a
  ULONG address=0;
  PUCHAR addr;
  PUCHAR p;
  addr=(PUCHAR)GetFunctionAddr(L"KeInsertQueueApc");
  for(p=addr;p<p+PAGE_SIZE;p++)
  {
    if((*(p-1)==sp_code1)&&(*p==sp_code2)&&(*(PUSHORT)(p+5)==sp_code3))
    {
      address=*(PULONG)(p+1)+(ULONG)(p+5);
      break;
    }
  }
  KdPrint(("[KeInsertQueueApc] addr %x\n",(ULONG)addr));
    KdPrint(("[KiInsertQueueApc] address %x\n",address));
    return address;
}
VOID HookKiInsertQueueApc()
{ 
  KIRQL Irql;
  g_KiInsertQueueApc=GetKiInsertQueueApcAddr();
  KdPrint(("[KiInsertQueueApc] KiInsertQueueApc %x\n",g_KiInsertQueueApc));
    // Save front bytes of original function保存原函数的前字节内容
    RtlCopyMemory (OriginalBytes, (BYTE*)g_KiInsertQueueApc, 5);
  //Offset address of new function to original function
    *( (ULONG*)(JmpAddress + 1) ) = (ULONG)DetourMyKiInsertQueueApc - (ULONG)g_KiInsertQueueApc - 5;
    // Prohibit write-protect of systerm, Lifting IRQL to DPC
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook function
  RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, JmpAddress, 5 );
    // recover write-protect , lower IRQL
    KeLowerIrql(Irql);
    WPON();
}
//Original function
_declspec (naked) VOID FASTCALL OriginalKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  _asm
  {
    //front five bytes
    mov edi,edi
      push ebp
      mov ebp,esp
    
      mov eax,g_KiInsertQueueApc
      add eax,5
      jmp eax
  }
}
//Handle function
//apc--kthread--apc_state--eprocess
VOID FASTCALL DetourMyKiInsertQueueApc(IN PKAPC Apc,IN KPRIORITY Increment)
{
  ULONG thread;
  ULONG process;
  if(MmIsAddressValid((PULONG)((ULONG)Apc+0x008)))    //address confirm  KAPC structure +008 ---> kthread
    thread=*((PULONG)((ULONG)Apc+0x008));
  else
    return ;
  if(MmIsAddressValid((PULONG)((ULONG)thread+0x044))) //kthread+30-->KAPC_STATE+10-->eprocess
    process=*((PULONG)((ULONG)thread+0x044));
  else
    return ;
    if(MmIsAddressValid((PULONG)((ULONG)process+0x174)))  //eprocess+174---->process's name
  {
    if((_stricmp((char *)((ULONG)process+0x174),"notepad.exe")==0)&&(Increment==2))
    {
      return ;
    }
    else
      OriginalKiInsertQueueApc(Apc,Increment);
  }
  else
    return;
}
//Unload function
VOID UnHookKiInsertQueueApc()
{
  KIRQL Irql;
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook function
    RtlCopyMemory ( (BYTE*)g_KiInsertQueueApc, OriginalBytes, 5);
    // Recover write-protect , lower IRQL
    KeLowerIrql(Irql);
    WPON();
}
 
Reply some questions:
1、Search Signature
Seach with kernel debug of windbg:
uf  KeInsertQueueApc
nt!KeInsertQueueApc+0x3b:
804e6d0a 8b450c          mov     eax,dword ptr [ebp+0Ch]
804e6d0d 8b5514          mov     edx,dword ptr [ebp+14h]
804e6d10 894724          mov     dword ptr [edi+24h],eax
804e6d13 8b4510          mov     eax,dword ptr [ebp+10h]
804e6d16 8bcf            mov     ecx,edi
804e6d18 894728          mov     dword ptr [edi+28h],eax
804e6d1b e8523fffff        call    nt!KiInsertQueueApc (804dac72)
804e6d20 8ad8 (error)  mov     bl,al
Signature : sp_code1=0x28 sp_code2=0xe8 sp_code3=0xd88a(windbg's display is error, that is d88a
)
The way is method of through already export function position non-export function,That is generality. In detail see code.
2、Get course of EPRocess
Apc-----kthread-----apc_stateeprocess
dt  _KAPC             offset 0x008 point KTHREAD
dt  _KTHREAD         offset 0x034 point KAPC_STATE
dt  _KAPC_STATE      offset 0x10 point EPROCESS
dt  _EPROCESS         offset 0x174 point process's name

(three)Summanry
 Many people is difficult about inline hook and handle it to is troubles. But i belive to watch my article, You must not think so,Only careful for inline hook and notice details no different with other hook.
 The code use hard code, Complie in sp3+VMware, Please modify by system oneself. Welcome reader talk with me.

Monday, August 15, 2011

Hook Specials 8: Realize ring3 hook

Author:bzhkl
That is original realized on code of russia ,The advantage of code is:
1. Don't first use hook on agency's function,be Called again ,Later hook again,Otherwise multithreading go wrong.
2. Non hard code ,The structure is better.

bool AfxHookCode(void* TargetProc, void* NewProc,void ** l_OldProc, int bytescopy = 5)
{

  DWORD dwOldProtect;

  ::VirtualProtect((LPVOID)TargetProc, bytescopy, PAGE_EXECUTE_READWRITE, &dwOldProtect);

  *l_OldProc = new unsigned char[bytescopy+5];          //Apple for Space for instruction of be covered to copyed.


  memcpy(*l_OldProc, TargetProc, bytescopy);           // Decided beforehandl to save code of be broken
  *((unsigned char*)(*l_OldProc) + bytescopy) = 0xe9;    // 我的内存的代码执行完 跳到原来的 代码 + 破坏的代码的长度 上去

                            //被破坏指令的长度     //E9 opcode长度          //算出偏移的OPCODE    = 被HOOK函数地址的地址 + 破坏指令的长度 - 我分配内存的结束地址                           
    *(unsigned int *)((unsigned char*)(*l_OldProc) +bytescopy          +     1)          =   (unsigned int)(TargetProc) + bytescopy - ( (unsigned int)((*l_OldProc)) + 5 + bytescopy ) ;  // 我内存代码跳到原来代码上的偏移


  *(unsigned char*)TargetProc =(unsigned char)0xe9;             //被HOOK的函数头改为jmp


                                  //算出偏移的OPCODE  = 代理函数地址 - 被HOOK函数地址
  *(unsigned int*)((unsigned int)TargetProc +1) = (unsigned int)NewProc - ( (unsigned int)TargetProc + 5) ; //被HOOK的地方跳到我的新过程 接受过滤


  ::VirtualProtect((LPVOID)TargetProc, bytescopy, dwOldProtect, 0);
  return true;
}


bool AfxUnHookCode(void* TargetAddress, void * l_SavedCode, unsigned int len)
{
    DWORD dwOldProtect;

    ::VirtualProtect((LPVOID)TargetAddress, len, PAGE_EXECUTE_READWRITE, &dwOldProtect);  

    // Recover code of be broken
    memcpy(TargetAddress, l_SavedCode, len);

    ::VirtualProtect((LPVOID)TargetAddress, len, dwOldProtect, 0);

    return true;
}



unsigned int *  OldProc;
typedef
int
(__stdcall * MYMESSAGEBOX)     //用于调用自己分配内存处的代码强制转换
   (
    IN HWND hWnd,
    IN LPCSTR lpText,
    IN LPCSTR lpCaption,
    IN UINT uType);


int
__stdcall       // 这里不声明成stdcall的话 编译器认为是C声明方式 要自己平衡堆栈 
MyMessageBox(
    IN HWND hWnd,
    IN LPCSTR lpText,
    IN LPCSTR lpCaption,
    IN UINT uType)
{

  // 这里可以执行一些过滤行为 比如改变参数 或者 直接模拟返回正确的结果
  if ( strcmp(lpText, "sample") == 0)
  {
    printf("filter");
    return 1;
  }


      //强制转换成API函数类型 调用原来的函数
   return ( (MYMESSAGEBOX) OldProc)(hWnd, lpText, lpCaption, uType);

}




int main()
{
  MessageBox(0, "sample", "caption", MB_OK);   //正常调用MSG

                                                                // 这里一定要传地址变量的地址  直接传地址的话 地址变量是一份拷贝 根本不能保存分配的内存的地址(在我看来 指针就是地址变量)
                                  //当初写的时候调了3遍才知道这个原因- -
  AfxHookCode((void*)MessageBox, (void*)MyMessageBox, (void**)&OldProc, 5);


  MessageBox(0, "sample", "caption", MB_OK);  //Be filtered
  AfxUnHookCode((void*)MessageBox, OldProc, 5); // Recover code of be hook

  getchar();

  return 0;
}

Sunday, August 14, 2011

Hook Specials 7: Main point (RootkitUnhook is unable to measure) of inlineHook which the noob understood

Author:skymelai

//Main point of inlineHook which the noob understood,It is published for like me to exchange more.

//inLineHook NtQuerySystemInformation function
//Demonstrate it to two kinds of treatment ways of function hook,Before call original function and After call original fuction
//I don't know why rootkit unhook is unable to detected.

#include "ntddk.h"

//Keep previous 10 bytes of original function.
unsigned char orig_code[10] = {0x90, 0x90, 0x90, 0x90, 0x90,0x90, 0x90, 0x90, 0x90, 0x90};
//Deposit command of oneself function is jumped,The var is used to modified the previous 10 bytes of original fuction
unsigned char hook_code[10] = {0xe9, 0, 0, 0, 0,0x90,0x90,0x90,0x90,0x90};
//存放跳转到原起始地址后5字节的段内跳指令
unsigned char jmp_org_code[5] = {0xe9, 0, 0, 0, 0};
//保存原函数的地址
ULONG OldFuncAddr;
ULONG CR0VALUE;
//原函数执行完的返回地址(用全局保存是为了不在我们自己的函数内去平衡堆栈)
ULONG adrOrgRet;
//我们自己的NtQuerySystemInformation函数用到的参数
ULONG g_SystemInformationClass;
PVOID g_SystemInformation;
ULONG g_SystemInformationLength;
PULONG g_ReturnLength;
NTSTATUS g_ntStatus;

//我们自己的NtQuerySystemInformation用到的时间变量
LARGE_INTEGER g_UserTime;
LARGE_INTEGER g_KernelTime;

//这里直接借用windows内核安全防护一书
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase; //Used only in checked build
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
#define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]

struct _SYSTEM_THREADS
{
        LARGE_INTEGER           KernelTime;
        LARGE_INTEGER           UserTime;
        LARGE_INTEGER           CreateTime;
        ULONG                           WaitTime;
        PVOID                           StartAddress;
        CLIENT_ID                       ClientIs;
        KPRIORITY                       Priority;
        KPRIORITY                       BasePriority;
        ULONG                           ContextSwitchCount;
        ULONG                           ThreadState;
        KWAIT_REASON            WaitReason;
};

struct _SYSTEM_PROCESSES
{
        ULONG                           NextEntryDelta;
        ULONG                           ThreadCount;
        ULONG                           Reserved[6];
        LARGE_INTEGER           CreateTime;
        LARGE_INTEGER           UserTime;
        LARGE_INTEGER           KernelTime;
        UNICODE_STRING          ProcessName;
        KPRIORITY                       BasePriority;
        ULONG                           ProcessId;
        ULONG                           InheritedFromProcessId;
        ULONG                           HandleCount;
        ULONG                           Reserved2[2];
        VM_COUNTERS                     VmCounters;
        IO_COUNTERS                     IoCounters; //windows 2000 only
        struct _SYSTEM_THREADS          Threads[1];
};

// Added by Creative of rootkit.com
struct _SYSTEM_PROCESSOR_TIMES
{
    LARGE_INTEGER          IdleTime;
    LARGE_INTEGER          KernelTime;
    LARGE_INTEGER          UserTime;
    LARGE_INTEGER          DpcTime;
    LARGE_INTEGER          InterruptTime;
    ULONG              InterruptCount;
};
NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
                        IN PVOID SystemInformation,
                        IN ULONG SystemInformationLength,
                        OUT PULONG ReturnLength);


typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
            ULONG SystemInformationCLass,
                        PVOID SystemInformation,
                        ULONG SystemInformationLength,
                        PULONG ReturnLength
);

///////////////////////////////////////////////////////////////////////
// ResoleLogic function
//这里直接借用windows内核安全防护一书函数,隐藏_root_开头的所有进程
// ResoleLogic() returns a linked list of processes.
// The function below imitates it, except it removes from the list any
// process who's name begins with "_root_".
ULONG ResoleLogic(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength)
{

      // Asking for a file and directory listing
      if(SystemInformationClass == 5)
      {
       // This is a query for the process list.
     // Look for process names that start with
     // '_root_' and filter them out.
         
     struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
         struct _SYSTEM_PROCESSES *prev = NULL;
    
     while(curr)
     {
            //DbgPrint("Current item is %x\n", curr);
      if (curr->ProcessName.Buffer != NULL)
      {
        if(0 == memcmp(curr->ProcessName.Buffer, L"_root_", 12))
        {
          g_UserTime.QuadPart += curr->UserTime.QuadPart;
          g_KernelTime.QuadPart += curr->KernelTime.QuadPart;

          if(prev) // Middle or Last entry
          {
            if(curr->NextEntryDelta)
              prev->NextEntryDelta += curr->NextEntryDelta;
            else  // we are last, so make prev the end
              prev->NextEntryDelta = 0;
          }
          else
          {
            if(curr->NextEntryDelta)
            {
              // we are first in the list, so move it forward
              (char *)SystemInformation += curr->NextEntryDelta;
            }
            else // we are the only process!
              SystemInformation = NULL;
          }
        }
      }
      else // This is the entry for the Idle process
      {
         // Add the kernel and user times of _root_*
         // processes to the Idle process.
         curr->UserTime.QuadPart += g_UserTime.QuadPart;
         curr->KernelTime.QuadPart += g_KernelTime.QuadPart;

         // Reset the timers for next time we filter
         g_UserTime.QuadPart = g_KernelTime.QuadPart = 0;
      }
      prev = curr;
        if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta);
        else curr = NULL;
       }
    }
    else if (SystemInformationClass == 8) // Query for SystemProcessorTimes
    {
         struct _SYSTEM_PROCESSOR_TIMES * times = (struct _SYSTEM_PROCESSOR_TIMES *)SystemInformation;
         times->IdleTime.QuadPart += g_UserTime.QuadPart + g_KernelTime.QuadPart;
    }

    return 1;
}

VOID WPOFF()
{
        _asm
               
        {
               
                push eax
                       
                        mov eax, cr0
                       
                        mov CR0VALUE, eax
                       
                        and eax, 0fffeffffh 
                       
                        mov cr0, eax
                       
                        pop eax
                        cli
                       
        };
       
}

VOID WPON()
{
    __asm
               
        {      
                sti
                push eax
                       
                        mov eax, CR0VALUE
                       
                        mov cr0, eax
                       
                        pop eax
                       
        };
}

__declspec(naked) int jmp_back()
{
  __asm
  {
  _emit 0x90  //原函数前10个字节
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90

  _emit 0x90  //跳转回原函数+10
  _emit 0x90
  _emit 0x90
  _emit 0x90
  _emit 0x90

  }
}
#if 0
//调用原函数前先处理
__declspec(naked) NTSTATUS NewZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength)
{

  //原函数参数弹入我们的变量
  __asm
  {
    pop SystemInformationClass
    pop SystemInformation
    pop SystemInformationLength
    pop ReturnLength
  }

  DbgPrint("NewZwQuerySystemInformation\n");
  //ResoleLogic(SystemInformationClass,SystemInformation,SystemInformationLength,ReturnLength);////可以加入函数过程

  __asm
  {
    //压栈过程
    push ReturnLength
    push SystemInformationLength
    push SystemInformation
    push SystemInformationClass
    //转到跳转函数,跳转到原函数中
    jmp jmp_back;
    ret;
  }

   //return STATUS_SUCCESS;
}

#else
//调用原函数后再处理
__declspec(naked) NTSTATUS NewZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength)
{
  ULONG t1;

 
  //保存参数给我们自己的函数使用,因为经过原函数后
  //堆栈已经被平衡过,所以不能再使用(这是我自己这样理解的,不一定正确)
  //这里用全局保存参数是为了自己不用实现堆栈帧和平衡堆栈(在这里做太麻烦,易出错)
  __asm
  {
    mov eax,dword ptr [esp+4]
    mov g_SystemInformationClass,eax
    mov eax,dword ptr [esp+8h]
    mov g_SystemInformation,eax
    mov eax,dword ptr [esp+0Ch]
    mov g_SystemInformationLength,eax
    mov eax,dword ptr [esp+10h]
    mov g_ReturnLength,eax
  }

  __asm
  {
    pop adrOrgRet  //保存返回地址
    push offset s1//用s1的地址来替代原地址,让原函数执行完后能返回S1
    //执行jmp_back
    jmp jmp_back
    s1: nop
  }

  //保存原函数的返回值
  _asm push eax
 
  //全局保存原函数返回值(原因和上面一样)
  _asm mov g_ntStatus,eax
  DbgPrint("NewZwQuerySystemInformation\n");  
  if( NT_SUCCESS(g_ntStatus)) 
    {
      ResoleLogic(g_SystemInformationClass,g_SystemInformation,g_SystemInformationLength,g_ReturnLength);
    }

  _asm pop eax

  __asm
  {
    //将原返回地址压栈,以便返回正确的地址
    //mov eax, 0; eax为原函数的返回值
    push adrOrgRet
    ret;
  }
}
#endif

//从ZwQuerySystemInformation的第2,3,4,5个字节
//得到NtQuerySystemInformation函数的地址
ULONG getOldFunAddress()
{
  return (ULONG)(SYSTEMSERVICE(ZwQuerySystemInformation));
}

void hook()
{
  ULONG MyFuncAddr;//我们的函数
  ULONG jmp_backAddr;
    KIRQL Irql;
  //得到原函数地址,并保存
  OldFuncAddr = getOldFunAddress();

  //我们的函数地址
  MyFuncAddr =(ULONG)NewZwQuerySystemInformation;
  //由我们的函数跳转回原函数的一个辅助函数地址
  jmp_backAddr = (ULONG)jmp_back;

  //关闭页保护属性
  WPOFF();
  //提升IRQ LEVEL
    Irql = KeRaiseIrqlToDpcLevel();

  //计算跳转地址,从原函数跳转到我们自己函数的偏移
  *((ULONG*)(hook_code+1)) = (ULONG)MyFuncAddr - ((ULONG)OldFuncAddr + 5);

  //保存原函数的前10个字节(取消HOOK时还原),因为我们要用跳转指令覆盖前10字节
  memcpy(orig_code,(unsigned char *)OldFuncAddr, 10);

  //用我们的跳转指令覆盖原函数前10个字节
  memcpy((unsigned char*)OldFuncAddr, hook_code, 10);

  //计算返回地址,从我们的跳转函数到原函数的第11个字节处
  *((ULONG*)(jmp_org_code+1)) = ((ULONG)OldFuncAddr+10)  - ((ULONG)jmp_backAddr + 15);

  //复制原函数的前10字节到我们的跳转函数的前10字节,因为我们要在跳转回原函数前
  //执行原函数的前10字节
  memcpy((unsigned char *)jmp_backAddr, orig_code, 10);

  //复制5字节的跳转指令到我们的跳转函数,由它来跳转回原函数
  memcpy((unsigned char *)jmp_backAddr + 10, jmp_org_code, 5);

    //降低IRQL
    KeLowerIrql(Irql);
  //恢复写保护
  WPON();
}

void unHook()
{
  KIRQL Irql;
  OldFuncAddr = getOldFunAddress();
 
    WPOFF();
    Irql = KeRaiseIrqlToDpcLevel();
    //inline hook函数
    RtlCopyMemory ( (unsigned char*)OldFuncAddr, orig_code, 10);
    // 恢复写保护,降低IRQL
    KeLowerIrql(Irql);
    WPON();       

}
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
  unHook();
  DbgPrint("OnUnload called\n");
}


NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
           IN PUNICODE_STRING theRegistryPath)
{
   g_UserTime.QuadPart = g_KernelTime.QuadPart = 0;

   theDriverObject->DriverUnload  = OnUnload;
  
   hook();
   return STATUS_SUCCESS;
}
标 题:答复
作 者:MatrixNERO
时 间:2009-11-23 22:48
#if 0
//调用原函数前先处理
__declspec(naked) NTSTATUS NewZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength)
{

  //原函数参数弹入我们的变量
  __asm
  {
    pop SystemInformationClass
    pop SystemInformation
    pop SystemInformationLength
    pop ReturnLength
  }

  DbgPrint("NewZwQuerySystemInformation\n");
  ResoleLogic(SystemInformationClass,SystemInformation,SystemInformationLength,ReturnLength);////可以加入函数过程

  __asm
  {
    //压栈过程
    push ReturnLength
    push SystemInformationLength
    push SystemInformation
    push SystemInformationClass
    //转到跳转函数,跳转到原函数中
    jmp jmp_back;
    ret;//这个Ret难道不多余吗?原来的返回不就是了吗??
  }

   //return STATUS_SUCCESS;
}

#else