Windows保护模式学习笔记(三)—— 长调用/短调用/调用门
Windows保護模式學習筆記(三)—— 長調用/短調用/調用門
- 要點回顧
- 長調用與短調用
- 一、短調用
- 二、長調用(跨段不提權)
- 三、長調用(跨段并提權)
- 長調用執行時:
- 執行返回(RETF)時:
- 總結
- 調用門
- 門描述符
- 調用門(無參)
- 實驗:構造一個無參調用門
- 第一步:初步構造參數
- 第二步:確定 Offset in Segment
- 第三步:將門描述符寫入GDT表
- 第四步:繼續執行第二步代碼
- 第五步:再次運行程序,觀察寄存器與堆棧的變化
- 第三步:修改代碼
- 調用門(有參)
- 實驗:構造一個帶參數的調用門
- 第一步:初步構造參數
- 第二步:執行代碼
- 總結
要點回顧
通過JMP FAR可以實現段間的跳轉,如果要實現跨段的調用就必須要學習CALL FAR,也就是長調用
CALL FAR 比 JMP FAR 要復雜,JMP并不影響堆棧,但CALL指令會影響
長調用與短調用
一、短調用
指令格式:
CALL 立即數 / 寄存器 / 內存
發生改變的寄存器:ESP EIP
二、長調用(跨段不提權)
指令格式:
CALL CS:EIP(EIP是廢棄的)
發生改變的寄存器:ESP EIP CS
三、長調用(跨段并提權)
指令格式:
CALL CS:EIP(EIP是廢棄的)
長調用執行時:
發生改變的寄存器:ESP EIP CS SS
注意:長調用執行后,堆棧已經不是原來的堆棧,而是0環的堆棧(ESP0)
執行返回(RETF)時:
發生改變的寄存器:ESP EIP CS SS
總結
跨段調用時,一旦有權限切換,就會切換堆棧
CS的權限一旦改變,SS的權限也要隨著改變,CS與SS的等級必須一樣
JMP FAR 只能跳轉到同級非一致代碼段,但CALL FAR可以通過調用門提權,提升CPL的權限
SS與ESP從哪里來?參見TSS段
調用門
指令格式:
CALL CS:EIP(EIP是廢棄的)
執行步驟:
門描述符
結構圖:
注意:
調用門(無參)
實驗:構造一個無參調用門
第一步:初步構造參數
Offset in Segment 31:16 = 0x0000 //暫定P = 1DPL = 二進制:11Param.Count = 二進制:00000Segment Selector = 0x0008 Offset in Segment 15:00 = 0x0000 //暫定由上述參數構造出的門描述符為:0000EC00`00080000
第二步:確定 Offset in Segment
在VC6中執行如下代碼并中斷
#include "stdafx.h" #include <windows.h>void __declspec(naked) GetRegister() {__asm{int 3retf // 注意返回,不能是ret} }int main(int argc, char* argv[]) {char buff[6];*(DWORD*)&buff[0] = 0x12345678; // 在這行設置斷點*(WORD*)&buff[4] = 0x48; // 段選擇子所在偏移__asm{call fword ptr[buff] // 長調用}getchar();return 0; }右鍵進入反匯編窗口,查看GetRegister函數起始地址,我這里是00401010
至此,門描述符的最終確定為:0040EC00`00081010
第三步:將門描述符寫入GDT表
第四步:繼續執行第二步代碼
運行后,虛擬機成功中斷至WinDbg
第五步:再次運行程序,觀察寄存器與堆棧的變化
長調用執行前:
長調用執行后:
寄存器:
堆棧:
結果和需求完全一致,調用門執行成功!
第三步:修改代碼
將代碼修改為如下:
#include <windows.h>BYTE GDT[6] = {0}; DWORD dwH2GValue;void __declspec(naked) GetRegister() {__asm{pushadpushfdmov eax,0x8003f00c // 讀取高2G內存mov ebx,[eax]mov dwH2GValue,ebxsgdt GDT; // 讀取GDTpopfdpopadretf} }void PrintRegister() {DWORD GDT_ADDR = *(PDWORD)(&GDT[2]);WORD GDT_LIMIT = *(PWORD)(&GDT[0]);printf("%x %x %x \n", dwH2GValue, GDT_ADDR, GDT_LIMIT); }int main(int argc, char* argv[]) {__asm{mov ebx,ebxmov ebx,ebx}char buff[6];*(DWORD*)&buff[0] = 0x12345678;*(WORD*)&buff[4] = 0x48; // segment select__asm{call fword ptr[buff]}PrintRegister();getchar();return 0; }注意:若GetRegister函數起始地址發生改變,則需要修調相應的門描述符
執行結果:
成功讀取了GDT,提權成功!
修正:sgdt命令實際上并不需要0環權限【大寫尷尬】
調用門(有參)
實驗:構造一個帶參數的調用門
第一步:初步構造參數
Offset in Segment 31:16 = 0x0000 // 暫定P = 1DPL = 二進制:11Param.Count = 二進制:00011 // 注意變化!Segment Selector = 0x0008 Offset in Segment 15:00 = 0x0000 // 暫定第二步:執行代碼
代碼如下:
#include <windows.h>DWORD x; DWORD y; DWORD z;void __declspec(naked) CateProc() {__asm{pushadpushfdmov eax,[esp+0x24+8+8]mov dword ptr ds:[x],eaxmov eax,[esp+0x24+8+4]mov dword ptr ds:[y],eaxmov eax,[esp+0x24+8+0]mov dword ptr ds:[z],eaxpopfdpopadretf 0xC // 注意堆棧平衡 寫錯藍屏} }void PrintRegister() {printf("%x %x %x \n", x, y, z); }int main(int argc, char* argv[]) {char buff[6];*(DWORD*)&buff[0] = 0x12345678;*(WORD*)&buff[4] = 0x48;__asm{push 1 // 參數1push 2 // 參數2push 3 // 參數3call fword ptr[buff]}PrintRegister();getchar();return 0; }注意:需要將門描述符的段偏移修改為CateProc函數的起始地址
執行結果:
傳入的參數被成功輸出,有參調用門構造成功!
思考:pushad、pushfd、popfd、popad 這幾條指令有什么意義?是必須的嗎?
總結
答:當然可以了,前"門"進,后"門"出
總結
以上是生活随笔為你收集整理的Windows保护模式学习笔记(三)—— 长调用/短调用/调用门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Windows保护模式学习笔记(二)——
- 下一篇: Windows保护模式学习笔记(四)——