使用 GetProcAddress 从 C++ 调用 Delphi DLL:回调函数因参数无效而失败

2024-02-27

我有一个第三方 Delphi DLL,我从 C++ 调用它。不幸的是,我无法访问 Pascal DLL 代码,并且我不是 Pascal 程序员。

没有lib文件,所以我使用GetProcAddress调用许多DLL函数,成功地按值、地址和引用传递参数。我还注册了一个回调函数,该函数会在预期时被调用。

我的问题是,在回调函数中,无法评估两个参数之一(地址 0x000001)。

以下是 Pascal DLL 函数声明

type
HANDLE = Pointer;       /// handle

(** This function Registers the callback function OnACLNeeded 
  *)
function RegisterCallback(
    h: HANDLE; 
    OnACLNeeded: MyCallbackFunc; 
    UserData: DWORD): Integer; stdcall;

这是调用应用程序的 Pascal 版本,即回调函数。 两个参数均通过引用 (var) 传递。

function TSNAPICongigF.OnACLNeeded(var keySettings, numOfKeys: Byte): Integer; stdcall;
begin
  keySettings:=$0F;
  numOfKeys:=1;
  Result:=0;
end;

这是我的回调函数的 C++ 版本

int __stdcall OnACLNeeded(byte& keySettings, byte& numOfKeys)
{
  keySettings = 0x0F;
  numOfKeys = 1;
  return 1;
}

这是我的 C++ 调用代码

int _tmain()
{
    HMODULE hLib = LoadLibrary(PASCAL_DLL);

    // DLL function pointer 
    typedef int (__stdcall *FnRegisterCallback)(HANDLE hKeyProvider,
        int (__stdcall *)(byte&, byte&),
        DWORD);

    FnRegisterCallback pfnRegisterCallback =
        (FnRegisterCallback)GetProcAddress(hLib, "RegisterCallback");

    // register my callback function
    int ret = (*pfnRegisteraCllback)(h, OnACLNeeded, (DWORD)1);
}

在调试器中运行时,我到达回调函数第一行的断点keySettings = 0x0F;
我发现numOfKeys是有效的,但是keySettings地址为 0x00000001,无法分配给。
如果我继续,应用程序将因访问冲突而崩溃。

