65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Windows 应用程序编程自生成代码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (738投票s)

2007年10月3日

CPOL

4分钟阅读

viewsIcon

500551

downloadIcon

847

在堆栈或堆中执行 VC++ 代码。

引言

自生成代码技术是反编译器的重要手段。你应该使用这项技术来保护你的应用程序,尽管这是一种“糟糕”的编程风格。至少有两种已知的修改应用程序代码的方法。第一,kernel32.dll 导出了 WriteProcessMemory 函数,顾名思义,该函数用于修改进程的内存。第二,实际上所有操作系统,包括 Windows 和 Linux,都允许修改放置在堆栈上的代码。我喜欢第二种方法,因为它在尝试在 VC++ IDE 中为 Windows 应用程序创建自生成代码时,具有更多的自由度和更少的限制。FAQ 是会导致异常并随后导致应用程序异常终止。

Screenshot - error.jpg

我尝试解决这些问题,并获得了一些经验如下:

函数代码必须是可重定位代码。

  1. 只使用局部变量,不要使用全局变量、静态变量和常量字符串变量。
  2. 如果函数代码想调用另一个函数,应该将该函数的指针传递给它。

有些人可能会对自生成代码有疑问:它的优点是什么?

答案是它可以隐藏关键或核心信息,例如生成密钥的程序或验证序列号的程序。

Using the Code

步骤 1:将 My_function 代码加密到头文件中

下面的代码是我想要在堆栈上执行的函数。当然,你可以修改它,添加自己的代码并进行测试,然后获得自己的经验。如果你愿意,可以在这里分享,或者写另一篇文章并在 The Code Project 上发布。

//
void __stdcall my_function( int x,
     int y,
     char* str_a,
     char* str_b,
     void* (__cdecl *_memcpy )( void *dest, const void *src, size_t count ),
     int (__cdecl *_sprintf )( char *buffer, const char *format, ... ),
     void* (__cdecl *_malloc )( size_t size ),
     void (__cdecl *_free )( void *memblock ),
     size_t (__cdecl *_strlen )( const char *string ),
     int (__stdcall* _MessageBox)(HWND hWnd, LPCTSTR lpText, 
     LPCTSTR lpCaption, UINT uType)
)
{
    char* pTemp;
    int str_a_len=_strlen(str_a);
    int str_b_len=_strlen(str_b);   
    pTemp=(char*)_malloc(str_a_len+str_b_len+20);

    if(x>y)
    {
        //_sprintf(pTemp,"%s%s",str_a,str_b);       
        //error:constant string variable
        _memcpy(pTemp,str_a,str_a_len);
        _memcpy(pTemp+str_a_len,str_b,str_b_len);
        pTemp[str_a_len+str_b_len]=0;

        //_MessageBox(NULL,pTemp,"",MB_OK);         
        //error:constant string variable
        _MessageBox(NULL,pTemp,str_a,MB_OK);
    }
    else
    {
        //_sprintf(pTemp,"%s%s",str_b,str_a);
        //error:constant string variable
        _memcpy(pTemp,str_b,str_b_len);
        _memcpy(pTemp+str_b_len,str_a,str_a_len);
        pTemp[str_a_len+str_b_len]=0;

        //MessageBox(NULL,pTemp,"title",MB_OK);
        //error:constant string variable
        _MessageBox(NULL,pTemp,str_b,MB_OK); 
    }

    for(int i=0;i<10;i++)
    {
        int j=1;
        j^=i;
    }

    _free(pTemp);
}  
//

my_function 中,我尝试调用一些运行时库函数和 Windows API 函数,以便比较参数 X 和 Y 来显示不同的消息框。

为了在堆栈上执行此函数,首先我需要加密 my_function 代码。这项工作是在名为 My_function 的项目中实现的。如果你阅读它,你会发现一个名为 void __stdcall my_function_END() 的函数。为了计算我的函数的长度,my_function_END 函数必须紧随 my_function 之后。

void encrypt_my_function()bool encrypt_function(BYTE* _my_function,unsigned int n_my_function_size,char* function_name) 用于将 my_function 代码数据加密到临时缓冲区,void build_h(BYTE* pInBuf,int InBufSize,char* function_name) 函数会将加密的代码数据写入头文件。

//
bool encrypt_function(BYTE* _my_function,unsigned int n_my_function_size,
    char* function_name)
{
    BYTE* buff=(BYTE*)malloc(n_my_function_size);
    if(buff==NULL) return false;

    //Note: Here is just a simple encryption algorithm,
    //you should replace it with your own.
    //There are a lot of encryption algorithms which you can get 
    //from the Internet.
    for(UINT i=0;i<n_my_function_size;i++) buff[i]=_my_function[i]^99;

    build_h(buff,n_my_function_size,function_name);
    free(buff);
    return true;
}

