[windows內核]調用門

Windows系統並沒有使用調用門,但是沒用並不等於沒有,所以我們這次主要是靠自己實現,來完成這次的探究
關於調用門的說明在手冊第三卷的5.83中
在這裏插入圖片描述
下面是調用門的段描述符格式
在這裏插入圖片描述
可以看到有些值是固定的,這是調用門特有的,我們來逐個看一下段描述符格式裏的各個意思

The segment selector field in a call gate specifies the code segment to be accessed. The offset field specifies the
entry point in the code segment. This entry point is generally to the first instruction of a specific procedure. The
DPL field indicates the privilege level of the call gate, which in turn is the privilege level required to access the selected procedure through the gate. The P flag indicates whether the call-gate descriptor is valid. (The presence
of the code segment to which the gate points is indicated by the P flag in the code segment’s descriptor.) The
parameter count field indicates the number of parameters to copy from the calling procedures stack to the new
stack if a stack switch occurs (see Section 5.8.5, “Stack Switching”). The parameter count specifies the number of
words for 16-bit call gates and doublewords for 32-bit call gates.
Note that the P flag in a gate descriptor is normally always set to 1. If it is set to 0, a not present (#NP) exception
is generated when a program attempts to access the descriptor. The operating system can use the P flag for special
purposes. For example, it could be used to track the number of times the gate is used. Here, the P flag is initially
set to 0 causing a trap to the not-present exception handler. The exception handler then increments a counter and
sets the P flag to 1, so that on returning from the handler, the gate descriptor will be valid.

在這裏插入圖片描述
段選擇子確定了將要訪問的代碼段
偏移量確定了在該代碼段中的入口點(該入口點通常指向特定例程的第一條指令)
DPL指明瞭該調用門的特權級,也就是通過該調用門訪問該例程所
必須具備的特權級
P標誌指明該調用門描述符是否有效
參數計數域的作用是,當發生了棧切換時,需要從調用進程的棧拷貝到新
棧的參數個數

調用門的大概執行流程

CALL CS:EIP//(EIP是廢棄的)

1.根據CS的值查GDT表找到對應的門描述符,這個描述符是一個調用門描述符
2.根據門調用門描述符裏的代碼段段選擇子查GDT表找到代碼段的段描述符
3.根據段選擇子指向的段描述符中的Base+調用門中的偏移量 就是真正要執行的地址
當然這裏沒有說權限檢查的問題,一是前面說過,二如果想看更詳細的權限問題可以去翻下手冊中第三卷5.8.4

下面我們來手動構造一個調用門(無參)
假設調用門的偏移是0x00401000 代碼段選擇子爲08(0環代碼段)

Offset in Segment 31:16   P   DPL  S  Type        Param Count
000000000100 00001110  1100  0000  0000
Segment Selector        Offset in Segment 15:000000 0000 0000 1000‬     ‭0001 0000 0000 0000

然後寫入GDT表
在這裏插入圖片描述

然後編譯以下代碼

#include "pch.h"
#include <iostream>
#include <windows.h>

BYTE GDT[6] = { 0 };
DWORD GDT_ADDR = 0;
DWORD dwValue = 0;
//0401000h
void __declspec(naked)GetRegister()
{
	__asm
	{

		//int 3;

		pushad;
		pushfd;

		mov eax, 0x80b95048;
		mov ebx, [eax];
		mov dwValue, ebx;
		
		sgdt GDT;//特權指令

		popfd;
		popad;

		retf  //不是ret
	}

}





int main()
{
	
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		call fword ptr[buff]
	}

	GDT_ADDR = *(PDWORD)(&GDT[2]);
	printf("GDT:%x  HighAddress:%x\n", GDT_ADDR,dwValue);
	system("pause");
	GetRegister();
	return 0;
}

運行,查看輸出

在這裏插入圖片描述
可以看到成功運行了調用門的代碼,讀取了高2g的內存,說明我們成功通過調用門提權,讀取高2g的的內存

下面我們再來做一下調用門有參的實驗,如果需要傳參那麼我們只需要修改Param Count字段即可

Offset in Segment 31:16   P   DPL  S  Type        Param Count
000000000100 00001110  1100  0000  0011
Segment Selector        Offset in Segment 15:000000 0000 0000 1000‬     ‭0001 0000 0000 0000

寫入GDT表
在這裏插入圖片描述
編譯如下代碼

#include "pch.h"
#include <iostream>
#include <windows.h>

DWORD A = 0;
DWORD B = 0;
DWORD C = 0;
//0401000h
void __declspec(naked)GetRegister()
{
	__asm
	{

		//int 3;

		pushad;
		pushfd;


		mov eax,[esp + 0x24 + 0x8 + 0x8];
		mov dword ptr ds : [A], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x4];
		mov dword ptr ds : [B], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x0];
		mov dword ptr ds : [C], eax;

		popfd;
		popad;

		retf 0xc //不是ret
	}

}





int main()
{
	
	char buff[6] = { 0 };
	*(DWORD*)&buff[0] = 0x12345678;
	*(WORD*)&buff[4] = 0x48;
	__asm
	{
		push 1;
		push 2;
		push 3;
		call fword ptr[buff]
	}

	printf("A:%x  B:%x  C:%x\n", A,B,C);
	system("pause");
	GetRegister();
	return 0;
}

運行
在這裏插入圖片描述
可以看到我們已經讀出了我們傳入的參數,證明了傳參是有效的,這裏需要我們手動壓棧和平衡棧,那麼我們在尋址參數時候爲什麼要這樣寫呢

		mov eax,[esp + 0x24 + 0x8 + 0x8];
		mov dword ptr ds : [A], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x4];
		mov dword ptr ds : [B], eax;
		mov eax,[esp + 0x24 + 0x8 + 0x0];
		mov dword ptr ds : [C], eax;

首先+0x24是因爲我們使用了pushad和pushfd
在這裏插入圖片描述
在這裏插入圖片描述
加起來壓入的一共是0x24位,再+0x8可以參考我們以前發過的圖

在這裏插入圖片描述
是代表返回的eip和cs,我們可以下斷看看
在這裏插入圖片描述
這一段就是代表返回的eip和cs,後面的就是我們的參數

總結:
當通過門,權限不變的時候,只會PUSH兩個值:CS 返回地址
新的CS的值由調用門決定

當通過門,權限改變的時候,會PUSH四個值:SS ESP CS 返回地址 新的CS的值由調用門決定 新的SS和ESP由TSS提供

通過門調用時,要執行哪行代碼有調用門決定,但使用RETF返回時,由堆棧中壓人的值決定,這就是說,進門時只能按指定路線走,出門時可以翻牆(只要改變堆棧裏面的值就可以想去哪去哪)

可不可以再建個門出去呢?也就是用Call 當然可以了 前門進 後門出

擴展:進門的時候只能走大門進去,但是出去的時候,RETF返回的依據就是堆棧中的值,ESP+0x24,如果在堆棧裏直接更改這個值爲新的函數,就會直接返回到別的地方

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章