我有一些软件,它使用了RSA认证代理的有文档的API。这是一种产品,在域中的客户端机器上作为服务运行,并通过与集中安装的"RSA身份验证管理器“进行本地通信来验证用户。
身份验证代理的API公开记录在这里:C开发人员指南认证代理API 8.1.1。然而,这些文档似乎是不正确的,而且我无法访问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位数量,例如,在AceInit、AceSetUserData和AceGetUserData的文档中。AceGetUserData文档的相关摘录
此函数是同步的,调用方必须作为第二个参数提供指向32位存储区域(即无符号int)的指针,以便将用户数据值复制到其中。
这显然是错误的--从一些实验来看,如果您传入一个指向填充了0xff的缓冲区中心的指针,AceGetUserData肯定会写出一个64位的值,而不是32位的数量。
我的aceclnt.dll版本是8.1.3.563;相应的文档被标记为“身份验证代理API8.1 SP1",这与身份验证代理本身的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;
}发布于 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并存储/读取该类型。
发布于 2017-03-13 16:24:49
为了避免出现未定义的行为问题,请用此函数替换您的测试函数,并报告它打印的内容。也请向我们展示完整的测试程序,以便访问这个库的人可以自己编译和运行它,并修改它。我特别希望看到api全局声明及其类型,以及初始化api的代码,并知道该类型来自何处(您是作为逆向工程练习的一部分编写的,还是从某个地方获得的?)
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);
}(如果你的假设是正确的,输出将是
DEBUG: testBuffer = a5a5a5a5 a5a5a5a5 00000000 00000000 a5a5a5a5 a5a5a5a5。)
https://stackoverflow.com/questions/42560402
复制相似问题