看雪论坛
发新帖
2

[逆向分析基础] [原创]理解OD的F4,F7,F8,F9

better 2008-11-22 22:56 50585
这个小程序只是hook(IAT hook)了OD的四个关键调试API:WaitForDebugEvent(),GetThreadContext(),SetThreadContext(),ContinueDebugEvent()
当程序运行时,OD通过WaitForDebugEvent等待系统调试事件,WaitForDebugEvent返回时代表有一个调试事件发生了,hook了它就可以轻而易举的知道所发生的具体事件。GetThreadContext得到一个CONTEXT结构,OD寄存器面板里的各个值就来源于这里,但是OD寄存器面板里的值不一定是原始真实的,hook了这个函数就可以看清清楚楚。最重要的是SetThreadContext,OD中F4,F7,F8,F9,shift+F7,shift+F9的功能就是设置CONTEXT结构,然后调用这个函数,设置线程环境,hook了这个函数就能弄清楚OD的调试花招。ContinueDebugEvent后系统就恢复执行被调试线程。

以最简单的F7为例:


看到图中EFLAGE:00000346(0…… 0011 0100 0110)
TF单步标志位是第9位,显然TF=1,表明执行完这条指令后引发单步异常
点确定后出现:


要恢复执行被调试线程了!
再点确定后出现:


图中可以清楚的看到ExceptionCode和ExceptionAddress,80000004就是上面提到单步异常(果然发生了),当然这个异常OD是自己处理的,不会发给被调试程序的!要注意的是硬件断点产生的异常也是80000004,所以最后要通过Dr6的最后四位来判断!

F7固然简单,F9就比我想象的要复杂,大家可以试一试,它们的第一步居然也是单步,然后才把TF置零,正真运行。原因是要落实各个断点(关键是刚刚命中的那个断点(如果有)),包括硬件断点!
F4跟F9一样,只是多设了一个硬件断点(如果用完只能用CC了)
F8最复杂,如果是call指令,则在call后面设置硬件断点(如果用完只能用CC了),否则就是单步。
shift+F7,shift+F9很奇特大家自己看看!
另外popfd是条奇怪的指令,F7、F8时会在下一条指令设置硬件断点!!恐怕OD作者怕popfd改变TF位而跑飞吧,,但这实际上是不可能的,去Intel手册上看看。

需要说明的是:各个OD版本处理可能并不一样!

Dllinject:
#include <windows.h>
#define BaseAddr 0x00400000
#define Dll_Export _declspec(dllexport)
extern "C"
{
  Dll_Export BOOL __stdcall My_ContinueDebugEvent(DWORD dwProcessId,
    DWORD dwThreadId,
    DWORD dwContinueStatus);
  Dll_Export BOOL __stdcall My_GetThreadContext(HANDLE hThread,
    LPCONTEXT lpContext);
  Dll_Export BOOL __stdcall My_SetThreadContext(HANDLE hThread,
    const CONTEXT* lpContext);
  Dll_Export BOOL __stdcall My_WaitForDebugEvent(
    LPDEBUG_EVENT lpDebugEvent,  // pointer to debug event structure
    DWORD dwMilliseconds         // milliseconds to wait for event
    );

}
DWORD *GetFuncAddrIAT(char *func_name);

DWORD *p1=NULL;  //kernel32.ContinueDebugEvent
DWORD *p2=NULL;  //kernel32.GetThreadContext
DWORD *p3=NULL;  //kernel32.SetThreadContext
DWORD *p4=NULL;  //kernel32.WaitForDebugEvent
DWORD old_p1,old_p2,old_p3,old_p4;

DWORD ExceptionAddress=0;
DWORD ExceptionCode=0;


