首页
社区
课程
招聘
[原创]编译器优化分析-逆向工程和破解适用
发表于: 2006-7-9 17:17 16037

[原创]编译器优化分析-逆向工程和破解适用

2006-7-9 17:17
16037
DFCGVC编译器优化分析-逆向工程和破解适用。[color]
EVE跃迁。。5个跳跃,,趁这个时间我写点东西。
希望能够对编程不是很熟悉的逆向工程者一些帮助
一样以VC8编译器为例,有可能穿插一点 Inter编译器。
Release为主。 Debug版本基本都看得懂而且,一般也不会用Debug版本发布软件。
1.        先从函数框架说起走
代码这样子:

#include "stdafx.h"
#include <stdlib.h>
void study1()
        {
        __asm int 3
        char str[10];
        gets(str);
        printf("你输入的内容是\n%s",str);
        __asm int 3        }
int _tmain(int argc, _TCHAR* argv[])
        {
        study1();
        system("PAUSE");
        return 0;
        }

默认情况Release编译吧。以两个__ASM int 3之间为代码
汇编代码:
#include "stdafx.h"
#include <stdlib.h>
void study1()
        {
        __asm int 3
        char str[10];
        gets(str);
        printf("你输入的内容是\n%s",str);
        __asm int 3
        }
int _tmain(int argc, _TCHAR* argv[])
        {
00401000  sub         esp,10h
00401003  mov         eax,dword ptr [___security_cookie (403000h)]
00401008  xor         eax,esp
0040100A  mov         dword ptr [esp+0Ch],eax
        study1();
0040100E  int         3   
0040100F  lea         eax,[esp]
00401012  push        eax  
00401013  call        dword ptr [__imp__gets (4020A4h)]
00401019  lea         ecx,[esp+4]
0040101D  push        ecx  
0040101E  push        offset string "\xc4\xe3\xca\xe4\xc8\xebbbbbbbbbbbb" (4020F4h)
00401023  call        dword ptr [__imp__printf (40209Ch)]
00401029  add         esp,0Ch
0040102C  int         3   
        system("PAUSE");
0040102D  push        offset string "PAUSE" (402108h)
00401032  call        dword ptr [__imp__system (402098h)]
        return 0;
        }
00401038  mov         ecx,dword ptr [esp+10h]
0040103C  add         esp,4
0040103F  xor         ecx,esp
00401041  xor         eax,eax
00401043  call        __security_check_cookie (40104Ch)
00401048  add         esp,10h
0040104B  ret              
我们用默认选项都可以看得出函数已经被内联了。
00401000  sub         esp,10h
分配16字节。 这里注意一下,一般的其他语言可能也多半是会用函数框架也就是push esp
Mov ebp,esp
然后靠ebp来引用局部变量,这一点在VC中是没有的。
你一定会觉得很奇怪明明分配的10字节怎么会变成了16字节。
答案是这样的。
00401003  mov         eax,dword ptr [___security_cookie (403000h)]
00401008  xor         eax,esp
0040100A  mov         dword ptr [esp+0Ch],eax
保存esp函数指针防止堆溢出。主要是在VC中靠esp引用局部变量所以千万错不得。。
但是还有两字节呢? 是这样的。VC中要进行指针对其 也就是说一般情况下会使4的倍数。
试验一下:
改为:
        char str[12];
        gets(str);
代码依然是
00401000  sub         esp,10h
00401003  mov         eax,dword ptr [___security_cookie (403000h)]
00401008  xor         eax,esp
0040100A  mov         dword ptr [esp+0Ch],eax

下面再来说说:
2.        关于ecx指针。在VC中虽然可以指定链接方式也就是C样式还是标准API样式或者是 Fastcall
不过一般而言是C样式 Fastcall相对用的较少。另外还有thiscall方式。也就是类指针。我们来看看MSDN
The __thiscall calling convention is used on member functions and is the default calling convention used by C++ member functions that do not use variable arguments. Under __thiscall, the callee cleans the stack, which is impossible for vararg functions. Arguments are pushed on the stack from right to left, with the this pointer being passed via register ECX, and not on the stack, on the x86 architecture.
ThisCall的定义如上所示
我们可以看到考ecx传递指针,参数有调用者恢复,以至于可以传递参数不定的函数。
还是来看段代码
void study2()
        {
        __asm int 3
        CString MyStr;
        MyStr="PeDiy By Fox";
        puts(MyStr);
        __asm int 3
        }
