从c#中通过c++ /CLI访问本机对象

本文关键字:CLI 访问 本机 对象 c++ | 更新日期: 2023-09-27 18:02:01

我写了一个c#应用程序,它将DLL注入到第三方可执行文件中(这恰好是使用Qt框架构建的)。这个DLL使用EasyHook来拦截对一些特定函数的调用。当我的注入代码被调用时,我会尝试检查作为这些函数参数的一些对象。

例如,我拦截了一个解析XML的调用:

virtual bool __thiscall QXmlSimpleReader::parse(class QXmlInputSource const &)

在我的c#代码中,我有一个PInvoke签名来匹配:

static extern bool XmlParse(IntPtr Reader, IntPtr Source)

我想调用"data()"函数,它是"Source"类的成员。也就是说,QXmlSimpleReader解析来自QXmlInputSource的原始XML,但在此之前,我试图通过这个"data()"函数检查原始XML。

根据这里一位专家的建议,我尝试使用c++/CLI来本地访问对象(参见在第三方dll中调用方法)。我在c++中构造了一个包装器对象,它接受来自c#代码的IntPtr:

头:

public ref class QXmlInputSource
{
public:
    // Constructor must be called with C# IntPtr
    QXmlInputSource(IntPtr Ptr);
    bool LoadQt(void);
    bool UnloadQt(void);
    String^ Data();
private:
    // Pointer to the native Qt object
    void * Native;
    HINSTANCE DllHandle;
    // SIGNATURE: virtual QString QXmlInputSource::data() const
    typedef void * (__thiscall *QXmlInputSource_Data)(void *);
    QXmlInputSource_Data fpData;
};

CPP文件:

QXmlInputSource::QXmlInputSource(IntPtr Ptr)
{
    LoadQt();
    Native = Ptr.ToPointer();
}
bool QXmlInputSource::LoadQt(void)
{
    FARPROC Addr;
    /* get handle to dll */
    std::wstring LibName = QtPath + QtXml;
    DllHandle = LoadLibrary(LibName.c_str()); 
    /* get pointer to the function in the dll*/ 
    Addr = GetProcAddress(HMODULE (DllHandle), "?data@QXmlInputSource@@UBE?AVQString@@XZ"); 
    fpData = QXmlInputSource_Data(Addr);
    return true;
}
bool QXmlInputSource::UnloadQt()
{
    /* Release the Dll */ 
    FreeLibrary(DllHandle);
    return true;
}
String^ QXmlInputSource::Data()
{
    void* Ptr = fpData(Native);
    return "EPIC FAIL";
}

当我尝试调用fpData()函数指针时,基于qt的应用程序崩溃。帮助:P

一些可能有帮助也可能没有帮助的附加信息:

  • 我已经成功地在"更简单"的对象上调用函数,例如使用相同的方法的QString.count()和QString.data()。(QString似乎只是一个轻量级的包装标准unicode字符串)。

  • 在包含我感兴趣的XML函数的QtXml4.dll文件中,实际上有两个parse()方法;一种是Source是const &,另一种是Source是const *。我不知道该用哪一种。我认为我的签名无论如何都不会改变。

  • 当我试图玩的时候,我尝试在c#代码中解引用IntPtr并将其传递给c++:

    IntPtr DerefSrc = (IntPtr)Marshal。PtrToStructure(来源、typeof (IntPtr));

如果我打印出这两个IntPtrs的值,Source的值约为3.5Mb,而DerefSrc的值为1.6Gb——这大致与内存中QtXml4.dll的地址匹配。我的猜测是3.5Mb是一个相对偏移量,而DerefSrc显然是一个绝对参考。是否值得一试将DerefSrc转换为相对地址并将其传递给c++…div ?

我发现了几个问题:

1。你不检查LoadLibrary和GetProcAddress的返回值。QXmlInputSource::data甚至是dll导出的方法吗?如果不是,那么GetProcAddress显然会失败。

2。如何实例化QXmlInputSource类?我问,因为它似乎是你试图在c#代码,这是很难做到正确(你需要知道所需的类的大小,分配一个适当对齐的内存块的大小,并调用构造函数)。

3。你调用函数指针的方式是错误的。您需要声明一个适当类型的方法指针:

FARPROC fp = ::GetProcAddress(...);
typedef QString (QXmlInputSource::*DataMethod)();
DataMethod mpData = reinterpret_cast<DataMethod>(fp);
QXmlInputSource source;
QString data = (source.*mpData)();

4。查看QXmlInputSource::data的文档,我看到它返回一个QString,这与指针有很大的不同(正如您目前所处理的那样)。将其转换为系统。字符串,您需要这样的代码:

QString s1 = (QChar*)L"example string";
String^ s2 = gcnew String((wchar_t*)s1.data()); // calls the String::String(wchar_t*) constructor overload

从c#中通过c++ /CLI访问本机对象