//
//
void encrypt_my_function()
{
    void ( __stdcall *_my_function)(int x,
        int y,
        char* str_a,
        char* str_b,
        void* (__cdecl *_memcpy )( void *dest, 
        const void *src, size_t count ),
        int (__cdecl *_sprintf )( char *buffer, const char *format, ... ),
        void* (__cdecl *_malloc )( size_t size ),
        void (__cdecl *_free )( void *memblock ),
        size_t (__cdecl *_strlen )( const char *string ),
        int (__stdcall* _MessageBox)(HWND hWnd, LPCTSTR lpText, 
        LPCTSTR lpCaption, UINT uType)
                                    );
    void ( __stdcall *_my_function_END)();
    unsigned int n_my_function_size;
    char* function_name="myfunction";

    _my_function=my_function;
    _my_function_END=my_function_END;
    n_my_function_size=abs((UINT)_my_function_END-(UINT)_my_function)+1;
    //calculate the length of my_function

    if(encrypt_function((BYTE*)_my_function,n_my_function_size,function_name))
    {
        AfxMessageBox(
            "My function is encrypted successfully ! 
            The encrypted code is included in myfunction.h file.");
    }
    else
    {
        AfxMessageBox("My function is encrypted unsuccessfully !");
    }
}
//

Myfunction.hMy_function 项目生成。

包含加密代码数据的头文件将如下所示:

//
//myfunction.h

unsigned char myfunction_00001_code[]="\
\xe8\x27\x47\x6f\x30\x36\x35\xe8\x17\x47\x53\x34\x33\
x9c\xb5\xe8\x0f\x47\x47\xe8\
\x9b\x36\x9c\xb5\xe8\xbb\xee\x2f\x58\x77\x32\x9c\x37\
x47\x5b\xe8\x37\x47\x43\xe8\
\x93\xe8\x27\x47\x47\xe0\xa7\x6f\x58\xb3\x1d\x5f\xe8\
x27\x47\x7f\x34\x33\x35\x9c\
\x37\x47\x53\x30\xee\x6f\x5d\x36\x32\x9c\x37\x47\x5f\
xe8\x27\x47\x57\xe0\xa7\x7b\
\xee\x77\x7d\x09\x63\x33\x35\x09\x63\xa5\x67\x59\x63\
x9c\x37\x47\x2b\x35\x9c\x37\
\x47\x57\xe0\xa7\x67\x3c\x3d\x3e\x38\xa1\x4b\x63\x30\
x36\x35\x9c\x37\x47\x53\xe8\
\x2f\x47\x4b\x60\xbd\x34\x32\x30\x9c\x37\x47\x5f\xe0\
xa7\x7b\xa5\x67\x58\x63\x09\
\x63\x0b\x17\x33\x23\x63\x35\x09\x63\x9c\x76\x1b\x50\
x23\x63\x09\x63\x36\x35\x09\
\x63\x9c\x37\x47\x2b\x35\x9c\x37\x47\x57\xe0\xa7\x67\
x3c\x3d\x3e\x38\xa1\x4b\x63\
\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xf3\xa0";
#define myfunction_00001_code_LEN 193

#define myfunction_ARRAY_NUM 1
#define myfunction_CODE_LEN 193

//

注意:如果你想定义一个 unsigned char 变量,比如 unsigned char myfunction_00001_code[],它的 maxlength2048。这是 VC++ 6.0 IDE 的限制。因此,如果函数代码太长,我必须将加密的代码数据写入多个 unsigned char 变量。这个函数可以在 build_h 中实现,如下所示:

//
void build_h(BYTE* pInBuf,int InBufSize,char* function_name)
{
    DWORD syslen=InBufSize;
    BYTE* sysbuffer=pInBuf;

    char hname[MAX_PATH];
    sprintf(hname,"%s%s",function_name,".h");

    HANDLE hHandle=CreateFileA(hname,
        GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);

    SetFilePointer(hHandle,0,0,FILE_BEGIN);

    DWORD RW;
    char _code_end[]="\";
    int l_end=strlen(&_code_end[0]);

    char cname[MAX_PATH];
    char* pszSlash=hname;

    char TX[5];
    int count=0;
    int arrary_num=1;
    int ar_num=0;

    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);

    WriteFile(hHandle,"//",2,&RW,NULL);
    WriteFile(hHandle,hname,strlen(hname),&RW,NULL);
    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);
    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);

    WriteFile(hHandle,
        "//Created by Your Name",strlen("//Created by Your Name"),&RW,NULL);
    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);

    WriteFile(hHandle, "//your email", strlen("//your email"), &RW, NULL);

    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);

    for(UINT i=0;i<syslen;i++){
        if(ar_num==0){
            WriteFile(hHandle,"\r",1,&RW,NULL);
            WriteFile(hHandle,"\n",1,&RW,NULL);

            int ee=sprintf(cname,"%s","unsigned char ");
            ee+=sprintf(cname+ee,"%s",pszSlash);
            ee=ee-2;
            ee+=sprintf(cname+ee,"%s%05d","_",arrary_num);
            ee=sprintf(cname+ee,"%s",_code[]=\"\\\r\n");
            int l=strlen(&cname[0]);
            WriteFile(hHandle,cname,l,&RW,NULL);
        }

        WriteFile(hHandle,"\\x",2,&RW,NULL);
        sprintf(TX,"%02x",sysbuffer[i]);
        WriteFile(hHandle,TX,2,&RW,NULL);
        count++;
        ar_num=ar_num+4;

        if (count==20){
        count=0;
        WriteFile(hHandle,"\\",1,&RW,NULL);
        WriteFile(hHandle,"\r",1,&RW,NULL);
        WriteFile(hHandle,"\n",1,&RW,NULL);
        }

        if(ar_num>2030){

        arrary_num=arrary_num+1;
        WriteFile(hHandle,_code_end,l_end,&RW,NULL);

        WriteFile(hHandle,"\r",1,&RW,NULL);
        WriteFile(hHandle,"\n",1,&RW,NULL);

        char len[MAX_PATH];
        int ee=sprintf(len,"%s","#define ");
        ee+=sprintf(len+ee,"%s",cname+14);
        ee=ee-7;
        ee+=sprintf(len+ee,"%s","_LEN ");
        ee+=sprintf(len+ee,"%d",ar_num/4);
        WriteFile(hHandle,len,ee,&RW,NULL);

        WriteFile(hHandle,"\r",1,&RW,NULL);
        WriteFile(hHandle,"\n",1,&RW,NULL);

        ar_num=0;
        count=0;
        }
    } 

    double yushu=fmod(syslen*1.0,508.0);
    if(yushu!=0){
        WriteFile(hHandle,_code_end,l_end,&RW,NULL);

        WriteFile(hHandle,"\r",1,&RW,NULL);
        WriteFile(hHandle,"\n",1,&RW,NULL);

        char len[MAX_PATH];
        int ee=sprintf(len,"%s","#define ");
        ee+=sprintf(len+ee,"%s",cname+14);
        ee=ee-7;
        ee+=sprintf(len+ee,"%s","_LEN ");
        ee+=sprintf(len+ee,"%d",ar_num/4);
        WriteFile(hHandle,len,ee,&RW,NULL);

        WriteFile(hHandle,"\r",1,&RW,NULL);
        WriteFile(hHandle,"\n",1,&RW,NULL);
    }

    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);

    char lLen[MAX_PATH];
    int ee=sprintf(lLen,"%s","#define ");
    ee+=sprintf(lLen+ee,"%s",cname+14);
    ee=ee-17;
    ee+=sprintf(lLen+ee,"%s","ARRAY_NUM ");
    ee+=sprintf(lLen+ee,"%d",arrary_num);


    WriteFile(hHandle,lLen,ee,&RW,NULL);

    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);


    ee=sprintf(lLen,"%s","#define ");
    ee+=sprintf(lLen+ee,"%s",cname+14);
    ee=ee-17;
    ee+=sprintf(lLen+ee,"%s","CODE_LEN ");
    ee+=sprintf(lLen+ee,"%d",syslen);


    WriteFile(hHandle,lLen,ee,&RW,NULL);

    WriteFile(hHandle,"\r",1,&RW,NULL);
    WriteFile(hHandle,"\n",1,&RW,NULL);
    CloseHandle(hHandle);
}
//