为了方便研究我们去掉了esp检查和C++异常
int _tmain(int argc, _TCHAR* argv[])
        {
00401040  push        ecx  
00401041  push        esi  
00401042  push        edi  保护寄存器就不说了
        //study1();
        study2();
00401043  int         3   
00401044  mov         eax,dword ptr [ATL::g_strmgr (403380h)]
00401049  mov         edx,dword ptr [eax+0Ch]
0040104C  mov         ecx,offset ATL::g_strmgr (403380h)
00401051  call        edx  
00401053  add         eax,10h
00401056  mov         dword ptr [esp+8],eax
0040105A  mov         edi,0Ch
0040105F  lea         eax,[esp+8]
00401063  call        ATL::CSimpleStringT<char,0>::SetString (4010B0h)
00401068  mov         esi,dword ptr [esp+8]
0040106C  push        esi  
0040106D  call        dword ptr [__imp__puts (4020C8h)]
00401073  add         esp,4
00401076  int         3   
00401077  lea         eax,[esi-10h]
0040107A  lea         ecx,[eax+0Ch]
0040107D  or          edx,0FFFFFFFFh
00401080  lock xadd   dword ptr [ecx],edx
00401084  dec         edx  
00401085  test        edx,edx
00401087  pop         edi  
00401088  pop         esi  
00401089  jg          main+55h (401095h)
0040108B  mov         ecx,dword ptr [eax]
0040108D  mov         edx,dword ptr [ecx]
0040108F  push        eax  
00401090  mov         eax,dword ptr [edx+4]
00401093  call        eax  
        system("PAUSE");
00401095  push        offset string "PAUSE" (402160h)
0040109A  call        dword ptr [__imp__system (4020C4h)]
        return 0;
004010A0  xor         eax,eax
        }
004010A2  add         esp,8
004010A5  ret         
注意,这里不是什么类型的CALL而是为了减小体积而来的代码内联,不存在保护寄存器之类的问题。

