我们通过一个示例来练习钩取Notepad.exe的WriteFile,保存文件时将小写字母全部转换为大写字母。下面我们先测试一下代码。
运行notepad.exe并查看PID。

在命令行窗口中输入命令与参数。

输入一串小写字母之后选择保存。

再次打开文件,之前的小写字母全部变成了大写字母。

我们来分析一下源代码,看看是怎么实现的。
首先看一下main函数。main函数通过DebugActiveProcess将调试器附加到该运行的进程上,然后进入DebugLoop处理来自被调试者的调试事件。

[cpp] view plaincopy
  1. int main(int argc, char* argv[])
  2. {
  3. DWORD dwPID;
  4. if( argc != 2 )
  5. {
  6. printf("\nUSAGE : hookdbg.exe <pid>\n");
  7. return 1;
  8. }
  9. // Attach Process
  10. dwPID = atoi(argv[1]);
  11. if( !DebugActiveProcess(dwPID) )
  12. {
  13. printf("DebugActiveProcess(%d) failed!!!\n"
  14. "Error Code = %d\n", dwPID, GetLastError());
  15. return 1;
  16. }
  17. // debugger loops
  18. DebugLoop();
  19. return 0;
  20. }

接下来是DebugLoop。它从被调试者处接收事件并处理,然后使被调试者继续运行。ContinueDebugEvent是一个使被调试者继续运行的函数。

[cpp] view plaincopy
  1. void DebugLoop()
  2. {
  3. DEBUG_EVENT de;
  4. DWORD dwContinueStatus;
  5. // Waiting for the event occurred by debuggee
  6. while( WaitForDebugEvent(&de, INFINITE) )
  7. {
  8. dwContinueStatus = DBG_CONTINUE;
  9. // Debuggee process generates or attaches event
  10. if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
  11. {
  12. OnCreateProcessDebugEvent(&de);
  13. }
  14. // Exception event
  15. else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
  16. {
  17. if( OnExceptionDebugEvent(&de) )
  18. continue;
  19. }
  20. // Debuggee process terminates event
  21. else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
  22. {
  23. // debuggee stop -> debugger stop
  24. break;
  25. }
  26. // Run the debuggee again
  27. ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
  28. }
  29. }

DebugLoop处理3种调试事件,分别是EXIT_PROCESS_DEBUG_EVENT、CREATE_PROCESS_DEBUG_EVENT、EXCEPTION_DEBUG_EVENT。
1.被调试进程终止时会触发EXIT_PROCESS_DEBUG_EVENT。这里在发生该事件时,调试器与被调试者将一起终止。
2.OnCreateProcessDebugEvent是CREATE_PROCESS_DEBUG_EVENT事件句柄,被调试进程启动(或者附加)时即调用执行该函数。下面看一下它的核心部分。

[cpp] view plaincopy
  1. BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
  2. {
  3. // get WriteFile() API address
  4. g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
  5. // API Hook - WriteFile()
  6. // change first byte to 0xCC(INT 3)
  7. memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
  8. ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  9. &g_chOrgByte, sizeof(BYTE), NULL);
  10. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  11. &g_chINT3, sizeof(BYTE), NULL);
  12. return TRUE;
  13. }

首先获取WriteFile的起始地址,它获取的不是被调试进程的内存地址,而是调试进行的内存地址。对于Windows XP的DLL而言,它们在所有进程中都会加载到相同的地址(虚拟内存)。由于调试器拥有被调试进程的句柄(带有调试权限),所以可以使用ReadProcessMemory和WriteProcessMemory对被调试进程的内存空间自由进行读写操作。
3.OnExceptionDebugEvent是EXCEPTION_DEBUG_EVENT事件句柄,它处理被调试者的INT3指令。下面看一下它的核心部分。