生成包含 my_funcion 加密代码数据的 myfunction.h 后,我创建了一个名为 self_engendered_code 的项目。在此项目中,My_function 被解密,然后被执行在堆栈或其他内存缓冲区中,如 malloc 分配的堆缓冲区,尽管有些人认为这是不允许的。

步骤 2:解密 My_function 代码并执行

首先,在 self_engendered_code 项目中包含 myfunction.h。其次,定义一些宏以将多个 unsigned char 变量加载到一个内存缓冲区中。

//
#include "..\\\self_engendered_code\\My_function\\myfunction.h"
#define _founc(x) myfunction_##x##_code
#define _founc_len(x) myfunction_##x##_code_LEN
unsigned char p_my_function[1024];
//

void Load_my_function() 可以将 My_function 代码解密到内存缓冲区:p_my_function 这是一个全局变量。它可以被替换为定义在函数体内的局部变量,也就是局部函数堆栈。

//
void Load_my_function()
{
    int code_len=myfunction_CODE_LEN;
    unsigned char* pcode=
        (unsigned char*)malloc(code_len*sizeof(unsigned char));

    if(pcode==NULL)
    {
        #ifdef _DEBUG
            AfxMessageBox("Memory used up!");
        #endif

        return;
    }

    int p;
    int hp=0;

    for(int k=1;k<=myfunction_ARRAY_NUM;k++)
    {
        switch (k)
        {
        //The number of case equal to myfunction_ARRAY_NUM 
        //defined in myfunction.h.
            case 1:
                for(p=0;p<_founc_len(00001);p++) pcode[hp+p]=_founc(00001)[p];
                hp=hp+p;
                break;
            /*
            case 2:
                for(p=0;p<_founc_len(00002);p++) pcode[hp+p]=_founc(00002)[p];
                hp=hp+p;
                break;
            case 3:
                for(p=0;p<_founc_len(00003);p++) pcode[hp+p]=_founc(00003)[p];
                hp=hp+p;
                break;
            case 4:
                for(p=0;p<_founc_len(00004);p++) pcode[hp+p]=_founc(00004)[p];
                hp=hp+p;
            break;
            .
            .
            .

            */
            default:
            break;
        } 
    }

    //Note: Here is just a simple encryption algorithm, you should 
    //replace it with your own.
    //There are a lot of encryption algorithms which you can get 
    //from the Internet. 
    for(int i=0;i<code_len;i++) p_my_function[i]=pcode[i]^99;
}
//