00401044  mov         eax,dword ptr [ATL::g_strmgr (403380h)]
00401049  mov         edx,dword ptr [eax+0Ch]
0040104C  mov         ecx,offset ATL::g_strmgr (403380h)
00401051  call        edx  
后面的就不详细分析。我要说的是。最后一句004010A2  add         esp,8
并不代表局部变量为2个DWORD而是只有一个。
原因是这样的,由于才用ESP传递指针,所以任何push pop操作都会影响指针。
而最前面就有三个PUSH 最后面对应的寄存器却只有两个。POP
那么实际上就只有一个变量而本身call        dword ptr [__imp__system (4020C4h)]就要恢复 add esp,4  再加上一个指针变量 就刚好是 add esp,8,可见编译器的高度优化。把保护寄存器和分配局部变量合并了、。令人佩服。
不怎么好读得懂。。。:(
再说一下
3:结构和数组。
struct test
{
int a;
char b;
};
void study3()
        {

                __asm int 3
                DWORD MyStr[]={'PeDi','Y\0'};
                test MyTest;
                MyTest.a='LOVE';
                MyTest.b='\0';
                puts((char *)&MyTest);
                puts((char *)MyStr);
        __asm int 3
        }00401020   /$  83EC 10             sub esp,10
00401023   |.  56                  push esi
00401024   |.  CC                  int3
00401025   |.  8B35 C4204000       mov esi,dword ptr ds:[<&MSVCR80.puts>]                   ;  MSVCR80.puts
0040102B   |.  8D4424 04           lea eax,dword ptr ss:[esp+4]
0040102F   |.  50                  push eax                                                 ; /s
00401030   |.  C74424 10 69446550  mov dword ptr ss:[esp+10],50654469                       ; |
00401038   |.  C74424 14 00590000  mov dword ptr ss:[esp+14],5900                           ; |
00401040   |.  C74424 08 45564F4C  mov dword ptr ss:[esp+8],4C4F5645                        ; |
00401048   |.  C64424 0C 00        mov byte ptr ss:[esp+C],0                                ; |
0040104D   |.  FFD6                call esi                                                 ; \puts
0040104F   |.  8D4C24 10           lea ecx,dword ptr ss:[esp+10]
00401053   |.  51                  push ecx                                                 ; /s
00401054   |.  FFD6                call esi                                                 ; \puts
00401056   |.  83C4 08             add esp,8
00401059   |.  CC                  int3
0040105A   |.  68 50214000         push 编译器学.00402150                                       ; /command = "PAUSE"
0040105F   |.  FF15 C0204000       call dword ptr ds:[<&MSVCR80.system>]                    ; \system
00401065   |.  83C4 04             add esp,4
00401068   |.  33C0                xor eax,eax
0040106A   |.  5E                  pop esi
0040106B   |.  83C4 10             add esp,10
0040106E   \.  C3                  retn
分配10个字节, 后面就是对堆操作. 但值得注意的是
0040102B   |.  8D4424 04          lea eax,dword ptr ss:[esp+4]
0040102F   |.  50                 push eax                                                 ; /s
00401030   |.  C74424 10 50654469 mov dword ptr ss:[esp+10],69446550                       ; |
00401038   |.  C74424 14 59000000 mov dword ptr ss:[esp+14],59                             ; |
00401040   |.  C74424 08 4C4F5645 mov dword ptr ss:[esp+8],45564F4C                        ; |
00401048   |.  C64424 0C 00       mov byte ptr ss:[esp+C],0                                ; |
第一句没有错,但为什么后面没有对[esp+4]操作了。。push eax。。这个。 因此相应的就应该是
从esp+8开始
以及对其。即使是CHAR也是一个INT所以在WINDOWS编程中应当尽量就行4字节操作(64位为8字节)
今天就讲到这么里
以后我会介绍 算数运算的编译器优化,以及条件语句。

[课程]Linux pwn 探索篇!

收藏
免费 7
支持
分享
最新回复 (8)
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
2
最初由 foxabu 发布
编译器优化分析-逆向工程和破解适用........


此话不假,学习后,对提高代码分析能力大有好处。
2006-7-9 17:39
0
雪    币: 223
活跃值: (25)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
2006-7-9 19:48
0
雪    币: 4
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
唉,~~~`一到关键就收,再讲点别的就好了
2010-1-9 23:14
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
向你学习,向你致敬
2010-1-10 10:18
0
雪    币: 724
活跃值: (81)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
...
分配16字节。 这里注意一下,一般的其他语言可能也多半是会用函数框架也就是push esp
Mov ebp,esp
然后靠ebp来引用局部变量,这一点在VC中是没有的。
...


是否建stack frame,VC是由编译选项/Oy控制的,楼主是Release版,应该是开了优化选项/O1 /O2 或 /Ox,这三项隐含了/Oy,如果楼主在编译命令行后加上/Oy-,则楼主的函数就会有stack frmae了(如果函数中不使用栈,则也不会建stack frame)。
2010-1-10 11:44
0
雪    币: 256
活跃值: (11)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
楼主说说常见的优化代码和相对应的C代码吧,如
Relase版exe

        if(c==3)
                c=7;
        else if(c==2)
                c=5;
        else c=8;

mov eax,dword ptr ds:[ecx]
cmp eax,3
jnz short class.0040100E
mov dword ptr ds:[ecx],7
retn
sub eax,2   (eax=2时)
neg eax
sbb eax,eax
and eax,3
add eax,5    (eax=0)
mov dword ptr ds:[ecx],eax
retn
还有好多好多,很多我们这些初学者都不清楚,知道这些对应关系,对逆向很有帮助啊
2010-1-11 14:14
0
雪    币: 346
活跃值: (129)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
8
不是很明白~~
  继续关注
2010-10-19 04:23
0
雪    币: 7
活跃值: (49)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
每四年一挖坟,挖出来也是宝哇
2014-4-29 12:34
0
游客
登录 | 注册 方可回帖
返回
//