分享

【原创】给OD扩展功能——支持帮助文档(Win7)

 我要摘它 2016-07-29
标 题: 【原创】给OD扩展功能——支持帮助文档(Win7)

  作 者: YaoJinAa

  时 间: 2016-01-19,19:19:29

  链 接: http://bbs./showthread.php?t=207265

  在使用OD的过程中碰到不懂的汇编指令经常会查下帮助文档,每次都要找到文档的所在目录,在打开好麻烦,还有win7居然不能打开hlp格式文件(当然要想打开需要去装个补丁),却可以打开chm格式的。Win7支持chm格式OD却不支持,刚好我用到的帮助文档是chm格式和一个doc的文档。好吧,自己动手对OD的帮助文档格式进行扩展,在添加一个F1打开帮助文档。

  效果如下:

  微软的Win32 API reference,己转成CHM:http://www./document/api/win32chm.rar

  


  一、扩展OD帮助文档打开格式


  1-1:定位格式字符串

  选择帮助文档是一个对话框,GetOpenFileName这个API的功能就是打开文件选择对话框。查下MSDN看下它的参数是一个指针,指向的是OPENFILENAME类型,该类型中第四个成员就是指向过滤的字符串,下面在OD中定位这个API看有没有使用。

  


  断下来后单步跑到0x764AA2C4位置处,对话框还没打开,然后按p跑起来,对话框打开了,证明0x764AA2C7位置的CALL里面会打开对话框。那么EBP+8的地方就是OPENFILENAME类型的指针。观察EBP+8指向的内存值。


  


  反复打开几次文件选择对话框文件过滤字符串都是在0x0018C64C位置中,对话框关闭后这个值会改变(这个是栈空间所以值会变),那么跟下这个值的最终来源位置。通过栈回溯一直找,最后找到_Browsefilename这个函数,在这个函数下断点并看0x0018B5F4中的第4个int位置的值是不是过滤字符串的地址,如果不是就对它下内存写入断点,如果已经是了就还需要往上回溯。一直找到它不是为止。通过内存写入断点找到是下面这行指令把过滤字符地址复制到对应位置:

  


  现在只要hook这行指令即可。

  所有代码都是以OD插件方式开发,开发OD需要的两个文件:

  头文件:Plugin.h

  lib文件:OLLYDBG.LIB

  插件开发帮助文档中说了连个必须函数ODBG_Plugindata和ODBG_Plugininit,这两个函数的功能帮助文档有说,使用方式我提供的工程中也有。

  1-2:Hook关键一行代码扩展文件选择类型

  把0x00476AD2位置的jmp修改为CALL(修改为call是懒得算跳回去的地址,在我的函数中直接用ret回去就行了),跳到我准备的函数中,我的函数如下:

  //新过滤字符串

  PCHAR g_lpszbuf = NULL;

  _export __declspec(naked) void MyOpenHelpDocument()

  {

  __asm

  {

  mov eax, g_lpszbuf

  mov dword ptr ss:[ebp - 0x2B68], eax

  ret

  }

  }

  Hook代码

  unsigned int g_pfilter = 0x476AD2;

  VOID SetFilter()

  {

  int i;

  FILTER Filter;

  unsigned int *pFilterAddress = (unsigned int*)g_pfilter;

  DWORD dwOld;

  int nCount;

  PCHAR pTemp;

  g_lpszbuf = (PCHAR)malloc(300);

  if (g_lpszbuf == NULL)

  {

  return;

  }

  memset(g_lpszbuf, 0, 300);

  //新过滤字符串

  PCHAR Temp[10];

  Temp[0] = 'chm帮助文件(*.chm)';

  Temp[1] = '*.chm';

  Temp[2] = 'hlp帮助文件(*.hlp)';

  Temp[3] = '*.hlp';

  Temp[4] = '文本文件(*.txt)';

  Temp[5] = '*.txt';

  Temp[6] = '图片(*.jmpg)';

  Temp[7] = '*.jmpg';

  Temp[8] = '所有(*.*)';

  Temp[9] = '*.*';

  nCount = 0;

  pTemp = g_lpszbuf;

  //拷贝准备好的过滤字符串到malloc开辟出来的空间中

  for (i = 0; i

  {

  do

  {

  strcpy(pTemp, Temp[i]);

  nCount = strlen(Temp[i]) + 1;

  } while (FALSE);

  if (i != 9)

  {

  pTemp += nCount;

  }

  }

  //准备Hook指令 (Filter是一个全局变量,自定义的一个6字节结构体)

  Filter.m_Comm = 0xE8;

  Filter.m_Offset = (int)MyOpenHelpDocument - g_pfilter - 5;

  //原始指令是6个字节,我们修改为JMP后是5字节,所以最后一字节修改为NOP

  Filter.m_Nop = 0x90;

  dwOld = 0;

  //打开内存保护并使用memcpy拷贝指令到目标位置,然后还原内存属性

  if (VirtualProtect((void*)g_pfilter, 6, PAGE_EXECUTE_READWRITE, &dwOld) == 0)

  {

  MessageBox(NULL, 'Error', 'SetHookFail', MB_OK);

  return;

  }

  memcpy((void*)g_pfilter, (void*)&Filter, sizeof(Filter));

  VirtualProtect((void*)g_unTargetAddress, 6, dwOld, &dwOld);

  }

  二、修复不能打开chm格式文件

  找到WinHelpA对它进行Hook使用ShellExecute来打开,代码如下:

  _export __declspec(naked) void __stdcall OpenHelp()

  {

  char *pFilePath;

  __asm

  {

  push eax

  mov eax , [esp+0xc]

  mov pFilePath, eax

  pop eax

  }

  ShellExecute(0, 'open', pFilePath/*'calc'*/, '', '', SW_SHOWNORMAL);

  __asm

  {

  retn 16

  }

  }

  Hook代码:

  BOOL SetHookWinHelp()

  {

  DWORD dwOld;

  HMODULE hModule = LoadLibrary('user32');

  if (hModule == NULL)

  {

  return FALSE;

  }

  DWORD pfnAddress = (DWORD)GetProcAddress(hModule, 'WinHelpA');

  if (pfnAddress == NULL)

  {

  return FALSE;

  }

  JMP Jmp;

  Jmp.m_Comm = 0xE9;

  Jmp.m_offset = (int)OpenHelp - (int)pfnAddress - 5;

  dwOld = 0;

  if (VirtualProtect((void*)pfnAddress, 5, PAGE_EXECUTE_READWRITE, &dwOld) == 0)

  {

  MessageBox(NULL, 'Error', 'SetHookFail', MB_OK);

  return FALSE;

  }

  memcpy((void*)pfnAddress, (void*)&Jmp, sizeof(Jmp));

  VirtualProtect((void*)pfnAddress, 5, dwOld, &dwOld);

  return TRUE;

  }

  三、对Od添加一个F1打开帮助文档

  Hook窗口过程函数,在我们的函数中判断是不是按下F1,如果是就使用PostMessage发送一个WM_COMMAND消息来给打开帮助文档这个菜单。具体看下面代码:

  操作过程

  首先要查看“打开帮助文档菜单”的ID号,给OD主窗口下个键盘按下断点,然后点击'打开帮助文档'断在过程函数中,看栈如下图:

  


  得到该菜单的ID号为0x9c8。

  实现代码

  typedef LRESULT (__stdcall *CALLWINDOWPROC)(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

  //新的过程函数

  LRESULT MyCallWindowProc(

  WNDPROC lpPrevWndFunc, // pointer to previous procedure

  HWND hWnd, // handle to window

  UINT Msg, // message

  WPARAM wParam, // first message parameter

  LPARAM lParam

  )

  {

  DWORD wNotifyCode = HIWORD(wParam);

  DWORD wID = LOWORD(wParam);

  if (wID == 0xe090)

  {

  OutputDebugString('F1');

  //

  PostMessage(G_hWin, WM_COMMAND, 0x9C8, NULL);

  return TRUE;

  }

  return pfnNextProcCall(lpPrevWndFunc, hWnd, Msg, wParam, lParam);

  }

  劫持过程函数的代码写在了OD插件的一个必须函数中,写在这里面主要是有窗口句柄方便。代码如下:

  //OD必须函数

  extc int _export cdecl ODBG_Plugininit(int ollydbgversion, HWND hw,

  ulong *features)

  {

  G_hWin = hw;

  if(ollydbgversion

  {

  return -1;

  }

  pfnNextProcCall = (CALLWINDOWPROC)GetWindowLong(hw, GWL_WNDPROC);

  if (pfnNextProcCall == 0)

  {

  return -1;

  }

  //Hook窗口过程

  if (SetWindowLong(hw, GWL_WNDPROC, (long)MyCallWindowProc) == 0)

  {

  return -1;

  }

  return 0;

  }

  到这里F1和帮助文档格式扩展就写完了,来科锐差不多一年了,从C到C++、汇编、逆向,学习逆向这段时间知道了编译器对C++代码的生成,对面向对象这块又有种豁然开朗的感觉。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多