最后,在 STACK 上执行 My_function

//
void Run_my_function()
{
    int x=1;//8
    int y=2;
    char str_a[]=" HELLO MY_FOUNCTION ! ";
    char str_b[]=" Hello my_function ! ";

    void* (__cdecl *_memcpy )( void *dest, const void *src, size_t count );
    int (__cdecl *_sprintf )( char *buffer, const char *format, ... );
    void* (__cdecl *_malloc )( size_t size );
    void (__cdecl *_free )( void *memblock );
    size_t (__cdecl *_strlen )( const char *string );
    int (__stdcall* _MessageBox)(HWND hWnd, LPCTSTR lpText, 
        LPCTSTR lpCaption, UINT uType);

    _memcpy=memcpy;
    _sprintf=sprintf;
    _malloc=malloc;
    _free=free;
    _strlen=strlen;
    _MessageBox=MessageBox;

    void ( __stdcall *_my_function)(int x,
        int y,
        char* str_a,
        char* str_b,
        void* (__cdecl *_memcpy )( void *dest, 
        const void *src, size_t count ),
        int (__cdecl *_sprintf )( char *buffer, const char *format, ... ),
        void* (__cdecl *_malloc )( size_t size ),
        void (__cdecl *_free )( void *memblock ),
        size_t (__cdecl *_strlen )( const char *string ),
        int (__stdcall* _MessageBox)(HWND hWnd, LPCTSTR lpText, 
        LPCTSTR lpCaption, UINT uType)
                                    );
    _my_function=(void ( __stdcall *)(int x,
        int y,
        char* str_a,
        char* str_b,
        void* (__cdecl *_memcpy )( void *dest, 
        const void *src, size_t count ),
        int (__cdecl *_sprintf )( char *buffer, const char *format, ... ),
        void* (__cdecl *_malloc )( size_t size ),
        void (__cdecl *_free )( void *memblock ),
        size_t (__cdecl *_strlen )( const char *string ),
        int (__stdcall* _MessageBox)(HWND hWnd, LPCTSTR lpText, 
        LPCTSTR lpCaption, UINT uType)
        )) &p_my_function[0];

    _my_function(x,
                 y,
                 str_a,
                 str_b,
                 _memcpy,
                 _sprintf,
                 _malloc,
                 _free,
                 _strlen,
                 _MessageBox
                 );
}
//

摘要

如果你想隐藏关键信息,你不应该调用 MessageBox API 函数。当然,这种保护的抵抗力微不足道。然而,它可以增加。有许多编程技巧可以达到此目的,包括动态异步解码、将比较结果替换为各种表达式中的因子,以及将代码的关键部分直接放置在密钥中。然而,自生成代码技术非常重要,已被 **Antidebug LIB** 采用。本文的目的不是提供现成的保护(黑客可以研究它),而是理论上证明和展示在 Windows 的控制下创建自生成代码是可能的。如何利用这个可能性是你的任务。

© . All rights reserved.