一个CrackMe的设计思路与分析(含源码)

我们一般在查找软件的注册关键点时,常用的手段有:
1、查找提示字符
2、F12堆栈暂停法
3、常用的API函数,如GetDlgItemTextA(W)、GetWindowTextA(W)等

针对第1点,一般的方法是采用字符串加密的方法,对关键的字符串,进行加密,如这个CrackMe里一样:


char strErr1[]="\x21\x17\x0D\x0A\x58\x36\x19\x15\x1D\x58\x11\x0B\x58\x16\x17\x0C\x58\x3D\x15\x08\x0C\x01\x54\x28\x14\x1D\x19\x0B\x1D\x58\x11\x16\x08\x0D\x0C\x59";        //经过加密的字符串

我这里是用自己写的一个小工具加密的,如图:

然后在调用时解密:


TCHAR szContent[1024];
memset(szContent,0,1024);
for(int i=0;strErr1[i];i++)
         szContent[i]=strErr1[i]^0x78;

MessageBox(hDlg,szContent,szContent,0);

而针对F12堆栈法,就需要打破程序的运行路径,如用异常捕获、专用的验证线程,这个CrackMe采用的是专用的验证线程:
1、首先在生成一个验证线程,可以在主界面生成之前,也可以在对话框的初始化过程中,这里的一个好处就是可以把对话框的句柄传递给生成的验证线程:


case WM_INITDIALOG:
         CreateThread(NULL,0,VerifyThread,(LPVOID)hDlg,0,&dwThreadId);

2、激活线程需要判断何时开始验证,当然也可以一开始生成验证线程时,采用CREATE_SUSPENDED标记,让线程挂起,然后再通过调用ResumeThread()来激活。不过这样较明显,这里采用了事件等待。


g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
…………
SetEvent(g_hEvent);

然后在验证线程里等待:
WaitForSingleObject(g_hEvent,INFINITE);
//开始验证…………

3、在开始验证前,还可以人为的设置一些反跟踪的陷阱,如这里的对MessageBoxA函数是有下有F2断点的测试:


BYTE *dwAddr=(BYTE*)GetProcAddress(GetModuleHandle("user32.dll"),"MessageBoxA");
  //测试MessageBoxA()函数前5个字节是否下有断点,有则退出
  for(int i=0;i<10;i++){
    if(*dwAddr == 0xCC)
      ExitProcess(0);
    dwAddr++;
  }

你可以判断更多的地方,呵呵……

4、补充一下异常的捕获验证,如下代码:


__try{
       //初步判断用户名与注册码
       _asm int 3  //使用内联汇编来人为设置int 3异常
}
//捕获异常,是int 3中断异常则开始验证
__except(EXCEPTION_BREAKPOINT==GetExceptionCode()){
       //开始验证
       VerifyUserCode();
}


源码见论坛:http://bbs.7softs.com/read.php?tid=31967
压缩包解压密码为:[size=3]b1NYzvsaq8GODPyH[/size]

发表评论