encryption的API调用失败

首先是:

我的testing系统:Windows 7 32位专业版Windows 7 64位专业版

编译器:gcc版本5.2.0(i686-win32-dwarf-rev0,由MinGW-W64项目build造)

构build选项:g ++ foo.cpp -o foo.exe

我的应用程序dynamic地从kernel32.dll接收函数地址。 这按预期工作。 但是,当我对这些API调用进行异或encryption并将解密的string传递给GetProcAdress函数时,取决于stringpVAE_D [strlen(pVAE_E)]的顺序。 和char pGTC_D [strlen(pGTC_E)]以及encryptionstring的顺序; 如果在char pVAE_D [strlen(pVAE_E)]之前设置字符pGTC_D [strlen(pGTC_E)](对于encryption的string是相同的)

`#include <windows.h> #include <stdio.h> typedef BOOL(WINAPI* _GetThreadContext) (HANDLE hThread, LPCONTEXT lpContext ); typedef LPVOID(WINAPI* _VirtualAllocEx) (HANDLE handle_Process, LPVOID longpointer_Address, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ); void encrStr(char *pStr, int len, char *pOut, char cryptochar); int main(void) { char pVAE_E[] = {0x32, 0x0d, 0x16, 0x10, 0x11, 0x05, 0x08, 0x25, 0x08, 0x08, 0x0b, 0x07, 0x21, 0x1c, 0x00}; // VirtualAllocEx encrypted with d char pGTC_E[] = { 0x3d, 0x1f, 0x0e, 0x2e, 0x12, 0x08, 0x1f, 0x1b, 0x1e, 0x39, 0x15, 0x14, 0x0e, 0x1f, 0x02, 0x0e, 0x00}; // GetThreadContext encrypted with z char pVAE_D[strlen(pVAE_E)]; char pGTC_D[strlen(pGTC_E)]; encrStr(pVAE_E, strlen(pVAE_E), pVAE_D, 'd'); encrStr(pGTC_E, strlen(pGTC_E), pGTC_D, 'z'); HMODULE hKernel32 = LoadLibraryA("kernel32.dll"); FARPROC fpGetThreadContext = GetProcAddress(hKernel32, pGTC_D); if (fpGetThreadContext == NULL) { printf("gtc failed.\n"); } _GetThreadContext kernel32GetThreadContext = (_GetThreadContext)fpGetThreadContext; FARPROC fpVirtualAllocEx = GetProcAddress(hKernel32, pVAE_D); if (fpVirtualAllocEx == NULL) { printf("vae failed.\n"); } _VirtualAllocEx kernel32VirtualAllocEx = (_VirtualAllocEx)fpVirtualAllocEx; } void encrStr(char *pStr, int len, char *pOut, char cryptochar) { // zero char must remain, therefore i < len for (int i = 0; i < len; i++) { pOut[i] = pStr[i] ^ cryptochar; printf("%c\n", pOut[i]); } pOut[len] = 0x00; printf("%s\n", pOut); }` 

如果我将解密缓冲区的内存大小增加1,它将独立于分配顺序。 为什么它在其他方面失败?

这是你的代码的两个问题。 首先,您正在使用strlen来确定解密字符串的缓冲区的大小。 这个函数不考虑空终止符字符'\ 0'(在你的情况下是0x00)。 这意味着缓冲区pVAE_DpGTC_D一个元素比包含加密字符串的相应缓冲区少一个元素。 第二个问题是你的encrStr函数。 在这个例子中,你正在使用语句pOut[len] = 0x00;来写入超出数组的范围pOut[len] = 0x00; 。 访问数组的索引在[0,len – 1]的范围内。 这就是你的函数(假设你正在传递一个输出缓冲区,其中有足够的字符串元素和它的空终止符)应该被执行:

 void encrStr(char *pStr, int len, char *pOut, char cryptochar) { // zero char must remain, therefore i < len for (int i = 0; i < len - 1; i++) { pOut[i] = pStr[i] ^ cryptochar; printf("%c\n", pOut[i]); } pOut[len - 1] = 0x00; printf("%s\n", pOut); } 

当你写入超出数组范围的时候,你正在破坏你的数组的堆栈,并且遇到UB(未定义的行为)。 这就是为什么这个样本有时会起作用,而有些时候却不行。 有些编译器会提醒你这个,但其他编译器则不会。 这些在C ++中是微妙的错误,可能会导致你很多头痛。 查看这些链接,了解更多关于未定义行为的信息:

C ++程序员应该知道的所有常见的未定义行为是什么?

https://en.wikipedia.org/wiki/Undefined_behavior