BOOL WINAPI DllMain(
          HINSTANCE hinstDLL,  // handle to DLL module
          DWORD fdwReason,     // reason for calling function
          LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
  case DLL_PROCESS_ATTACH:
    // Initialize once for each new process.
    // Return FALSE to fail DLL load.

    p1=GetFuncAddrIAT("ContinueDebugEvent");
    p2=GetFuncAddrIAT("GetThreadContext");
    p3=GetFuncAddrIAT("SetThreadContext");
    p4=GetFuncAddrIAT("WaitForDebugEvent");
    if (!(p1&&p2&&p3&&p4))
    {
      MessageBox(NULL,"At Least One Func Hook Fail!","Exit",MB_ICONERROR);
      return FALSE;
    }

    //保存原来的值
    old_p1=*p1;
    old_p2=*p2;
    old_p3=*p3;
    old_p4=*p4;
    //Hook
    *p1=(DWORD)My_ContinueDebugEvent;
    *p2=(DWORD)My_GetThreadContext;
    *p3=(DWORD)My_SetThreadContext;
    *p4=(DWORD)My_WaitForDebugEvent;
    MessageBox(NULL,"Dll Inject Success!!","Injected!",MB_OK);
    break;
    
  case DLL_PROCESS_DETACH:
    // Perform any necessary cleanup.
    //restore
    *p1=old_p1;
    *p2=old_p2;
    *p3=old_p3;
    *p4=old_p4;
    MessageBox(NULL,"Dll即将退出","Waring!",MB_OK);
    break;

  default: 
    break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}


BOOL __stdcall My_ContinueDebugEvent(DWORD dwProcessId,
               DWORD dwThreadId,
               DWORD dwContinueStatus)
{
  MessageBox(NULL,"Start ContinueDebugEvent!","OD",MB_OK);
  return ContinueDebugEvent(dwProcessId,dwThreadId,dwContinueStatus);
}

BOOL __stdcall My_GetThreadContext(HANDLE hThread,
             LPCONTEXT lpContext)
{
//  MessageBox(NULL,"Start GetThreadContext!","OD",MB_OK);
  char buf[256]={0};
  char buf1[512]={0};

  if (ExceptionCode)
  {
    wsprintf(buf1,"ExceptionCode=%08x\nExceptionAddress=%08x\n\n",ExceptionCode,ExceptionAddress);
  }
  BOOL b=GetThreadContext(hThread,lpContext);
  wsprintf( buf,
       "EAX:%08x\nECX:%08x\nEDX:%08x\nEBX:%08x\nESP:%08x\nEBP:%08x\nESI:%08x\nEDI:%08x\n\nEIP:%08x\n\nDr0:%08x\nDr1:%08x\nDr2:%08x\nDr3:%08x\nDr6:%08x\nDr7:%08x\n\nEFLAG:%08x\n",
        lpContext->Eax,
        lpContext->Ecx,
        lpContext->Edx,
        lpContext->Ebx,
        lpContext->Esp,
        lpContext->Ebp,
        lpContext->Esi,
        lpContext->Edi,
        lpContext->Eip,
        lpContext->Dr0,
        lpContext->Dr1,
        lpContext->Dr2,
        lpContext->Dr3,
        lpContext->Dr6,
        lpContext->Dr7,
        lpContext->EFlags
        );
  strcat(buf1,buf);
  MessageBox(NULL,buf1,"GetThreadContext!",MB_OK);
  ExceptionAddress=0;
  ExceptionCode=0;
  return b;
}

BOOL __stdcall My_SetThreadContext(HANDLE hThread,
            const CONTEXT* lpContext)
{
//  MessageBox(NULL,"Start SetThreadContext!","OD",MB_OK);
  char buf[256]={0};
  wsprintf( buf,
      "EAX:%08x\nECX:%08x\nEDX:%08x\nEBX:%08x\nESP:%08x\nEBP:%08x\nESI:%08x\nEDI:%08x\n\nEIP:%08x\n\nDr0:%08x\nDr1:%08x\nDr2:%08x\nDr3:%08x\nDr6:%08x\nDr7:%08x\n\nEFLAG:%08x\n",
      lpContext->Eax,
      lpContext->Ecx,
      lpContext->Edx,
      lpContext->Ebx,
      lpContext->Esp,
      lpContext->Ebp,
      lpContext->Esi,
      lpContext->Edi,
      lpContext->Eip,
      lpContext->Dr0,
      lpContext->Dr1,
      lpContext->Dr2,
      lpContext->Dr3,
      lpContext->Dr6,
      lpContext->Dr7,
      lpContext->EFlags
        );
  MessageBox(NULL,buf,"SetThreadContext!",MB_OK);
  BOOL b=SetThreadContext(hThread,lpContext);
  return b;
}

BOOL __stdcall My_WaitForDebugEvent(
                  LPDEBUG_EVENT lpDebugEvent,  // pointer to debug event structure
                  DWORD dwMilliseconds         // milliseconds to wait for event
                  )
{
  BOOL b=WaitForDebugEvent(lpDebugEvent,dwMilliseconds);
  if(lpDebugEvent->dwDebugEventCode==EXCEPTION_DEBUG_EVENT)
  {
    ExceptionAddress=(DWORD)(lpDebugEvent->u.Exception.ExceptionRecord.ExceptionAddress);
    ExceptionCode=lpDebugEvent->u.Exception.ExceptionRecord.ExceptionCode;      
  }
  return b;
}

