2013-02-17 102 views
0

背景:C++ COM對象Hotpatching?

我掛在windows COM對象上。

使用的方法是vtable修改。說我們有接口一個命名例如,它包含在接口oldmethod的一個實例,我換成newmethod。但是,在我的新方法我需要知道oldmethod的地址,以便我可以在做我自己的事情後調用oldmethod

這是不安全的存儲地址在全局變量oldmethod,因爲有可能是背後接口一個不止一個實現,比方說有兩種實現方式,類A1A2級。因此,我的新方法需要存儲兩個A1-> oldmethodA2-> oldmethod,並根據實例類型調用適當的函數。做到這一點

  • 一種方式就是,我不斷在地圖上,存儲(虛表的地址 - > oldmethod)。由於vtable的地址可以作爲A1類和A2類的區別。在我的新方法中,針對當前實例檢查地圖的正確方法oldmethod。但是,這會使程序每次都檢查地圖,這會增加成本,並且地圖上的線程安全性會增加成本。

  • 的另一種方式是使封閉的,我分配可執行存儲器的塊,和寫我newmethod內的二進制碼(可以被減小到最小尺寸,所以大小是沒有問題的)。我修改了每個實例的二進制代碼oldmethod的地址。在這種情況下,不會搜索地圖成本。

問題1

是第二種方式以安全的方式做到這一點,或者說是第一種方式更好?其中任何一個都有任何潛在的安全問題?

問題2

在第二種方式中,我創建封閉包含類特定的數據,這是oldmethod指針。如果我需要將實例特定數據存儲在我的新方法中,除了保留(此指針 - >數據)映射之外是否還有其他策略?我盡力而爲,找不到方法。

+0

有沒有你不某種特定原因只是從現有的實現派生的coclass,幾乎覆蓋了「oldmethod」與「newmethod」的簽名*必須*匹配或者你是根據定義,違反了COM的爲合同發佈IID和接口的引腳。當然,除非這不是你的代碼(舊的方法),並且你基本上試圖去鉤住別人的coclass。你也可以使用coclass別名,但這聽起來像你更喜歡這樣做的狡猾。 – WhozCraig 2013-02-17 09:26:24

+0

我掛在現有的COM對象來修改它的行爲,我沒有實現。 – 2013-02-17 10:21:50

+0

@WhozCraig我不直接調用方法,我也沒有調用者的代碼。 – 2013-02-17 10:25:33

回答

1

您可能沒有類A1的源代碼,但是當它被實例化時(通過「new」,CoCreateInstance還是其他一些工廠函數),您可以控制它嗎?如果是這樣,那麼只需實現一個實現接口A的類,並將接口A上的所有調用轉發給實際對象並攔截您關心的方法。

在下面的例子中,我們替換

class InterfaceA : public IUnknown 
{ 
public: 

    virtual int M1() = 0; 
    virtual int M2(int x, int y) = 0; 
    virtual int M3() = 0; 
}; 


class CMyWrapperClass : public InterfaceA 
{ 
public: 

    int _refcount; 
    InterfaceA* _pInner; 

    CSomeClass2(InterfaceA* pInner) 
    { 
     _pInner = pInner; 
     _pInner->AddRef(); 
     _refcount = 1; 
    } 

    ~CSomeClass2() 
    { 
     _pInner->Release(); 
    } 

    virtual int M1() {return _pInner->M1();} 
    virtual int M2(int x, int y) {printf("CSomeClass2::M2(x=%d, y=%d)\n", x, y); return _pInner->M2(x,y); } 
    virtual int M3() {return _pInner->M3();} 

    // not shown - addRef, release, queryinterface 
}; 


    // example instantiation 
    hr = CoCreateInstance(CLSID_A1, NULL, CLXCTX_ALL, IID_InterfaceA, (void**)&pInterfaceA); 

    // now do the wrap 
    pInterfaceA = new CMyWrapperClass(pInterfaceA); 

的一個例子,如果你沒有你想的hotpatch類的實例化的控制,我有代碼共享爲。但它顯然有點複雜。如果這不起作用,我會發布另一個與hotpatching COM vtable直接相關的答案。

+0

我想過這個,但我認爲這不是安全的,在調用者知道的不僅僅是界面中的功能的情況下。在這種情況下,包裝將失敗。我試圖找到一種能處理所有情況並避免危險的一般方法。我對你的其他解決方案很感興趣。 – 2013-02-17 11:23:26

+0

請給我一個上述「不安全」的例子。 – selbie 2013-02-17 12:39:34

+0

class ClassA:InterfaceA。 ClassA具有InterfaceA所具有的所有虛擬功能。但是,ClassA還有一個M4()方法,該軟件的作者可能具有M4()的知識,並在某處調用M4(),但提供的頭文件可能不包含M4,因此它保持私有狀態。我知道這將是一個不好的做法,編寫我自己的程序時,我不會這樣做。我說我試圖找到一種非常安全的方式,該程序在掛鉤之前和之後都可以工作。 – 2013-02-17 13:33:24

0

我花了一段時間來理解這個問題。我實際上寫了一大段代碼,我想演示如何修補一個vtable,然後在包裝類中調用原始方法。修補vtable很容易。

然後我發現你指的是這個問題。那就是當打補丁的vtable方法稱爲(newmethod),即使它在另一個類中定義的,「本」是原來的對象,你沒有這些被調用實例的任何方面。所以你不能簡單地引用一個成員變量來回到你已經保存的「oldmethod」。

所以經過一番思考,我認爲全球地圖是這樣做的最安全的方法。您可能只需要一個鎖(critical_section)來防止插入,刪除或查找地圖中的函數指針。在您從地圖安全地檢索舊方法後,您可能不需要保持鎖定狀態。因此,執行此操作的運行時間開銷非常小。