【文章标题】: 记对程序监控大师的破解体会
【文章作者】: 冷夜/nightx
【作者邮箱】: lengyeasu@163.com
【作者主页】: http://nightx.info/
【作者QQ号】: 648274142
【软件名称】: 程序监控大师
【软件大小】: 499,200 字节
【下载地址】: 自己搜索下载
【加壳方式】: ASPack 2.12 -> Alexey Solodovnikov
【保护方式】: 加壳,重启验证注册码
【编写语言】: Delphi
【使用工具】: Ollyice,Peid,Keymake
【操作平台】: Windows
【软件介绍】: 对系统中的程序运行进行监控,防止未经授权的程序运
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  首先Peid查一下壳,加的是ASPack 2.12 -> Alexey Solodovnikov。手工用ESP定律很简单。这里不这样做。
  为了入门者学习更多,我以脚本的方法脱壳。
  编写OD的脚本我整理了一些注释。
  -------
  var//定义变量
  sto//单步,相当于F8
  mov addr,esp//将ESP寄存器地址存入addr中
  bphws addr,"r" //下硬件访问断点 
  bphws addr, "x"  //下硬件执行断点 
  bphwc addr //取消断点
  run//相当于F9
  MSG "Something Find!It might be OEP!"//提示信息 
  cmt eip,"..."//在EIP处加注释
  rtu //ALT+F9返回
  rtr //执行到返回,相当与CTRL+F9
  ret  //结束脚本
  sti //F7跟进
  esto//SHIFT+F9忽略异常
  findop eip, #FF630C#//查找特征码"FF630C" 
  bp addr //下F2断点
  bc addr//取消断点
  repl eip, #0F85????????#, #0F84????????#, 10//修改当前指令
  MSGYN "。。。"//提示一些信息,YN对话框
  cmp $RESULT, 0//比较是否点"否"或"取消"
  je end//如果点"否"或"取消",就来到end处,end是自己定义的一个函数模块
  gpa "GetModuleHandleA", "kernel32.dll"//找特征API函数
  mov addr, $RESULT //把找到的地址放变量addr中
  add addr,5//addr=addr+5
  find eip, #0F84????????# //查找特征码,"?"为通配符
  -------
  下面就分析一下脱壳过程:单步一次,一次ESP定律,单步三次即可。
  这样编写脚本就很简单,如下:
  -------
  //////////////////////////////////////////////////
  /// ASPack 2.12 -> Alexey Solodovnikov脱壳脚本 /// 
  /// by 冷夜 /// 
  ///http://nightx.info /// 
  /// 2011.8.5 /// 
  //////////////////////////////////////////////////
  var addr//定义地址变量
  sto//单步,相当于F8
  mov addr,esp//将ESP寄存器地址存入addr中
  bphws addr,"r" //下硬件访问断点 
  run//相当于F9
  bphwc addr //取消断点
  sto//单步,相当于F8
  sto
  sto
  cmt eip,"此处为OEP,脱了吧..."//在EIP处加注释
  MSG "Something Find!It might be OEP!"//提示信息 
  ret  //结束脚本
  -------
  这里写脚本只是为了给入门者一点编写脚本的例子。至于这个简单的壳制作脚本自然是花哨了。
  OK,编写完后测试一下。按照我们的要求到达OEP,如图1,


