目录
一、前言
windows和linux这样的现代化操作系统,都是是运行在保护模式下,在保护模式下CPU分为了特权级权限和用户级权限,操作系统根据CPU提供的不同权限将系统分为了内核态和用户态,用户态是不允许直接访问内核态的内存的,那就说明了用户态就不能直接调用内核态提供的函数的,那就引出了本片文章的主题用户态如何调用内核态的函数。
由于系统调用流程比较复杂,涉及内容比较多暂时打算将该内容分为三篇给大家分享,当前篇主要分享用户态进入内核态前的工作。
二、环境
调试机器:windows10
调试工具:windbg、x32dbg
被调试机器:windows 7 sp1 x86
三、词汇说明
由于网上很多使用的是非标准名称,而是使用了一些行业中的名词有些新手恐怕不太明白,我将该文章中使用的行业名词进行说明,方便大家理解。
3环:在windows中泛指用户态。
0环:在windows中泛指内核态。
领空:当前代码执行在什么库中,比如调用ntdll.dll中的函数,并且正在该函数中执行,那么当前的领空就是ntdll.dll。该名称一般用于在动态分析中。
四、调用流程
4.1 调用系统代码
编写一个需要调用0环代码,当前我使用的是OpenProcess来进行跟踪3环的执行流程,大家可以选择其他的函数只要这个函数最终要调用0环都可以。
#include <Windows.h>
int main()
{
__asm int 3;
OpenProcess(0, 0, 0);
}
4.2 调用过程
4.2.1 kernel32.dll
调用OpenProcess最先进入的Kernel32.dll的代码领空,并且在该函数中什么都没有做就跳转了一下。
4.2.2 kernelbase.dll
当前领空为kernelbase.dll,说明当前执行到了kernelbase.dll中的OpenProces函数,在这个函数的关键点是绿色箭头指向的Call,调用了一个名字为NtOpenProcess的函数。使用蓝色框起来的那些指令的作用就是构建调用NtOpenProcess函数的参数,不是该文章的主线就不进行分析了。
4.2.3 ntdll.dll
当前领空为ntdll.dll,说明了kernelbase.dll调用的NtOpenProcess函数是ntdll.dll中的函数,这个函数就需要分析一下了。
- mov eax,BE 将一个立即数存入到eax中。
- mov edx,7ffe0300 将一个立即数存入到edx中。注意调试将7ffe0300翻译成了KiFastSystemCall.
- mov dword ptr ds:[edx] 使用call调用了一下这个立即数。
当前领空为ntdll.dll,说明在NtOpenProcess函数中的call又调用了ntdll中的KiFastSystemCall.
调用号
这里又引入了一个概念 调用号 ,其实在上面代码中eax存入是0环NtOpenProcess的调用号,当前大家就先知道它是调用号就可以了,下一篇文章将会说明它的作用。
_KUSER_SHARED_DATA结构
//0x5f0 bytes (sizeof)
struct _KUSER_SHARED_DATA
{
ULONG TickCountLowDeprecated; //0x0
ULONG TickCountMultiplier; //0x4
volatile struct _KSYSTEM_TIME InterruptTime; //0x8
volatile struct _KSYSTEM_TIME SystemTime; //0x14
volatile struct _KSYSTEM_TIME TimeZoneBias; //0x20
USHORT ImageNumberLow; //0x2c
USHORT ImageNumberHigh; //0x2e
WCHAR NtSystemRoot[260]; //0x30
ULONG MaxStackTraceDepth; //0x238
ULONG CryptoExponent; //0x23c
ULONG TimeZoneId; //0x240
ULONG LargePageMinimum; //0x244
ULONG Reserved2[7]; //0x248
enum _NT_PRODUCT_TYPE NtProductType; //0x264
UCHAR ProductTypeIsValid; //0x268
ULONG NtMajorVersion; //0x26c
ULONG NtMinorVersion; //0x270
UCHAR ProcessorFeatures[64]; //0x274
ULONG Reserved1; //0x2b4
ULONG Reserved3; //0x2b8
volatile ULONG TimeSlip; //0x2bc
enum _ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture; //0x2c0
ULONG AltArchitecturePad[1]; //0x2c4
union _LARGE_INTEGER SystemExpirationDate; //0x2c8
ULONG SuiteMask; //0x2d0
UCHAR KdDebuggerEnabled; //0x2d4
UCHAR NXSupportPolicy; //0x2d5
volatile ULONG ActiveConsoleId; //0x2d8
volatile ULONG DismountCount; //0x2dc
ULONG ComPlusPackage; //0x2e0
ULONG LastSystemRITEventTickCount; //0x2e4
ULONG NumberOfPhysicalPages; //0x2e8
UCHAR SafeBootMode; //0x2ec
union
{
UCHAR TscQpcData; //0x2ed
struct
{
UCHAR TscQpcEnabled:1; //0x2ed
UCHAR TscQpcSpareFlag:1; //0x2ed
UCHAR TscQpcShift:6; //0x2ed
};
};
UCHAR TscQpcPad[2]; //0x2ee
union
{
ULONG SharedDataFlags; //0x2f0
struct
{
ULONG DbgErrorPortPresent:1; //0x2f0
ULONG DbgElevationEnabled:1; //0x2f0
ULONG DbgVirtEnabled:1; //0x2f0
ULONG DbgInstallerDetectEnabled:1; //0x2f0
ULONG DbgSystemDllRelocated:1; //0x2f0
ULONG DbgDynProcessorEnabled:1; //0x2f0
ULONG DbgSEHValidationEnabled:1; //0x2f0
ULONG SpareBits:25; //0x2f0
};
};
ULONG DataFlagsPad[1]; //0x2f4
ULONGLONG TestRetInstruction; //0x2f8
ULONG SystemCall; //0x300
ULONG SystemCallReturn; //0x304
ULONGLONG SystemCallPad[3]; //0x308
union
{
volatile struct _KSYSTEM_TIME TickCount; //0x320
volatile ULONGLONG TickCountQuad; //0x320
ULONG ReservedTickCountOverlay[3]; //0x320
};
ULONG TickCountPad[1]; //0x32c
ULONG Cookie; //0x330
ULONG CookiePad[1]; //0x334
LONGLONG ConsoleSessionForegroundProcessId; //0x338
ULONG Wow64SharedInformation[16]; //0x340
USHORT UserModeGlobalLogger[16]; //0x380
ULONG ImageFileExecutionOptions; //0x3a0
ULONG LangGenerationCount; //0x3a4
ULONGLONG Reserved5; //0x3a8
volatile ULONGLONG InterruptTimeBias; //0x3b0
volatile ULONGLONG TscQpcBias; //0x3b8
volatile ULONG ActiveProcessorCount; //0x3c0
volatile USHORT ActiveGroupCount; //0x3c4
USHORT Reserved4; //0x3c6
volatile ULONG AitSamplingValue; //0x3c8
volatile ULONG AppCompatFlag; //0x3cc
ULONGLONG SystemDllNativeRelocation; //0x3d0
ULONG SystemDllWowRelocation; //0x3d8
ULONG XStatePad[1]; //0x3dc
struct _XSTATE_CONFIGURATION XState; //0x3e0
};
在上述代码中将一个立即数存入到了edx中,其实是将_KUSER_SHARED_DATA结构的SystemCall存入到了edx中,该结构是0环和3环共用的,0环有修改权限而3环只有只读权限,而且这个结构是地址是固定3环在7ffe0000这个位置。
有的朋友会好奇为什么需要将函数地址存储到一个结构中而不直接写到代码中呢?这里就引出一个一个兼容的问题当前分析的流程是使用sysenter进行进入0环的,但是存在一些CPU是不支持这种快速调用就使用了中断的方式进入的0环,在windodws启动的时候会去检查当前CPU是否支持sysenter这种指令来确定该结构中的值。
sysenter指令说明
该指针负责将3环权限提示为0环权限,我们需要知道提权需要执行以下操作:
- 将cs修改为0环权限
- 将ss修改为0环权限
sysenter执行流程:
- 将msr寄存器的174位存入到cs中
- 将msr寄存器的176位存入到eip中
- 将cs寄存器的GDT表项加8的位置存入到ss中
- 将msr寄存器的175位存入到esp中
- 清除eflags寄存器的IF位
五、总结
5.1 调用流程
3环的调用流程相对比较简单,就是构建了以下0环函数需要使用的参数,然后就进入了0环了。
5.2 环境
最后需要记录以下三环进入0环的环境状态,我将比较重要的环境进行列出方便进入0环后的分析。
5.2.1 寄存器
- eax:当前存入的是BE
- edx:当前存入的是三环的ESP