谈谈NP和HS的通用unhook

 标 题: 【原创】谈谈NP和HS的通用unhook
作 者: zhuwg
时 间: 2008-02-04,20:59:00
链 接: http://bbs.pediy.com/showthread.php?t=59324

祝各位新年快乐 
一边听音乐一边写代码真的很舒服阿很舒服
 
先简单说以下,NP和HS都hook了以下一些SSDT
什么是SSDT?我不想说那些复杂的理论,希望以简单的事实帮助介绍
先向大S道歉,下文如有冒犯,还请大S原谅 

怎么说呢,SSDT本身就是一个地址表,我们当他是个地址簿,也就是写了每个人的地址的记录表
今天,偶想给大S送新年礼物,怎么送到大S家里呢?就得查看地址簿了
上面是这样写的  大S: 游戏达人,外挂高人,破解牛人,脱壳圣人;分身有如月,堀北真希,花泽类,猪的理想;实际上是个阳光宝宝,吃的的白白胖胖的,每天都很开心;家住上海市X区X路X楼,进门左转、左转、再左转,敲门说找S.H.E就行了。
标准SSDT hook的方式就是改变这个地址,HS就是八地址改写成abnlab实验室,如果你照着走就到abnlab实验室了
他们就要检查你了,这么大一箱礼物?是不是带了炸弹阿,识别你的身份阿,但是这样怎么能行呢,经过他们一审核
我的新年礼物岂不是要过年以后才能到了?不行不行
只能先联系大S,大S说家在上海X别墅区X,偶于是就上门了,到了别墅,门卫不让偶进去阿,理由是偶没有邀请函

                         ; MmDeleteTeb(x,x)+Ep ...
.text:004129B3
.text:004129B3 BugCheckParameter1= dword ptr  8
.text:004129B3 arg_4           = dword ptr  0Ch
.text:004129B3
.text:004129B3 ; FUNCTION CHUNK AT .text:00445DD6 SIZE 00000049 BYTES
.text:004129B3
.text:004129B3                 mov     edi, edi
.text:004129B5                 push    ebp
.text:004129B6                 mov     ebp, esp
.text:004129B8                 push    esi
.text:004129B9                 push    edi
.text:004129BA                 mov     eax, large fs:124h
.text:004129C0                 mov     edi, [ebp+BugCheckParameter1]
.text:004129C3                 mov     esi, eax
.text:004129C5                 cmp     [esi+44h], edi
.text:004129C8                 jz      short loc_4129FF
.text:004129CA                 cmp     byte ptr [esi+165h], 0
.text:004129D1                 jnz     loc_445DD6
.text:004129D7                 mov     eax, large fs:994h
.text:004129DD                 test    eax, eax
.text:004129DF                 jnz     loc_445DD6
.text:004129E5                 call    ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:004129EB                 mov     byte ptr [ebp+BugCheckParameter1], al
.text:004129EE                 lea     eax, [esi+14Ch]
.text:004129F4                 push    eax
.text:004129F5                 push    [ebp+BugCheckParameter1]
.text:004129F8                 push    edi
.text:004129F9                 push    esi
.text:004129FA                 call    _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:004129FF
.text:004129FF loc_4129FF:                             ; CODE XREF: KeAttachProcess(x)+15j
.text:004129FF                 pop     edi
.text:00412A00                 pop     esi
.text:00412A01                 pop     ebp
.text:00412A02                 retn    4
.text:00412A02 _KeAttachProcess@4 endp

.text:004189DB ; __stdcall KeStackAttachProcess(x, x)
.text:004189DB                 public _KeStackAttachProcess@8
.text:004189DB _KeStackAttachProcess@8 proc near       ; CODE XREF: MmAttachSession(x,x)+58p
.text:004189DB                                         ; MiAttachToSecureProcessInSession(x)+43p ...
.text:004189DB
.text:004189DB arg_0           = dword ptr  8
.text:004189DB arg_4           = dword ptr  0Ch
.text:004189DB
.text:004189DB                 mov     edi, edi
.text:004189DD                 push    ebp
.text:004189DE                 mov     ebp, esp
.text:004189E0                 push    esi
.text:004189E1                 push    edi
.text:004189E2                 mov     eax, large fs:124h
.text:004189E8                 mov     esi, eax
.text:004189EA                 mov     eax, large fs:994h
.text:004189F0                 test    eax, eax
.text:004189F2                 jnz     loc_445DF1
.text:004189F8                 mov     edi, [ebp+arg_0]
.text:004189FB                 cmp     [esi+44h], edi
.text:004189FE                 jz      short loc_418A34
.text:00418A00                 call    ds:__imp__KeRaiseIrqlToDpcLevel@0 ; KeRaiseIrqlToDpcLevel()
.text:00418A06                 cmp     byte ptr [esi+165h], 0
.text:00418A0D                 mov     byte ptr [ebp+arg_0], al
.text:00418A10                 jnz     loc_445E0D
.text:00418A16                 lea     eax, [esi+14Ch]
.text:00418A1C                 push    eax
.text:00418A1D                 push    [ebp+arg_0]
.text:00418A20                 push    edi
.text:00418A21                 push    esi
.text:00418A22                 call    _KiAttachProcess@16 ; KiAttachProcess(x,x,x,x)
.text:00418A27                 mov     eax, [ebp+arg_4]
.text:00418A2A                 and     dword ptr [eax+10h], 0
.text:00418A2E
.text:00418A2E loc_418A2E:                             ; CODE XREF: KeStackAttachProcess(x,x)+63j
.text:00418A2E                                         ; KeAttachProcess(x)+33467j
.text:00418A2E                 pop     edi
.text:00418A2F                 pop     esi
.text:00418A30                 pop     ebp
.text:00418A31                 retn    8