DWORD *GetFuncAddrIAT(char *func_name)
{
  DWORD FuncAddr=(DWORD)GetProcAddress(LoadLibrary("Kernel32.dll"),func_name);

  //Get Import Table
  DWORD *DATA_DIRECTORY_ADDR=(DWORD *)(*(DWORD *)(BaseAddr+0x3C)+0x78+BaseAddr);
  DWORD *Import_table_addr=(DWORD *)(*(DATA_DIRECTORY_ADDR+2)+BaseAddr);
  DWORD Import_Size=*(DATA_DIRECTORY_ADDR+3);

  DWORD OldProtect;  //有些OD输入表只读,害我郁闷了半天!
  VirtualProtect(Import_table_addr,Import_Size,PAGE_READWRITE,&OldProtect);

  DWORD *Find_Addr=Import_table_addr;
  while (Import_Size>0)
  {
    if (*Find_Addr==FuncAddr)
      break;
    Import_Size-=4;
    Find_Addr++;
  }
  if (!Import_Size)
    Find_Addr=0;
  return Find_Addr;
}


inject_tool:
#include <windows.h>
#include <Tlhelp32.h>
#define BUF 0x0050B000
#define DLLPATH "C:\\Dllinject.dll"

DWORD GetProcessIDbyName(char *name);

int main()
{
  DWORD ProcessID=GetProcessIDbyName("OllyICE.exe");
  if (!ProcessID)
  {
    ProcessID=GetProcessIDbyName("OllyDBG.exe");
    if (!ProcessID)
    {
      MessageBox(NULL,"开了OD了吗?","Not Found!",MB_ICONERROR);
      return -1;
    }
  }
  HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID);
  char dllpath[MAX_PATH]=DLLPATH;
  if(!WriteProcessMemory(hProcess,(LPVOID)BUF,dllpath,strlen(dllpath),NULL))
  {
    MessageBox(NULL,"写入进程失败!!","Err!",MB_ICONERROR);
    return -1;
  }
  PVOID lpStartAddress=GetProcAddress(LoadLibrary("Kernel32.dll"),"LoadLibraryA");
  if(!CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)lpStartAddress,(LPVOID)BUF,0,NULL))
  {
    MessageBox(NULL,"启动远程线程失败!!","Err!",MB_ICONERROR);
    return -1;
  }
  MessageBox(NULL,"Inject Complete!!","OK!",MB_OK);
  return 0;
}

DWORD GetProcessIDbyName(char *name)
{
  PROCESSENTRY32 stProcess;
  HANDLE hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
  
  Process32First(hSnapShot,&stProcess);
  do 
  {
    if(!stricmp(stProcess.szExeFile,name))  //大小写不敏感
    {
      CloseHandle(hSnapShot);
      return stProcess.th32ProcessID;
    }
  } while (Process32Next(hSnapShot,&stProcess));
  //not found
  CloseHandle(hSnapShot);
  return 0;
}


使用方法:
1.Dllinject.dll放到C盘根目录下
2.启动OD
3.想试验的时候运行inject_tool.exe
上传的附件:
本主题帖已收到 0 次赞赏,累计¥0.00
最新回复 (70)
2
better 2008-11-22 23:00
2
忘了附件:

又更新了一下附件:增加了很多别的DEBUG_EVENT的提示

