三、系统调用
3.1.系统调用
OpenProcess和ReadProcessMemory从3环进0环的过程
-
kernel32.OpenProcess
-
KernelBase.OpenProcess
-
Ntdll.NtOpenProcess
-
7FFE0300(获取KiFastCallEntry)
-
sysenter
kernel32.OpenProcess(什么都不干,直接jmp到KernelBase) --> KernelBase.OpenProcess -->Ntdll.NtOpenProcess (导出的函数是ZwOpenProcess) -->7FFE0300(调用KiFastCallEntry) -->sysenter-->进入内核
kernel32.ReadProcessMemory --> KernelBase.ReadProcessMemory -->Ntdll.NTReadProcessMemory
-->7FFE0300 -->sysenter
ZwOpenProcess
所有进程在R3和R0共享同一个结构_KUSER_SHARED_DATA
-
R3地址为:7FFE0000
-
R0地址为:FFFE0000
//R3
kd> dt _KUSER_SHARED_DATA 7FFE0000
+0x300 SystemCall : 0x772b70b0
//R0
kd> dt _KUSER_SHARED_DATA FFDF0000
+0x300 SystemCall : 0x772b70b0
kd> u 0x772b70b0
ntdll!KiFastSystemCall:
772b70b0 8bd4 mov edx,esp
772b70b2 0f34 sysenter
ntdll!KiFastSystemCallRet:
772b70b4 c3 ret
772b70b5 8da42400000000 lea esp,[esp]
772b70bc 8d642400 lea esp,[esp]
ntdll!KiIntSystemCall:
772b70c0 8d542408 lea edx,[esp+8]
772b70c4 cd2e int 2Eh
772b70c6 c3 ret
KiFastSystemCall
API实现OpenProcess功能
main.cpp
#include <Windows.h>
#include <winternl.h>
typedef struct _CLIENT_ID
{
HANDLE UniqueProcess;
HANDLE UniqueThread;
}CLIENT_ID, *PCLIENT_ID;
//ZwOpenProcess函数原型
typedef NTSTATUS (WINAPI *ZwOpenProcessProc)
(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);
int main()
{
HMODULE hModule = LoadLibraryA("ntdll.dll");
PUCHAR temp = (PUCHAR)GetProcAddress(hModule, "ZwOpenProcess");
ULONG size = 0;
for (int i = 0; i < 100; i++)
{
if (temp[i] == 0xc2)
{
size = i+ 2; //ZwOpenProcess函数结尾
break;
}
}
ZwOpenProcessProc func =(ZwOpenProcessProc)VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(func, temp, size);
HANDLE outProcess = NULL;
CLIENT_ID client = {0};
client.UniqueProcess = (HANDLE)5776; //要打开的进程pid
OBJECT_ATTRIBUTES obattr = {0};
obattr.Length = sizeof(OBJECT_ATTRIBUTES);
NTSTATUS status = func(&outProcess, PROCESS_ALL_ACCESS, &obattr, &client);
return 0;
}
3.2.系统调用进内核
1.在 Ring3 的代码调用了 sysenter 指令之后,CPU 会做出如下的操作:
1. 将 SYSENTER_CS_MSR 的值装载到 cs 寄存器 174
2. 将 SYSENTER_EIP_MSR 的值装载到 eip 寄存器 176
3. 将 SYSENTER_CS_MSR 的值加 8(Ring0 的堆栈段描述符)装载到 ss 寄存器。
4. 将 SYSENTER_ESP_MSR 的值装载到 esp 寄存器 175
5. 将特权级切换到 Ring0
6. 如果 EFLAGS 寄存器的 VM 标志被置位,则清除该标志
7. 开始执行指定的 Ring0 代码
2.查看msr寄存器
kd> rdmsr 174
msr[174] = 00000000`00000008 //CS
kd> rdmsr 176
msr[176] = 00000000`83e7a760 //EIP
kd> u 83e7a760
nt!KiFastCallEntry:
83e7a760 b923000000 mov ecx,23h
83e7a765 6a30 push 30h
83e7a767 0fa1 pop fs
83e7a769 8ed9 mov ds,cx
83e7a76b 8ec1 mov es,cx
83e7a76d 648b0d40000000 mov ecx,dword ptr fs:[40h]
83e7a774 8b6104 mov esp,dword ptr [ecx+4]
83e7a777 6a23 push 23h
kd> rdmsr 175
msr[175] = 00000000`80790000 //ESP
//读写MSR的方式,只能在0环,MSR寄存器属于特权指令
//1.读 eax:存低32位 edx:存高32位
mov ecx,0x175
rdmsr
//2,写
mov eax,0x8402a9dc
mov edx,0
mov ecx,176h
wrmsr
3._KTRAP_FRAME
dt _KTRAP_FRAME:内核的上下文
ntdll!_KTRAP_FRAME
+0x000 DbgEbp : Uint4B
+0x004 DbgEip : Uint4B
+0x008 DbgArgMark : Uint4B
+0x00c DbgArgPointer : Uint4B
+0x010 TempSegCs : Uint2B
+0x012 Logging : UChar
+0x013 Reserved : UChar
+0x014 TempEsp : Uint4B
+0x018 Dr0 : Uint4B
+0x01c Dr1 : Uint4B
+0x020 Dr2 : Uint4B
+0x024 Dr3 : Uint4B
+0x028 Dr6 : Uint4B
+0x02c Dr7 : Uint4B
+0x030 SegGs : Uint4B
+0x034 SegEs : Uint4B
+0x038 SegDs : Uint4B
+0x03c Edx : Uint4B
+0x040 Ecx : Uint4B
+0x044 Eax : Uint4B
+0x048 PreviousPreviousMode : Uint4B
+0x04c ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : Uint4B
+0x054 Edi : Uint4B
+0x058 Esi : Uint4B
+0x05c Ebx : Uint4B
+0x060 Ebp : Uint4B
+0x064 ErrCode : Uint4B
+0x068 Eip : Uint4B
+0x06c SegCs : Uint4B
+0x070 EFlags : Uint4B
+0x074 HardwareEsp : Uint4B
+0x078 HardwareSegSs : Uint4B
+0x07c V86Es : Uint4B
+0x080 V86Ds : Uint4B
+0x084 V86Fs : Uint4B
+0x088 V86Gs : Uint4B
dt _KPCR 每个cpu逻辑核的状态
kd> dt _KPCR
ntdll!_KPCR
+0x000 NtTib : _NT_TIB
+0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
+0x004 Used_StackBase : Ptr32 Void
+0x008 Spare2 : Ptr32 Void
+0x00c TssCopy : Ptr32 Void
+0x010 ContextSwitches : Uint4B
+0x014 SetMemberCopy : Uint4B
+0x018 Used_Self : Ptr32 Void
+0x01c SelfPcr : Ptr32 _KPCR
+0x020 Prcb : Ptr32 _KPRCB
+0x024 Irql : UChar
+0x028 IRR : Uint4B
+0x02c IrrActive : Uint4B
+0x030 IDR : Uint4B
+0x034 KdVersionBlock : Ptr32 Void
+0x038 IDT : Ptr32 _KIDTENTRY
+0x03c GDT : Ptr32 _KGDTENTRY
+0x040 TSS : Ptr32 _KTSS //fs:40的位置为 TSS
+0x044 MajorVersion : Uint2B
+0x046 MinorVersion : Uint2B
+0x048 SetMember : Uint4B
+0x04c StallScaleFactor : Uint4B
+0x050 SpareUnused : UChar
+0x051 Number : UChar
+0x052 Spare0 : UChar
+0x053 SecondLevelCacheAssociativity : UChar
+0x054 VdmAlert : Uint4B
+0x058 KernelReserved : [14] Uint4B
+0x090 SecondLevelCacheSize : Uint4B
+0x094 HalReserved : [16] Uint4B
+0x0d4 InterruptMode : Uint4B
+0x0d8 Spare1 : UChar
+0x0dc KernelReserved2 : [17] Uint4B
+0x120 PrcbData : _KPRCB
5.KUSER_SHARED_DATA
kd> dt _KUSER_SHARED_DATA
ntdll!_KUSER_SHARED_DATA
+0x304 SystemCallReturn : Uint4B
6.KUSER_SHARED_DATA FFDF0000
kd> dt _KUSER_SHARED_DATA FFDF0000
ntdll!_KUSER_SHARED_DATA
+0x304 SystemCallReturn : 0x76de70b4
7.ntdll.dll --> ZwOpenProcess
8.ntdll.dll --> KiFastSystemCall
9.ntoskrnl.exe -->kiFastCallEntry
10.SSDT表
KeServiceDescriptorTable
-
ServiceTable:指向函数地址表,KeServiceDescriptorTable+服务号*4 = 函数地址
-
Count:系统服务表被调用的次数
-
ServiceLimit:函数数量
-
ArgmentTable:函数参数表,每个参数4个字节.获取字节数后处以4,得到函数参数个数
kd> .thread
Implicit thread is now 83f87380
kd> dt _KTHREAD 83f87380
+0x0bc ServiceTable : 0x83fbcb00 Void //线程的0xbc是ServiceTable
kd> dd 0x83fbcb00
83fbcb00 83ed052c 00000000 00000191 83ed0b74
83fbcb10 00000000 00000000 00000000 00000000
83fbcb20 842175b0 841804f2 83f2f567 00000000
83fbcb30 025224f9 000000bb 00000011 00000100
83fbcb40 83ed052c 00000000 00000191 83ed0b74
83fbcb50 a1496000 00000000 00000339 a149702c
83fbcb60 5385d2ba d717548f 00000000 00000000
83fbcb70 83fbcb6c 00000340 00000340 85ce1a38
kd> dd KeServiceDescriptorTable
83fbcb00 83ed052c 00000000 00000191 83ed0b74
83fbcb10 00000000 00000000 00000000 00000000
83fbcb20 842175b0 841804f2 83f2f567 00000000
83fbcb30 025224f9 000000bb 00000011 00000100
83fbcb40 83ed052c 00000000 00000191 83ed0b74
83fbcb50 a1496000 00000000 00000339 a149702c
83fbcb60 5385d2ba d717548f 00000000 00000000
83fbcb70 83fbcb6c 00000340 00000340 85ce1a38
kd> dd 83ed052c +0xbe*4 //通过服务号,获取到当前函数地址
83ed0824 84065cb1 840b852f 840a5ffe 83fd1118
83ed0834 840bdbab 840390ae 840daf91 840a1fad
83ed0844 840b22ae 840cc83a 840a6115 8415ecc3
83ed0854 841474ed 84148785 840374e8 84094d6b
83ed0864 8414709e 84146dbe 84147156 84146e76
83ed0874 8404a98f 84019e7c 84034a34 84148ee0
83ed0884 84148fa6 840967ff 840e7bbe 840abef8
83ed0894 841598e2 84159d25 83f172b6 840cb0e2
kd> u 84065cb1
nt!NtOpenProcess:
84065cb1 8bff mov edi,edi
84065cb3 55 push ebp
84065cb4 8bec mov ebp,esp
84065cb6 51 push ecx
84065cb7 51 push ecx
84065cb8 64a124010000 mov eax,dword ptr fs:[00000124h]
84065cbe 8a803a010000 mov al,byte ptr [eax+13Ah]
84065cc4 8b4d14 mov ecx,dword ptr [ebp+14h]
//根据服务号,算出参数的字节数为10,转换为10进制是16,然后16/4=4,得出参数个数为4
kd> db 83ed0b74+be
83ed0c32 10 0c 10 14 0c 0c 0c 0c-10 10 14 0c 14 18 0c 14 ................
83ed0c42 08 08 08 08 0c 14 18 10-0c 14 08 08 08 08 08 08 ................
83ed0c52 04 2c 1c 08 24 14 08 14-14 14 14 14 14 14 14 14 .,..$...........
83ed0c62 14 14 14 04 08 14 14 14-18 14 14 08 10 08 00 24 ...............$
83ed0c72 14 18 14 14 0c 10 14 10-18 04 14 0c 18 18 14 14 ................
83ed0c82 18 0c 18 24 24 08 18 14-08 04 04 14 04 10 08 0c ...$$...........
83ed0c92 04 14 18 08 08 08 0c 0c-08 10 14 08 08 0c 08 0c ................
83ed0ca2 0c 04 08 08 08 08 08 08-0c 0c 24 00 08 08 08 0c ..........$.....
3.3.系统调用返回R3
在 Ring0 代码执行完毕,调用 SYSEXIT 指令退回 Ring3 时,CPU 会做出如下操作
1. 将 SYSENTER_CS_MSR 的值加 16(Ring3 的代码段描述符)装载到 cs 寄存器
2. 将寄存器 edx 的值装载到 eip 寄存器
3. 将 SYSENTER_CS_MSR 的值加 24(Ring3 的堆栈段描述符)装载到 ss 寄存器
4. 将寄存器 ecx 的值装载到 esp 寄存器
5. 将特权级切换到 Ring3
6. 继续执行 Ring3 的代码
3.4.SSDThook
tools.h
#pragma once
#include <ntifs.h>
PWCH GetSystemRootNtdllPath();
PUCHAR MapOfViewFile(PWCH path);
VOID UmMapOfViewFile(PVOID mapBase);
ULONG64 ExportTableFuncByName(char* pData, char* funcName);
tools.c
#include "Tools.h"
#include <ntstrsafe.h>
#include <ntimage.h>
EXTERN_C NTSTATUS MmCreateSection(
__deref_out PVOID* SectionObject,
__in ACCESS_MASK DesiredAccess,
__in_opt POBJECT_ATTRIBUTES ObjectAttributes,
__in PLARGE_INTEGER InputMaximumSize,
__in ULONG SectionPageProtection,
__in ULONG AllocationAttributes,
__in_opt HANDLE FileHandle,
__in_opt PFILE_OBJECT FileObject
);
PWCH GetSystemRootNtdllPath()
{
//获取根目录 256
PWCH systemPath = ExAllocatePool(PagedPool, PAGE_SIZE);
memset(systemPath, 0, PAGE_SIZE);
RtlStringCbPrintfW(systemPath, PAGE_SIZE, L"\\??\\%s\\system32\\ntdll.dll", SharedUserData->NtSystemRoot);
DbgPrintEx(77, 0, "[db]:%S\r\n", systemPath);
//ExFreePool(systemPath);
return systemPath;
}
PUCHAR MapOfViewFile(PWCH path)
{
UNICODE_STRING fileName = { 0 };
RtlInitUnicodeString(&fileName, path);
OBJECT_ATTRIBUTES ObjectAttributes;
InitializeObjectAttributes(&ObjectAttributes, &fileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
IO_STATUS_BLOCK IoStatusBlock = { 0 };
HANDLE hFile = NULL;
NTSTATUS status = ZwCreateFile(&hFile, GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL,
FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, NULL);
if (!NT_SUCCESS(status))
{
return 0;
}
//创建节区
PVOID hSection = NULL;
OBJECT_ATTRIBUTES sectionAddr;
InitializeObjectAttributes(§ionAddr, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
LARGE_INTEGER InputMaximumSize = { 0 };
status = MmCreateSection(&hSection, SECTION_ALL_ACCESS, §ionAddr,
&InputMaximumSize, PAGE_EXECUTE_READWRITE, 0x1000000, hFile, NULL);
if (!NT_SUCCESS(status))
{
ZwClose(hFile);
return 0;
}
PVOID mapBase = NULL;
SIZE_T viewSize = { 0 };
status = MmMapViewInSystemSpace(hSection, &mapBase, &viewSize);
ObDereferenceObject(hSection);
ZwClose(hFile);
if (NT_SUCCESS(status))
{
return mapBase;
}
return 0;
}
VOID UmMapOfViewFile(PVOID mapBase)
{
if (!mapBase) return;
MmUnmapViewInSystemSpace(mapBase);
}
//获取到 LoadLibraryExW
ULONG64 ExportTableFuncByName(char* pData, char* funcName)
{
PIMAGE_DOS_HEADER pHead = (PIMAGE_DOS_HEADER)pData;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pData + pHead->e_lfanew);
int numberRvaAndSize = pNt->OptionalHeader.NumberOfRvaAndSizes;
PIMAGE_DATA_DIRECTORY pDir = (PIMAGE_DATA_DIRECTORY)&pNt->OptionalHeader.DataDirectory[0];
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pData + pDir->VirtualAddress);
ULONG64 funcAddr = 0;
for (int i = 0; i < pExport->NumberOfNames; i++)
{
int* funcAddress = pData + pExport->AddressOfFunctions;
int* names = pData + pExport->AddressOfNames;
short* fh = pData + pExport->AddressOfNameOrdinals;
int index = -1;
char* name = pData + names[i];
if (strcmp(name, funcName) == 0)
{
index = fh[i];
}
if (index != -1)
{
funcAddr = pData + funcAddress[index];
break;
}
}
if (!funcAddr)
{
KdPrint(("没有找到函数%s\r\n", funcName));
}
else
{
KdPrint(("找到函数%s addr %p\r\n", funcName, funcAddr));
}
return funcAddr;
}
ssdt.h
#pragma once
#include <ntifs.h>
typedef struct _SsdtItem
{
PULONG funcTable;
ULONG_PTR totalCount;
ULONG_PTR funcMaxNumber;
PUCHAR paramTable;
}SsdtItem;
typedef struct _SsdtTable
{
SsdtItem ssdt;
SsdtItem sssdt;
}SsdtTable, * PSsdtTable;
BOOLEAN SsdtInit();
VOID SsdtDestory();
ULONG SsdtGetFunctionIndex(char* funName);
ULONG_PTR SsdtSetHook(char* funName, ULONG_PTR newFunction);
ssdt.c
#include "ssdt.h"
#include "tools.h"
#include <intrin.h>
PUCHAR gMapNtdll = NULL;
//导出服务表
extern PSsdtTable KeServiceDescriptorTable;
//关闭写保护
ULONG wpOff()
{
ULONG cr0 = __readcr0();
_disable(); //关闭中断
__writecr0(cr0 & (~0x10000)); //关闭写保护
return cr0;
}
VOID wpOn(ULONG value)
{
__writecr0(value);
_enable();
}
BOOLEAN SsdtInit()
{
if (gMapNtdll) return TRUE;
PWCH path = GetSystemRootNtdllPath();
gMapNtdll = MapOfViewFile(path);
ExFreePool(path);
return TRUE;
}
VOID SsdtDestory()
{
if (!gMapNtdll) return;
UmMapOfViewFile(gMapNtdll);
gMapNtdll = NULL;
}
PSsdtTable SsdtGet()
{
return (PSsdtTable)((PUCHAR)KeServiceDescriptorTable + 0x40);
}
ULONG SsdtGetFunctionIndex(char* funName)
{
PUCHAR func = (PUCHAR)ExportTableFuncByName(gMapNtdll, funName);
if (!func) return -1;
return *(PULONG)(func + 1);
}
ULONG_PTR SsdtSetHook(char* funName, ULONG_PTR newFunction)
{
PSsdtTable ssdtTable = SsdtGet();
ULONG index = SsdtGetFunctionIndex(funName);
if (index == -1) return 0;
ULONG function = ssdtTable->ssdt.funcTable[index];
ULONG _cr0 = wpOff();
ssdtTable->ssdt.funcTable[index] = newFunction;
wpOn(_cr0);
return function;
}
main.c
#include <ntifs.h>
#include <ntstrsafe.h>
#include "tools.h"
#include "ssdt.h"
ULONG_PTR goldFunc = 0;
typedef NTSTATUS(NTAPI* OpenProcessProc)(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
);
NTSTATUS NTAPI MyOpenProcess(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
)
{
DbgPrintEx(77, 0, "[db]:%s\r\n", __FUNCTION__);
OpenProcessProc func = (OpenProcessProc)goldFunc;
return func(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}
VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
SsdtSetHook("ZwOpenProcess", goldFunc);
SsdtDestory();
//延时
LARGE_INTEGER inTime = { 0 };
inTime.QuadPart = -10000 * 3000; //3s
KeDelayExecutionThread(KernelMode, FALSE, &inTime);
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
SsdtInit();
goldFunc = SsdtSetHook("ZwOpenProcess", MyOpenProcess);
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}