Answer the question
In order to leave comments, you need to log in
Stack corruption around a variable - how to overcome?
There is Windows 7 SP1 Pro 64 bit RUS. Beneath it, Visual C++ 2017 compiled a 32-bit command-line program. EXE calls a function from a DLL.
int main(int argc, char *argv[])
// куча всяких printf();
#ifdef _WIN32
char version[2500];
if (getWindowsVersion(version))
printf("Operating System: %s\n", version);
#endif
#if defined(__USE_LARGEFILE) && defined(__USE_LARGEFILE64)
printf("\nLarge file available: %d offset\n", __USE_FILE_OFFSET64);
#endif
}
bool getWindowsVersion(char* version)
{
int index = 0;
OSVERSIONINFOEX osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (!GetVersionEx((OSVERSIONINFO*)&osvi))
return false;
if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
return false;
// Windows Vista / Windows 7
if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 1)
{
if (osvi.wProductType == VER_NT_WORKSTATION)
{
if (osvi.dwMinorVersion == 1)
index += sprintf(version + index, "Microsoft Windows 7");
else
index += sprintf(version + index, "Microsoft Windows Vista");
uint32_t productType = 0;
HMODULE hKernel = GetModuleHandle("KERNEL32.DLL");
if (hKernel)
{
typedef bool (__stdcall *funcGetProductInfo)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t*);
funcGetProductInfo pGetProductInfo = (funcGetProductInfo)GetProcAddress(hKernel, "GetProductInfo");
if (pGetProductInfo)
pGetProductInfo(6, 0, 0, 0, &productType);
switch (productType)
{
case PRODUCT_STARTER:
{
index += sprintf(version + index, " Starter");
break;
}
case PRODUCT_HOME_BASIC_N:
{
index += sprintf(version + index, " Home Basic N");
break;
}
case PRODUCT_HOME_BASIC:
{
index += sprintf(version + index, " Home Basic");
break;
}
case PRODUCT_HOME_PREMIUM:
{
index += sprintf(version + index, " Home Premium");
break;
}
case PRODUCT_BUSINESS_N:
{
index += sprintf(version + index, " Business N");
break;
}
case PRODUCT_BUSINESS:
{
index += sprintf(version + index, " Business");
break;
}
case PRODUCT_ENTERPRISE:
{
index += sprintf(version + index, " Enterprise");
break;
}
case PRODUCT_ULTIMATE:
{
index += sprintf(version + index, " Ultimate");
break;
}
default:
break;
}
}
}
else if (osvi.wProductType == VER_NT_SERVER)
{
if (osvi.dwMinorVersion == 1)
index += sprintf(version + index, "Microsoft Windows Server 2008 R2");
else
index += sprintf(version + index, "Microsoft Windows Server 2008");
if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
index += sprintf(version + index, " Datacenter Edition");
else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
index += sprintf(version + index, " Enterprise Edition");
else if (osvi.wSuiteMask == VER_SUITE_BLADE)
index += sprintf(version + index, " Web Edition");
else
index += sprintf(version + index, " Standard Edition");
}
}
// Windows Server 2003
else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
{
index += sprintf(version + index, "Microsoft Windows Server 2003");
if (GetSystemMetrics(SM_SERVERR2))
index += sprintf(version + index, " R2");
if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
index += sprintf(version + index, " Datacenter Edition");
else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
index += sprintf(version + index, " Enterprise Edition");
else if (osvi.wSuiteMask == VER_SUITE_BLADE)
index += sprintf(version + index, " Web Edition");
else
index += sprintf(version + index, " Standard Edition");
}
// Windows XP
else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
{
index += sprintf(version + index, "Microsoft Windows XP");
if (GetSystemMetrics(SM_MEDIACENTER))
index += sprintf(version + index, " Media Center Edition");
else if (GetSystemMetrics(SM_STARTER))
index += sprintf(version + index, " Starter Edition");
else if (GetSystemMetrics(SM_TABLETPC))
index += sprintf(version + index, " Tablet PC Edition");
else if (osvi.wSuiteMask & VER_SUITE_PERSONAL)
index += sprintf(version + index, " Home Edition");
else
index += sprintf(version + index, " Professional");
}
// Windows 2000
else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
{
index += sprintf(version + index, "Microsoft Windows 2000");
if (osvi.wProductType == VER_NT_WORKSTATION)
{
index += sprintf(version + index, " Professional");
}
else if (osvi.wProductType == VER_NT_SERVER)
{
if (osvi.wSuiteMask & VER_SUITE_DATACENTER)
index += sprintf(version + index, " Datacenter Server");
else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
index += sprintf(version + index, " Advanced Server");
else
index += sprintf(version + index, " Server");
}
}
// Windows NT 4
else if (osvi.dwMajorVersion == 4)
{
index += sprintf(version + index, "Microsoft Windows NT 4");
if (osvi.wProductType == VER_NT_WORKSTATION)
{
index += sprintf(version + index, " Workstation");
}
else if (osvi.wProductType == VER_NT_SERVER)
{
if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
index += sprintf(version + index, " Server, Enterprise Edition");
else
index += sprintf(version + index, " Server");
}
}
else
{
index += sprintf(version + index, "Microsoft Windows");
}
// Service pack and full version info
if (strlen(osvi.szCSDVersion) > 0)
{
index += sprintf(version + index, " %s", osvi.szCSDVersion);
}
index += sprintf(version + index, " (%d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber & 0xFFFF);
// 64-bit Windows
#ifdef _WIN64
index += sprintf(version + index, "; 64-bit");
#else
bool isWow64 = false;
HMODULE hKernel = GetModuleHandle("kernel32.dll");
if (hKernel)
{
typedef bool (__stdcall *funcIsWow64Process)(void*, bool*);
funcIsWow64Process pIsWow64Process = (funcIsWow64Process)GetProcAddress(hKernel, "IsWow64Process");
if (pIsWow64Process)
{
pIsWow64Process(GetCurrentProcess(), &isWow64);
}
}
if (isWow64)
index += sprintf(version + index, "; 64-bit");
else
index += sprintf(version + index, "; 32-bit");
#endif
index += sprintf(version + index, ")");
return true;
}
Answer the question
In order to leave comments, you need to log in
The convention __stdcall
obliges the function user to pass function parameters on the stack by value, but frees him to clean up the stack after the call. The stack of parameters is cleared by the called function itself.
As a result of changing the signature from BOOL (__stdcall *)(HANDLE, PBOOL)
to, the bool (__stdcall *)(void*, bool*)
compiler thinks about one stack size, and the function code thinks about others.
Therefore, the signatures and calling convention of an imported function must always match the declaration of this function in the imported library! Generally always! Even with the following text.
However, specifically here the problem we have is not a mismatch of types, because.
__stdcall
transfers its result through the register. register is integer or real. For an integer register, the type promotion rule is used. This means that the function, having written the value of the type BOOL
(size 4B), will not spoil anything for the user code, which will read all 4B from the register, taking into account the promotion rule.
The real problem is that a pointer to a one-byte integer ( bool*
) is passed into use as a pointer to a four-byte integer ( BOOL*
or PBOOL
). After all, the called function has a signature BOOL (__stdcall *)(HANDLE, PBOOL)
and works with the second parameter as with a 4B integer by pointer.
This is what leads to damage to the stack and you, the author, are very lucky that you start in debugging, where each value on the stack is framed by a fence, the safety of which is always looked after by a special service code between calls to subroutines.
This is the fence next toisWow64
and was broken as a result of a call IsWow64Process
with an inappropriate length parameter. Change the type isWow64
to BOOL
and everything will be fine, even the handle "kernel32.dll"
can then be released normally.
In getWindowsVersion , pass the size of the buffer.
Replace spintf with snprintf and control the buffer overflow. Yes, snprintf can also return a negative value on error.
If snprintf is called with the first two parameters set to 0, it will calculate the required buffer size for your template and parameters and return it without really copying anything. You can use this opportunity.
Well, if you add the missing include, and fix one line at the beginning of the function (causing a syntax error!) OSVERSIONINFOEX osvi = {};
then everything works ... At least debug, at least release, at least x86, at least amd65. See a piece with corrections (if you combine everything into one file).
Used CLion and MS BuildTools. 2019. The Windows version is the same.
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <windows.h>
bool getWindowsVersion(char* version);
int main(int argc, char* argv[])
{
// куча всяких printf();
#ifdef _WIN32
char version[2500];
if (getWindowsVersion(version))
printf("Operating System: %s\n", version);
#endif
#if defined(__USE_LARGEFILE) && defined(__USE_LARGEFILE64)
printf("\nLarge file available: %d offset\n", __USE_FILE_OFFSET64);
#endif
}
bool getWindowsVersion(char* version)
{
int index = 0;
OSVERSIONINFOEX osvi;
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question