[cpp] view plaincopy
  1. BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
  2. {
  3. CONTEXT ctx;
  4. PBYTE lpBuffer = NULL;
  5. DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
  6. PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
  7. // BreakPoint exception (INT 3)
  8. if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
  9. {
  10. // BP at WriteFile() API
  11. if( g_pfWriteFile == per->ExceptionAddress )
  12. {
  13. // #1. Unhook
  14. // restore 0xCC to original byte
  15. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  16. &g_chOrgByte, sizeof(BYTE), NULL);
  17. // #2. Get Thread Context
  18. ctx.ContextFlags = CONTEXT_CONTROL;
  19. GetThreadContext(g_cpdi.hThread, &ctx);
  20. // #3. Get WriteFile() param 2, 3
  21. //   param 2 : ESP + 0x8
  22. //   param 3 : ESP + 0xC
  23. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
  24. &dwAddrOfBuffer, sizeof(DWORD), NULL);
  25. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
  26. &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
  27. // #4. Allocates temp buf
  28. lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
  29. memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
  30. // #5. Copy WriteFile() buf to temp buf
  31. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
  32. lpBuffer, dwNumOfBytesToWrite, NULL);
  33. printf("\n### original string ###\n%s\n", lpBuffer);
  34. // #6. Lower case letters -> Upper case letters
  35. for( i = 0; i < dwNumOfBytesToWrite; i++ )
  36. {
  37. if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
  38. lpBuffer[i] -= 0x20;
  39. }
  40. printf("\n### converted string ###\n%s\n", lpBuffer);
  41. // #7. Copy to WriteFile() buf
  42. WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
  43. lpBuffer, dwNumOfBytesToWrite, NULL);
  44. // #8. release temp buf
  45. free(lpBuffer);
  46. // #9. Change EIP to WriteFile() address
  47. ctx.Eip = (DWORD)g_pfWriteFile;
  48. SetThreadContext(g_cpdi.hThread, &ctx);
  49. // #10. Run Debuggee process
  50. ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
  51. Sleep(0);
  52. // #11. API Hook
  53. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  54. &g_chINT3, sizeof(BYTE), NULL);
  55. return TRUE;
  56. }
  57. }
  58. return FALSE;
  59. }

首先脱钩,因为在将小写字母转换为大写字母之后需要正常调用WriteFile。接着获取线程的上下文,获取WriteFile的第二个参数和第三个参数的值。调用WriteFile时,我们要在传递过来的参数中知道param2(缓冲区地址)和param3(缓冲区大小)这两个参数。函数参数存储在栈中,通过CONTEXT.Esp成员可以获得它们的值。然后把小写字母转换为大写字母之后覆写WriteFile缓冲区,把线程上下文的EIP修改为WriteFile起始地址。因为在WriteFile的起始地址处设置了断点,被调试者内部调用WriteFile时,会在起始地址处遇到INT3指令。执行该指令时,EIP的值会增加一个字节,所以EIP的当前地址为WriteFile+1。修改好CONTEXT.Eip成员后,调用SetThreadContext。现在运行调试进程。如果没有Sleep(0)语句,Notepad.exe在调用WriteFile的过程中我们的程序会尝试将WriteFile的首字节修改为0xCC,这可能导致内存访问异常。最后设置钩子,方便下次钩取操作。
完整的代码如下。