我们有以下办法
1.直接执行函数+5字节处
这样可以,但是有其他问题,如果NP把函数hook改到函数中间或者末尾,我们很难定位
2.执行底层函数KiAttachProcess
KiAttachProcess(EPROCESS *Process,Irql)

.text:00412855 ; __stdcall KiAttachProcess(x, x, x, x)
.text:00412855 _KiAttachProcess@16 proc near           ; CODE XREF: KeAttachProcess(x)+47p
.text:00412855                                         ; KeStackAttachProcess(x,x)+47p ...
.text:00412855
.text:00412855 arg_0           = dword ptr  8
.text:00412855 arg_4           = dword ptr  0Ch
.text:00412855 arg_8           = byte ptr  10h
.text:00412855 arg_C           = dword ptr  14h
.text:00412855
.text:00412855 ; FUNCTION CHUNK AT .text:00445D4F SIZE 0000001E BYTES
.text:00412855 ; FUNCTION CHUNK AT .text:00445D72 SIZE 00000064 BYTES
.text:00412855
.text:00412855                 mov     edi, edi
.text:00412857                 push    ebp
.text:00412858                 mov     ebp, esp
.text:0041285A                 push    ebx
.text:0041285B                 mov     ebx, [ebp+arg_4]
.text:0041285E                 inc     word ptr [ebx+60h]
.text:00412862                 push    esi
.text:00412863                 mov     esi, [ebp+arg_0]
.text:00412866                 push    edi
.text:00412867                 push    [ebp+arg_C]
.text:0041286A                 lea     edi, [esi+34h]
.text:0041286D                 push    edi
.text:0041286E                 call    _KiMoveApcState@8 ; KiMoveApcState(x,x)
.text:00412873                 mov     [edi+4], edi
.text:00412876                 mov     [edi], edi
.text:00412878                 lea     eax, [esi+3Ch]
.text:0041287B                 mov     [eax+4], eax
.text:0041287E                 mov     [eax], eax
.text:00412880                 lea     eax, [esi+14Ch]
.text:00412886                 cmp     [ebp+arg_C], eax
.text:00412889                 mov     [esi+44h], ebx
.text:0041288C                 mov     byte ptr [esi+48h], 0
.text:00412890                 mov     byte ptr [esi+49h], 0
.text:00412894                 mov     byte ptr [esi+4Ah], 0
.text:00412898                 jnz     short loc_4128AD
.text:0041289A                 mov     [esi+138h], eax
.text:004128A0                 mov     [esi+13Ch], edi
.text:004128A6                 mov     byte ptr [esi+165h], 1
.text:004128AD
.text:004128AD loc_4128AD:                             ; CODE XREF: KiAttachProcess(x,x,x,x)+43j
.text:004128AD                 cmp     byte ptr [ebx+65h], 0
.text:004128B1                 jnz     loc_445D72
.text:004128B7                 lea     esi, [ebx+40h]
.text:004128BA
.text:004128BA loc_4128BA:                             ; CODE XREF: KiAttachProcess(x,x,x,x)+33513j
.text:004128BA                 mov     eax, [esi]
.text:004128BC                 cmp     eax, esi
.text:004128BE                 jnz     loc_445D4F
.text:004128C4                 mov     eax, [ebp+arg_C]
.text:004128C7                 push    dword ptr [eax+10h]
.text:004128CA                 push    ebx
.text:004128CB                 call    _KiSwapProcess@8 ; KiSwapProcess(x,x)
.text:004128D0                 mov     cl, [ebp+arg_8]
.text:004128D3                 call    @KiUnlockDispatcherDatabase@4 ; KiUnlockDispatcherDatabase(x)
.text:004128D8
.text:004128D8 loc_4128D8:                             ; CODE XREF: .text:00445D6Dj
.text:004128D8                                         ; KiAttachProcess(x,x,x,x)+3357Cj
.text:004128D8                 pop     edi
.text:004128D9                 pop     esi
.text:004128DA                 pop     ebx
.text:004128DB                 pop     ebp
.text:004128DC                 retn    10h
.text:004128DC _KiAttachProcess@16 endp
.text:004128DC

