[C++] 優雅玩耍函數指標呼叫,把你同事玩弄得嫑嫑的。(離職前記得回顧這篇文)

前言

最近正好寫一些玩具想模組化,以前在處理 Function Pointer 都是強轉型 + typedef 然後 ^C^V 瘋狂複製貼上函數型別來做到 C/C++ 內對指標函數呼叫。最近正好摸到一些 C++11 有支援到的正規轉型別方式,發現乾...XD 不用在手殘複製貼上啦,原來 C++11 一堆關鍵字已經可以讓你快速定義型別 + 把函數模組化放到自己的樣板內了超ㄎㄧㄤ

btw 這篇只是筆記文,最一開始我只是想查 C/C++ 內到底能不能編譯時期就取得函數的型別XDDDD(結果是:幹,居然真的可以咧XDDDDD)

#murmur
如果看完這篇文還是不懂怎麼做,那就算惹,我也沒很認真想解釋細節,不要太認真XD(只是覺得分頁欄有點滿,寫到部落格來不要佔 Chrome 的分頁,反正我看了哪些參考文獻都附在底下了)

另外,這篇都在講 Windows 上的做法,不過理論上把 LoadLibrary 跟 GetProcAddress 替換為對應 macOS/Linux 的函數應該都可以 work(N 年前好像有玩一下 macOS 上是可以跑的)

考.考.考.考考古學家 

可以先參考這篇 [C++] How to GetProcAddress() like a boss  裡面純 C 寫法的強轉型函數指標做法,這相信常玩函數指標的人都有用過這樣玩法:

原理就只是:
  1. 宣告一個新的函數型別 ShellAboutProc,型別、呼叫約制跟你想呼叫的系統函數必須一致(對啦,不然等等堆疊爛掉你就知道惹)
  2. 接著透過 LoadLibrary() 載入系統函數模組取得模組地址、透過 GetProcAddress() 取得該函數位於該函數庫上真正地址
  3. 以 ShellAboutProc 函數型別宣告一個變數 shellAbout
  4. 最後將該函數地址強轉型為我們定義好的 ShellAboutProc 型別覆寫入 shellAbout 變數內,大功告成 la,shellAbout 變數就可以被當原生函數呼叫了
這做法超簡單、也實用,不過看也知道一堆地方可以省略XD,比方說根本不需要額外開一個變數來暫存函數指標,可以直接用內聯函數型態的方式、取代宣告函數型別與變數(離題惹)


進階優雅玩法

我寫這篇筆記前有先 survey 了一些文章,有興趣可以參考:Fixing Function Pointers with decltypeGet types of C++ function parameters、 [C++] How to GetProcAddress() like a boss,看完之後你應該會知道大概就是像這樣:

簡單來說:
  1. typedef decltype(&函數名) 型別名; 可以由 &函數名 refer 到函數型別(純函數名拿到的是函數記憶體地址)再透過 decltype 取出函數宣告型別,然後透過 typedef 將其命名為 a 型別。
  2. 透過 GetProcAddress() 取得函數地址後,透過 reinterpret_cast<a> 關鍵字可以將括弧中物件轉型為 T 型別,最後這樣回傳回來就是 a 型別的函數宣告好的原生函數,就可以當原生函數呼叫啦
當然上面看起來很醜,你也可以像底下這樣一句話:
(reinterpret_cast(GetProcAddress(LoadLibraryA("User32"), "MessageBoxA")))(0, "hi", "info", 0);

這樣還不夠

如果你跟我一樣非常偷懶,希望把函數物件化來呼叫...XD。參考了一下 [C++] How to GetProcAddress() like a boss,大概就可以寫出這樣的東西:





後續

這種花式玩法主要依賴的兩個關鍵字,可以上微軟爸爸 MSDN 官網上查閱 decltype (C++)reinterpret_cast Operator。reinterpret_cast 早在 VC2008 之後就支持了、而 decltype 也在 VC2010 也支持了,基本上大部分現在的 IDE 要這樣玩應該都 ok la 我猜

留言

這個網誌中的熱門文章

[C#] Lambda花式應用噁爛寫法(跨UI委派秒幹、多線程處理...etc)

[Black Asia Arsenal] puzzCode: 專注開發後門的編譯器, 自帶反逆向、對抗病毒特徵碼定位技術

[Windows] 逆向工程 C++ 中入口函數參數 main(argc, argv) 與如何正確的進行參數劫持