轉載自: http://stenlyho.blogspot.com/2007/04/c_04.html
文章中有詳細介紹#define的使用方式,還有範例可以查看。
此作者轉載的連結已經失效,所以我只能再轉載第二手資料:
原轉載:http://miaozl.spaces.live.com/?_c11_blogpart_blogpart=blogview&_c=blogpart&_c02_owner=1&partqs=amonth%3d2%26ayear%3d2007
而這篇文章中說的"宏"是指大陸用語,應該就是我們說的Macro(巨集)
所以在看這篇文章的時候,還是要先了解一下兩岸用詞的不同啦!
將自己踏入BIOS領域中所學習到的知識做一些心得整理,像是Legacy BIOS、EFI BIOS、Windows Driver...etc. ※版權與智慧財產權聲明:保留所有法律權利。我在寫文章時如果有引用到其他人的地方我會盡量說明參考出處,如果有遺漏的地方請告訴我,我會馬上註明! 而轉貼我的文章時也請您註明出處!
星期二, 7月 12, 2011
星期四, 6月 16, 2011
DIY-DEBUG.EXE
一直都覺得Debugger.exe 很好玩,不過都沒時間自己手動寫一個。
剛好趁最近有閒暇時間,就寫了一個搞笑版的Debug.exe 來測試它的原理。
我把我的主程式分成上圖中左半邊的那幾個區塊,一開始的時候先去把INT1/3中斷向量串走,讓他能夠被我控制,接著就去觸發一個INT 3h,我在自己的NewInt3hHandler內去把TF Flag設定起來,那麼離開INT 3h的時候,CPU自己每執行一行指令就會觸發一次INT 1h。
接著,我在自己的NewInt1hHanlder內去處理我的Command,從可以讓我控制我要的動作,而整個執行的畫面就會像是上圖中的右半邊一樣。
不過,寫到這邊時才發現原來反組譯才是最難的啦~
看起來接下來的功課就是去研究反組譯要怎麼做了 >.<
剛好趁最近有閒暇時間,就寫了一個搞笑版的Debug.exe 來測試它的原理。
我把我的主程式分成上圖中左半邊的那幾個區塊,一開始的時候先去把INT1/3中斷向量串走,讓他能夠被我控制,接著就去觸發一個INT 3h,我在自己的NewInt3hHandler內去把TF Flag設定起來,那麼離開INT 3h的時候,CPU自己每執行一行指令就會觸發一次INT 1h。
接著,我在自己的NewInt1hHanlder內去處理我的Command,從可以讓我控制我要的動作,而整個執行的畫面就會像是上圖中的右半邊一樣。
不過,寫到這邊時才發現原來反組譯才是最難的啦~
看起來接下來的功課就是去研究反組譯要怎麼做了 >.<
星期三, 6月 15, 2011
C語言Printf 自動補0
在一般的C語言中顯示HEX時總是會希望能夠補0,像是HEX: 0x10 顯示的時候就希望是0010
那要如何做呢?
其實很簡單,你只要控制printf 內的顯示控制就可以了。
例如:
printf ("%4X",MyHex); <--4個字元,但不會補0
printf ("%04X",MyHex); <--會補0
從下圖中的黑色部分中可以看到AX的值會有兩種結果~
那要如何做呢?
其實很簡單,你只要控制printf 內的顯示控制就可以了。
例如:
printf ("%4X",MyHex); <--4個字元,但不會補0
printf ("%04X",MyHex); <--會補0
從下圖中的黑色部分中可以看到AX的值會有兩種結果~
星期日, 2月 28, 2010
C語言複習3
~複習一下一些指標的概念~
問題一: 底下的這個做法的概念
int a;
*&a=30;
Ans:
int a; 可以看成 _A WORD ?
&a 可以看成 LEA BX,_A
* 可以看成 MOV 東西到某個記憶體,所以*&a=30 等同於MOV [BX],30
其實他是等同於a=30,但是這樣子寫會跑更慢!code更大!
問題二:
*200 = 30;
Ans:
這樣子寫是非法的,因為雖然a 在記憶體200的位址,但是你不知道a是多大
所以你要告訴Compiler要一次搬兩個byte. 所以(int *) 等同於word ptr.
*(int *)200=30;
用組語來看會類似 mov word ptr [200],30
其中word ptr是假指令,用來一次搬兩個BYTE
[200] 代表記憶體位址200
問題一: 底下的這個做法的概念
int a;
*&a=30;
Ans:
int a; 可以看成 _A WORD ?
&a 可以看成 LEA BX,_A
* 可以看成 MOV 東西到某個記憶體,所以*&a=30 等同於MOV [BX],30
其實他是等同於a=30,但是這樣子寫會跑更慢!code更大!
問題二:
*200 = 30;
Ans:
這樣子寫是非法的,因為雖然a 在記憶體200的位址,但是你不知道a是多大
所以你要告訴Compiler要一次搬兩個byte. 所以(int *) 等同於word ptr.
*(int *)200=30;
用組語來看會類似 mov word ptr [200],30
其中word ptr是假指令,用來一次搬兩個BYTE
[200] 代表記憶體位址200
星期一, 2月 22, 2010
C語言複習2
1. &&與& 容易混淆
&& 是邏輯運算
& 是AND運算
EX: 當 x=1,y=2
if(x&&y) 這邊會是1&&2=TRUE
if(x&y) 這邊會是1&2 = 0 = FALSE
2.條件式判斷
Expression1?Expression2:Expression3
EX: 當x>3 成立時,會執行x=1
XXX= x > 3?1:0
他等同於:
if(x>3)
XXX=1
else
XXX=0
3.逗號語法
Expression1,Expression2,Expression3,....ExpressionN
if( x+1,y+2,z<=3)
k=1
else
k=0
他會依序執行將 X+1,Y+2,直到遇到判斷式Z<=3時才會跳出if()
簡單說就是當Z<=3成立時(TRUE),會得到K=1,雖然沒有人會這樣子寫,但是可以這樣子用!
一般比較常看到的寫法會是:
while(ch=getch(),ch=='a')
{
...
}
4.陣列
array[i]等同於*(array+i)
&& 是邏輯運算
& 是AND運算
EX: 當 x=1,y=2
if(x&&y) 這邊會是1&&2=TRUE
if(x&y) 這邊會是1&2 = 0 = FALSE
2.條件式判斷
Expression1?Expression2:Expression3
EX: 當x>3 成立時,會執行x=1
XXX= x > 3?1:0
他等同於:
if(x>3)
XXX=1
else
XXX=0
3.逗號語法
Expression1,Expression2,Expression3,....ExpressionN
if( x+1,y+2,z<=3)
k=1
else
k=0
他會依序執行將 X+1,Y+2,直到遇到判斷式Z<=3時才會跳出if()
簡單說就是當Z<=3成立時(TRUE),會得到K=1,雖然沒有人會這樣子寫,但是可以這樣子用!
一般比較常看到的寫法會是:
while(ch=getch(),ch=='a')
{
...
}
4.陣列
array[i]等同於*(array+i)
C語言複習
1.變數有效區(Scope)
EX1:
{
int i; //只在括號內有效
}
EX2:
static int i; //只在本文件內有效
EX3:
extern int i; //不管幾個文件中都只有一個實體i;
//他等同於你在File Scope(你在寫#include的那個區塊)中宣告int i;
//只是為了可讀性所以加入extern 這個關鍵字,不加也是可以!
2.for/while/do..while/break/continue
while(Expression)
Statement

for(Expression1;Expression2;Expression3)
Statement

do{
Statement
}while(Expression)
EX1:
{
int i; //只在括號內有效
}
EX2:
static int i; //只在本文件內有效
EX3:
extern int i; //不管幾個文件中都只有一個實體i;
//他等同於你在File Scope(你在寫#include的那個區塊)中宣告int i;
//只是為了可讀性所以加入extern 這個關鍵字,不加也是可以!
2.for/while/do..while/break/continue
while(Expression)
Statement

for(Expression1;Expression2;Expression3)
Statement

do{
Statement
}while(Expression)

星期二, 4月 21, 2009
C/C++ Compiler Intrinsics
Compiler Intrinsics 的相關連結,因為最近寫C語言時常在使用所以收集此連結!
EX:
__debugbreak();
__enable();
__disable();
http://msdn.microsoft.com/zh-tw/library/26td21ds.aspx
EX:
__debugbreak();
__enable();
__disable();
http://msdn.microsoft.com/zh-tw/library/26td21ds.aspx
星期一, 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)