或者执行更加底层函数KiSwapProcess
.text:00404AC4 ; __stdcall KiSwapProcess(x, x)
.text:00404AC4 _KiSwapProcess@8 proc near              ; CODE XREF: KiAttachProcess(x,x,x,x)+76p
.text:00404AC4                                         ; KeDetachProcess()+73p ...
.text:00404AC4
.text:00404AC4 arg_0           = dword ptr  4
.text:00404AC4
.text:00404AC4                 mov     edx, [esp+arg_0]
.text:00404AC8                 xor     eax, eax
.text:00404ACA                 cmp     [edx+20h], ax
.text:00404ACE                 jz      short loc_404AFF
.text:00404AD0                 mov     ecx, ds:0FFDFF03Ch
.text:00404AD6                 mov     eax, [edx+20h]
.text:00404AD9                 mov     [ecx+48h], eax
.text:00404ADC                 mov     eax, [edx+24h]
.text:00404ADF                 mov     [ecx+4Ch], eax
.text:00404AE2                 mov     ecx, ds:0FFDFF038h
.text:00404AE8                 mov     eax, [edx+28h]
.text:00404AEB                 mov     [ecx+108h], eax
.text:00404AF1                 mov     eax, [edx+2Ch]
.text:00404AF4                 mov     [ecx+10Ch], eax
.text:00404AFA                 mov     eax, 48h
.text:00404AFF
.text:00404AFF loc_404AFF:                             ; CODE XREF: KiSwapProcess(x,x)+Aj
.text:00404AFF                 lldt    ax
.text:00404B02                 mov     ecx, ds:0FFDFF040h
.text:00404B08                 mov     edx, [esp+arg_0]
.text:00404B0C                 xor     eax, eax
.text:00404B0E                 mov     gs, ax
.text:00404B10                 mov     eax, [edx+18h]
.text:00404B13                 mov     [ecx+1Ch], eax
.text:00404B16                 mov     cr3, eax
.text:00404B19                 mov     ax, [edx+30h]
.text:00404B1D                 mov     [ecx+66h], ax
.text:00404B21                 retn    8
.text:00404B21 _KiSwapProcess@8 endp
.text:00404B21

我们仔细看代码,就是更改cr3
我们自己用代码完成-以下代码参考sinister

