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






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

我尝试解决这些问题,并获得了一些经验如下:
函数代码必须是可重定位代码。
- 只使用局部变量,不要使用全局变量、静态变量和常量字符串变量。
- 如果函数代码想调用另一个函数,应该将该函数的指针传递给它。
有些人可能会对自生成代码有疑问:它的优点是什么?
答案是它可以隐藏关键或核心信息,例如生成密钥的程序或验证序列号的程序。
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.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
变量,比如 unsigned char myfunction_00001_code[]
,它的 maxlength
是 2048
。这是 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 的控制下创建自生成代码是可能的。如何利用这个可能性是你的任务。