c - RSA Authentication Agent API 文档中的函数签名是否正确?

标签 c reverse-engineering two-factor-authentication securid

我有一些软件使用 RSA 身份验证代理的文档化 API。这是一种在域中的客户端计算机上作为服务运行的产品,并通过与集中安装的“RSA 身份验证管理器”进行通信来在本地对用户进行身份验证。

身份验证代理的 API 在此处公开记录:Authentication Agent API 8.1.1 for C Developers Guide .但是,文档似乎不正确,而且我无权访问 RSA 头文件——它们不是公开的;只有 PDF 文档可供下载,无需向 RSA 支付 $$。如果这里有人可以访问最新的头文件,您能帮我确认文档是否已过时吗?

API 文档中给出的函数签名似乎不正确 - 事实上,我绝对相信它们在 x64 机器上是错误的。例如,最新的 PDF 文档显示如下:

int WINAPI AceSetUserData(SDI_HANDLE hdl, unsigned int userData)
int WINAPI AceGetUserData(SDI_HANDLE hdl, unsigned int *pUserData)

文档多次指出“userData”值是一个 32 位数量,例如在 AceInitAceSetUserDataAceGetUserData 的文档中AceGetUserData 文档的相关摘录:

This function is synchronous and the caller must supply, as the second argument, a pointer to a 32-bit storage area (that is, an unsigned int) into which to copy the user data value.

这显然是错误的 - 从一些实验来看,如果你传入一个指向填充了 0xff 的缓冲区中心的指针,AceGetUserData 肯定写出一个 64-位值,而不是 32 位数量。

我的aceclnt.dll版本是8.1.3.563;相应的文档标记为“Authentication Agent API 8.1 SP1”,这对应于 Authentication Agent 本身的版本 7.3.1。

测试代码

给出了完整的测试代码,即使它与问题根本无关......如果其他人运行测试代码对我来说没有用(我知道它的作用!),我需要的是可以访问的人可以确认函数签名的RSA头文件。

#include <assert.h>
#include <stdlib.h>
#include <stdint.h>

#ifdef WIN32
#include <Windows.h>
#include <tchar.h>
#define SDAPI WINAPI
#else
#define SDAPI
#endif
typedef int SDI_HANDLE;
typedef uint32_t SD_BOOL;
typedef void (SDAPI* AceCallback)(SDI_HANDLE);
#define ACE_SUCCESS                    1
#define ACE_PROCESSING                 150

typedef SD_BOOL (SDAPI* AceInitializeEx_proto)(const char*, char*, uint32_t);
typedef int (SDAPI* AceInit_proto)(SDI_HANDLE*, void*, AceCallback);
typedef int (SDAPI* AceClose_proto)(SDI_HANDLE, AceCallback);

typedef int (SDAPI* AceGetUserData_proto)(SDI_HANDLE, void*);
typedef int (SDAPI* AceSetUserData_proto)(SDI_HANDLE, void*);

struct Api {
  AceInitializeEx_proto AceInitializeEx;
  AceInit_proto AceInit;
  AceClose_proto AceClose;
  AceGetUserData_proto AceGetUserData;
  AceSetUserData_proto AceSetUserData;
} api;

static void api_init(struct Api* api) {
  // All error-checking stripped...
  HMODULE dll = LoadLibrary(_T("aceclnt.dll")); // leak this for the demo
  api->AceInitializeEx = (AceInitializeEx_proto)GetProcAddress(dll, "AceInitializeEx");
  api->AceInit = (AceInit_proto)GetProcAddress(dll, "AceInit");
  api->AceClose = (AceClose_proto)GetProcAddress(dll, "AceClose");
  api->AceGetUserData = (AceGetUserData_proto)GetProcAddress(dll, "AceGetUserData");
  api->AceSetUserData = (AceSetUserData_proto)GetProcAddress(dll, "AceSetUserData");

  int success = api->AceInitializeEx("C:\\my\\conf\\directory", 0, 0);
  assert(success);
}

