2012-05-04 68 views
6

我有一個彎路問題。衆所周知,Detours只能在5個字節的空間中移動(即'jmp'調用和4個字節的地址)。正因爲如此,在一個類(方法)中不可能有'鉤子'函數,所以你不能提供'this'指針,因爲沒有足夠的空間(here's這個問題更加徹底的解釋)。所以我一整天都在爲解決方案進行頭腦風暴,現在我想要關於這個問題的想法,所以我不開始一個3-5天的項目,不知道它是否可行。我想讓'鉤子'函數成爲類方法,我希望整個方法都是面向對象的(沒有靜態函數或全局對象),最壞/最難的部分是完全動態。這是我的(理論上)解決方案;用程序集可以在運行時修改函數(一個完美的例子就是任何繞行方法)。所以,因爲我可以動態地修改函數,所以我不能動態創建它們嗎?例如;我分配內存,比如說~30個字節(通過malloc/new)。難道僅僅用對應於不同彙編操作符的二進制數替換所有字節(如0xE9是'jmp'),然後直接調用地址(因爲它包含一個函數)?C++和FULLY動態函數

注意:我事先知道返回值以及所有要繞行的函數的所有參數,而且由於我使用的是GCC,因此這種調用約定與_cdecl約定幾乎相同。

所以這是我的想法/即將實施;我創建了一個'Function'類。該構造函數接受可變參數(除了第一個參數,它描述目標函數的返回值)。

每個參數都是鉤子將接收的參數(大小,以及它是否是指針)的描述。假設我想爲int * RandomClass::IntCheckNum(short arg1);創建一個Function類。然後我只需要這樣做:Function func(Type(4, true), Type(4, true), Type(2, false));。其中「類型」定義爲Type(uint size, bool pointer)。然後通過彙編,我可以動態地創建函數(注意:這將全部使用_cdecl調用約定),因爲我可以計算參數的數量和總大小。

編輯:隨着例如,Type(4, true)是返回值(INT *),則scond Type(4, true)是RandomClass this指針和Type(2, false)描述的第一個參數(短ARG1)。

在這個實現中,我可以很容易地將類方法作爲回調函數,但是它需要大量的彙編代碼(我甚至沒有經驗過)。 最後,唯一的非動態的東西將是我的回調類中的方法(這也需要前後回調)。

所以我想知道;這可能嗎?它需要多少工作,我是否會在我的頭上?

編輯:對不起,如果我提出了一切有點模糊,但如果有什麼你想要更徹底地解釋,請問!編輯2:我也想知道,如果我能找到所有裝配操作符的十六進制值在某處?列表將有助於一噸!和/或如果可以以某種方式「保存」asm(「」);代碼在一個內存地址(我非常懷疑)。

+0

爲什麼要使用彎路呢?你不能使用純粹的C++解決方案,比如'std :: function',還是我錯過了一些東西? –

+0

不像我能幫你澄清事情。 你想要一個類中的可重寫函數嗎?(我的意思是你可以在運行時改變它們) 如果是這樣,我認爲它(完成時)可能會爲C++中的AI編程開啓巨大的機會。 +1 – akaltar

+0

@akaltar這被稱爲[遺傳編程](http://en.wikipedia.org/wiki/Genetic_programming),實際上並不需要可重寫的函數。 –

回答

4

你所描述的通常被稱爲「thunking」,並且通常被實現。從歷史上看,最常見的用途是在16位和32位代碼之間進行映射(通過自動生成一個新的32位函數來調用現有的16位或反之亦然)。我相信一些C++編譯器也會產生類似的函數來調整基類指針到多繼承中的子類指針。

它似乎是一個可行的解決方案,您的問題,我不會預見任何大問題。只要確保在內存中分配操作系統中所需的任何標誌以確保內存可執行(大多數現代操作系統默認情況下都會發出不可執行的內存)。

你會發現這個鏈接有用的,特別是如果在Win32中工作:http://www.codeproject.com/Articles/16785/Thunking-in-Win32-Simplifying-Callbacks-to-Non-sta

關於尋找組裝業務的十六進制值,我所知道的最好的參考是附錄的NASM彙編的說明書(和我不要只因爲我幫忙寫出來而這麼說)。這裏有一份副本:http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html

+0

哇!偉大的鏈接!閱讀thunking過程真的非常有趣(儘管Win32太糟糕了)。現在請原諒,如果我聽起來很愚蠢,但正如前面提到的,我對程序集沒有特別的經驗(我只知道一點AT&T語法),所以我不得不詢問你引用的NASM彙編程序。我有2個問題;所有的ASM操作員只使用1個字節嗎?其次,由於每個運營商都有許多不同的價值,我對哪一個感興趣?我想這取決於我的變量的大小;但對於'推'有13個不同的值,我怎麼知道我想要哪一個? –

+1

對於不同類型的推送指令(寄存器種類,即時值,間接存儲器引用),這些是不同的變體。指南的頂部有所有不同模式的描述,所以使用它來確定你想要的是哪一個,然後只需查看列表就可以找到你需要的指令格式。假設你想推EBX:這是一個reg32,所以你需要第二個變種,即「o32 50 + r」。 o32是一個操作數大小前綴,如果您使用的是32位代碼,則會被忽略; 50 + r是50十六進制加上寄存器的代碼(3,它們在頂部列出),所以53h是你的代碼。 – Jules

+1

在回答你的第一個問題時,不存在多於一個字節的指令,並且一些指令的大小取決於上下文而變化(參見上面的PUSH示例:'o32'前綴不會在32位中生成任何代碼模式,但是如果你生成的是16位代碼,它將是在指令開始時出現的額外的66h字節)。然而,所有最常見的指令都是單字節。 – Jules