首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在RSA身份验证代理API文档中,函数签名是否正确?

在RSA身份验证代理API文档中,函数签名是否正确?
EN

Stack Overflow用户
提问于 2017-03-02 16:11:21
回答 2查看 233关注 0票数 3

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

身份验证代理的API公开记录在这里:C开发人员指南认证代理API 8.1.1。然而,这些文档似乎是不正确的,而且我无法访问RSA头文件--它们不是公共的;只有PDF文档可以下载,而无需向RSA支付$$。如果这里有人可以访问最新的头文件,您能为我确认文档是否过期吗?

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

代码语言:javascript
复制
int WINAPI AceSetUserData(SDI_HANDLE hdl, unsigned int userData)
int WINAPI AceGetUserData(SDI_HANDLE hdl, unsigned int *pUserData)

文档多次声明"userData“值是32位数量,例如,在AceInitAceSetUserDataAceGetUserData的文档中。AceGetUserData文档的相关摘录

此函数是同步的,调用方必须作为第二个参数提供指向32位存储区域(即无符号int)的指针,以便将用户数据值复制到其中。

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

我的aceclnt.dll版本是8.1.3.563;相应的文档被标记为“身份验证代理API8.1 SP1",这与身份验证代理本身的7.3.1版本相对应。

测试代码

给出了完整的测试代码,尽管它与问题无关.如果其他人运行测试代码(我知道它做了什么!),我需要的是能够访问RSA头文件的人,他可以确认函数签名。

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

发布于 2017-03-14 07:39:00

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

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

您运行吗?我记得VS可以在调试模式下进入一个函数调用并显示程序集,虽然不确定它现在是怎样的。但是任何反汇编程序都应该做到这一点。作为x64 ABI寄存器,rcx获得第一个参数,rdx获得第二个参数。如果该函数在内部使用32位寄存器名,或者清除高于32位的整数,则可以假定为32位整数。如果它使用它来加载一个地址(例如lea指令),您可以假设一个指针。但正如你所见,那可能不是一条你想走的路.

你还剩下什么?

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

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

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

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

票数 1
EN

Stack Overflow用户

发布于 2017-03-13 16:24:49

为了避免出现未定义的行为问题,请用此函数替换您的测试函数,并报告它打印的内容。也请向我们展示完整的测试程序,以便访问这个库的人可以自己编译和运行它,并修改它。我特别希望看到api全局声明及其类型,以及初始化api的代码,并知道该类型来自何处(您是作为逆向工程练习的一部分编写的,还是从某个地方获得的?)

代码语言:javascript
复制
static void demoFunction(SDI_HANDLE handle) {
  int err = api.AceSetUserData(handle, 0);
  assert(err == ACE_SUCCESS);

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

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

  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);
    printf(stderr, "%02x", u.testBuffer[i]);
  }
  fputc('\n', stderr);
}

(如果你的假设是正确的,输出将是

代码语言:javascript
复制
DEBUG: testBuffer = a5a5a5a5 a5a5a5a5 00000000 00000000 a5a5a5a5 a5a5a5a5

。)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/42560402

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档