static void demoFunction(SDI_HANDLE handle) {
  union {
    unsigned char testBuffer[sizeof(void *) * 3];
    void *forceAlignment;
  } u;

  memset(u.testBuffer, 0xA5, sizeof u.testBuffer);

  int err = api.AceGetUserData(handle, (void*)(u.testBuffer + sizeof(void*)));
  assert(err == ACE_SUCCESS);

  fputs("DEBUG: testBuffer =", stderr);
  for (size_t i = 0; i < sizeof(u.testBuffer); i++) {
    if (i % 4 == 0)
      putc(' ', stderr);
    fprintf(stderr, "%02x", u.testBuffer[i]);
  }
  fputc('\n', stderr);
  // Prints:
  // DEBUG: testBuffer = a5a5a5a5 a5a5a5a5 00000000 00000000 a5a5a5a5 a5a5a5a5
  // According to the docs, this should only write out a 32-bit value
}

static void SDAPI demoCallback(SDI_HANDLE h) {
  fprintf(stderr, "Callback invoked, handle = %p\n", (void*)h);
}

int main(int argc, const char** argv)
{
  api_init(&api);
  SDI_HANDLE h;

  int err = api.AceInit(&h, /* contentious argument */ 0, &demoCallback);
  assert(err == ACE_PROCESSING);

  demoFunction(h);

  api.AceClose(h, 0);

  return 0;
}

最佳答案

当您从文档中复制函数/类型定义时,您基本上没有也永远不会对您正在使用的 .dll 版本有正确的定义,并且可能总是以崩溃或崩溃告终更糟糕的是,未定义的行为。

你可以做的是调试相应的.dll:

您运行 Visual Studio 吗?我记得 VS 可以在 Debug模式下进入函数调用并显示程序集,但不确定今天如何。但是任何反汇编程序都应该可以解决问题。截至x64 ABI register rcx 获取第一个参数,rdx 第二个。如果该函数在内部使用 32 位寄存器名称或清除高 32 位,则您可以假设一个 32 位整数。如果它使用它来加载地址(例如 lea 指令),您可以假设一个指针。但正如您所见,这可能不是您想走的路...

那你还剩下什么?

您链接的文档说明了 32 位和 64 位库 - 取决于您使用的平台。我猜你使用的是 64 位库,RSA 没有更新这个库的文档,但在某些时候开发人员需要将库升级到 64 位。

所以请这样想:如果您是 API 开发人员,什么可以迁移到 64 位,什么不能。例如。无法触及需要跨 32/64 实现工作的所有内容(通过网络发送或存储和共享在磁盘上的内容)。但是实例本地的所有内容都可以迁移。由于 userData 似乎是一个运行时的东西,支持平台提供的任何东西都是有意义的:unsigned long 在 64 位上和 unsigned int 在 32 位上.

您已经知道 userData 必须是 64 位的。但不是因为该函数写出一个 64 位整数,而是因为该函数看到一个 64 位值开始。由于整数是按值传递的(我猜一般情况下,但肯定是在 WINAPI 中),如果函数是 32 位数据类型,则该函数绝对不可能看到完整的 64 位值。因此,API 开发人员很可能将数据类型更改为 unsigned long(无论如何更改为 64 位类型)。

PS:如果您最终将指针放入 userData,请将指针转换为 uintptr_t 并存储/读取该类型。

关于c - RSA Authentication Agent API 文档中的函数签名是否正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42560402/

相关文章:

python - 调试器和 cpu 仿真器不检测自修改代码

reverse-engineering - 在没有 .proto 文件的情况下解析 Protocol-Buffers

java - 任何人都可以使用任何 Java 应用程序的代码吗?

google-cloud-platform - 在 Google Cloud Platform 中启用双因素身份验证

python - Web2Py:自定义两步验证表单

c - strcpy() 不写入新字符串

c - SPI_IOC_MESSAGE 返回 EINVAL

c - 如何使用 C 中的 libxml 删除 XML 中元素/节点之间的空格?

c - 循环不会运行多次

python - 无法从登录页面进一步移动,[django-otp]