有些人對作業系統關機很有興趣,我這邊就大概描述一下BIOS在關機的動作中做了什麼事情,以及如何利用軟體工具去撰寫一段關機的程式碼。
一般在NoteBook上面控制電源的部分都是EC BIOS所控制,而有時候我們會透過Send EC Cmd方式給EC BIOS來達到關機的目的(Power Off)。
像是某些BIOS中會使用到關機的地方就是在Setup Menu-->Exit-->Shutdown 選項,而這個地方的BIOS code 就可以利用EC Cmd或是PM Register來達到關機的目的。
另外在進入作業系統後我們也可以透過上面的方式來強制關閉系統,所使用的方式就是利用PM Control Register。
所以BIOS扮演的角色就是去設定PM Register IO Address Range & ACPI Table ,這樣說或許很多人搞不懂,簡單說就是BIOS提供一些資訊給OS,提供的方式是透過ACPI Table(放在記憶體中),OS自己會去記憶體中找到ACPI Table,然後得到BIOS所提供的資訊,其中這些資訊就包含PM Register的IO Address。
底下提供一個範例程式來做系統關機的動作,如果你是在DOS下可直接用Debug.com 執行
如果是在 Windows下請自行撰寫Ring 0 程式去對IO Access 或利用相關工具:
mov ax,3c00h <--請參考ICH 手冊上面的說明
mov dx,0404h <--PM Register I/O Address. (PMBase+04h)
out dx,ax
上面有提到PM Register IO Address 資訊是由BIOS所提供,因此不同的BIOS廠商所設定 PM Register 的IO Address range也會不同,所以如果想要強制關機就必須先找到這個位址,這樣才能對PM Register做Soft off (S5 satte)的動作。 一般比較常見的就是Port 0404h,所以如果你不知道BIOS工程師設定在哪個位址,那麼你可以利用ACPI Table查詢或是請教你們的BIOS工程師。
另外如果你自己在Windows下寫了一個Ring 0 層級的程式去做S5 state(Soft Off),你會發現系統會直接關機,也就是OS不會去通知Driver工作站要關機,而是直接(立即)由硬體那邊關閉系統。
Reference
ACPI Spec 3.0
ICH8 Spec
將自己踏入BIOS領域中所學習到的知識做一些心得整理,像是Legacy BIOS、EFI BIOS、Windows Driver...etc. ※版權與智慧財產權聲明:保留所有法律權利。我在寫文章時如果有引用到其他人的地方我會盡量說明參考出處,如果有遺漏的地方請告訴我,我會馬上註明! 而轉貼我的文章時也請您註明出處!
星期五, 7月 06, 2007
星期一, 7月 02, 2007
逆向工程-基礎知識
MASM 運算式 C++ 運算式
========================================
BYTE PTR [bx] *(unsigned char) ebx
WORD PTR [bp] *(unsigned short *) ebp
DWORD PTR [bp] *(unsigned long *) ebp
OFFSET Var &Var
;----------------------------
; 呼叫協定 test1(par1,par2,par3)
;----------------------------
__cdecl PASCAL __stdcall
push par3; push par1 push par3
push par2; push par2 push par2
push par1; push par3 push par1
call test1 call test1 call test1;//函數內回復堆疊
add esp,0ch //回復堆疊
由上面範例可以得知,_cdecl 與_stdcall的參數都是由右邊開始堆到堆疊去,也就是說par1會在堆疊頂端。
依照__stdcall 範例:
push par2
push par1
call test2;
{
push ebp //保留原來的ebp
mov ebp,esp //ebp指向目前的堆疊頂端
mov eax, [ebp+0c] //參數par2
mov ebx, [ebp+08] //參數par1
sub esp,8 //如果程式內有變數,則保留變數大小,例如int i,j
...
add esp,8 //釋放變數佔用的堆疊
pop ebp //復原ebp
ret 8 //相當於ret : add esp,8
}
;----------------------------------------------------
;堆疊示意圖 (堆疊位址是由高往下,也就是堆疊頂端位址是低位址)
;----------------------------------------------------
... <-- ESP 會一直指向堆疊的最頂端
程式內的變數j <-- EBP-8h
程式內的變數i <-- EBP-4h
保留的EBP <-- EBP + 0 (進入副程式後會mov ebp,esp)
返回位址(Offset) <-- EBP + 4h
Par1 <-- EBP + 8h
Par2 <-- EBP + 0Ch (由左至右堆入堆疊,所以第一個被堆進去)
...
========================================
BYTE PTR [bx] *(unsigned char) ebx
WORD PTR [bp] *(unsigned short *) ebp
DWORD PTR [bp] *(unsigned long *) ebp
OFFSET Var &Var
;----------------------------
; 呼叫協定 test1(par1,par2,par3)
;----------------------------
__cdecl PASCAL __stdcall
push par3; push par1 push par3
push par2; push par2 push par2
push par1; push par3 push par1
call test1 call test1 call test1;//函數內回復堆疊
add esp,0ch //回復堆疊
由上面範例可以得知,_cdecl 與_stdcall的參數都是由右邊開始堆到堆疊去,也就是說par1會在堆疊頂端。
依照__stdcall 範例:
push par2
push par1
call test2;
{
push ebp //保留原來的ebp
mov ebp,esp //ebp指向目前的堆疊頂端
mov eax, [ebp+0c] //參數par2
mov ebx, [ebp+08] //參數par1
sub esp,8 //如果程式內有變數,則保留變數大小,例如int i,j
...
add esp,8 //釋放變數佔用的堆疊
pop ebp //復原ebp
ret 8 //相當於ret : add esp,8
}
;----------------------------------------------------
;堆疊示意圖 (堆疊位址是由高往下,也就是堆疊頂端位址是低位址)
;----------------------------------------------------
... <-- ESP 會一直指向堆疊的最頂端
程式內的變數j <-- EBP-8h
程式內的變數i <-- EBP-4h
保留的EBP <-- EBP + 0 (進入副程式後會mov ebp,esp)
返回位址(Offset) <-- EBP + 4h
Par1 <-- EBP + 8h
Par2 <-- EBP + 0Ch (由左至右堆入堆疊,所以第一個被堆進去)
...
C語言複習~~~指標與函數
年紀大,東西都忘東忘西的,今天複習C Primer Plus 4/e,把一些東西記錄下來。
高度技巧的宣告
* 表示一個指標
() 表示一個函數
[] 表示一個陣列
※()與[]有相同的優先順序,他們的結合姓是由左到右 →
int a[8][8]; //int陣列的陣列
int **ptr; //指向int 指標的指標
int *risks[10]; //指向int 型態的10個指標陣列
int (*risks)[10]; //指向10個int 陣列的指標
int *oof[3][4]; //指向int型態的3x4指標陣列
int (*uuf)[3][4]; //指向3x4 int 陣列的指標
int (*uof[3])[4]; //指向4個元素的int 陣列的3個元素的指標陣列
char *fump(); //回傳指向字元的指標的函數
char (*fump)(); //指向會回傳字元型態的函數的指標
char (*fump[3])(); //3個指向會回傳字元型態的函數的指標陣列
typedef int arr5[5];
typedef arr5 *P_arr5;
typedef p_arr5 arrp10[10];
arr5 togs; //togs是有5個int元素的陣列
p_arr5 P2; //p2是指向有5個int陣列的指標
arrp10 ap; //ap是10個指向5個int陣列的指標陣列
函數宣告與指標
void ToUpper(char *); //將字串轉大寫
其中ToUpper()函數型態是void,參數是char *,如果宣告為這個函數宣告一個 pf指標,就會像下面一樣:
void (*pf)(char*); //指向函數的指標,例如: pf=ToUpper,但是不能寫成pf=ToUpper();
其中括號內的(*pf)會被括號結合,所以閱讀時他會是一個指標,所以他是一個指向函數的指標,如果省略就會不同意義:
void *pf(char *) ; //pf 是回傳指標的函數
※一般如果沒有註明時,定義的名稱本身就是位址起始,例如:
int a[10];
int *p;
p=a; 或是 p=&a[0];
void ToUpper(char *);
void (*pf)(char*);
pf=ToUpper;
應用範例:
void ToUpper(char *);
void (*pf)(char*);
void Show(void (*ff)(char*), char *str);
pf=ToUpper;
Show(ToUpper, mis); //Show函數使用ToUpper函數: ff=ToUpper
Show(pf, mis); //Show函數使用ToUpper函數: ff=pf
void Show(void (*ff)(char*), char *str)
{
(*ff)(str); //呼叫ToUpper函數,參數=str
puts(str); //顯示字串
}
C語言與組合語言對應
;-------------------------------------------
; VOID Funcation_A(VOID);
;-------------------------------------------
Funcation_A PROC PUBLIC
push eax
....
pop eax
ret
Funcation_A ENDP
;------------------------------------------------------------------------------
; VOID Funcation_B (IN UINT32 RegisterInEax, OUT OEM_REGISTER *Reg);
;
;typedef struct {
; UINT32 RegEax;
; UINT32 RegEbx;
; UINT32 RegEcx;
; UINT32 RegEdx;
;}OEM_REGISTER;
;------------------------------------------------------------------------------
Funcation_B PROC PUBLIC RegisterInEax:UINT32, Reg:PTR EFI_CPUID_REGISTER
pushad
...
mov edi, DWORD PTR Reg
...
mov DWORD PTR [edi].RegEax, eax ; Reg->RegEax
...
popad
ret
Funcation_B ENDP
;------------------------------------------------------------------------------
; UINT32 OemIoPortRead32 (IN UINT64 Address); //回傳值放EAX
;------------------------------------------------------------------------------
OemIoPortRead32 PROC PUBLIC Address:UINT64
push edx
mov edx, DWORD PTR Address
in eax, dx
pop edx
ret
OemIoPortRead32 ENDP
高度技巧的宣告
* 表示一個指標
() 表示一個函數
[] 表示一個陣列
※()與[]有相同的優先順序,他們的結合姓是由左到右 →
int a[8][8]; //int陣列的陣列
int **ptr; //指向int 指標的指標
int *risks[10]; //指向int 型態的10個指標陣列
int (*risks)[10]; //指向10個int 陣列的指標
int *oof[3][4]; //指向int型態的3x4指標陣列
int (*uuf)[3][4]; //指向3x4 int 陣列的指標
int (*uof[3])[4]; //指向4個元素的int 陣列的3個元素的指標陣列
char *fump(); //回傳指向字元的指標的函數
char (*fump)(); //指向會回傳字元型態的函數的指標
char (*fump[3])(); //3個指向會回傳字元型態的函數的指標陣列
typedef int arr5[5];
typedef arr5 *P_arr5;
typedef p_arr5 arrp10[10];
arr5 togs; //togs是有5個int元素的陣列
p_arr5 P2; //p2是指向有5個int陣列的指標
arrp10 ap; //ap是10個指向5個int陣列的指標陣列
函數宣告與指標
void ToUpper(char *); //將字串轉大寫
其中ToUpper()函數型態是void,參數是char *,如果宣告為這個函數宣告一個 pf指標,就會像下面一樣:
void (*pf)(char*); //指向函數的指標,例如: pf=ToUpper,但是不能寫成pf=ToUpper();
其中括號內的(*pf)會被括號結合,所以閱讀時他會是一個指標,所以他是一個指向函數的指標,如果省略就會不同意義:
void *pf(char *) ; //pf 是回傳指標的函數
※一般如果沒有註明時,定義的名稱本身就是位址起始,例如:
int a[10];
int *p;
p=a; 或是 p=&a[0];
void ToUpper(char *);
void (*pf)(char*);
pf=ToUpper;
應用範例:
void ToUpper(char *);
void (*pf)(char*);
void Show(void (*ff)(char*), char *str);
pf=ToUpper;
Show(ToUpper, mis); //Show函數使用ToUpper函數: ff=ToUpper
Show(pf, mis); //Show函數使用ToUpper函數: ff=pf
void Show(void (*ff)(char*), char *str)
{
(*ff)(str); //呼叫ToUpper函數,參數=str
puts(str); //顯示字串
}
C語言與組合語言對應
;-------------------------------------------
; VOID Funcation_A(VOID);
;-------------------------------------------
Funcation_A PROC PUBLIC
push eax
....
pop eax
ret
Funcation_A ENDP
;------------------------------------------------------------------------------
; VOID Funcation_B (IN UINT32 RegisterInEax, OUT OEM_REGISTER *Reg);
;
;typedef struct {
; UINT32 RegEax;
; UINT32 RegEbx;
; UINT32 RegEcx;
; UINT32 RegEdx;
;}OEM_REGISTER;
;------------------------------------------------------------------------------
Funcation_B PROC PUBLIC RegisterInEax:UINT32, Reg:PTR EFI_CPUID_REGISTER
pushad
...
mov edi, DWORD PTR Reg
...
mov DWORD PTR [edi].RegEax, eax ; Reg->RegEax
...
popad
ret
Funcation_B ENDP
;------------------------------------------------------------------------------
; UINT32 OemIoPortRead32 (IN UINT64 Address); //回傳值放EAX
;------------------------------------------------------------------------------
OemIoPortRead32 PROC PUBLIC Address:UINT64
push edx
mov edx, DWORD PTR Address
in eax, dx
pop edx
ret
OemIoPortRead32 ENDP
訂閱:
文章 (Atom)