引用:
当调用此函数时先把要切换的进程的内核堆栈数加一(EPROCESS->Stack
Count),并从形参中取得当前线程保存 APC 状态的位置,(ETHREAD->SaveApcState)
得到当前线程 APC 状态(ETHREAD->ApcState),来调用 KiMoveApcState()  函数,将当
前线程APC 状态 (ETHREAD->ApcState) 复制到当前线程 SavedApcState 处保存。然后
将当前线程内核 APC 的 Progress 状态、内核模式 APC 的 Pending 状态、用户模式 
APC 的 Pending 态均设置为 FALSE(ETHREAD->KernelApcInProgress、ETHREAD->Kernel
ApcPending、ETHREAD->UserApcPending),并初始化当前线程内核模式与用户模式 APC 
状态链(ETHREAD->>ApcState.ApcListHead[KernelMode]、ETHREAD->ApcState.ApcList
Head[UserMode]),然后将当前线程所在进程(ETHREAD->ApcState->EPROCESS)设置为
要切换的进程(EPROCESS),比较当前线程保存 APC 状态(ETHREAD->SavedApcState)
是否与形参中(SavedApcState)要输出的保存 APC 态相等,如相等则需要将当前线程
APC 状态与保存 APC 状态(ETHREAD->ApcState、ETHREAD->SavedApcState)分别赋与
当前线程的 ApcStatePointer[0] 与 ApcStatePointer[1],(ApcStatePointer 是 
KAPC_STATE 结构数组而 KAPC_STATE 又是由 LIST_ENTRY 链表描述的)然后在设置当前
线程 APC 状态索引为 1(ETHREAD->ApcStateIndex)。
代码:
KiAttachProcess(EPROCESS *Process,Irql){  //CurThread=fs:124h //CurProcess=CurThread->ApcState.Process;  if(CurProcess!=Process){      if(CurProcess->ApcStateIndex || KPCR->DpcRoutineActive)KeBugCheckEx...      }  //if we already in process's context if(CurProcess==Process){KiUnlockDispatcherDatabase(Irql);return;}  Process->StackCount++; KiMoveApcState(&CurThread->ApcState,&CurThread->SavedApcState);  // init lists CurThread->ApcState.ApcListHead[0].Blink=&CurThread->ApcState.ApcListHead[0]; CurThread->ApcState.ApcListHead[0].Flink=&CurThread->ApcState.ApcListHead[0]; CurThread->ApcState.ApcListHead[1].Blink=&CurThread->ApcState.ApcListHead[1]; CurThread->ApcState.ApcListHead[1].Flink=&CurThread->ApcState.ApcListHead[1];;  //fill curtheads's fields CurThread->ApcState.Process=Process;  CurThread->ApcState.KernelApcInProgress=0; CurThread->ApcState.KernelApcPending=0; CurThread->ApcState.UserApcPending=0;  CurThread->ApcState.ApcStatePointer.SavedApcState=&CurThread->SavedApcState; CurThread->ApcState.ApcStatePointer.ApcState=&CurThread->ApcState;  CurThread->ApcStateIndex=1;  //if process ready, just swap it... if(!Process->State)//state==0, ready      {      KiSwapProcess(Process,CurThread->SavedApcState.Process);      KiUnlockDispatcherDatabase(Irql);      return;      }  CurThread->State=1; //ready? CurThread->ProcessReadyQueue=1;  //put Process in Thread's waitlist CurThread->WaitListEntry.Flink=&Process->ReadyListHead.Flink; CurThread->WaitListEntry.Blink=Process->ReadyListHead.Blink;  Process->ReadyListHead.Flink->Flink=&CurThread->WaitListEntry.Flink; Process->ReadyListHead.Blink=&CurThread->WaitListEntry.Flink;  // else, move process to swap list and wait if(Process->State==1){//idle?      Process->State=2; //trans      Process->SwapListEntry.Flink=&KiProcessInSwapListHead.Flink;      Process->SwapListEntry.Blink=KiProcessInSwapListHead.Blink;         KiProcessInSwapListHead.Blink=&Process->SwapListEntry.Flink;      KiSwapEvent.Header.SignalState=1;      if(KiSwapEvent.Header.WaitListHead.Flink!=&KiSwapEvent.Header.WaitListHead. Flink)                     KiWaitTest(&KiSwapEvent,0xa); //fastcall      }  CurThread->WaitIrql=Irql; KiSwapThread(); return; } 
引用:
从这个函数可以得到以下结论。进程可以处于以下状态——0(准备),1(Idle),2(Tra
ns——切换)。这证实了高层次的信息。KiAttachProcess使用了另外两个函数KiSwapProce
ss和KiSwapThread。

/************************* KiSwapProcess ****************************/

KiSwapProcess(EPROCESS* NewProcess, EPROCESS* OldProcess)
{
// just reload cr3 and small work with TSS

     // TSS=KPCR->TSS;
     // xor eax,eax
     // mov gs,ax
TSS->CR3=NewProcess->DirectoryTableBase;//0x1c
     // mov cr3,NewProcess->DirectoryTableBase
TSS->IopmOffset=NewProcess->IopmOffset;//0x66
if(WORD(NewProcess->LdtDescriptor)==0){lldt 0x00; return;//}
     //GDT=KPCR->GDT;
(QWORD)GDT->0x48=(QWORD)NewProcess->LdtDescriptor;
(QWORD)GDT->0x108=(QWORD)NewProcess->Int21Descriptor;
lldt 0x48;
return;
}

切换进程上下文。正如我所料,这个函数只是重新加载CR3寄存器,再加上一点相关的操作。
例如,用IopmOffset域的值建立TSS中的I/O位图的偏移。还必需将选择子的值加载到ldt(只
用于VDM session)。

/************************* SwapContext ******************************/

SwapContext(NextThread,CurThread,WaitIrql)
{

NextThread.State=ThreadStateRunning; //2
KPCR.DebugActive=NextThread.DebugActive;

cli();

//Save Stack
CurThread.KernelStack=esp;

//Set stack
KPCR.StackLimit=NextThread.StackLimit;
KPCR.StackBase=NextThread.InitialStack;

tmp=NextThread.InitialStack-0x70;
newcr0=cr0&0xfffffff1|NextThread.NpxState|*(tmp+0x6c);
if(newcr0!=cr0)reloadcr0();
if(!*(tmp-0x1c)&0x20000)tmp-=0x10;
TSS=KPCB.TSS;
TSS->ESP0=tmp;

//set pTeb
KPCB.Self=NextThread.pTeb;
esp=NextThread.KernelStack;
sti();

//correct GDT
GDT=KPCB.GDT;
WORD(GDT->0x3a)=NextThread.pTeb;
BYTE(GDT->0x3c)=NextThread.pTeb>>16;
BYTE(GDT->0x3f)=NextThread.pTeb>>24;

//if we must swap processes, do it (like KiSwapProcess)

if(CurThread.ApcState.Process!=NextThread.ApcState.Process)
     {
     //******** like KiSwapProcess
     }

NextThread->ContextSwitches++;

KPCB->KeContextSwitches++;

if(!NextThread->ApcState.KernelApcPending)return 0;

//popf;
// jnz HalRequestSoftwareInterrupt// return 0

return 1;
}

切换堆栈,修正GDT,以使FS寄存器指向TEB。如果线程属于当前进程,则不进行上下文切换
。否则,进行的操作和KiSwapProcess中的大致差不多。

介绍比较多,如果不想看的话可以直接复制代码:lol 
下面的代码完成2个函数模拟,感谢KiSSinGGer大哥写的代码
NTSTATUS
WriteProcessMem( 
  IN HANDLE  hProcess,
  IN PVOID  DstAddr, 
  IN PVOID  SrcAddr,  
  IN ULONG  Size
)
{
  NTSTATUS  Status = STATUS_UNSUCCESSFUL;
  PEPROCESS  Process = NULL;
  KAPC_STATE   ApcState;
  PMDL    pMdl = NULL;
  PCHAR    MemBlock = NULL;
  PCHAR    MappedDstAddr = NULL;
  
  if( SrcAddr == NULL || DstAddr == NULL || Size == 0 )
    return Status;
    
  Status = ObReferenceObjectByHandle(
        hProcess, 
        PROCESS_VM_WRITE|PROCESS_VM_READ, 
        NULL, 
        KernelMode, 
        &Process, 
        NULL
        );
  if( !NT_SUCCESS(Status) )
  {
    DbgMsg( "ObReferenceObjectByHandle Failed.\n" );
    return Status;
  }
  
  //
  // Get Current Process' Memory Block
  //
  
  MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
  if( MemBlock == NULL )
  {
    Status = STATUS_UNSUCCESSFUL;
    goto __EXIT2;
  }
  
  __try
  {
    if( MmIsAddressValid(SrcAddr) )
    {
      ProbeForRead( SrcAddr, Size, sizeof(CHAR) );
      RtlCopyMemory( MemBlock, SrcAddr, Size );
    }
    else
    {
      DbgMsg( "SrcAddr not valid.\n" );
      Status = STATUS_UNSUCCESSFUL;
      goto __EXIT2;
    }
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
    
    DbgMsg( "Read SrcAddr raise an exception.\n" );
    Status = STATUS_UNSUCCESSFUL;
    goto __EXIT2;
  }
  
  //
  // Attach to Dst Process
  //
  
  KeStackAttachProcess( &(Process->Pcb), &ApcState);
  
  __try
  {
    pMdl = IoAllocateMdl( DstAddr, Size, FALSE, FALSE, NULL );
    if( pMdl )
    {
      MmProbeAndLockPages (pMdl, KernelMode, IoWriteAccess);
      MappedDstAddr = MmMapLockedPagesSpecifyCache (pMdl,KernelMode,MmCached,NULL,FALSE,NormalPagePriority);
      if( MappedDstAddr )
      {
        RtlCopyMemory( MappedDstAddr, MemBlock, Size );
        Status = STATUS_SUCCESS;
        goto __EXIT1;      
      }
      else
        Status = STATUS_UNSUCCESSFUL;
    }
    else
      Status = STATUS_UNSUCCESSFUL;
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
    DbgMsg( "Process DstAddr raise an exception.\n" );
    Status = STATUS_UNSUCCESSFUL;
    goto __EXIT1;
  }
  
  __EXIT1:
  
  //
  // Detach to Dst Process
  //
  
  KeUnstackDetachProcess( &ApcState );
  if( MappedDstAddr )
  {
    MmUnmapLockedPages( MappedDstAddr, pMdl);
  }
  if( pMdl )
  {
    MmUnlockPages( pMdl );
    IoFreeMdl( pMdl );
  }
  
  __EXIT2:
  if( Process )
  {
    ObDereferenceObject( Process );
  }
  if( MemBlock )
  {
    ExFreePool( MemBlock );
  }
  
  return Status; 
}


NTSTATUS
ReadProcessMem( 
  IN HANDLE  hProcess, 
  IN PVOID  BaseAddr,
  IN PVOID  Buffer,
  IN ULONG  Size
)
{
  NTSTATUS  Status = STATUS_UNSUCCESSFUL;
  PEPROCESS  Process = NULL;
  KAPC_STATE  ApcState;
  PCHAR    MemBlock = NULL;  
  
  
  if( BaseAddr == NULL || Buffer == NULL || Size == 0 )
    return Status;
    
  Status = ObReferenceObjectByHandle(
        hProcess, 
        PROCESS_VM_WRITE|PROCESS_VM_READ, 
        NULL, 
        KernelMode, 
        &Process, 
        NULL
        );
  if( !NT_SUCCESS(Status) )
  {
    DbgMsg( "ObReferenceObjectByHandle Failed.\n" );
    return Status;
  }
  
  MemBlock = ExAllocatePoolWithTag( NonPagedPool, Size, '-_-!');
  if( MemBlock == NULL )
  {
    ObDereferenceObject( Process );
    return STATUS_UNSUCCESSFUL;
  }
  
  KeStackAttachProcess( &(Process->Pcb), &ApcState);
  
  __try
  {
    if( MmIsAddressValid(BaseAddr) )
    {
      ProbeForRead( BaseAddr, Size, sizeof(CHAR) );
      RtlCopyMemory( MemBlock, BaseAddr, Size );
    }
    else
    {
      DbgMsg( "BaseAddr not valid.\n" );
      Status = STATUS_UNSUCCESSFUL;
      goto __EXIT2;
    }
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
    DbgMsg( "Process BaseAddr raise an exception.\n" );
    Status = STATUS_UNSUCCESSFUL;
    goto __EXIT2;
  }

  KeUnstackDetachProcess( &ApcState );
  
  __try
  {
    if( MmIsAddressValid(Buffer) )
    {
      ProbeForWrite( Buffer, Size, sizeof(CHAR));
      RtlCopyMemory( Buffer, MemBlock, Size );
    }
    else
    {
      DbgMsg( "Buffer not valid.\n" );
      Status = STATUS_UNSUCCESSFUL;
      goto __EXIT2;
    }
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
    DbgMsg( "Process Buffer raise an exception.\n" );
    Status = STATUS_UNSUCCESSFUL;
    goto __EXIT2;
  }
  
  
  
  __EXIT2:
  
  KeUnstackDetachProcess( &ApcState );
  
  if( Process )
  {
    ObDereferenceObject( Process );
  }
  if( MemBlock )
  {
    ExFreePool( MemBlock );
  }
  return Status; 
}

下面的代码完成最后一个事情
HANDLE ZwOpenProcess(HANDLE ProcessId)
{
  PEPROCESS Process;
  NTSTATUS St;
  HANDLE hProcess = NULL;

  PsLookupProcessByProcessId(ProcessId, &Process);

  ObOpenObjectByPointer(Process, 0, NULL, 0, NULL, UserMode, &hProcess);

  ObDereferenceObject(Process);

  return hProcess;
}
很好,一些hook函数我们完成了模拟这些内核hook已经对我们不在有意义了

R3部分,这个只有NP才有,我们需要拦截线程创建函数禁止np注入邪恶模块给我们的od
Win32在创建用户态线程的时候,大致流程如下
  CreateThread (kernel32.dll)
  CreateRemoteThread (kernel32.dll)
  NtCreateThread (ntoskrnl.exe)
  PspCreateThread (ntos\ps\create.c:237)
PspCreateThread函数在创建用户态线程时,使用PspUserThreadStartup函数(ntos\ps\
create.c:1639)作为入口函数参数,线程被创建后直接进入此函数。PspUserThreadStartu
p函数对非僵死线程和没有结束的线程初始化其APC;

对于载入和卸载DLL,实际的调用流程如下:
  LoadLibrary (kernel32.dll)
  LoadLibraryEx (kernel32.dll)
  BasepLoadLibraryAsDataFile (kernel32.dll)
  NtMapViewOfSection (ntos\mm\mapview.c:204)

注:Kernel APC与User APC的区别:
         1)前者是在APC_LEVEL上运行,后者是在PASSIVE_LEVEL上运行
          2)只有KTHREAD中的AlertAble或KTHREAD-ApcState-UserApcPending
             为1的情况下,User APC中的Routine才会被执行,Kernel APC没有这个限制
