首页
社区
课程
招聘
[原创]PhantOm 驱动浅析
发表于: 2009-11-10 09:15 36134

[原创]PhantOm 驱动浅析

2009-11-10 09:15
36134

[目录]

一. 前言
二. PhantOm 在驱动层的保护方式
三. 挂钩的SSDT函数
四. 挂钩的SSDT Shadow函数
五. 结语

[内容]

一. 前言

相信脱壳和破解界的朋友们大部分都用过PhantOm这个隐藏OD的插件,以前可以说是过TMD这
种加密壳的利器,它可以在应用层和驱动层对OD进行保护。貌似应用层的保护方式已经给某
强人分析过了,这里就不再重复了,现在分析一下PhantOm的驱动保护。拿来开刀的是
PhantOm V1.54版本的。

PhantOm插件的驱动文件在PhantOm.dll的资源中,在OD运行时自动从资源中释放驱动到临时
文件夹中,我们可以通过PE_Stud等PE工具进行提取。

二. PhantOm的驱动层保护方式

当一个程序被调试,驱动将会将OD和被调试程序的相关信息存储在一个结构中,并以双链表
的形式存放。其结构如下:

typedef struct _DEBUG_INFO
{
  struct _DEBUG_INFO *Prev;
  struct _DEBUG_INFO *Next;
  PLONG  Debugger_Id;  // OD的id号
  PEPROCESS  Debugger_Eprocess;  // OD的EPROCESS结构
  PEPROCESS  Debuggee_Eprocess;  // 被调试程序的EPROCESS结构
} DEBUG_INFO, *PDEBUG_INFO;
VOID SetDebugInfo (PVOID DebugIdBuf)
{
  NTSTATUS status;
  PEPROCESS pEprocess;
  PDEBUG_INFO NodeAddr, LastNode;
  ULONG Debugger_id, Debuggee_id;
  Debugger_id = *(PULONG)DebugIdBuf;
  Debuggee_id = *(PULONG)((ULONG)DebugIdBuf+1);
  if (DebugIdBuf == NULL)
    return;
  
  // 判断相关结构是否已经存在
  if ( !GetDebugInfoByPid(*(PULONG)DebugIdBuf)
  {
    // 建立OD与被调试程序的关联
    // 传入被调试程序的id
    status = PsLookupProcessByProcessId(Debuggee_id), 
      EprocessOfDebuggee);
    if (status == STATUS_SUCCESS)
    {
      Debug_Info.Debuggee_Eprocess = EprocessOfDebuggee;
      ObDereferenceObject(EprocessOfDebuggee);
    }
  }
  else   // 相关结构不存在
  {
    // 为节点分配空间
    NodeAddr = (PDEBUG_INFO)ExAllocatePool(PagedPool, sizeof(DEBUG_INFO));
    if (NodeAddr == NULL)
      return;
      
    RtlZeroMemory(NodeAddr, 16);
    // 查找链表最后一个节点
    LastNode = GetLast();
    if (LastNode)
    {
      // 非空链表, 插入到表尾
      LastNode->Next = NodeAddr;
      NodeAddr->Prev = LastNode;
    }
    else
      // 空链表,插入到表头
      DebugInfoHeader = NodeAddr;
    // 填充链表结构
    NodeAddr->Debugger_Id = Debugger_id;
    if (STATUS_SUCCESS == PsLookupProcessByProcessId(Debugger_id, pEprocess))
    {
      NodeAddr->Debugger_Eprocess = pEprocess;
      ObDereferenceObject(pEprocess);
    }
    if (STATUS_SUCCESS == PsLookupProcessByProcessId(Debuggee_id, pEprocess))
    {
      NodeAddr->Debuggee_Eprocess = pEprocess;
      ObDereferenceObject(pEprocess);
    }
  }
}
NTSTATUS WINAPI New_NtQuerySystemInformation(
  __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
  __inout    PVOID SystemInformation,
  __in       ULONG SystemInformationLength,
  __out_opt  PULONG ReturnLength
  )
{
  NTSTATUS status;
  PEPROCESS cur_eprocess;
  PSYSTEM_PROCESSES system_process;
  PSYSTEM_PROCESSES system_process_prev;
  PSYSTEM_HANDLE_INFORMATION_EX handle_info;
  ULONG NextEntryDelta;
  ULONG handle_count, index, total_size;
  status = Org_NtQuerySystemInformation(
    SystemInformationClass,
    SystemInformation,
    SystemInformationLength,
    ReturnLength);
  if (status != STATUS_SUCCESS | IS_HOOK != TRUE)
    return status;

  cur_eprocess = IoGetCurrentProcess();
  // 当前进程是OD
  if (FindDebugInfoByEprocess(cur_eprocess))
    return status;

  switch (SystemInformationClass)
  {
  case SystemProcessesAndThreadsInformation:  // 5
    {
      system_process = (PSYSTEM_PROCESSES)SystemInformation;
      system_process_prev = system_process;
      NextEntryDelta = 0;
      do 
      {
        system_process = (PSYSTEM_PROCESSES)((ULONG)system_process + NextEntryDelta);
        if (GetDebugInfoByPid(system_process->ProcessId)) // 下面断链
        {
          // 非最后一项
          if (system_process->NextEntryDelta)
            system_process_prev->NextEntryDelta += system_process->NextEntryDelta;
          // 最后一项
          else
            system_process_prev->NextEntryDelta = 0;
          // 抹掉进程名字信息
          RtlZeroMemory(system_process->ProcessName.Buffer, 
            system_process->ProcessName.Length*2);
          // 然后将整个SYSTEM_PROCESS抹去
          total_size = system_process->ThreadCount << 6 + 0xB4;
          RtlZeroMemory(&system_process, total_size);
        }
        system_process_prev = system_process;
        NextEntryDelta = system_process->NextEntryDelta;
      } while (NextEntryDelta);
      system_process = (PSYSTEM_PROCESSES)SystemInformation;
      NextEntryDelta = 0;
      do 
      {
        system_process = (PSYSTEM_PROCESSES)((ULONG)system_process + NextEntryDelta);
        if (GetDebugInfoByPid(system_process->InheritedFromProcessId))
          // 如果父进程是OD,则将父进程的ID改为explorer.exe的ID号
          system_process->InheritedFromProcessId = EXPLORER_ID;
        NextEntryDelta = system_process->NextEntryDelta;
      } while (NextEntryDelta);
    }
    break;

  case SystemKernelDebuggerInformation:  // 35
    RtlZeroMemory(SystemInformation, SystemInformationLength);
    break;

  case SystemHandleInformation:  // 16
    {
      handle_info = (PSYSTEM_HANDLE_INFORMATION_EX)SystemInformation;
      handle_count = handle_info->NumberOfHandles;
      index = 0;
      while (handle_count)
      {
        if (GetDebugInfoByPid(handle_info->Information[index].ProcessId))
          handle_info->Information[index].ProcessId = EXPLORER_ID;
        handle_count--;
        index++;
      }
    }
    break;
  }

  return STATUS_SUCCESS;
}
NTSTATUS  New_NtOpenProcess (    
             __out PHANDLE  ProcessHandle,    
             __in ACCESS_MASK  DesiredAccess,    
             __in POBJECT_ATTRIBUTES  ObjectAttributes,    
             __in_opt PCLIENT_ID  ClientId    
             )
{
  NTSTATUS status;
  if (IS_HOOK == TRUE && !FindDebugInfoByEprocess(IoGetCurrentProcess()))
  {
    status = STATUS_INVALID_PARAMETER;
    if (!MmIsAddressValid(ClientId) ||
      ClientId.UniqueProcess == CSRSS_ID ||
      GetDebugInfoByPid(ClientId->UniqueProcess))
      return status;
  }
  return Org_NtOpenProcess(ProcessHandle,
               DesiredAccess,
               ObjectAttributes,
               ClientId);
}
NTSTATUS
NTAPI
New_NtSetInformationThread(
               IN HANDLE               ThreadHandle,
               IN THREAD_INFORMATION_CLASS ThreadInformationClass,
               IN PVOID                ThreadInformation,
               IN ULONG                ThreadInformationLength 
               )
{
  NTSTATUS status;
  PVOID Object;
  if (IS_HOOK == TRUE)
  {
    status = ObReferenceObjectByHandle(ThreadHandle, 0, 0, KernelMode, Object, 0);
    if (status != STATUS_SUCCESS)
      return STATUS_INVALID_HANDLE;
    ObDereferenceObject(Object);
    if (!FindDebugInfoByEprocess(IoGetCurrentProcess()) &&
      ThreadInformationClass == ThreadHideFromDebugger)
      return STATUS_SUCCESS;
  }
  return Org_NtSetInformationThread(ThreadHandle,
                    ThreadInformationClass,
                    ThreadInformation,
                    ThreadInformationLength);
}
NTSTATUS 
NTAPI
New_NtClose(HANDLE ObjectHandle)
{
  PVOID Object;
  NTSTATUS status;
  if (IS_HOOK == FALSE)
    return Org_NtClose(ObjectHandle);
  // 防止使用无效句柄来检测调试器
  InterlockedIncrement(Lock_Number);
  status = ObReferenceObjectByHandle(ObjectHandle, 0, 0, 0, Object, 0);
  if (status == STATUS_SUCCESS)
  {
    ObDereferenceObject(Object);
    status = Org_NtClose(ObjectHandle);
  }
  else
    status = STATUS_SUCCESS;
  InterlockedDecrement(Lock_Number);
  return status;
}
NTSTATUS New_NtYieldExecution()
{
  Org_NtYieldExecution();
  return STATUS_NO_YIELD_PERFORMED;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (30)
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
2
这样的帖子,应该支持一下的~~~

另:广告沙发位置出租……
2009-11-10 09:24
0
雪    币: 433
活跃值: (1870)
能力值: ( LV17,RANK:1820 )
在线值:
发帖
回帖
粉丝
3
support,膜拜!!!!
2009-11-10 09:27
0
雪    币: 262
活跃值: (36)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
哎呀,抢了我沙发位
生平还没抢到过沙发,
本来想自己占自己的沙发
2009-11-10 09:28
0
雪    币: 362
活跃值: (25)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
5
写的不错,顶一个.
2009-11-10 09:52
0
雪    币: 101
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
好像以前有人发过逆出来的源代码了
2009-11-10 12:28
0
雪    币: 262
活跃值: (36)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
7
好像是有人逆过,不过是应用层的,驱动层貌似还没人发过
2009-11-10 15:38
0
雪    币: 291
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
是逆了应用层,驱动的这帖是第一份。逆向的两位都很牛!
2009-11-10 21:54
0
雪    币: 163
活跃值: (103)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
9
膜拜~驱动大牛~~~~~~~~
2009-11-10 22:21
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
10
蓝屏其实是小事情,phant0m有很多东西处理不到位
比如说,在有驱动的情况下,ZwClose((HANDLE)0x12345678)的返回值是STATUS_SUCCESS;等等

当然,StrongOD的驱动也有或多或少的小问题,很多人都不去分析,所以侥幸逃过一劫~~
2009-11-11 00:24
0
雪    币: 405
活跃值: (10)
能力值: ( LV9,RANK:1130 )
在线值:
发帖
回帖
粉丝
11
非常强悍。啊
2009-11-12 15:39
0
雪    币: 1485
活跃值: (884)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
有VM....
2009-11-12 19:52
0
雪    币: 357
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
收藏,有空再来仔细阅读。
2009-11-13 13:47
0
雪    币: 351
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
surport!!
2009-11-14 09:28
0
雪    币: 257
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
牛人啊,看雪牛人真多
2009-11-15 19:39
0
雪    币: 220
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
Very Good ! Thank you!
2009-11-16 23:27
0
雪    币: 608
活跃值: (91)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
17
非常精彩。。
2009-11-17 12:40
0
雪    币: 170
活跃值: (90)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
18
写的真不错  好文啊
2009-11-18 13:25
0
雪    币: 303
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
强,白摸!!!
2009-11-25 12:38
0
雪    币: 142
活跃值: (22)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
看不懂,但是要支持
2009-12-1 09:37
0
雪    币: 161
活跃值: (261)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
寫的很詳細!
但是我還看不懂!
2009-12-8 10:59
0
雪    币: 163
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
好东西,收藏了。谢谢楼主。
2010-8-29 22:29
0
雪    币: 243
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
支持,一哈字
2010-8-30 01:39
0
雪    币: 262
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
不顶不行``
2011-1-3 05:05
0
雪    币: 611
活跃值: (251)
能力值: ( LV12,RANK:390 )
在线值:
发帖
回帖
粉丝
25
好贴,学习!
hook NtYieldExecution是因为用户程序可以通过NtYieldExecution进行debugger检测。
NtYieldExecution作用:当前线程放弃剩余时间,让给其他线程执行。如果没有其他准备好的线程,该函数返回false;如果有,返回true。
当前线程如果被调试,那么调试器线程若处于单步状态,随时等待继续运行,则被调试线程执行NtYieldExecution时,调试器线程会恢复执行。
此时NtYieldExecution返回true。该线程则认为自身被调试了。否则认为没有被调试
2011-1-4 22:33
0
游客
登录 | 注册 方可回帖
返回
//