基于ring0层的内存搜索,实现枚举线程.

代码的既乱又烂,将就下~_~.
原理是枚举_ethread
vs2008 + ddkwizard写的

extern "C"
{
#include <ntddk.h>
}

#define  OBJECT_HEADER_SIZE        0x018
#define  OBJECT_HEADER_TYPE_OFFSET    0x008
#define  ETHREAD_CID_OFFSET        0x1ec
#define  ETHREAD_STARTADDRESS_OFFSET    0x224
#define  ETHRAED_KTHREAD_OFFSET      0x000
#define  KTHREAD_TEB_OFFSET        0x020
#define  ETHREAD_EXITTIME_OFFSET      0x1c8

PETHREAD  pEThread;
PEPROCESS  pSystem;
ULONG    ThreadType;
ULONG    uThreadCount;
ULONG    ThreadMaxAddr;

VOID Unload(IN PDRIVER_OBJECT  DriverObject)
{
  KdPrint(("Driver Unload!\n"));
}

ULONG GetThreadType()
{
  KdPrint(("GetThreadType!\n"));
  ULONG  ThreadType;

  ThreadType = *(PULONG)((ULONG)pEThread - OBJECT_HEADER_SIZE + OBJECT_HEADER_TYPE_OFFSET);
  return ThreadType;
}


BOOLEAN IsThreadType(IN ULONG i)
{
  if(!MmIsAddressValid((PVOID)i))
  {
    return FALSE;
  }
  ULONG  objectaddress = i - KTHREAD_TEB_OFFSET - ETHRAED_KTHREAD_OFFSET - OBJECT_HEADER_SIZE +OBJECT_HEADER_TYPE_OFFSET;

  if(MmIsAddressValid((PVOID)objectaddress))
  {
    if(*(PULONG)objectaddress == ThreadType)
    {
      return TRUE;
    }
  }
  return FALSE;
}

VOID ShowThread(IN ULONG i, IN ULONG pid)
{
  /*PLARGE_INTEGER ExitTime;
  ExitTime = (PLARGE_INTEGER)(i + ETHREAD_EXITTIME_OFFSET);

  if(ExitTime->QuadPart == 0)
  {
    return;
  }*/

  CLIENT_ID  cid;
  ULONG    teb;
  ULONG    address;

  address = *(PULONG)(i + ETHREAD_STARTADDRESS_OFFSET);
  cid.UniqueProcess  = *(PHANDLE)((ULONG)i + ETHREAD_CID_OFFSET);
  cid.UniqueThread  = *(PHANDLE)((ULONG)i+ ETHREAD_CID_OFFSET + 4);
  teb = *(PULONG)(i + ETHRAED_KTHREAD_OFFSET + KTHREAD_TEB_OFFSET);

  if((ULONG)cid.UniqueProcess != pid)
  {
    return ;
  }
  KdPrint(("address of ethread:0x%x\n", i));
  KdPrint(("thread addr:0x%x, tid:%5d, pid:0x%x, teb:0x%x\n", address, (ULONG)cid.UniqueThread, (ULONG)cid.UniqueProcess, teb));
  uThreadCount++;
}

// 需要列举线程的进程pid
VOID EnumThread(IN ULONG pid)
{
  KdPrint(("Enum thread!\n"));
  ULONG  teb;

  for(ULONG i = 0x80000000; i < (ULONG)ThreadMaxAddr; i += 4)
  {
    if(MmIsAddressValid((PVOID)i)) // 内存判定这块也不太熟先填上再说了.
    {
      if((0x817f0020) == i)
      {
        KdPrint(("address valid\n\n\n\n"));
      }
      teb = *(PULONG)i;
      if((teb & 0xfff00fff) == 0x7ff00000 || teb == 0) // teb的地址前3个要么7ff,要么全0
      {
        if(IsThreadType(i))
        {
          ShowThread(i - KTHREAD_TEB_OFFSET - ETHRAED_KTHREAD_OFFSET, pid);
        }
      }
    }
  }

  KdPrint(("Enum thread leave!\n"));
}

extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT  DriverObject,
                IN PUNICODE_STRING  RegistryPath
                )
{
  uThreadCount = 0;

  pEThread = PsGetCurrentThread();

  CLIENT_ID  cid;
  ULONG    address;

  /*if(pEThread != NULL)
  {
    address = *(PULONG)((ULONG)pEThread + ETHREAD_STARTADDRESS_OFFSET);
    cid.UniqueProcess  = *(PHANDLE)((ULONG)pEThread + ETHREAD_CID_OFFSET);
    cid.UniqueThread  = *(PHANDLE)((ULONG)pEThread + ETHREAD_CID_OFFSET + 4);

    KdPrint(("pEThread : 0x%x, tid: 0x%x, address : 0x%x\n",(ULONG)pEThread, (ULONG)cid.UniqueThread, address));
  }*/

  pSystem = PsGetCurrentProcess();
  ThreadMaxAddr = (ULONG)pSystem; // 不管了,先把system进程地址当成线程地址最大值了

  ThreadType = GetThreadType();

  KdPrint(("Thread type: 0x%x\n", ThreadType));

  EnumThread(1480);  // 我这里vmware上的explorer.exe的pid.每次都手动填,将就下吧...

  KdPrint(("Total thread: %4d\n", uThreadCount));
  DriverObject->DriverUnload = Unload;

  return STATUS_SUCCESS;
}

在vmware上,通过pspcidtable查system的 _eprocess的地址为817bd830.

列举别的进程中的线程还好,列举system的线程发现会漏掉线程,结果发现线程的最大地址比system进程_eprocess的地址还大(就是线程地址大于817bd830).不知道线程地址最大值应该填什么好?

MmIsAddressValid 判断的太少了

其实MmIsAddressValid的判断只有参考价值
只要不上锁,虚拟地址空间随时可以改变

if(*(PULONG)objectaddress == ThreadType)
{
      return TRUE;
}

这个判断太脆弱了。。。

posted on 2009-09-06 14:43  sky.wind  阅读(570)  评论(0编辑  收藏  举报