[cpp] view plaincopy
  1. #include "windows.h"
  2. #include "stdio.h"
  3. LPVOID g_pfWriteFile = NULL;
  4. CREATE_PROCESS_DEBUG_INFO g_cpdi;
  5. BYTE g_chINT3 = 0xCC, g_chOrgByte = 0;
  6. BOOL OnCreateProcessDebugEvent(LPDEBUG_EVENT pde)
  7. {
  8. // get WriteFile() API address
  9. g_pfWriteFile = GetProcAddress(GetModuleHandleA("kernel32.dll"), "WriteFile");
  10. // API Hook - WriteFile()
  11. // change first byte to 0xCC(INT 3)
  12. memcpy(&g_cpdi, &pde->u.CreateProcessInfo, sizeof(CREATE_PROCESS_DEBUG_INFO));
  13. ReadProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  14. &g_chOrgByte, sizeof(BYTE), NULL);
  15. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  16. &g_chINT3, sizeof(BYTE), NULL);
  17. return TRUE;
  18. }
  19. BOOL OnExceptionDebugEvent(LPDEBUG_EVENT pde)
  20. {
  21. CONTEXT ctx;
  22. PBYTE lpBuffer = NULL;
  23. DWORD dwNumOfBytesToWrite, dwAddrOfBuffer, i;
  24. PEXCEPTION_RECORD per = &pde->u.Exception.ExceptionRecord;
  25. // BreakPoint exception (INT 3)
  26. if( EXCEPTION_BREAKPOINT == per->ExceptionCode )
  27. {
  28. // BP at WriteFile() API
  29. if( g_pfWriteFile == per->ExceptionAddress )
  30. {
  31. // #1. Unhook
  32. // restore 0xCC to original byte
  33. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  34. &g_chOrgByte, sizeof(BYTE), NULL);
  35. // #2. Get Thread Context
  36. ctx.ContextFlags = CONTEXT_CONTROL;
  37. GetThreadContext(g_cpdi.hThread, &ctx);
  38. // #3. Get WriteFile() param 2, 3
  39. //   param 2 : ESP + 0x8
  40. //   param 3 : ESP + 0xC
  41. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0x8),
  42. &dwAddrOfBuffer, sizeof(DWORD), NULL);
  43. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)(ctx.Esp + 0xC),
  44. &dwNumOfBytesToWrite, sizeof(DWORD), NULL);
  45. // #4. Allocates temp buf
  46. lpBuffer = (PBYTE)malloc(dwNumOfBytesToWrite+1);
  47. memset(lpBuffer, 0, dwNumOfBytesToWrite+1);
  48. // #5. Copy WriteFile() buf to temp buf
  49. ReadProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
  50. lpBuffer, dwNumOfBytesToWrite, NULL);
  51. printf("\n### original string ###\n%s\n", lpBuffer);
  52. // #6. Lower case letters -> Upper case letters
  53. for( i = 0; i < dwNumOfBytesToWrite; i++ )
  54. {
  55. if( 0x61 <= lpBuffer[i] && lpBuffer[i] <= 0x7A )
  56. lpBuffer[i] -= 0x20;
  57. }
  58. printf("\n### converted string ###\n%s\n", lpBuffer);
  59. // #7. Copy to WriteFile() buf
  60. WriteProcessMemory(g_cpdi.hProcess, (LPVOID)dwAddrOfBuffer,
  61. lpBuffer, dwNumOfBytesToWrite, NULL);
  62. // #8. release temp buf
  63. free(lpBuffer);
  64. // #9. Change EIP to WriteFile() address
  65. ctx.Eip = (DWORD)g_pfWriteFile;
  66. SetThreadContext(g_cpdi.hThread, &ctx);
  67. // #10. Run Debuggee process
  68. ContinueDebugEvent(pde->dwProcessId, pde->dwThreadId, DBG_CONTINUE);
  69. Sleep(0);
  70. // #11. API Hook
  71. WriteProcessMemory(g_cpdi.hProcess, g_pfWriteFile,
  72. &g_chINT3, sizeof(BYTE), NULL);
  73. return TRUE;
  74. }
  75. }
  76. return FALSE;
  77. }
  78. void DebugLoop()
  79. {
  80. DEBUG_EVENT de;
  81. DWORD dwContinueStatus;
  82. // Waiting for the event occurred by debuggee
  83. while( WaitForDebugEvent(&de, INFINITE) )
  84. {
  85. dwContinueStatus = DBG_CONTINUE;
  86. // Debuggee process generates or attaches event
  87. if( CREATE_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
  88. {
  89. OnCreateProcessDebugEvent(&de);
  90. }
  91. // Exception event
  92. else if( EXCEPTION_DEBUG_EVENT == de.dwDebugEventCode )
  93. {
  94. if( OnExceptionDebugEvent(&de) )
  95. continue;
  96. }
  97. // Debuggee process terminates event
  98. else if( EXIT_PROCESS_DEBUG_EVENT == de.dwDebugEventCode )
  99. {
  100. // debuggee stop -> debugger stop
  101. break;
  102. }
  103. // Run the debuggee again
  104. ContinueDebugEvent(de.dwProcessId, de.dwThreadId, dwContinueStatus);
  105. }
  106. }
  107. int main(int argc, char* argv[])
  108. {
  109. DWORD dwPID;
  110. if( argc != 2 )
  111. {
  112. printf("\nUSAGE : hookdbg.exe <pid>\n");
  113. return 1;
  114. }
  115. // Attach Process
  116. dwPID = atoi(argv[1]);
  117. if( !DebugActiveProcess(dwPID) )
  118. {
  119. printf("DebugActiveProcess(%d) failed!!!\n"
  120. "Error Code = %d\n", dwPID, GetLastError());
  121. return 1;
  122. }
  123. // debugger loops
  124. DebugLoop();
  125. return 0;
  126. }

逆向工程核心原理读书笔记-API钩取之记事本小写转大写相关推荐

  1. 逆向工程核心原理读书笔记-API钩取之隐藏进程(一)

    简评: 整体看了下代码,其实就是应用层的inline hook, 钩子勾住ntdll.dll里面的ZwQuerySystemInformation函数, xp环境测试成功了,win7测试失败了,不知道 ...

  2. 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制

    我们通过一个示例来练习钩取IE8的InternetConnect函数,用IE8连接指定网站时,使之连接到另一个网站.和以前钩取CreateProcess不同,这次我们钩取更低级的ZwResumeThr ...

  3. 逆向工程核心原理读书笔记-API钩取之隐藏进程(二)

    上一篇文章我们实现的隐藏进程如果重新打开任务管理器或者被隐藏的进程就没有隐藏的效果了.为了弥补这个问题,我们不仅需要钩取当前运行的所有进程,还要钩取将来运行的所有进程.由于所有的进程都是由父进程使用C ...

  4. 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字

    我们通过一个示例来练习向计算机进程插入用户的DLL文件,钩取负责向计算器显示文本的SetWindowTextW,使得计算器中显示中文数字而不是原来的阿拉伯数字.钩取前后的原理图如下所示. 下面我们先测 ...

  5. 逆向工程核心原理读书笔记-代码注入

    代码注入是一种向目标进程插入独立运行代码并使之运行的技术,它一般调用CreateRemoteThread()API以远程线程形式运行插入的代码,所以也被称为线程注入. 和DLL注入相比代码注入占用内存 ...

  6. 逆向工程核心原理学习笔记(三):检索API方法

    打开OD,载入程序 鼠标右键,如图. 然后就可以看到程序调用的所有API函数,便于分析. 找到messagebox函数,双击进去,就是函数所在反汇编地址了

  7. 逆向工程核心原理学习笔记(四):检索API方法2-设置断点

    同样,还是用我们的那个helloworld小程序,然后,如图所示,查找所有模块的名称 然后,结果如图所示 在这个界面,我们敲键盘字母,messagebox,一个一个敲出来,就会自己跳到相应的地址,如图 ...

  8. 逆向工程核心原理学习笔记(一):寻找程序的主函数(Main)

    转自:http://blog.csdn.net/qq_36810340/article/details/70169640 首先编译release版本的helloword程序,代码如下: 编译完成,拖进 ...

  9. 逆向工程核心原理学习笔记(十四):栈帧1

    栈帧的话,直接截了一些图,大家看一下就好了,理解起来很简单,就是简单的参数转存. 看完之后,我们需要用一个小程序来进一步学习我们的栈帧了. 下载地址:http://t.cn/RaUSglI 代码写法: ...

最新文章

  1. 【CyberSecurityLearning 8】PKI技术与应用
  2. 在WebIDE里导入SAP Fiori应用
  3. leetcode712. 两个字符串的最小ASCII删除和(动态规划)-Gogo
  4. Java23种设计模式之工厂模式
  5. BZOJ1977 [BJOI2010]次小生成树
  6. Leetcode309. Best time to sell stock with cooldown
  7. JavaScript事件冒泡和事件委托
  8. C#工控上位机实例_HINET智能网关用于三菱FX1S/1N/2N/3S/3G/3UPLC远程编程和上位机监控...
  9. 【考研数学】视频,你喜欢看哪位老师?
  10. c盘清理代码_WIN10 C盘空间不够怎么办?几个小方法助你清理硬盘空间
  11. 在html中实现word中打批注的功能
  12. Knockout开发中文API系列1
  13. Spring 中 AOP 的实现原理——动态代理
  14. 教你编写一份高质量的软件测试报告
  15. 十二进制转二进制--2018
  16. ulp(unit in the last place)是什么意思
  17. Ping值和带宽之间关系
  18. 初识Kodu开发软件---Kodu少儿编程第三天
  19. ccs读取dat文件c语言程序,CCS中dat文件的格式
  20. python统计列表中元素个数_python中计算一个列表中连续相同的元素个数方法

热门文章

  1. 任务调度之Quartz2
  2. Spring源码分析前篇
  3. POI的入门:绘制图形
  4. SpringBoot集成其他技术-集成Redis
  5. oracle exp 二进制,Oracle备份之exp自动逻辑备份(二)
  6. Spring Security源码解析(二)——引入
  7. redis命令-key操作
  8. 编程开发使用的辅助软件大全
  9. 数据库连接池的实现及原理
  10. LinuxUSB驱动程序调试--009:编写应用程序---验证协议【转】