我正在编写各种调用相对复杂的Win32 API函数的东西.这是一个例子:
//Encapsulates calling NtQuerySystemInformation buffer management.
WindowsApi::AutoArray NtDll::NtQuerySystemInformation(
SystemInformationClass toGet ) const
{
AutoArray result;
ULONG allocationSize = 1024;
ULONG previousSize;
NTSTATUS errorCheck;
do
{
previousSize = allocationSize;
result.Allocate(allocationSize);
errorCheck = WinQuerySystemInformation(toGet,
result.GetAs<void>(), allocationSize, &allocationSize);
if (allocationSize <= previousSize)
allocationSize = previousSize * 2;
} while (errorCheck == 0xC0000004L);
if (errorCheck != 0)
{
THROW_MANUAL_WINDOWS_ERROR(WinRtlNtStatusToDosError(errorCheck));
}
return result;
}
//Client of the above.
ProcessSnapshot::ProcessSnapshot()
{
using Dll::NtDll;
NtDll ntdll;
AutoArray systemInfoBuffer = ntdll.NtQuerySystemInformation(
NtDll::SystemProcessInformation);
BYTE * currentPtr = systemInfoBuffer.GetAs<BYTE>();
//Loop through the results, creating Process objects.
SYSTEM_PROCESSES * asSysInfo;
do
{
// Loop book keeping
asSysInfo = reinterpret_cast<SYSTEM_PROCESSES *>(currentPtr);
currentPtr += asSysInfo->NextEntryDelta;
//Create the process for the current iteration and fill it with data.
std::auto_ptr<ProcImpl> currentProc(ProcFactory(
static_cast<unsigned __int32>(asSysInfo->ProcessId), this));
NormalProcess* nptr = dynamic_cast<NormalProcess*>(currentProc.get());
if (nptr)
{
nptr->SetProcessName(asSysInfo->ProcessName);
}
// Populate process threads
for(ULONG idx = 0; idx < asSysInfo->ThreadCount; ++idx)
{
SYSTEM_THREADS& sysThread = asSysInfo->Threads[idx];
Thread thread(
currentProc.get(),
static_cast<unsigned __int32>(sysThread.ClientId.UniqueThread),
sysThread.StartAddress);
currentProc->AddThread(thread);
}
processes.push_back(currentProc);
} while(asSysInfo->NextEntryDelta != 0);
}
Run Code Online (Sandbox Code Playgroud)
我的问题在于模拟NtDll::NtQuerySystemInformation方法 - 即,返回的数据结构很复杂(嗯,这里它实际上相对简单,但它可能很复杂),编写一个像API调用一样构建数据结构的测试可以采取编写使用API的代码的时间长度的5-6倍.
我想做的是调用API,并以某种方式记录它,以便我可以将该记录值返回给测试中的代码,而无需实际调用API.返回的结构不能简单地进行memcpy,因为它们通常包含内部指针(指向同一缓冲区中其他位置的指针).有问题的库需要检查这些类型的东西,并能够在重放时将指针值恢复到类似的缓冲区.(即检查每个指针大小的值,如果它可以被解释为缓冲区内的指针,将其更改为偏移量,并记住在重放时将其更改回指针 - 此处可以接受误报率)
有没有什么可以做这样的事情?
手工实现真的那么难吗?
#include <stddef.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
typedef char BYTE;
//Note: Buffer is MODIFIED after this call (hence why it's not const)! Don't re-use it!
void saveBuffer(FILE* stream, BYTE* buffer, size_t bufferSize)
{
assert(bufferSize % sizeof(void*) == 0);
fwrite(&bufferSize, sizeof(bufferSize), 1, stream);
for (size_t i = 0; i < bufferSize - sizeof(void*) + 1; i += sizeof(void*))
{
void** pAsPointer = (void**)&buffer[i];
if (*pAsPointer >= buffer && *pAsPointer < buffer + bufferSize)
{
fwrite(&i, sizeof(i), 1, stream);
*pAsPointer = pAsPointer;
}
}
void* null = NULL;
fwrite(&null, sizeof(null), 1, stream); //Null-terminator
fwrite(buffer, 1, bufferSize, stream);
}
size_t loadBuffer(FILE* stream, BYTE* buffer, size_t bufferCapacity) //Buffer size is in stream
{
size_t bufferSize;
fread(&bufferSize, sizeof(bufferSize), 1, stream);
if (bufferSize >= bufferCapacity)
{
memset(buffer, 0, bufferSize);
for (;;)
{
size_t pointerOffset;
fread(&pointerOffset, sizeof(pointerOffset), 1, stream);
if (pointerOffset != 0)
{ *(size_t*)&buffer[pointerOffset] = (size_t)buffer + pointerOffset; }
else { break; }
}
for (size_t i = 0; i < bufferSize; i += sizeof(void*))
{
if (*(void**)&buffer[i] == NULL)
{ fread(&buffer[i], sizeof(size_t), 1, stream); }
}
}
return bufferSize;
}
Run Code Online (Sandbox Code Playgroud)
(抱歉,我还没有测试过它,但它应该非常接近工作了。)
唯一的麻烦是假设所有可能是指针的值实际上都是指针,但除此之外,它似乎很简单。