网络安全编程:编写密码显示程序

 [[399113]]

本文使用调试API针对CrackMe来编写一个显示密码的程序。

在编写关于CrackMe的密码显示程序以前需要准备两项工作,第一项工作是知道要在什么地方合理地下断点,第二项工作是从哪里能读取到密码。带着这两个问题重新来思考一下。在这里的程序中,要对两个字符串进行比较,而比较的函数是strcmp(),该函数有两个参数,分别是输入的密码和真正的密码。也就是说,在调用strcmp()函数的位置下断点,通过查看它的参数是可以获取到正确的密码的。在调用strcmp()函数的位置设置INT3断点,也就是将0xCC机器码写入这个地址。用OD看一下调用strcmp()函数的地址,如图1所示。

图1  调用strcmp()函数的地址

从图1中可以看出,调用strcmp()函数的地址为00401E9E。有了这个地址,只要找到该函数的两个参数,就可以找到输入的错误的密码及正确的密码。从图1中可以看出,正确的密码的起始地址保存在EDX中,错误的密码的起始地址保存在ECX中。只要在00401E9E地址处下断点,并通过线程环境读取EDX和ECX寄存器值就可以得到两个密码的起始地址。

进行准备的工作已经做好了,下面来写一个控制台的程序。先定义两个常量,一个是用来设置断点的地址,另一个是INT3指令的机器码。定义如下: 

  1. // 需要设置 INT3 断点的位置  
  2. #define BP_VA 0x00401E9E  
  3. // INT3 的机器码  
  4. const BYTE bInt3 = '\xCC'

把CrackMe的文件路径及文件名当参数传递给显示密码的程序。显示的程序首先要以调试的方式创建CrackMe,代码如下: 

  1. // 启动信息  
  2. STARTUPINFO si = { 0 };  
  3. si.cb = sizeof(STARTUPINFO);  
  4. GetStartupInfo(&si);  
  5. // 进程信息  
  6. PROCESS_INFORMATION pi = { 0 };  
  7. // 创建被调试进程  
  8. BOOL bRet = CreateProcess(pszFileName,  
  9.   NULL,NULL,NULL,FALSE,  
  10.   DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,  
  11.   NULL,NULL,&si,&pi);  
  12. if ( bRet == FALSE )  
  13.  
  14.   printf("CreateProcess Error \r\n");  
  15.   return -1;  
  16. }  

然后进入调试循环,要处理两个调试事件,一个是CREATE_PROCESS_DEBUG_EVENT,另一个是EXCEPTION_DEBUG_EVENT下的EXCEPTION_BREAKPOINT。处理CREATE_PROCESS_DEBUG_EVENT的代码如下: 

  1. // 创建进程时的调试事件  
  2. case CREATE_PROCESS_DEBUG_EVENT:  
  3.  
  4.   // 读取欲设置 INT3 断点处的机器码  
  5.   // 方便后面恢复  
  6.   ReadProcessMemory(pi.hProcess,(LPVOID)BP_VA,  
  7.     (LPVOID)&bOldByte,sizeof(BYTE),&dwReadWriteNum); 
  8.   // 将 INT3 的机器码 0xCC 写入断点处  
  9.   WriteProcessMemory(pi.hProcess,(LPVOID)BP_VA,  
  10.     (LPVOID)&bInt3,sizeof(BYTE),&dwReadWriteNum);  
  11.   break;  

在CREATE_PROCESS_DEBUG_EVENT中对调用strcmp()函数的地址处设置INT3断点,再将0xCC写入这里时要把原来的机器码读取出来。读取原机器码使用ReadProcess Memory(),写入INT3的机器码使用WriteProcessMemory()。读取原机器码的作用是当写入的0xCC产生中断以后,需要将原机器码写回,以便程序可以正确继续运行。

再来看一下EXCEPTION_DEBUG_EVENT下的EXCEPTION_BREAKPOINT是如何进行处理的,代码如下: 

  1. // 产生异常时的调试事件  
  2. case EXCEPTION_DEBUG_EVENT:  
  3.  
  4.   // 判断异常类型  
  5.   switch ( de.u.Exception.ExceptionRecord.ExceptionCode )  
  6.   {  
  7.     // INT3 类型的异常  
  8.     case EXCEPTION_BREAKPOINT:  
  9.     {  
  10.       // 获取线程环境  
  11.       context.ContextFlags = CONTEXT_FULL 
  12.       GetThreadContext(pi.hThread, &context);  
  13.       // 判断是否断在设置的断点位置处  
  14.       if ( (BP_VA + 1) == context.Eip )  
  15.       {  
  16.         // 读取正确的密码  
  17.         ReadProcessMemory(pi.hProcess,(LPVOID)context.Edx,  
  18.           (LPVOID)pszPassword,MAXBYTE,&dwReadWriteNum);  
  19.         // 读取错误密码  
  20.         ReadProcessMemory(pi.hProcess,(LPVOID)context.Ecx,  
  21.           (LPVOID)pszErrorPass,MAXBYTE,&dwReadWriteNum);  
  22.         printf("你输入的密码是: %s \r\n", pszErrorPass);  
  23.         printf("正确的密码是: %s \r\n", pszPassword);  
  24.         //指令执行了 INT3 而被中断  
  25.         // INT3 的机器指令长度为 1 字节  
  26.         // 因此需要将 EIP 减一来修正 EIP  
  27.         // EIP 是指令指针寄存器  
  28.         // 其中保存着下条要执行指令的地址  
  29.         context.Eip --;  
  30.         // 修正原来该地址的机器码  
  31.         WriteProcessMemory(pi.hProcess,(LPVOID)BP_VA,  
  32.           (LPVOID)&bOldByte,sizeof(BYTE),&dwReadWriteNum); 
  33.         // 设置当前的线程环境  
  34.         SetThreadContext(pi.hThread, &context);  
  35.       }  
  36.       break;  
  37.     }  
  38.   }  

对于调试事件的处理,应该放到调试循环中。上面的代码给出的是对调试事件的处理,再来看一下调试循环的大体代码: 

  1. while ( TRUE )  
  2.  
  3.   // 获取调试事件  
  4.   WaitForDebugEvent(&de, INFINITE);  
  5.   // 判断事件类型  
  6.   switch ( de.dwDebugEventCode )  
  7.   {  
  8.     // 创建进程时的调试事件  
  9.     case CREATE_PROCESS_DEBUG_EVENT:  
  10.     {  
  11.       break;  
  12.     }  
  13.     // 产生异常时的调试事件  
  14.     case EXCEPTION_DEBUG_EVENT:  
  15.     { 
  16.       // 判断异常类型  
  17.       switch ( de.u.Exception.ExceptionRecord.ExceptionCode )  
  18.       {  
  19.         // INT3 类型的异常  
  20.         case EXCEPTION_BREAKPOINT:  
  21.         {  
  22.         }  
  23.         break;  
  24.       }  
  25.     }  
  26.   }  
  27.   ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE);  

只要把调试事件的处理方法放入调试循环中,程序就完整了。接下来编译连接一下,然后把CrackMe直接拖放到这个密码显示程序上。程序会启动CrackMe进程,并等待用户的输入。输入账号及密码后,单击“确定”按钮,程序会显示出正确的密码和用户输入的密码,如图2所示。

图2  显示正确密码

根据图2显示的结果进行验证,可见获取的密码是正确的。程序到此结束,大家可以把该程序改成通过附加调试进程来显示密码,以巩固所学的知识。 

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/151887.html<

(0)
运维的头像运维
上一篇2025-03-13 18:19
下一篇 2025-03-13 18:20

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注