«August 2025»
12
3456789
10111213141516
17181920212223
24252627282930
31


公告

本站技术贴除标明为“原创”的之外,其余均为网上转载,文中我会尽量保留原作者姓名,若有侵权请与我联系,我将第一时间做出修改。谢谢!

             ——既瑜


天气预报(南京)


我的分类(专题)

首页(183)
【趣味文摘】(22)
【五子连珠】(13)
【技术文档】(136)
【电脑技术】(6)
【疑难问题】(1)
【我的心情】(5)


最新日志
花语(中英文对照版)
各种花的花语
NTFS格式的7个精彩问答(pconli
童言无忌,有趣得一蹋
给MM修电脑的三个步骤[转载]
J2EE 面试题综合
JAVA编程规则
[转] P2P之UDP穿透NAT的原理与
[转]词法分析器
文件加密技术
一个让人发狂的PI求解C程序
[转]直线生成算法之DDA
[转]利用内核对象----互斥量实现应用
[转]如何正确的计算文件收发进度
双机调试VC程序
[转]分治法优化大整数乘法 C++实现
浮点数值的内存结构
[转]双链表实现大整数的加法与乘法[VC
拜占廷将军问题[转]
某人的挂QQ的程序源代码,虽然没用了,拿

最新回复
回复:vc中的CString的操作
回复:[转]分治法优化大整数乘法 C++
回复:[转]分治法优化大整数乘法 C++
回复:花语(中英文对照版)
回复:基本排序算法比较与选择[转载]
回复:c++中强制类型转换操作符小结
回复:c++中强制类型转换操作符小结
何必那么执着于是大头猫还是愤怒的小鸟,淡
回复:浮点数值的内存结构
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:32位位图到24位位图的转换
dren, ages 16 and 20
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:花语(中英文对照版)
回复:各种花的花语

留言板
签写新留言

不是0-1背包喔
桂花的花语``
谢谢
提议
提议

统计
blog名称:★既瑜★
日志总数:183
评论数量:636
留言数量:-25
访问次数:1405947
建立时间:2005年3月12日

链接


http://www.nju.edu.cn
http://bbs.nju.edu.cn 
http://www.t7-online.com
http://www.csdn.net
http://www.91f.net
http://www.crsky.com
我的MSN BLOG 

联系我

  OICQ:215768265
  njucs2001@hotmail.com
  erichoo1982@gmail.com

 

W3CHINA Blog首页    管理页面    写新日志    退出


[【技术文档】]对重要代码的防破解[转载]
既瑜(224499) 发表于 2005/3/20 0:05:00

 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) | 编辑 | 精华


发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)

站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.328 second(s), page refreshed 144767429 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号