技术开发 频道

编程实现Windows应用程序自产生代码

  【IT168 专稿】自产生代码技术对阻碍反汇编程序来说,起到了重要的作用。你应该通过它这种“糟糕”的编程风格技术来保护你的应用程序。首先,这个kernel32.dll组件对外暴露了一个WriteProcessMemory函数。从它的的名称就知道它的作用了,用于修改一个进程的内存。其次,几乎所有的操作系统,包括Windows和Linux都允许修改的代码放置到堆上。我喜欢第二种方式,因为当我试着在VC++开发环境中为Windows应用程序创建自产生代码时能获得更多的自由和受到更少的限制。下图展示了应用程序异常终止所产生的一个异常。


  我试着解决这些问题,下面是解决该问题所获得的一些个人感受:

 1.仅使用局部变量,不要使用全局变量、静态变量、和常量字符串变量。
  2 如果遇到函数中的代码需要调用另一个函数,应该传递该函数的指针过去。

  关于自产生代码的一个问题:它到底有什么特点?

  答案就是它能够隐藏一些关键功能,诸如产生键值的程序或者检查序列号等工作。

  使用代码

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

  下面的代码是我想要在堆上执行的函数。当然你可以对它进行修改,并添加你组件的代码进行测试以获得你自己的体验。如果你喜欢,你可以共享到这里来或者单独写一篇文章并发表到代码工程(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.h文件由My_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 myfunction_00001_code[]那样的unsigned char,它的maxlength是2048。这受到了VC++ 6.0开发环境的限制。因此,如果该函数代码太长,我必须编写加密的代码数据到多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文件。其次,定义一些macro 以便加载多个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;
}
//

  最终在堆上执行了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控制下创建自产生代码的可能性理论。对于如何使用这就是你的任务了。

0
相关文章