IDC:函数
IDC:函數
-
IDC腳本中的函數必須要有返回值。在IDC腳本中,支持兩類函數:
-
內建函數
-
用戶自定義函數
-
用戶自定義函數一般是像下面這樣的方式寫的:
static func(arg1,arg2,arg3) {statements ... }需要注意的一點是,聲明函數參數的時候,沒必要指定函數參數的類型了,因為IDC會根據你傳入的參數自動進行參數類型轉換的。
-
默認情況下,函數調用的時候參數傳遞是按值傳遞的,但是以下三種情況例外(引用傳遞):
-
對象類型的參數總是使用引用方式傳參
-
函數類型的參數總是使用應用方式傳參
-
還可以強制使用 & 符號來讓參數使用引用方式傳參
-
如果IDC腳本中調用的函數不存在,IDA就會嘗試解析并使用當前被調試的進程中的符號或者標簽,如果解析成功,那么就會執行一次 AppCall(AppCall稍后會進一步解釋)
IDC:AppCall
-
首先需要明確一點,AppCall是IDC腳本中的一個內建函數
-
先來看函數原型
anyvalue Appcall(ea, type, ...); -
-
功能:調用被調試進程的函數
-
參數:ea - 調用的函數地址
-
參數:type - 調用的函數的類型或者說方式,支持三種調用形式
-
字符串形式,比如:“int func(void);”
-
類型對象形式,比如:GetTinfo(ea)
-
零:相當于讓IDA自行決定,這種情況下,類型一般是從idb中進行獲取的
-
-
參數:..., 這個是可變參數,用來傳遞你要調用的函數的參數
-
返回值:被調用函數的返回值
-
Remark:如果函數調用失敗,并且失敗的原因是內存訪問異常或者其他異常,腳本會拋出一個runtime錯誤信息,可以在腳本中使用 try/catch 進行異常捕獲。在實際的使用過程中,很少使用AppCall這個函數調用,只是說IDA擁有這種在IDC腳本中存在未知函數的時候嘗試在被調試進程中匹配符號的能力,舉個例子:_printf("hello\n") 這條語句會調用被分析程序的 _printf 函數
AppCall函數有2個選項可以使用,我們可以在IDC腳本中使用 SetAppcallOptions宏進行設置:
#define APPCALL_MANUAL 0x0001
-
只設置AppCall, 不執行,執行完畢之后,需要調用一次 CleanupAppcall
#define APPCALL_DEBEV 0x0002
-
返回調試詳細信息, 如果設置了這個標志位,當AppCall執行過程中發生異常的時候,會生成一個包含詳細異常信息的異常對象
#define APPCALL_TIMEOUT 0x0004
-
AppCall調用的超時時間, 超時時間是以毫秒為單位的,并且值是放在option的高2位字節中,如果AppCall調用超時,錯誤信息會放到 errbuf中,并且值是字符串 timeout
#define SET_APPCALL_TIMEOUT(x) ((x<<16)|0x0004)
-
指定AppCall超時時間的時候,需要拼裝option的值,這個就是一個輔助宏,用來生成option的值
使用AppCall這個功能,可以很隨意的調用被調試進程內的函數而不需要進行任何的dll注入或者修改被調試進程的內存,如果被調用的函數名稱存在的話,AppCall還可以簡化成 func(args)的形式,前提是func這個符號是存在的,舉個例子:
verinfo = object(); verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); GetVersionExA(&verinfo);上面這段代碼將會在被調試進程內創建一個OSVERSIONINFOA結構體,并且將結構體地址傳遞給GetVersionExA調用,調用完畢之后,verinfo 對象就被轉換成了IDC對象,內存結構如下:
object__at__: 18FEB0hdwBuildNumber: 7600.dwMajorVersion: 6.dwMinorVersion: 1.dwOSVersionInfoSize:dwPlatformId: 2.szCSDVersion: "\x00\x00\x00\x00..."其中_at_屬性表示這個結構體的內存地址,在這個例子中verinfo是一個臨時變量,所以_at_的值意義不大,大家在使用使用的過程中可能會遇到這個值很有用的情況。
AppCall會自動在IDC對象和C對象之間進行轉換,轉換的時候依據IDA中擁有的類型信息來進行,但是還要遵循以下幾個轉換規則:
基本數據類型:
-
如果目標數據類型也是一個基本數據類型(非指針),只需要執行簡單的轉換即可(附帶符號處理或者截斷處理),比如: IDC中的值-1轉換成 _int32(0xFFFFFFFF),IDC中的0x555轉換成 _int8(0x55)
指針:
-
如果目標類型是一個指針,并且IDC的值是一個字符串,這個字符串就轉換成一個指針對象,字符串的內容直接拷貝到被調試進程,后面追加一個結束符 \0
-
如果對應的IDC的值是一個數字,轉換之后的結果是指針所指向的內存的值是這個數值,如果你想得到一個數值的地址,可以直接使用 &符號
-
如果對應的IDC值不是字符串,那這個值將會轉換成一個對象,指針指向的內存使用這個對象進行初始化
結構體
-
如果目標類型是一個結構體,IDA會通過對應的屬性來嘗試一個一個的初始化結構體的成員。比如:在上面的例子中只有dwOSVersionInfoSize 屬性存在,那么這個結構體字段就使用這個屬性類初始化,對于不存在的字段直接初始化成0
數組
-
數組的每個元素都是單獨初始化的,當然如果對應的IDC值是字符串除外,因為字符串本身就是可以當成一個完整的數組來使用的。
下面針對上述的幾種情況,我們來舉一些例子:
調用printf
auto n = 5;auto s = "short";_printf("Hello world, number is %d, string is %s\n", n, s);調用sscanf
auto x; auto nsuccess = _sscanf(s, "%d", &x);結構體使用
verinfo = object();GetVersionExA(verinfo);另外,_userCall 這個內建調用也是支持這些自動轉換的
對于會發生異常的調用,配合APPCALL_MANUAL 標志位,可以實現單步的效果,具體方式還需要大家仔細去研究一下.
總結
- 上一篇: linux 基础常用命令集
- 下一篇: uboot配置和编译过程详解