« | August 2025 | » | 日 | 一 | 二 | 三 | 四 | 五 | 六 | | | | | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | | | | | | | |
|
公告 |
本站技术贴除标明为“原创”的之外,其余均为网上转载,文中我会尽量保留原作者姓名,若有侵权请与我联系,我将第一时间做出修改。谢谢!
——既瑜 |
统计 |
blog名称:★既瑜★ 日志总数:183 评论数量:636 留言数量:-25 访问次数:1405947 建立时间:2005年3月12日 |
OICQ:215768265
njucs2001@hotmail.com
erichoo1982@gmail.com |
|
W3CHINA Blog首页 管理页面 写新日志 退出
[【技术文档】]对重要代码的防破解[转载] |
500)this.width=500'>重要代码防破解.rar
开发环境:vc6.0+win2000Professional工具:UEDIT32,w32Dasm(黄金版)
本例将演示对一个MessageBox的保护,采用了“花指令”、“对代码段动态修改”、“动态生成代码”。通过这种手法可阻拦较大一部分破解者(当然这种方法跟复杂的加密、加壳、反跟踪的软件来说,这对破解高手可能只是小菜一碟!)。如果只是小型软件,那么我们花一点小手段阻拦一部分破解者,那何乐而不为呢?
这里得讲到一点,由于MessageBox是API,它是由dll导出的,可以修改对应点而达到除去MessageBox的效果,但这样不算成功,因为这样一来,将始整个程序的MessageBox都失效,对程序引起了破坏!
那我们到底怎样防破解呢?手段上面已经给出了!首先,为了防破解者很轻松静态分析你的代码,那么我们就得加花指令。其二,为了防破解者动态找到MessageBox,然后静态修改程序文件,让你转变成一串nop,那么我们应该在其后要再度验证这个MessageBox代码(我采用的是动态修改对应代码,让下一次再执行时,做其它事情)。其三,我们要动态修改代码,也就等于要动态生成代码了!
在vc中嵌入式汇编不支持伪指令,所以加花指令时,我只好在程序中用了个nop指令,然后等程序编译出来之后,找到对应的nop,用UEDIT32手工修改。(注意,这个值得取得好一点,千万别恰恰是一条指令对应的数值,那你就等于什么也没做)一般情况下代码段是不允许读和写的,但通过在编译嚣连接选项中给出如下开关:/section:.text,RWE 那对应的.Text段便是可读、可写、可执行的了。(注意,VC默认生成的代码在.Text段)讲到这里,相信大家都明白是怎么一回事了!说不定还可以写出更好的代码了。我是如下做的:
char m_Code[40]; // CTestADlg类成员变量,将来作生成代码存于此处
BOOL CTestADlg::OnInitDialog(){ CString str; //用来从资源装载“Test” void *p; p=(void*)&m_Code; //p将作生成代码段的起点, __asm lea eax, A010; //为加花指令,而加 __asm or eax,eax; //无实际意义,但对于某此静态反汇编工具有用(见①) __asm jmp eax; //跳至A010: __asm nop; //用来编译后手工做花指令用 (如:0x89)A010: str.LoadString(IDS_MSG1); //在此装载“Test”,如果在此之前,不加花指令,反汇编工具将报告: //此处用到IDS_MSG1,对应"Test",破解者简单一搜便可找到目标! char *pstr=str.GetBuffer(str.GetLength()); this->m_Code[0]=0x0; //清0A100: __asm LEA eax, A200; __asm push eax; //A200的地址 __asm LEA eax, A450; __asm push eax; //A450的地址 __asm lea ebx, A300; __asm or ebx,ebx; __asm jmp ebx; //跳至 A300; 请见A300 __asm nop; //用来编译后手式做花指令用 (如: 0x80)A200: ::AfxMessageBox(pstr); //嘿嘿,他们的目标在这里 /* //这些是AfxMessageBox对应的指令,注意,只有" 0"颜色每次编译保证一样。 6A 00 push 0 6A 00 push 0 8B 55 E4 mov edx,dword ptr [ebp-1Ch] 52 push edx E8 52 08 00 00 call AfxMessageBox (004022ec) */ __asm jmp A350; A300: this->SendMessage(WM_MESSAGE1); //向m_Code中生成一段代码,之所以用消息,是想增加对手理解难度。 __asm jmp A500; //请见A500
A350: //到此时正常情况下MessageBox以显示过了,我应该效验AfxMessageBox的代码是否正确 //我采用了,向m_Code生成一段代码,再由这段代码修改AfxMessageBox,让其变为: (见④) // 90 90 nop; nop; // 6A 00 push 0; // 8B 55 E4 mov edx,dword ptr [ebp-1Ch] // 52 push edx // FF E3 jmp ebx; //ebx在此之前由m_Code内的代码存为A450的地址 //向m_Code生成如下代码 /* _asm { nop; pop ecx; //函数返回地址 pop ebx; //A450地址 pop eax; //A200地址 push ecx; push eax; add [eax], 0x90-0x6A; //.1 修改push 0为nop;nop; add [eax+1], 0x90; //.2 add eax, 0x8 //指向MessageBox的call指令 add [eax], 0xff-E8; //.1 修改call CWnd::AfxMessageBox为:jmp ebx; mov [eax+1], 0xe3; //.2 pop eax; //A200地址 jmp eax; }*/ this->m_Code[0] += 0x0; //nop; this->m_Code[1] = 0x59; //pop ecx; this->m_Code[2] = 0x5B; //pop ebx; this->m_Code[3] = 0x58; //pop eax; this->m_Code[4] = 0x51; //push ecx; this->m_Code[5] = 0x50; //push eax; this->m_Code[6] = 0x80; //.1 add [eax], 0x90-0x6A; this->m_Code[7] = 0x00; //.2 this->m_Code[8] = 0x26; //.3 this->m_Code[9] = 0x80; //.1 add [eax+1], 0x90; this->m_Code[10] = 0x40; //.2 this->m_Code[11] = 0x01; //.3 this->m_Code[12] = 0x90; //.4 this->m_Code[13] = 0x83; //.1 add eax, 0x8 this->m_Code[14] = 0xC0; //.2 this->m_Code[15] = 0x08; //.3 this->m_Code[16] = 0x80; //.1 add [eax], 0x8 使call -->jmp this->m_Code[17] = 0x00; //.2 this->m_Code[18] = 0x17; //.3 this->m_Code[19] = 0xC6; //.1 mov [eax+1], 0xe3; this->m_Code[20] = 0x40; //.2 this->m_Code[21] = 0x01; //.3 this->m_Code[22] = 0xe3; //.4 this->m_Code[23] = 0x58; //pop eax; this->m_Code[24] = 0xFF; //.1 jmp eax this->m_Code[25] = 0xE0; //.2A448: __asm call p; //执行生成的代码,生成的代码将会跳转到OnInitDialog,如果AfxMessageBox出错,将处于混乱中 __asm ret; //返回 对应A500的callA450: CDialog::OnInitDialog(); __asm pop eax; //栈维护 __asm pop ebx; __asm pop ebx; __asm ret; //返回 对应A448的callA500: __asm call p; (②) //执行生成在m_Code中的代码,以对应代码见OnMessage1, //在对应代码将jmp到 A200,请大家见A200
AF00: //执行到此,实际上相当于执行了, //AfxMessageBox(); // CDialog::OnInitDialog(); //所有的防范结束。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); return TRUE;}
//自定义消息处理函数void CTestADlg::OnMessage1(WPARAM wParam, LPARAM lParam){ //向m_Code生成如下代码 (③) /* _asm { 90 nop; 59 pop ecx; //弹出返回地址 5B pop ebx; //弹出A450 58 pop eax; //弹出A200 51 push ecx; //压入返回地址 50 push eax; //压入A200 53 push ebx; //压入A450 FF E0 jmp eax; //jmp A200 } */ this->m_Code[0] += 0x90; //nop; 因在此之前已经清0 this->m_Code[1] = 0x59; //pop ecx; this->m_Code[2] = 0x5B; //pop ebx; this->m_Code[3] = 0x58; //pop eax; this->m_Code[4] = 0x51; //push ecx; this->m_Code[5] = 0x50; //push eax; this->m_Code[6] = 0x53; //push ebx; this->m_Code[7] = 0xFF; //.1 jmp eax; this->m_Code[8] = 0xE0; //.2}
注解①: 对应三条指令对应一条指令jmp A010,但为什么没有这么做,就是因为某些反汇编工具,它会重新以jmp指令对应点为启点反汇编,这样你加的花指令就无效。 第二句 __asm or eax,eax;实际是没有实际意义的,但对有此工具不加这一句,它同样明白: __asm lea eax, A010; __asm jmp eax; 是要跳至A010,也就会从对应点重新反汇编。(如w32Dasm好象就是如此)
注解②: 我采用的是call p, 有人会问可不可以写成((void (__stdcall *)(void))p)(); 逻辑上没错,但实际编译器将在函数返回时作一些栈检查,而由于在p里面不正常做法较多,将会出错。
注解③: 由于在函数内部调用函数内代码,那么栈中的局部变量的相对位置己经发生变化了,所以这也是为什么没有定义几个变量存放标号的地址的原因,而是通过压栈得到的,我觉得这样更简单点。 每次函数被调用时,我先将返回地址移到参数底部,同时弹栈时也就得到了参数。注解④: 首先,生成代码 接着,call这段代码,这段代码将修改AfxMessageBox处指令, 再接着,在修改完成后,跳至原AfxMessageBox 然后,由于在修改后的AfxMessageBox处已经变成jmp A450; 所以将跳至A450,开始InItDialog
Ok, 总算差不多了。大家可对附件修改试试。唉,这写东西还真难,星期天一下午就这样唰过去了,写了这么多还不知大家能否看懂?不管啦,看不懂代码,看懂三种手法也好!
|
阅读全文(2590) | 回复(0) | 编辑 | 精华 |
|