hook api 反OD调试的一种思路
by胡华

本程序在backer的耐心指导下完成,非常感谢backer的帮助!!!
关于OD怎样在程序中下断点相信大家都知道,就是用WriteProcessMemory函数在要下断点的地方写入0xCC,也就是int 3的中断,
如果我们挂钩OD的WriteProcessMemory函数不让它下断点是不是就可以让OD不能正常跟踪了呢?有了这个思路,下面就是实践了。

首先得解决几个问题:
1.我们的目标是最好在OD正常加载本程序后就已经被hook,怎么解决?

这个问题其实很简单,我们知道全局类的初始化工作在main函数之前,OD正常加载后会停在WinMain函数处,如果把hook api 代
码放在一个全局类的构造函数中就可以了,呵呵,就这么简单。


2.要得到OD的输入表中的WriteProcessMemory函数的跳转地址

呵呵,用OD来调试OD不就很容易得到了么?
用OD调试OD后我们得到WriteProcessMemory函数的跳转地址放在0x0050D450这个地方。

3.如果前面的两个问题解决,现在剩下的就是程序在运行期间要判断自己的父进程是否是OD,如果是OD那么就可以肯定被OD加载,
问题是怎么方便的取得父进程?如果用列取进程列表的方式来获的父进程,这比较麻烦,但是的确是一种思路。我们不采取这种
思路,其实在ntdll.dll中有一个函数可以得到父进程,下面用代码来说话吧:

NTDLL.DLL中有一个函数叫NtQueryInformationProcess,用它可以将指定类型的进程信息拷贝到某个缓冲。其原型如下:
NTSYSAPI
NTSTATUS
NTAPI
NtQueryInformationProcess (
IN HANDLE ProcessHandle, // 进程句柄
IN PROCESSINFOCLASS InformationClass, // 信息类型
OUT PVOID ProcessInformation, // 缓冲指针
IN ULONG ProcessInformationLength, // 以字节为单位的缓冲大小
OUT PULONG ReturnLength OPTIONAL // 写入缓冲的字节数
);
    第一个参数是希望操作的进程句柄,这个句柄必须以PROCESS_QUERY_INFORMATION模式存取。为了取得一个句柄,我们必须用
OpenProcess函数:
HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,FALSE,dwProcessID);
    第二个参数是请求信息的类型,这个参数可以有许多个值,本文例子中将用ProcessBasicInformation (值为0)。
    因此,如果第二个参数是ProcessBasicInformation的话,则第三个参数必须为一个指针指向结构PROCESS_BASIC_INFORMATION:
typedef struct
{
      DWORD ExitStatus; // 接收进程终止状态
      DWORD PebBaseAddress; // 接收进程环境块地址
      DWORD AffinityMask; // 接收进程关联掩码
      DWORD BasePriority; // 接收进程的优先级类
      ULONG UniqueProcessId; // 接收进程ID
      ULONG InheritedFromUniqueProcessId; //接收父进程ID
} PROCESS_BASIC_INFORMATION;

  这个结构的最后一个参数是InheritedFromUniqueProcessId,它就是我们所要的东西。

得到了父进程id之后,呵呵,现在开始打开进程了
        DWORD dwId=GetCurrentProcessId();
  DWORD dwParentId;
        dwParentId=GetParentProcessID(dwId); //获取父进程id
  hParentProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwParentId);
又有一个问题怎么判断是否是OD呢?
我们可以用读取内存的方式查看某一段代码是不是OD的就行了,要检查哪段代码那就随你了,
我这里是读取OD的0x00401000开始的几个字节。
更简单的方法是读取0x0050D450这个地址判断是不是WriteProcessMemory这个函数的真实跳转地址,如果是那就基本可以断定是OD了,呵呵,不会那么巧其它的有的程序也会是这个地址吧!

判断是OD后,现在要做的就是在OD中申请一段空间把要执行的代码写在这个地址了
BYTE bRet=0xC3; //这是hook api后转到这要执行的代码
LPVOID lpWriteFunAddress=(void*)0x0050D450;
LPVOID lpNewWriteFunAddress=NULL;
lpNewWriteFunAddress=VirtualAllocEx(hParentProcess,NULL,sizeof(bRet),MEM_COMMIT,PAGE_READWRITE);
//插入函数返回代码
if(lpNewWriteFunAddress==NULL)
{
   MessageBox(NULL,"virtualalloc err","error",MB_OK);
      
}
if(WriteProcessMemory(hParentProcess, lpNewWriteFunAddress, &bRet, sizeof(bRet), NULL)==0)
{
  MessageBox(NULL,"insert retn err","error",MB_OK);      
}
写入后现在就是把0x0050D450这个地址的WriteProcessMemory函数的真实跳转地址改为我们上面申请的地址了:
       //改变api地址
    
  DWORD flOldProtect;
  VirtualProtect(lpWriteFunAddress,sizeof               (lpWriteFunAddress),PAGE_READWRITE,&flOldProtect);
  if(WriteProcessMemory(hParentProcess, lpWriteFunAddress, &lpNewWriteFunAddress,                                                sizeof(lpNewWriteFunAddress), NULL)==0)
  {
       MessageBox(NULL,"insert fun addr err","error",MB_OK);
      
  }

不知道大家注意没,我插入的要执行的代码只有一个字节BYTE bRet=0xC3,
其实就是ret,呵呵,知道了吧,只要在OD中按F2下断点就会执行WriteProcessMemory这个函数,而这个函数现在是什么都不做就直接返回了,也不用考虑堆栈平衡,我们要的就是OD悄无声息的挂掉!如果想忽悠一下而不让OD挂掉,那就用ret14了,大家尽情的发挥吧!!!

好了,到这里已经结束了,再次感谢backer的指导!!!

  • 标 题:答复
  • 作 者:forgot
  • 时 间:2007-07-23 16:17

od可以选择停在system breakpoint或者entrypoint of program
bp OpenProcess你这个就挂了

btw: 只是修改writeprocessmemory可以绕开,参考themida

  • 标 题:答复
  • 作 者:foxabu
  • 时 间:2007-07-23 16:30

其次最简单的办法就是我以前写过的插件
OllgDbg为Admin权限
把被调试的程序的权限令牌弄成Users就行了。
操作系统会帮你返回Access Denied.
目前因为窗体没有权限表,所以说要判断还是从窗体判断最好。