无导入表编译原理
前置知识
pe文件结构
windows api
API字符串隐藏原理
思路
- 获取kernel32.dll 基地址;
- 定位 GetProcAddress函数的地址;
- 使用GetProcAddress确定 LoadLibrary函数的地址;
- 然后使用 LoadLibrary加载DLL文件(例如user32.dll);
- 使用 GetProcAddress查找某个函数的地址(例如MessageBox);
- 指定函数参数;
- 调用函数。
通过PEB获取kernel32.dll 基地址
在windows操作系统中每一个进程系统都维护着一个描述该进程的结构体,我们称之为peb(进程环境块),如可执行文件加载到内存的位置,模块列表(DLL),指示进程是否被调试的标志,不同发行版的windows系统该结构体可能存在着差异,在这个结构体里就维护者一个描述所有载入模块的链表(InMemoryOrderModuleList),无论我们是否使用,系统都会载入kernel32.dll到进程的虚拟地址空间。
C++代码
// ConsoleApplication6.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <stdio.h>
#include "windows.h"
#define NT_SUCCESS(x) ((x) >= 0)
#define ProcessBasicInformation 0
typedef NTSTATUS(NTAPI *pfnNtWow64QueryInformationProcess64)(
IN HANDLE ProcessHandle,
IN ULONG ProcessInformationClass,
OUT PVOID ProcessInformation,
IN ULONG ProcessInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
typedef NTSTATUS(NTAPI *pfnNtWow64ReadVirtualMemory64)(
IN HANDLE ProcessHandle,
IN PVOID64 BaseAddress,
OUT PVOID Buffer,
IN ULONG64 Size,
OUT PULONG64 NumberOfBytesRead
);
typedef
NTSTATUS(WINAPI *pfnNtQueryInformationProcess)
(HANDLE ProcessHandle, ULONG ProcessInformationClass,
PVOID ProcessInformation, UINT32 ProcessInformationLength,
UINT32* ReturnLength);
typedef struct _PROCESS_BASIC_INFORMATION32 {
NTSTATUS ExitStatus;
UINT32 PebBaseAddress;
UINT32 AffinityMask;
UINT32 BasePriority;
UINT32 UniqueProcessId;
UINT32 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION32;
typedef struct _UNICODE_STRING32
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING32, *PUNICODE_STRING32;
typedef struct _PEB32
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG Mutant;
ULONG ImageBaseAddress;
ULONG Ldr;
ULONG ProcessParameters;
ULONG SubSystemData;
ULONG ProcessHeap;
ULONG FastPebLock;
ULONG AtlThunkSListPtr;
ULONG IFEOKey;
ULONG CrossProcessFlags;
ULONG UserSharedInfoPtr;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
ULONG ApiSetMap;
} PEB32, *PPEB32;
typedef struct _PEB_LDR_DATA32
{
ULONG Length;
BOOLEAN Initialized;
ULONG SsHandle;
LIST_ENTRY32 InLoadOrderModuleList;
LIST_ENTRY32 InMemoryOrderModuleList;
LIST_ENTRY32 InInitializationOrderModuleList;
ULONG EntryInProgress;
} PEB_LDR_DATA32, *PPEB_LDR_DATA32;
typedef struct _LDR_DATA_TABLE_ENTRY32
{
LIST_ENTRY32 InLoadOrderLinks;
LIST_ENTRY32 InMemoryOrderModuleList;
LIST_ENTRY32 InInitializationOrderModuleList;
ULONG DllBase;
ULONG EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING32 FullDllName;
UNICODE_STRING32 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union
{
LIST_ENTRY32 HashLinks;
ULONG SectionPointer;
};
ULONG CheckSum;
union
{
ULONG TimeDateStamp;
ULONG LoadedImports;
};
ULONG EntryPointActivationContext;
ULONG PatchInformation;
} LDR_DATA_TABLE_ENTRY32, *PLDR_DATA_TABLE_ENTRY32;
typedef struct _PROCESS_BASIC_INFORMATION64 {
NTSTATUS ExitStatus;
UINT32 Reserved0;
UINT64 PebBaseAddress;
UINT64 AffinityMask;
UINT32 BasePriority;
UINT32 Reserved1;
UINT64 UniqueProcessId;
UINT64 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION64;
typedef struct _PEB64
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG64 Mutant;
ULONG64 ImageBaseAddress;
ULONG64 Ldr;
ULONG64 ProcessParameters;
ULONG64 SubSystemData;
ULONG64 ProcessHeap;
ULONG64 FastPebLock;
ULONG64 AtlThunkSListPtr;
ULONG64 IFEOKey;
ULONG64 CrossProcessFlags;
ULONG64 UserSharedInfoPtr;
ULONG SystemReserved;
ULONG AtlThunkSListPtr32;
ULONG64 ApiSetMap;
} PEB64, *PPEB64;
typedef struct _PEB_LDR_DATA64
{
ULONG Length;
BOOLEAN Initialized;
ULONG64 SsHandle;
LIST_ENTRY64 InLoadOrderModuleList;
LIST_ENTRY64 InMemoryOrderModuleList;
LIST_ENTRY64 InInitializationOrderModuleList;
ULONG64 EntryInProgress;
} PEB_LDR_DATA64, *PPEB_LDR_DATA64;
typedef struct _UNICODE_STRING64
{
USHORT Length;
USHORT MaximumLength;
ULONG64 Buffer;
} UNICODE_STRING64, *PUNICODE_STRING64;
typedef struct _LDR_DATA_TABLE_ENTRY64
{
LIST_ENTRY64 InLoadOrderLinks;
LIST_ENTRY64 InMemoryOrderModuleList;
LIST_ENTRY64 InInitializationOrderModuleList;
ULONG64 DllBase;
ULONG64 EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING64 FullDllName;
UNICODE_STRING64 BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
union
{
LIST_ENTRY64 HashLinks;
ULONG64 SectionPointer;
};
ULONG CheckSum;
union
{
ULONG TimeDateStamp;
ULONG64 LoadedImports;
};
ULONG64 EntryPointActivationContext;
ULONG64 PatchInformation;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;
int main()
{
DWORD dwPid = 4480;
HANDLE m_ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
BOOL bTarget = FALSE;
BOOL bSource = FALSE;
IsWow64Process(GetCurrentProcess(), &bSource);
IsWow64Process(m_ProcessHandle, &bTarget);
SYSTEM_INFO si;
GetSystemInfo(&si);
if (bTarget == FALSE && bSource == TRUE)
{
HMODULE NtdllModule = GetModuleHandle(L"ntdll.dll");
pfnNtWow64QueryInformationProcess64 NtWow64QueryInformationProcess64 = (pfnNtWow64QueryInformationProcess64)GetProcAddress(NtdllModule, "NtWow64QueryInformationProcess64");
pfnNtWow64ReadVirtualMemory64 NtWow64ReadVirtualMemory64 = (pfnNtWow64ReadVirtualMemory64)GetProcAddress(NtdllModule, "NtWow64ReadVirtualMemory64");
PROCESS_BASIC_INFORMATION64 pbi64 = { 0 };
if (NT_SUCCESS(NtWow64QueryInformationProcess64(m_ProcessHandle, ProcessBasicInformation, &pbi64, sizeof(pbi64), NULL)))
{
DWORD64 Ldr64 = 0;
LIST_ENTRY64 ListEntry64 = { 0 };
LDR_DATA_TABLE_ENTRY64 LDTE64 = { 0 };
wchar_t ProPath64[256];
if (NT_SUCCESS(NtWow64ReadVirtualMemory64(m_ProcessHandle, (PVOID64)(pbi64.PebBaseAddress + offsetof(PEB64, Ldr)), &Ldr64, sizeof(Ldr64), NULL)))
{
if (NT_SUCCESS(NtWow64ReadVirtualMemory64(m_ProcessHandle, (PVOID64)(Ldr64 + offsetof(PEB_LDR_DATA64, InLoadOrderModuleList)), &ListEntry64, sizeof(LIST_ENTRY64), NULL)))
{
if (NT_SUCCESS(NtWow64ReadVirtualMemory64(m_ProcessHandle, (PVOID64)(ListEntry64.Flink), &LDTE64, sizeof(_LDR_DATA_TABLE_ENTRY64), NULL)))
{
while (1)
{
if (LDTE64.InLoadOrderLinks.Flink == ListEntry64.Flink) break;
if (NT_SUCCESS(NtWow64ReadVirtualMemory64(m_ProcessHandle, (PVOID64)LDTE64.FullDllName.Buffer, ProPath64, sizeof(ProPath64), NULL)))
{
printf("模块基址:0x%llX\n模块大小:0x%X\n模块路径:%ls\n", LDTE64.DllBase, LDTE64.SizeOfImage, ProPath64);
}
if (!NT_SUCCESS(NtWow64ReadVirtualMemory64(m_ProcessHandle, (PVOID64)LDTE64.InLoadOrderLinks.Flink, &LDTE64, sizeof(_LDR_DATA_TABLE_ENTRY64), NULL))) break;
}
}
}
}
}
}
else if (bTarget == TRUE && bSource == TRUE || si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_AMD64 ||
si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_IA64)
{
HMODULE NtdllModule = GetModuleHandle(L"ntdll.dll");
pfnNtQueryInformationProcess NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(NtdllModule, "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION32 pbi32 = { 0 };
if (NT_SUCCESS(NtQueryInformationProcess(m_ProcessHandle, ProcessBasicInformation, &pbi32, sizeof(pbi32), NULL)))
{
DWORD Ldr32 = 0;
LIST_ENTRY32 ListEntry32 = { 0 };
LDR_DATA_TABLE_ENTRY32 LDTE32 = { 0 };
wchar_t ProPath32[256];
if (ReadProcessMemory(m_ProcessHandle, (PVOID)(pbi32.PebBaseAddress + offsetof(PEB32, Ldr)), &Ldr32, sizeof(Ldr32), NULL))
{
if (ReadProcessMemory(m_ProcessHandle, (PVOID)(Ldr32 + offsetof(PEB_LDR_DATA32, InLoadOrderModuleList)), &ListEntry32, sizeof(LIST_ENTRY32), NULL))
{
if (ReadProcessMemory(m_ProcessHandle, (PVOID)(ListEntry32.Flink), &LDTE32, sizeof(_LDR_DATA_TABLE_ENTRY32), NULL))
{
while (1)
{
if (LDTE32.InLoadOrderLinks.Flink == ListEntry32.Flink) break;
if (ReadProcessMemory(m_ProcessHandle, (PVOID)LDTE32.FullDllName.Buffer, ProPath32, sizeof(ProPath32), NULL))
{
printf("模块基址:0x%X\n模块大小:0x%X\n模块路径:%ls\n",LDTE32.DllBase,LDTE32.SizeOfImage,ProPath32);
}
if (!ReadProcessMemory(m_ProcessHandle, (PVOID)LDTE32.InLoadOrderLinks.Flink, &LDTE32, sizeof(_LDR_DATA_TABLE_ENTRY32), NULL)) break;
}
}
}
}
}
}
CloseHandle(m_ProcessHandle);
getchar();
}
// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单
// 入门提示:
// 1. 使用解决方案资源管理器窗口添加/管理文件
// 2. 使用团队资源管理器窗口连接到源代码管理
// 3. 使用输出窗口查看生成输出和其他消息
// 4. 使用错误列表窗口查看错误
// 5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
// 6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件
汇编代码
HMODULE GetKernel32() {
HMODULE hModule;
_asm {
mov eax, fs: [0x30]//fs:[00]位置存放着当前线程的线程环境块(teb),teb的0x30偏移处存放着当前线程所属进程的peb。
mov eax, [eax + 0xc]//EAX = PEB->Ldr
mov esi, [eax + 0x14]//ESI = PEB->Ldr.InMemOrder
lodsd //EAX = Second module
xchg eax, esi //EAX = ESI, ESI = EAX
lodsd //Next module
mov ebx, [eax + 0x10]//EBX = Base address
mov hModule,ebx
}
return hModule;
}
无导入表运行shellcode
int main() {
//UCHAR shellcode[] = {'\xbd','\xb0','\x63','\xa7','\x89','\xda','..........【shellcode】..........'};
typedef LPVOID(WINAPI *pVirtualAlloc)(LPVOID, DWORD, DWORD, DWORD);
typedef BOOL(WINAPI *pVirtualProtect)(LPVOID,DWORD,DWORD,PDWORD);
typedef BOOL(WINAPI* pWriteProcessMemory)(HANDLE, LPVOID, LPVOID, DWORD, LPDWORD);
DWORD oldProtect=0;
HMODULE hKernal32 = GetKernal32();
pVirtualAlloc VirtualAlloc=(pVirtualAlloc)GetProcAddress(hKernal32, "VirtualAlloc");
pVirtualProtect VirtualProtect = (pVirtualProtect)GetProcAddress(hKernal32, "VirtualProtect");
pWriteProcessMemory WriteProcessMemory = (pWriteProcessMemory)GetProcAddress(hKernal32, "WriteProcessMemory");
//PVOID Address = VirtualAlloc(NULL, sizeof(shellcode) + 1, MEM_COMMIT, PAGE_READWRITE);
//WriteProcessMemory(GetCurrentProcess(), Address, &shellcode, sizeof(shellcode), NULL);
//VirtualProtect(Address, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect);
VirtualProtect(&shellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect);
((void(*)(void)) &shellcode)();
VirtualProtect(&shellcode, sizeof(shellcode), oldProtect, NULL);
//VirtualProtect(Address, sizeof(shellcode), oldProtect, NULL);
return 0;
}
参考
【1】WOW64通过PEB获取32/64位进程模块信息 https://www.52pojie.cn/thread-872501-1-1.html
【2】过所有主流杀软查杀的“免杀壳”编写揭秘 https://bbs.pediy.com/thread-252772.htm
【3】重新编译开源代码绕过杀毒软件 https://www.t00ls.net/thread-53697-1-1.html
【4】通过重写ring3 API函数实现免杀 https://www.t00ls.net/thread-53717-1-1.html
【5】勒索软件中的API哈希-为什么以及如何做? https://blag.nullteilerfrei.de/2019/11/09/api-hashing-why-and-how/