CREATE_PROCESS_DEBUG_EVENT时,进程名通过别的方法获取了,但LOAD_DLL_DEBUG_EVENT时模块名无法获取,它们的lpImageName是NULL(或无效),用PSAPI也不行,刚刚得到这个事件时别的工具也无法察出来,OD里的模块列表应该是后来枚举出来的……不知道大家有什么高招!◎#¥%……※×
上传的附件:
17
海风月影 2008-11-22 23:09
3
不错~~~
顶~~
2
better 2008-11-22 23:20
4
一开始图片没了,,,编辑了我半天……
qdk 2008-11-23 00:00
5
好文要顶。。。。。
14
笨笨雄 2008-11-23 00:59
6
顶~~
不错~~~
28
冷血书生 2008-11-23 02:44
7
强大 ,顶!!!!!!!!!!
太难了 2008-11-23 09:19
8
收藏+顶。。。。。。。。。。。。。。。
狼行wolf 2008-11-23 09:53
9
用五粮液 顶 文章 真的不错
1
飞雪 2008-11-24 13:52
10
文章不错,加油。
2
better 2008-11-24 21:50
11
不知道更新几次了……还是帮我想想LOAD_DLL_DEBUG_EVENT时怎么获得模块名吧,,
pzk 2008-11-24 23:53
12
好文章,学习!
11
火影 2008-11-25 00:57
13
思路很清晰哦
26
combojiang 2008-11-25 08:54
14
好文,学习。
断剑七郎 2008-11-25 09:34
15
..支持..
11
qifeon 2008-11-25 11:07
16
好文,学习了。
fool 2008-11-25 12:44
17
看完了,不错
adwardwang 2008-11-26 10:07
18
支持一下......
xievazi 2008-11-26 16:51
19
支持,呵呵~~~
cjteam 2009-1-30 20:10
20
想操练已下,HOOK函数少了,没找到程序,
wuzhi 2009-2-12 16:24
21
学习了  厉害
5
nba2005 2009-2-13 00:46
22
最近对这些断点的理论重新整理温习了一下,再看了LZ的大作,感觉比以前的理解更清晰了许多。
cqsks 2009-3-2 17:18
23
好文要顶。。。。。..
lixupeng 2009-3-2 20:57
24
学习了!!
qplchen 2009-5-8 23:18
25
不得为菜鸟们欢呼了~转论坛
zhaoding 2009-5-9 22:49
26
恩  很好的文章
brkdancer 2009-5-10 21:44
27
谢谢!顶~好东西,谢谢分享
丽史成爱 2009-6-6 00:03
28
顶一个啊 多谢
ggggg 2009-6-13 17:10
29
见识不少...
xtwksse 2009-7-27 14:08
30
什么意思,看不懂!!!!高手请教!!
hackqf 2009-8-11 09:37
31
..支持..
zhuxx 2009-8-24 22:36
32
支持!!!!!
speedboy 2009-9-20 19:43
33
咱看雪的资料就是讲的细致,打印下来慢慢看。
ginus 2009-10-5 12:40
34
厉害学习了学习了,谢谢
jaspv 2009-11-20 16:17
35
那个代码是不是C++写的啊
1
Cyane 2009-11-20 16:22
36
学习 用代码来分析 挺好
jiftle 2010-1-4 10:04
37
我需要下载。。。
2
better 2010-1-5 10:20
38
就是C++写的呀
[QUOTE=jiftle]我需要下载。。。 [/QUOTE]
二楼不是有吗
pdavid 2010-1-19 19:12
39
ReadProcessMemory(pi.hProcess, de.u.LoadDll.lpImageName, &ptr, sizeof(ptr), NULL);
ReadProcessMemory(pi.hProcess, ptr, UnicodeModuleName, sizeof(UnicodeModuleName),
NULL);
我在网上看到的,在LOAD_DLL_DEBUG_EVENT事件中可以通过lpImageName获取模块名啊。我的程序中也可以也。
3
StudyRush 2010-3-13 09:36
40
表示仰慕啊。什么时候自己有这样的水平就不错了。
战争残骸 2010-4-21 17:06
41
本人刚刚接触这东西,看代码后有一问题不明白请赐教:
"#define BUF 0x0050B000"
这个地址是什么地址,如何得到,有什么用处
onecold 2010-4-22 16:52
42
正在学习中
wshrdiy 2010-4-23 10:43
43
好好学习,天天向上
kreaty 2010-4-23 10:57
44
烧一把火,正在进军这里
地图 2010-5-10 10:38
45
菜鸟学习,没看懂!
crjkxxy 2010-5-24 00:24
46
下来学习下,谢谢
xiaocszn 2010-7-29 22:17
47
学习了!谢谢楼主!
brambleszp 2010-8-11 08:47
48
此楼层已删除
thirtten 2010-8-11 08:57
49
谢谢lz,又让我学到了很多东西
tongfei 2010-8-17 11:31
50
好文章值得学习
返回



©2000-2017 看雪学院 | Based on Xiuno BBS | 域名 加速乐 保护 | SSL证书 又拍云 提供 | 微信公众号:ikanxue
Time: 0.017, SQL: 11 / 京ICP备10040895号-17