int __stdcall OnACLNeeded(byte& keySettings, byte& numOfKeys)
{
    keySettings = 0x0F;

我尝试声明为 __cdecl 没有效果。
我尝试将字节参数声明为 byte*,但得到了相同的无效参数。

我使用的是在 Win7 64 位上运行的 Visual Studio 2010,但编译为 Win32。
这些是我的编译和链接命令

/ZI /nologo /W3 /WX- /Od /Oy- /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Debug\CallPascal.pch" /Fa"Debug\" /Fo"Debug\" /Fd"Debug\vc100.pdb" /Gd /analyze- /errorReport:queue 

/OUT:"...\Debug\CallPascal.exe" /INCREMENTAL /NOLOGO "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /MANIFEST /ManifestFile:"Debug\CallPascal.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"...\Debug\CallPascal.pdb" /SUBSYSTEM:CONSOLE /PGD:"...y\Debug\CallPascal.pgd" /TLBID:1 /DYNAMICBASE /NXCOMPAT /MACHINE:X86 /ERRORREPORT:QUEUE

非常感谢收到任何建议。谢谢。

- - - 编辑 - - -

我添加了结构

struct Bodge {
  void* code;
  void* instance;
};

Bodge bodge;

bodge.code = OnACLNeeded;
bodge.instance = (void*)0x99; // just to test

我的回调变成

Integer __stdcall OnACLNeeded(void* instance, Byte& keySettings, Byte& numOfKeys);

我的函数调用注册回调变成

typedef Integer (__stdcall *FnRegisterCallback)(
    HANDLE,
    Bodge,
    DWORD);

并被这样称呼

ret = (*pfnRegisterCallback)(
    h, 
    bodge, 
    (DWORD)1);

此调用会产生错误

ESP 的值未在函数调用期间正确保存。这通常是用一种调用约定声明的函数和用另一种调用约定声明的函数指针调用的结果。

我认为这也可能表明堆栈已损坏。
但是如果我忽略错误并继续,我就会进入回调函数体,并且两个参数现在都有效!
所以一种成功,而且,void* instanceparam 的值为零,而不是我设置的 0x99。
我觉得我们已经到达那里了!

- - - 编辑 - - -

这是来自原始 Pascal 调用代码的注册回调的函数调用。

  * @param hKeyProvider the key provider handle for Desfire card created previously with LASSeOKeyProvider_CreateHandle
  * @param OnACLNeeded supplies a callback to be called for quering host application for the PICC master key settings
  * @param UserData unsigned integer values specifiing any custom provided data to be returned when the callback is called
  * @return 0 - on success; <>0 - denotes error code

  RegisterCallback(hKeyProv,
                  @TSNAPICongigF.OnACLNeeded,
                  (Self));

请注意“自我”引用。 C++ 的等价物是什么? (我没有使用课程)


问题在于 Delphi 版本的回调是一个实例方法。你的 C++ 没有回调它。这是一个严重的不匹配。这个 Delphi 接口设计得很糟糕,如果没有一些技巧,就无法从 C++ 调用。

Delphi 实例方法作为两个指针传递,一个指向代码,一个指向实例。你可以在 C++ 中通过声明来伪造这个RegisterCallback像这样的函数:

typedef int (__stdcall *FnRegisterCallback)(
    HANDLE hKeyProvider,
    void* code,
    void* instance,
    DWORD
);

然后,一旦你加载了它GetProcAddress,这样称呼它:

int ret = (*pfnRegisterCallback)(h, OnACLNeeded, NULL, (DWORD)1);

什么并不重要instance参数是因为当它传递给我们时我们将忽略它OnACLNeeded.

最后一步是安排您的函数的行为类似于 Delphi 实例方法。通过添加一个额外的参数来表示实例来做到这一点。

int __stdcall OnACLNeeded(void* instance, byte& keySettings, byte& numOfKeys)
{
    keySettings = 0x0F;
    numOfKeys = 1;
    return 1;
}

您将收到在instance参数,无论你传递什么instance调用时的参数RegisterCallback.

通过这些更改,您应该能够伪造 Delphi DLL,让您相信您的代码是 Delphi 实例方法!

作为参考,我建议您阅读程序控制 http://docwiki.embarcadero.com/RADStudio/en/Program_ControlDelphi 语言指南的主题。

最后,祝你好运!


Update

对这个问题的最新编辑为正在发生的事情提供了一些新的线索。具体来说这段代码:

RegisterCallback(
    hKeyProv,
    @TSNAPICongigF.OnACLNeeded,
    DWORD(Self)
);

The @TSNAPICongigF.OnACLNeeded参数仅产生方法的代码部分。也就是说它只是一个指针。该实例通过以下参数传递DWORD(Self)。这段代码的作者所做的假设是回调函数将传递三个参数,用户数据后跟两个 var 字节。技巧在于,这样的函数相当于方法调用,因为方法调用是通过在实际参数之前将实例作为隐藏的隐式参数传递来在幕后实现的。

所以,我相信你可以很轻松地解决这个问题。只需一路回滚到您提出问题时代码所在的位置即可。然后更改您的回调函数以接受此额外参数:

int __stdcall OnACLNeeded(DWORD UserData, byte& keySettings, byte& numOfKeys)
{
    keySettings = 0x0F;
    numOfKeys = 1;
    return 1;
}

我现在有信心这会奏效。

您可以像这样调用注册函数:

int ret = (*pfnRegisteraCllback)(h, OnACLNeeded, (DWORD)1);

并且您的回调函数应该看到的值1 in the UserData范围。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 GetProcAddress 从 C++ 调用 Delphi DLL:回调函数因参数无效而失败 的相关文章

  • 检查两个数是否是彼此的排列?

    给定两个数字 a b 使得 1 例如 123 是 312 的有效排列 我也不想对数字中的数字进行排序 如果您指的是数字的字符 例如 1927 和 9721 则 至少 有几种方法 如果允许排序 一种方法是简单地sprintf将它们放入两个缓冲
  • 如何验证文件名称在 Windows 中是否有效?

    是否有一个 Windows API 函数可以将字符串值传递给该函数 该函数将返回一个指示文件名是否有效的值 我需要验证文件名是否有效 并且我正在寻找一种简单的方法来完成此操作 而无需重新发明轮子 我正在直接使用 C 但针对的是 Win32
  • 访问私人成员[关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 通过将类的私有成员转换为 void 指针 然后转换为结构来访问类的私有成员是否合适 我认为我无权修改包含我需要访问的数据成员的类 如果不道德 我
  • UML类图:抽象方法和属性是这样写的吗?

    当我第一次为一个小型 C 项目创建 uml 类图时 我在属性方面遇到了一些麻烦 最后我只是将属性添加为变量 lt
  • 未解决的包含:“cocos2d.h” - Cocos2dx

    当我在 Eclipse 中导入 cocos2dx android 项目时 我的头文件上收到此警告 Unresolved inclusion cocos2d h 为什么是这样 它实际上困扰着我 该项目可以正确编译并运行 但我希望这种情况消失
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 如何在列表框项目之间画一条线

    我希望能够用水平线分隔列表框中的每个项目 这只是我用于绘制项目的一些代码 private void symptomsList DrawItem object sender System Windows Forms DrawItemEvent
  • 如何忽略“有符号和无符号整数表达式之间的比较”?

    谁能告诉我必须使用哪个标志才能使 gcc 忽略 有符号和无符号整数表达式之间的比较 警告消息 gcc Wno sign compare 但你确实应该修复它警告你的比较
  • 如果使用 SingleOrDefault() 并在数字列表中搜索不在列表中的数字,如何返回 null?

    使用查询正数列表时SingleOrDefault 当在列表中找不到数字时 如何返回 null 或像 1 这样的自定义值 而不是类型的默认值 在本例中为 0 你可以使用 var first theIntegers Cast
  • Qt moc 在头文件中实现?

    是否可以告诉 Qt MOC 我想声明该类并在单个文件中实现它 而不是将它们拆分为 h 和 cpp 文件 如果要在 cpp 文件中声明并实现 QObject 子类 则必须手动包含 moc 文件 例如 文件main cpp struct Sub
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • vector 超出范围后不清除内存

    我遇到了以下问题 我不确定我是否错了或者它是一个非常奇怪的错误 我填充了一个巨大的字符串数组 并希望在某个点将其清除 这是一个最小的例子 include
  • 在数据库中搜索时忽略空文本框

    此代码能够搜索数据并将其加载到DataGridView基于搜索表单文本框中提供的值 如果我将任何文本框留空 则不会有搜索结果 因为 SQL 查询是用 AND 组合的 如何在搜索 从 SQL 查询或 C 代码 时忽略空文本框 private
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • Discord.net 无法在 Linux 上运行

    我正在尝试让在 Linux VPS 上运行的 Discord net 中编码的不和谐机器人 我通过单声道运行 但我不断收到此错误 Unhandled Exception System Exception Connection lost at
  • 如何在 VBA 中声明接受 XlfOper (LPXLOPER) 类型参数的函数?

    我在之前的回答里发现了问题 https stackoverflow com q 19325258 159684一种无需注册即可调用 C xll 中定义的函数的方法 我之前使用 XLW 提供的注册基础结构 并且使用 XlfOper 类型在 V
  • 实体框架 4 DB 优先依赖注入?

    我更喜欢创建自己的数据库 设置索引 唯一约束等 使用 edmx 实体框架设计器 从数据库生成域模型是轻而易举的事 现在我有兴趣使用依赖注入来设置一些存储库 我查看了 StackOverflow 上的一些文章和帖子 似乎重点关注代码优先方法
  • 在 Dynamics CRM 插件中访问电子邮件发件人地址

    我正在编写一个 Dynamics CRM 2011 插件 该插件挂钩到电子邮件实体的更新后事件 阶段 40 pipeline http msdn microsoft com en us library gg327941 aspx 并且在此阶
  • ASP.NET MVC 6 (ASP.NET 5) 中的 Application_PreSendRequestHeaders 和 Application_BeginRequest

    如何在 ASP NET 5 MVC6 中使用这些方法 在 MVC5 中 我在 Global asax 中使用了它 现在呢 也许是入门班 protected void Application PreSendRequestHeaders obj
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 删除第一个 git 提交

    我第一次犯错的时候就犯了一个错误 我在另一个人的计算机上 并使用他们的用户名而不是我的用户名推送了一个版本 我怎样才能删除这个 您可以使用以下命令将包含您想要的初始提交的存储库强制推送到 Githubgit push force
  • 是否有任何 HTML 代码可以显示椭圆形或圆角矩形?

    我不确定 HTML 中是否可以 但我仍然会在这里问 是否有任何 HTML 代码可以代表椭圆或圆角矩形 换个角度想 这也是很有可能的 就这样 http virkkunen net b oh dear html http virkkunen n
  • 自动释放:当你被 NARC 逮捕时总是使用吗?

    我知道这个问题看起来像一个骗局 我检查过 但事实并非如此 在谈论 NARC 时 该博客的作者说 http vgable com blog 2010 05 19 n a r c 就我个人而言 我喜欢在同一行立即自动释放我 NARC 编辑的任何
  • 带有 html5 的图像按钮

    我正在尝试制作一个图像按钮 我正在使用 学习 html5 和 jquery mobile 这是我的示例代码 img src img beer png alt beer
  • 检查 bash 脚本中 C 程序的返回值?

    我有一个 bash 脚本 在其中使用 检查上次运行命令的退出代码变量 但现在我正在执行一个 C 程序 来自该脚本 如果程序成功执行 则返回 0 有什么方法可以在我的 bash 脚本中捕获 C 程序的返回值吗 我相信 awk sed 等不同的
  • jQuery 解析/显示来自 php json_encode 的 json 数据

    jquery 中的初始 ajax 调用 ajax type post url items data php data id id dataType json success function data if data make item r
  • 我怎样才能拥有一个同时包含图像和文本的 UIBarButtonItem?

    当我尝试对 UIBarButtonItem 使用图像时 不显示文本 有没有办法同时显示文字和图像 您可以使用具有图像和文本的自定义视图来初始化 UIBarButtonItem 这是一个使用 UIButton 的示例 UIImage chat
  • 在 Angular 中向 ckeditor 添加简单的上传适配器

    我确实通过安装在我的角度项目中启动了经典的ckeditor npm install save ckeditor ckeditor5 angular npm install save ckeditor ckeditor5 build clas
  • 使用 Python(加密)生成 CSR

    我有一个可以工作的骨架 但我有点坚持以下几点 我没有找到使用加密库处理 SAN subjectAltName 的方法 希望我在术语上没有错误 但如果我说 一个主要主机名 test test edu 然后又希望该主机也为 Pushu edu
  • Scala 中两个集合的并集

    从链接的问题来看here https stackoverflow com questions 6963792 scala functional set problem 我在 Scala 中找到了 Union 的实现 def union a
  • 获取合适的VS2017实例进行自主开发扩展

    我正在开发一个 Visual Studio 扩展 以使用从代码窗口中的右键单击上下文菜单调用的自定义命令来替换当前活动 cs 文件中的文本 到目前为止 访问文档是有效的 但如果我启动多个 VS2017 实例 那么我希望在新实例中完成的更改将
  • 在应用程序购买中添加二进制文件

    我已提交新版本的应用程序和新的应用程序购买 苹果有强调新版本的应用程序 但不接受应用程序内购买 应用内购买 新商业模式 我们已开始审核您的应用内购买 但无法继续 因为您提交的应用内购买表明您的应用的业务模式发生了变化 因此 我们需要验证您提
  • 是否有可用于在浮点表示形式之间进行转换的 C++ 库?

    我最近需要解释 DEC 32 位浮点表示 它与 IEEE 浮点表示法的不同之处在于分配给指数和尾数的位数 以下是一堆浮点格式的描述 http www quadibloc com comp cp0201 htm http www quadib
  • docker nginx ERR_NAME_NOT_RESOLVED

    运行 4 个 docker 容器 服务器 客户端 nginx mongo 反向代理 客户端似乎在端口 4200 上工作 我可以看到我的应用程序和路由工作 当尝试注册用户时 我请求将以下 httpclient 发布到名为 myserver 的
  • Excel VBA 宏用于跟踪单独工作表中的更改

    我正在尝试编写一个 VBA 宏来跟踪对单独工作表中工作簿的更改 如果您手动执行此操作 命令顺序为 工具 gt 跟踪更改 gt 突出显示更改 并选择 单独工作表 选项 您必须执行该命令的两次迭代 一次是激活内联跟踪 第二次是将跟踪移动到单独的
  • android:singleLine 的 TextView 不再处于“已弃用”状态?

    我面临 android ellipsize 在 TextView 中不起作用的问题 但要在 android singleLine 上正常工作 我听说 android singleLine 已 弃用 但 Android Developer 的
  • 系统 IO 异常:进程无法访问该文件,因为该文件正在被另一个进程使用 c#

    我已经看到了关于这个问题的几篇文章 我已经实现了所有建议 例如在流写入器和连接对象上使用flush close 方法 使用GC Collect 强制清理 使用using 自动处置 我正在从数据库进行简单的获取操作并写入文本文件 这是我的代码
  • 使用Javascript将所有span标签更改为label标签?

    我想做的 调用卸载函数来更改 span some content span into
  • Rascal 中的布局

    当我导入 Lisra 配方时 import demo lang Lisra Syntax 这将创建语法 layout Whitespace t n r lexical IntegerLiteral 0 9 gt gt 0 9 lexical
  • 使用 GetProcAddress 从 C++ 调用 Delphi DLL:回调函数因参数无效而失败

    我有一个第三方 Delphi DLL 我从 C 调用它 不幸的是 我无法访问 Pascal DLL 代码 并且我不是 Pascal 程序员 没有lib文件 所以我使用GetProcAddress调用许多DLL函数 成功地按值 地址和引用传递