直接用OD插件DUMP出来即可。脱壳后的文件保存为unpack.exe  运行一下,软件是重启验证型的。
  载入Ollyice中,查找一下字符串,软件很简单,找到了“未购买用户”这一串。双击进入下面代码区域:
  -------
  004D9413  |. /75 07                  jnz     short 004D941C
  004D9415  |. |C605 F0F54D00 01       mov     byte ptr [4DF5F0], 1
  004D941C  |> \8BC3                   mov     eax, ebx
  004D941E  |.  E8 59A8F2FF            call    00403C7C
  004D9423  |.  803D F0F54D00 00       cmp     byte ptr [4DF5F0], 0
  004D942A  |.  75 27                  jnz     short 004D9453
  004D942C  |.  8D55 E8                lea     edx, dword ptr [ebp-18]
  004D942F  |.  A1 ECF54D00            mov     eax, dword ptr [4DF5EC]
  004D9434  |.  E8 1B8AF6FF            call    00441E54
  004D9439  |.  8D45 E8                lea     eax, dword ptr [ebp-18]
  004D943C  |.  BA E8944D00            mov     edx, 004D94E8                             ;   - 未购买用户
  004D9441  |.  E8 82B8F2FF            call    00404CC8
  004D9446  |.  8B55 E8                mov     edx, dword ptr [ebp-18]
  004D9449  |.  A1 ECF54D00            mov     eax, dword ptr [4DF5EC]
  004D944E  |.  E8 318AF6FF            call    00441E84
  004D9453  |>  33C0                   xor     eax, eax
  004D9455  |.  5A                     pop     edx
  004D9456  |.  59                     pop     ecx
  004D9457  |.  59                     pop     ecx
  004D9458  |.  64:8910                mov     dword ptr fs:[eax], edx
  004D945B  |.  68 7D944D00            push    004D947D
  004D9460  |>  8D45 E8                lea     eax, dword ptr [ebp-18]
  004D9463  |.  E8 A0B5F2FF            call    00404A08
  004D9468  |.  8D45 EC                lea     eax, dword ptr [ebp-14]
  004D946B  |.  BA 05000000            mov     edx, 5
  004D9470  |.  E8 B7B5F2FF            call    00404A2C
  004D9475  \.  C3                     retn
  004D9476   .^ E9 95AFF2FF            jmp     00404410
  004D947B   .^ EB E3                  jmp     short 004D9460
  -------
  字符串参考还有部分是
  -------
  004D97DC  /.  55                     push    ebp
  004D97DD  |.  8BEC                   mov     ebp, esp
  004D97DF  |.  6A 00                  push    0
  004D97E1  |.  33C0                   xor     eax, eax
  004D97E3  |.  55                     push    ebp
  004D97E4  |.  68 59984D00            push    004D9859
  004D97E9  |.  64:FF30                push    dword ptr fs:[eax]
  004D97EC  |.  64:8920                mov     dword ptr fs:[eax], esp
  004D97EF  |.  803D F0F54D00 01       cmp     byte ptr [4DF5F0], 1
  004D97F6  |.  75 3C                  jnz     short 004D9834
  004D97F8  |.  8D45 FC                lea     eax, dword ptr [ebp-4]
  004D97FB  |.  8B0D F4F54D00          mov     ecx, dword ptr [4DF5F4]
  004D9801  |.  BA 6C984D00            mov     edx, 004D986C                             ;  本软件已注册给:
  004D9806  |.  E8 01B5F2FF            call    00404D0C
  004D980B  |.  8B55 FC                mov     edx, dword ptr [ebp-4]
  004D980E  |.  A1 7CD24D00            mov     eax, dword ptr [4DD27C]
  004D9813  |.  8B00                   mov     eax, dword ptr [eax]
  004D9815  |.  8B80 20030000          mov     eax, dword ptr [eax+320]
  004D981B  |.  E8 6486F6FF            call    00441E84
  004D9820  |.  A1 7CD24D00            mov     eax, dword ptr [4DD27C]
  004D9825  |.  8B00                   mov     eax, dword ptr [eax]
  004D9827  |.  8B80 20030000          mov     eax, dword ptr [eax+320]
  004D982D  |.  B2 01                  mov     dl, 1
  004D982F  |.  E8 4085F6FF            call    00441D74
  004D9834  |>  A1 7CD24D00            mov     eax, dword ptr [4DD27C]
  004D9839  |.  8B00                   mov     eax, dword ptr [eax]
  004D983B  |.  8B10                   mov     edx, dword ptr [eax]
  004D983D  |.  FF92 E8000000          call    near dword ptr [edx+E8]
  004D9843  |.  33C0                   xor     eax, eax
  004D9845  |.  5A                     pop     edx
  004D9846  |.  59                     pop     ecx
  004D9847  |.  59                     pop     ecx
  004D9848  |.  64:8910                mov     dword ptr fs:[eax], edx
  004D984B  |.  68 60984D00            push    004D9860
  004D9850  |>  8D45 FC                lea     eax, dword ptr [ebp-4]
  004D9853  |.  E8 B0B1F2FF            call    00404A08
  004D9858  \.  C3                     retn
  -------
  想来这部分就是软件判断注册成功后跳转的部分了。我们先不管这。在“未购买用户”部分有一个跳转:
  004D942A  |.  75 27                  jnz     short 004D9453
  上方有个Call:
  004D941E  |.  E8 59A8F2FF            call    00403C7C
  在这个Call按下F2下普通断点运行一下,堆栈里就会发现注册码了。如图2。



  这样破解追码就OK了。我们要做的当然不止这些。下面再做个补丁。
  在断点上面有一处
  004D940D  |.  58                     pop     eax              
  在这里下断点后运行。程序中断。此时寄存器EDX中就存放了真正的注册码。如图3。



  这样就可以用KeyMake制作一个内存注册机。
  设置注册机信息:
  1.添加程序。添加 中断地址 004D940D 次数 1 指令 58 长度 1
  2.注册码选择内存方式--勾选寄存器--选择EDX
  3.最后修改一下用户信息。选择一种方式生成即可。
  运行一下。如图4,成功得到真码。



 文章还没完。既然用KeyMake可以编写。那我们自己也可以编写一下获取注册码:
  C语言的代码如下;
  ------
  #include <windows.h>
  void main()
  {
      PROCESS_INFORMATION pi;
      STARTUPINFO si;
      DEBUG_EVENT devent;
      CONTEXT Regs;
      Regs.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_CONTROL;
      ZeroMemory(&pi, sizeof(pi));
      ZeroMemory(&si, sizeof(si));
      si.cb = sizeof(si);
      LPVOID breakAddr = (LPVOID)0x004D940D;  //这是断点地址,就是用工具生成注册机时要填的那个地址
      BYTE firstByte = 0x58;      //同样是生成注册机时要填的那个第一字节
      BYTE bp = 0xCC;
      DWORD real;
      BYTE buf[64];
      int nCount = 0;
      DWORD way;
      //用调试方式创建需要破解的程序进程,此处进程为Cxjk.exe
      if(CreateProcess("Cxjk.exe", NULL, NULL, NULL, FALSE, 
          DEBUG_ONLY_THIS_PROCESS | DEBUG_PROCESS, NULL, NULL, &si, &pi))
      {
          //调试循环体
          while(TRUE)
          {
              //等待调试事件
              if(WaitForDebugEvent(&devent, INFINITE))
              {
                  way = DBG_EXCEPTION_NOT_HANDLED;
                  switch(devent.dwDebugEventCode)
                  {
                  case EXIT_PROCESS_DEBUG_EVENT:  //进程退出事件
                      //return;
                      break;
                  case EXCEPTION_DEBUG_EVENT:     //异常事件
                      {
                          if(devent.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT)
                          {
                              nCount++;
                              //创建进程后会产生第一次异常中断,此时可以设置断点
                              if(nCount == 1)
                              {
                                  //读取第一个字节进行效验
                                  ReadProcessMemory(pi.hProcess, breakAddr, buf, 1, &real);
                                  if(buf[0] == firstByte)                
                                  {
                                      //设置断点
                                      DWORD oldProtect;
                                      VirtualProtectEx(pi.hProcess, breakAddr, 1, PAGE_READWRITE, &oldProtect);
                                      WriteProcessMemory(pi.hProcess, breakAddr, &bp, 1, &real);
                                      VirtualProtectEx(pi.hProcess, breakAddr, 1, oldProtect, NULL);
                                  }
                                  way = DBG_CONTINUE;
                              }
                              //如果是在断点处暂停,就可以读取内存中的注册码
                              else if(devent.u.Exception.ExceptionRecord.ExceptionAddress == breakAddr)
                              {
                                  GetThreadContext(pi.hThread, &Regs);
                                  ReadProcessMemory(pi.hProcess, (char *)Regs.Edx, buf, 64, &real);  //从寄存器Edx中读取注册码
                                  MessageBox(NULL, (char *)buf, "注册机 By 冷夜 http://nightx.info/     ", 0);
                                  //去除断点
                                  DWORD oldProtect;
                                  VirtualProtectEx(pi.hProcess, breakAddr, 1, PAGE_READWRITE, &oldProtect);
                                  WriteProcessMemory(pi.hProcess, breakAddr, &firstByte, 1, NULL);
                                  VirtualProtectEx(pi.hProcess, breakAddr, 1, oldProtect, NULL);
                                  //回到断点处继续执行
                                  Regs.Eip = (DWORD)breakAddr;
                                  SetThreadContext(pi.hThread, &Regs);
                                  way = DBG_CONTINUE;
                              }
                          }
                          break;
                      }
                  }
                  ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, way);
              }
          }
      }
      else
          MessageBox(NULL, "请将注册机和要破解程序放在同一目录", NULL, 0);
  }
  -------
  声明一下,代码不是我写的,是东拼西凑借鉴程序员的代码得到的。呵呵。运行一下。也成功。
  用户名:冷夜 注册码;是29ACF5B22A1B6323A3A72075DB4447407E1C5D9FBAE52D13
  
--------------------------------------------------------------------------------
【经验总结】
  软件还是很简单的。重要的只是思路而已。希望文章能对初学者有所帮助。
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于冷夜/nightx, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2011年08月05日 08:26:48