我之所以不使用广为人知的PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine
PsSetLoadImageNotifyRoutine。 原因很简单,他们都是插入APC,本质上,其实线程,进程,模块
已经建立或者载入,不过没有开始运行--原因是resume的时候会先执行APC
我们需要从本质上禁止插入邪恶模块,而不是插入1个没有开始运行的模块
当然我也尝试过在apc中无效化模块(故意填充0),但是发现不起作用,邪恶模块还是运行了

还有一个问题是我们不得不关注的——运行程序的时候父进程会“帮助”子进程创建子进程的主要线程(因为这个时候子进程还没有线程,所以不可能自己创建),所以这就引出了另外一个问题——如何判断这个远程线程创建是注入还是正常的程序运行呢?
简单分析下我们不难发现,二者的区别在于将被创建线程的进程是否还存在其他线程——因为我们的代码是在NtCreateThread之前执行的,所以如果是正常运行的程序的话,这个时候它不应当有任何的线程,而线程注入则不同,线程注入的话目标进程应当已经有了至少一个线程(一个主线程和若干个附属线程(或者没有附属线程)
我们需要检查EPROCESS结构的+0x190 ThreadListHead : _LIST_ENTRY
EPROCESS 结构是一个很大的结构,而且不同版本系统结构还不一样
typedef struct _EPROCESS {
/*000*/ BYTE                   Pcb[0x6C];
/*06C*/ NTSTATUS               ExitStatus;
/*070*/ KEVENT                 LockEvent;
/*080*/ DWORD                  LockCount;
/*084*/ DWORD                  dw084;
/*088*/ LARGE_INTEGER          CreateTime;
/*090*/ LARGE_INTEGER          ExitTime;
/*098*/ PVOID                  LockOwner;
/*09C*/ DWORD                  UniqueProcessId;
/*0A0*/ LIST_ENTRY             ActiveProcessLinks; // see PsActiveListHead
/*0A8*/ DWORD                  QuotaPeakPoolUsage[2]; // NP, P
/*0B0*/ DWORD                  QuotaPoolUsage[2]; // NP, P
/*0B8*/ DWORD                  PagefileUsage;
/*0BC*/ DWORD                  CommitCharge;
/*0C0*/ DWORD                  PeakPagefileUsage;
/*0C4*/ DWORD                  PeakVirtualSize;
/*0C8*/ LARGE_INTEGER          VirtualSize;
/*0D0*/ MMSUPPORT              Vm;
/*100*/ LIST_ENTRY             SessionProcessLinks;
/*108*/ DWORD                  dw108[6];
/*120*/ PVOID                  DebugPort;
/*124*/ PVOID                  ExceptionPort;
/*128*/ PVOID                  ObjectTable;
/*12C*/ PVOID                  Token;
/*130*/ FAST_MUTEX             WorkingSetLock;
/*150*/ DWORD                  WorkingSetPage;
/*154*/ BOOLEAN                ProcessOutswapEnabled;
/*155*/ BOOLEAN                ProcessOutswapped;
/*156*/ BOOLEAN                AddressSpaceInitialized;
/*157*/ BOOLEAN                AddressSpaceDeleted;
/*158*/ FAST_MUTEX             AddressCreationLock;
/*178*/ KSPIN_LOCK             HyperSpaceLock;
/*17C*/ DWORD                  ForkInProgress;
/*180*/ WORD                   VmOperation;
/*182*/ BOOLEAN                ForkWasSuccessful;
/*183*/ BYTE                   MmAgressiveWsTrimMask;
/*184*/ DWORD                  VmOperationEvent;
/*188*/ PVOID                  PaeTop;
/*18C*/ DWORD                  LastFaultCount;
/*190*/ DWORD                  ModifiedPageCount;
/*194*/ PVOID                  VadRoot;
/*198*/ PVOID                  VadHint;
/*19C*/ PVOID                  CloneRoot;
/*1A0*/ DWORD                  NumberOfPrivatePages;
/*1A4*/ DWORD                  NumberOfLockedPages;
/*1A8*/ WORD                   NextPageColor;
/*1AA*/ BOOLEAN                ExitProcessCalled;
/*1AB*/ BOOLEAN                CreateProcessReported;
/*1AC*/ HANDLE                 SectionHandle;
/*1B0*/ PVOID                  Peb;
/*1B4*/ PVOID                  SectionBaseAddress;
/*1B8*/ PVOID                  QuotaBlock;
/*1BC*/ NTSTATUS               LastThreadExitStatus;
/*1C0*/ DWORD                  WorkingSetWatch;
/*1C4*/ HANDLE                 Win32WindowStation;
/*1C8*/ DWORD                  InheritedFromUniqueProcessId;
/*1CC*/ ACCESS_MASK            GrantedAccess;
/*1D0*/ DWORD                  DefaultHardErrorProcessing; // HEM_*
/*1D4*/ DWORD                  LdtInformation;
/*1D8*/ PVOID                  VadFreeHint;
/*1DC*/ DWORD                  VdmObjects;
/*1E0*/ PVOID                  DeviceMap;
/*1E4*/ DWORD                  SessionId;
/*1E8*/ LIST_ENTRY             PhysicalVadList;
/*1F0*/ PVOID                  PageDirectoryPte;
/*1F4*/ DWORD                  dw1F4;
/*1F8*/ DWORD                  PaePageDirectoryPage;
/*1FC*/ CHAR                   ImageFileName[16];
/*20C*/ DWORD                  VmTrimFaultValue;
/*210*/ BYTE                   SetTimerResolution;
/*211*/ BYTE                   PriorityClass;
/*212*/ WORD                   SubSystemVersion;
/*214*/ PVOID                  Win32Process;
/*218*/ PVOID                  Job;
/*21C*/ DWORD                  JobStatus;
/*220*/ LIST_ENTRY             JobLinks;
/*228*/ PVOID                  LockedPagesList;
/*22C*/ PVOID                  SecurityPort;
/*230*/ PVOID                  Wow64;
/*234*/ DWORD                  dw234;
/*238*/ IO_COUNTERS            IoCounters;
/*268*/ DWORD                  CommitChargeLimit;
/*26C*/ DWORD                  CommitChargePeak;
/*270*/ LIST_ENTRY             ThreadListHead;
/*278*/ PVOID                  VadPhysicalPagesBitMap;
/*27C*/ DWORD                  VadPhysicalPages;
/*280*/ DWORD                  AweLock;
/*284*/ } EPROCESS, *PEPROCESS;
代码如下
//==========================================
BOOLEAN
ProcessNoThread( PEPROCESS Process)
{
PLIST_ENTRY Entry;
PLIST_ENTRY ThreadListEntry;
PLIST_ENTRY ListHead;

ThreadListEntry = (PLIST_ENTRY)((ULONG)Process + ThreadListHead);
Entry = ThreadListEntry->Flink;
return (Entry==ThreadListEntry);

也就是判断ProcessNoThread了,如果建立进程就通过,反之则拦截

一些结构很复杂很难,偶也不想多写,其实偶也没很明白,更加不敢写,
详细的查阅其他资料把,
==========================================================================

本来在DEBUGMAN看到有人发了一篇调戏HS的 但碍于声望不够看不到 似乎听到有人说是"白名单"大法 于是搜了些资料 于是就KO了 于是就有此文了。

"白名单","白名单","白名单","白名单"-------"伪装进程"?

先来看看 TEB。

lkd> dt _TEB
nt!_TEB
   +0x000 NtTib            : _NT_TIB
   +0x01c EnvironmentPointer : Ptr32 Void
   +0x020 ClientId         : _CLIENT_ID
   +0x028 ActiveRpcHandle : Ptr32 Void
   +0x02c ThreadLocalStoragePointer : Ptr32 Void
   +0x030 ProcessEnvironmentBlock : Ptr32 _PEB        //这里指向PEB

再来看看PEB。

lkd> dt _PEB
nt!_PEB
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 SpareBool        : UChar
   +0x004 Mutant           : Ptr32 Void
   +0x008 ImageBaseAddress : Ptr32 Void
   +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
   +0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS //这里指向进程参数

接着再来看_RTL_USER_PROCESS_PARAMETERS 的结构。

lkd> dt _RTL_USER_PROCESS_PARAMETERS
nt!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : Uint4B
   +0x004 Length           : Uint4B
   +0x008 Flags            : Uint4B
   +0x00c DebugFlags       : Uint4B
   +0x010 ConsoleHandle    : Ptr32 Void
   +0x014 ConsoleFlags     : Uint4B
   +0x018 StandardInput    : Ptr32 Void
   +0x01c StandardOutput   : Ptr32 Void
   +0x020 StandardError    : Ptr32 Void
   +0x024 CurrentDirectory : _CURDIR 
  +0x030 DllPath          : _UNICODE_STRING
   +0x038 ImagePathName    : _UNICODE_STRING //指向映像完整路径 

再来看下_UNICODE_STRING (似乎看过瘾了?)

lkd> dt _UNICODE_STRING
nt!_UNICODE_STRING
   +0x000 Length           : Uint2B
   +0x002 MaximumLength    : Uint2B
   +0x004 Buffer           : Ptr32 Uint2B //]//指向存放模块名的UNICODE字符串指针



好了,你会发现有HS保护的游戏 用任务管理器结束得了,噢,对了,这就是"白名单"大法. 我们可以修改ImagePathName 指向任务管理器(C:\WINDOWS\system32\taskmgr.exe).

具体实现代码:

代码:
procedure Kill(name:widestring); //Writer:fairystory begin  asm    mov eax,dword ptr fs:[$30] //指向PEB    mov eax,[eax+$10] //指向_RTL_USER_PROCESS_PARAMETERS    add eax,$38 //指向ImagePathName    add eax,$4   //指向ImagePathName.buffer    mov ebx,name     mov [eax],ebx     mov name,eax  //赋值  end; end;  procedure TForm1.FormCreate(Sender: TObject); var lj:widestring; begin      lj:='C:\WINDOWS\system32\taskmgr.exe'; //任务管理器路径      Kill(lj); end; 
好了,你在加些代码FindWindow,GetWindowThreadProcessId,OpenProcess,ReadProcessMemory/WriteProcessMemory.去试下读写. 发现没? 可以读也可以写了..

你可能感兴趣的:(职场,休闲,unhook)