Wednesday, August 17, 2011

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.

No comments:

Post a Comment