You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1394 lines
35 KiB
1394 lines
35 KiB
/*++
|
|
|
|
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
Badfunc.cpp
|
|
|
|
Abstract:
|
|
|
|
Contains all the bad functions. These are the functions
|
|
that will have Compatibility Fixes applied.
|
|
|
|
Notes:
|
|
|
|
ANSI only - must run on Win9x.
|
|
|
|
History:
|
|
|
|
01/30/01 rparsons Created
|
|
01/10/02 rparsons Revised
|
|
02/13/02 rparsons Use strsafe functions
|
|
|
|
--*/
|
|
#include "demoapp.h"
|
|
|
|
extern APPINFO g_ai;
|
|
|
|
//
|
|
// Pointer to the exported function that we get from our DLL.
|
|
//
|
|
LPFNDEMOAPPEXP DemoAppExpFunc;
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines if we're running Windows 95.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if we are, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadIsWindows95(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// Most applications perform some sort of version check when they first
|
|
// begin. This is usually okay, assuming they perform the check properly.
|
|
// The problem is that instead of doing a greater than comparison, they
|
|
// do an equal/not equal to. In other words, they look just for Win9x,
|
|
// not Win9x or greater. Usually the application will function properly
|
|
// on NT/2K/XP, so this check can be hooked and we can return NT/2K/XP
|
|
// version info.
|
|
//
|
|
OSVERSIONINFO osvi;
|
|
|
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
if (!GetVersionEx(&osvi)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check for Windows 9x (don't do a greater than).
|
|
//
|
|
if ((osvi.dwMajorVersion == 4) &&
|
|
(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays a debug message - this only happens on NT/Windows 2000/XP.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadLoadBogusDll(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// Some applications will display a debug message under Windows NT/2000/XP.
|
|
// For example, they try to locate a DLL that is available on Win9x, but
|
|
// not on NT/2K/XP. If they don't find it, they'll complain, but will
|
|
// most likely still work. In the example below, we try to find a function
|
|
// in a DLL that would return TRUE on Win9x, but will fail on NT/2000/XP.
|
|
//
|
|
HRESULT hr;
|
|
HINSTANCE hInstance;
|
|
char szDll[MAX_PATH];
|
|
const char szCvt32Dll[] = "cvt32.dll";
|
|
|
|
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
|
|
hr = StringCchPrintf(szDll,
|
|
sizeof(szDll),
|
|
"%hs\\%hs",
|
|
g_ai.szSysDir,
|
|
szCvt32Dll);
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// This will fail on NT/2K/XP because cvt32.dll doesn't exist on
|
|
// these platforms. It will fail on Win9x/ME if the FAT32 conversion
|
|
// tools are not installed.
|
|
//
|
|
hInstance = LoadLibrary(szDll);
|
|
|
|
if (!hInstance) {
|
|
MessageBox(g_ai.hWndMain,
|
|
"Debug: Couldn't load CVT32.DLL.",
|
|
0,
|
|
MB_ICONERROR);
|
|
goto exit;
|
|
|
|
}
|
|
|
|
FreeLibrary(hInstance);
|
|
|
|
exit:
|
|
|
|
SetErrorMode(0);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Apps call EnumPrinters using the PRINTER_ENUM_LOCAL flag,
|
|
but expect to get back network printers also. This was a
|
|
***design feature*** in Windows 9x, but not in Windows 2000/XP.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadEnumPrinters(
|
|
void
|
|
)
|
|
{
|
|
BOOL fReturn = FALSE;
|
|
DWORD dwBytesNeeded = 0;
|
|
DWORD dwNumReq = 0;
|
|
DWORD dwLevel = 5;
|
|
LPPRINTER_INFO_5 pPrtInfo = NULL;
|
|
|
|
//
|
|
// Get the required buffer size.
|
|
//
|
|
if (!EnumPrinters(PRINTER_ENUM_LOCAL,
|
|
NULL,
|
|
dwLevel,
|
|
NULL,
|
|
0,
|
|
&dwBytesNeeded,
|
|
&dwNumReq)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pPrtInfo = (LPPRINTER_INFO_5)HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
dwBytesNeeded);
|
|
|
|
if (!pPrtInfo) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now perform the enumeration.
|
|
//
|
|
fReturn = EnumPrinters(PRINTER_ENUM_LOCAL, // types of printer objects to enumerate
|
|
NULL, // name of printer object
|
|
dwLevel, // specifies type of printer info structure
|
|
(LPBYTE)pPrtInfo, // pointer to buffer to receive printer info structures
|
|
dwBytesNeeded, // size, in bytes, of array
|
|
&dwBytesNeeded, // pointer to variable with no. of bytes copied (or required)
|
|
&dwNumReq); // pointer to variable with no. of printer info. structures copied
|
|
|
|
HeapFree(GetProcessHeap(), 0, pPrtInfo);
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Apps call printer APIs passing NULL.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
A handle if we opened the printer successfully.
|
|
|
|
--*/
|
|
HANDLE
|
|
BadOpenPrinter(
|
|
void
|
|
)
|
|
{
|
|
HANDLE hPrinter = NULL;
|
|
|
|
OpenPrinter(NULL, &hPrinter, NULL);
|
|
|
|
return hPrinter;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to delete a registry key that has subkeys.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadDeleteRegistryKey(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// This function demonstrates a difference in the RegDeleteKey API.
|
|
// If an application running on Windows NT/2000/XP attempts to
|
|
// delete a key that has subkeys, the call will fail. This is not
|
|
// the case on Windows 9x/ME.
|
|
//
|
|
HKEY hKey;
|
|
HKEY hSubKey;
|
|
HKEY hRootKey;
|
|
LONG lReturn;
|
|
|
|
//
|
|
// Create the key or open it if it already exists.
|
|
//
|
|
lReturn = RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
DEMO_REG_APP_KEY, // Software\Microsoft\DemoApp2
|
|
0,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
0,
|
|
&hKey,
|
|
NULL);
|
|
|
|
if (ERROR_SUCCESS != lReturn) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now create a subkey underneath the key was just created.
|
|
//
|
|
lReturn = RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
DEMO_REG_APP_SUB_KEY, // Software\Microsoft\DemoApp2\Sub
|
|
0,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
0,
|
|
&hSubKey,
|
|
NULL);
|
|
|
|
if (ERROR_SUCCESS != lReturn) {
|
|
RegCloseKey(hKey);
|
|
return FALSE;
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
RegCloseKey(hSubKey);
|
|
|
|
//
|
|
// Open the key up, but one level higher.
|
|
//
|
|
lReturn = RegOpenKeyEx(HKEY_CURRENT_USER,
|
|
DEMO_REG_APP_ROOT_KEY, // Software\Microsoft
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hRootKey);
|
|
|
|
if (ERROR_SUCCESS != lReturn) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Now try to delete our key.
|
|
//
|
|
lReturn = RegDeleteKey(hRootKey, "DemoApp2");
|
|
|
|
RegCloseKey(hRootKey);
|
|
|
|
return (lReturn == ERROR_SUCCESS ? TRUE : FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reboots the computer, if desired.
|
|
|
|
Arguments:
|
|
|
|
fReboot - A flag to indicate if the PC should be rebooted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadRebootComputer(
|
|
IN BOOL fReboot
|
|
)
|
|
{
|
|
//
|
|
// On Windows 9x, there is essentially no security, so anyone is able
|
|
// to reboot the computer. On Windows NT/2000/XP, things are a bit
|
|
// more restrictive. If an application wants to do a reboot at the
|
|
// end of a setup, it needs adjust the privileges of the user by
|
|
// calling the AdjustTokenPrivileges API.
|
|
//
|
|
// In this case, we evaluate the flag passed and decide if we should
|
|
// adjust the privileges.
|
|
//
|
|
// Obviously applications should not do this (evaluate some kind of flag)
|
|
// as this is just for demonstration purposes.
|
|
//
|
|
if (!fReboot) {
|
|
ExitWindowsEx(EWX_REBOOT, 0);
|
|
} else {
|
|
ShutdownSystem(FALSE, TRUE);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to get available disk space.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE if enough disk space is available, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadGetFreeDiskSpace(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// GetDiskFreeSpace returns a maximum total size and maximum free
|
|
// size of 2GB. For example, if you have a 6GB volume and 5GB are
|
|
// free, GetDiskFreeSpace reports that the drive's total size is 2GB
|
|
// and 2GB are free. This limitation originated because the first version
|
|
// of Windows 95 only supported volumes of up to 2GB in size. Windows 95
|
|
// OSR2 and later versions, including Windows 98, support volumes larger
|
|
// than 2GB. GetDiskFreeSpaceEx does not have a 2GB limitation, thus
|
|
// it is preferred over GetDiskFreeSpace. GetDiskFreeSpace returns a
|
|
// maximum of 65536 for the numbers of total clusters and free clusters
|
|
// to maintain backward compatibility with the first version of Windows 95.
|
|
// The first version of Windows 95 supports only the FAT16 file system,
|
|
// which has a maximum of 65536 clusters. If a FAT32 volume has more than
|
|
// 65536 clusters, the number of clusters are reported as 65536 and the
|
|
// number of sectors per cluster are adjusted so that the size of volumes
|
|
// smaller than 2GB may be calculated correctly. What this means is that
|
|
// you should not use GetDiskFreeSpace to return the true geometry
|
|
// information for FAT32 volumes.
|
|
//
|
|
// In case you're wondering, always use the GetDiskFreeSpaceEx API
|
|
// on Windows 2000/XP.
|
|
//
|
|
BOOL fResult = FALSE;
|
|
HRESULT hr;
|
|
char szWinDir[MAX_PATH];
|
|
char* lpDrive = NULL;
|
|
|
|
DWORD dwSectPerClust = 0,
|
|
dwBytesPerSect = 0,
|
|
dwFreeClusters = 0,
|
|
dwTotalClusters = 0,
|
|
dwTotalBytes = 0,
|
|
dwFreeBytes = 0,
|
|
dwFreeMBs = 0;
|
|
|
|
//
|
|
// Get the drive that windows is installed on and pass it
|
|
// to the GetDiskFreeSpace call.
|
|
//
|
|
hr = StringCchCopy(szWinDir, sizeof(szWinDir), g_ai.szWinDir);
|
|
|
|
if (FAILED(hr)) {
|
|
return FALSE;
|
|
}
|
|
|
|
lpDrive = strstr(szWinDir, "\\");
|
|
|
|
//
|
|
// Make the buffer just 'C:\' (or whatever).
|
|
//
|
|
if (lpDrive) {
|
|
*++lpDrive = '\0';
|
|
}
|
|
|
|
fResult = GetDiskFreeSpace(szWinDir,
|
|
&dwSectPerClust,
|
|
&dwBytesPerSect,
|
|
&dwFreeClusters,
|
|
&dwTotalClusters);
|
|
|
|
if (fResult) {
|
|
//
|
|
// Normally we would use the __int64 data type,
|
|
// but we want the calculations to fail for demonstration.
|
|
//
|
|
dwTotalBytes = dwTotalClusters * dwSectPerClust * dwBytesPerSect;
|
|
|
|
dwFreeBytes = dwFreeClusters * dwSectPerClust * dwBytesPerSect;
|
|
|
|
dwFreeMBs = dwFreeBytes / (1024 * 1024);
|
|
|
|
if (dwFreeMBs < 100) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays the readme, if desired.
|
|
|
|
Arguments:
|
|
|
|
fDisplay - A flag to indicate if the readme
|
|
should be displayed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadDisplayReadme(
|
|
IN BOOL fDisplay
|
|
)
|
|
{
|
|
// Windows NT/2000/XP contains a new registry data type, REG_EXPAND_SZ,
|
|
// that was not found in Win9x. The type contains a variable that needs
|
|
// to be expanded before it can be referred to. For example,
|
|
// %ProgramFiles% would expand to something like C:\Program Files.
|
|
// Most applications are unaware of this data type and therefore
|
|
// don't handle it properly.
|
|
//
|
|
CRegistry creg;
|
|
HRESULT hr;
|
|
LPSTR lpWordpad = NULL;
|
|
char szExpWordpad[MAX_PATH];
|
|
char szCmdLineArgs[MAX_PATH];
|
|
|
|
lpWordpad = creg.GetString(HKEY_LOCAL_MACHINE,
|
|
REG_WORDPAD,
|
|
NULL); // we want the (Default) value
|
|
|
|
if (!lpWordpad) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// At this point, the path looks something like this:
|
|
// "%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE"
|
|
// If the user wants to see the readme (bad functionality
|
|
// is disabled), we're going to expand the variable to
|
|
// get the true path, then launch wordpad
|
|
// If not, we'll try to display the readme using the bogus
|
|
// path.
|
|
|
|
if (fDisplay) {
|
|
//
|
|
// Expand the environment strings, then build a path to our
|
|
// readme file, then launch it.
|
|
//
|
|
ExpandEnvironmentStrings(lpWordpad, szExpWordpad, MAX_PATH);
|
|
|
|
hr = StringCchPrintf(szCmdLineArgs,
|
|
sizeof(szCmdLineArgs),
|
|
"\"%hs\\demoapp.txt\"",
|
|
g_ai.szDestDir);
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
BadCreateProcess(szExpWordpad, szCmdLineArgs, TRUE);
|
|
|
|
} else {
|
|
//
|
|
// Do all the work above, but don't expand the data
|
|
// We don't check the return of the CreateProcess call,
|
|
// so the user simply doesn't get to see the readme,
|
|
// and we don't display an error.
|
|
// This is consistent with most setup applications.
|
|
//
|
|
hr = StringCchPrintf(szCmdLineArgs,
|
|
sizeof(szCmdLineArgs),
|
|
"\"%hs\\demoapp.txt\"",
|
|
g_ai.szDestDir);
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// This will fail. See the BadCreateProcess
|
|
// function for details.
|
|
//
|
|
BadCreateProcess(lpWordpad, szCmdLineArgs, FALSE);
|
|
}
|
|
|
|
exit:
|
|
|
|
if (lpWordpad) {
|
|
creg.Free(lpWordpad);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Displays the help file, if desired.
|
|
|
|
Arguments:
|
|
|
|
fDisplay - A flag to indicate if the help file
|
|
should be displayed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadLaunchHelpFile(
|
|
IN BOOL fDisplay
|
|
)
|
|
{
|
|
char szCmdLineArgs[MAX_PATH];
|
|
char szExeToLaunch[MAX_PATH];
|
|
const char szWinHlp[] = "winhelp.exe";
|
|
const char szWinHlp32[] = "winhlp32.exe";
|
|
HRESULT hr;
|
|
|
|
if (fDisplay) {
|
|
hr = StringCchPrintf(szExeToLaunch,
|
|
sizeof(szExeToLaunch),
|
|
"%hs\\%hs",
|
|
g_ai.szSysDir,
|
|
szWinHlp32);
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
hr = StringCchPrintf(szCmdLineArgs,
|
|
sizeof(szCmdLineArgs),
|
|
"%hs\\demoapp.hlp",
|
|
g_ai.szCurrentDir);
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
} else {
|
|
hr = StringCchPrintf(szExeToLaunch,
|
|
sizeof(szExeToLaunch),
|
|
"%hs\\%hs",
|
|
g_ai.szWinDir,
|
|
szWinHlp);
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
hr = StringCchPrintf(szCmdLineArgs,
|
|
sizeof(szCmdLineArgs),
|
|
"%hs\\demoapp.hlp",
|
|
g_ai.szCurrentDir);
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
BadCreateProcess(szExeToLaunch, szCmdLineArgs, fDisplay);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a shortcut on the desktop, if desired.
|
|
|
|
Arguments:
|
|
|
|
fCorrectWay - Indicates whether the shortcut should be created.
|
|
lpDirFileName - Directory and filename where the shortcut
|
|
points to.
|
|
lpWorkingDir - Working directory (optional).
|
|
lpDisplayName - Display name for the shortcut.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadCreateShortcut(
|
|
IN BOOL fCorrectWay,
|
|
IN LPSTR lpDirFileName,
|
|
IN LPCSTR lpWorkingDir,
|
|
IN LPSTR lpDisplayName
|
|
)
|
|
{
|
|
//
|
|
// Hard-coded paths are simply a bad practice. APIs are available that
|
|
// return proper locations for common folders. Examples are the
|
|
// Program Files, Windows, and Temp directories. SHGetFolderPath,
|
|
// GetWindowsDirectory, and GetTempPath would provide the correct path
|
|
// in each case. Hard-coded paths should never be used.
|
|
//
|
|
CShortcut cs;
|
|
|
|
if (!fCorrectWay) {
|
|
//
|
|
// A hard-coded path is very bad! We should use SHGetFolderPath
|
|
// to get the correct path.
|
|
//
|
|
const char szDirName[] = "C:\\WINDOWS\\DESKTOP";
|
|
|
|
cs.CreateShortcut(szDirName,
|
|
lpDirFileName,
|
|
lpDisplayName,
|
|
"-runapp",
|
|
(LPCSTR)lpWorkingDir,
|
|
SW_SHOWNORMAL);
|
|
|
|
} else {
|
|
//
|
|
// Create the shortcut properly.
|
|
// Notice that we pass a CSIDL for the "common" desktop
|
|
// directory. We want this to be displayed to All Users.
|
|
//
|
|
cs.CreateShortcut(lpDirFileName,
|
|
lpDisplayName,
|
|
"-runapp",
|
|
lpWorkingDir,
|
|
SW_SHOWNORMAL,
|
|
CSIDL_COMMON_DESKTOPDIRECTORY);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Demonstrates an AV because we used a fixed-size
|
|
buffer for a call to GetFileVersionInfo.
|
|
|
|
|
|
Arguments:
|
|
|
|
fCorrect - A flag to indicate if we should work properly.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadBufferOverflow(
|
|
IN BOOL fCorrect
|
|
)
|
|
{
|
|
//
|
|
// Although this problem has only been seen in one application, it's
|
|
// worth mentioning here. On Win9x/ME, version resources on DLLs are much
|
|
// smaller than ones on NT/2000/XP. Specifically, in the case below,
|
|
// the Windows 2000/XP resource size is 6 times larger than the 9x/ME size!
|
|
// One particular application used a stack-based buffer (which is fixed in
|
|
// size) for the call to GetFileVersionInfo. This worked properly on
|
|
// Win9x/ME because the required size was very small. But the required size
|
|
// is larger on NT/2000/XP, thus the buffer gets overwritten, causing stack
|
|
// corruption. The proper way is to call the GetFileVersionInfoSize API,
|
|
// and then allocate a heap-based buffer of an approriate size.
|
|
//
|
|
//
|
|
DWORD cbReqSize = 0;
|
|
DWORD dwHandle = 0;
|
|
DWORDLONG dwVersion = 0;
|
|
UINT nLen = 0;
|
|
VS_FIXEDFILEINFO* pffi;
|
|
char szDll[MAX_PATH];
|
|
HRESULT hr;
|
|
PVOID pBadBlock = NULL;
|
|
PVOID pVersionBlock = NULL;
|
|
HANDLE hHeap = NULL;
|
|
SYSTEM_INFO si;
|
|
|
|
hr = StringCchPrintf(szDll,
|
|
sizeof(szDll),
|
|
"%hs\\%hs",
|
|
g_ai.szSysDir,
|
|
szDll);
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the size of the buffer that will be required for a call to
|
|
// GetFileVersionInfo.
|
|
//
|
|
cbReqSize = GetFileVersionInfoSize(szDll, &dwHandle);
|
|
|
|
if (!cbReqSize == 0) {
|
|
return;
|
|
}
|
|
|
|
if (fCorrect) {
|
|
//
|
|
// If we're doing this properly, allocate memory from the heap.
|
|
//
|
|
pVersionBlock = HeapAlloc(GetProcessHeap(),
|
|
HEAP_ZERO_MEMORY,
|
|
cbReqSize);
|
|
|
|
if (!pVersionBlock) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the version info and query the root block.
|
|
//
|
|
if (!GetFileVersionInfo(szDll,
|
|
dwHandle,
|
|
cbReqSize,
|
|
pVersionBlock)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VerQueryValue(pVersionBlock,
|
|
"\\",
|
|
(LPVOID*)&pffi,
|
|
&nLen))
|
|
{
|
|
|
|
dwVersion = (((DWORDLONG)pffi->dwFileVersionMS) << 32) +
|
|
pffi->dwFileVersionLS;
|
|
}
|
|
} else {
|
|
//
|
|
// Use a non-growable heap that's way too small.
|
|
//
|
|
GetSystemInfo(&si);
|
|
|
|
//
|
|
// Create a block and allocate memory from it.
|
|
//
|
|
hHeap = HeapCreate(0,
|
|
si.dwPageSize,
|
|
504); // this was the size needed on Win9x
|
|
|
|
if (!hHeap) {
|
|
return;
|
|
}
|
|
|
|
pBadBlock = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 504);
|
|
|
|
if (!pBadBlock) {
|
|
HeapDestroy(hHeap);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the version info, passing the buffer (which is too small)
|
|
// with a size argument that's incorrect.
|
|
//
|
|
if (!GetFileVersionInfo(szDll,
|
|
dwHandle,
|
|
3072, // this is the size needed on Win2K/XP
|
|
pBadBlock))
|
|
{
|
|
goto cleanup;
|
|
}
|
|
|
|
if (VerQueryValue(pBadBlock,
|
|
"\\",
|
|
(LPVOID*)&pffi,
|
|
&nLen))
|
|
{
|
|
|
|
dwVersion = (((DWORDLONG)pffi->dwFileVersionMS) << 32) +
|
|
pffi->dwFileVersionLS;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (pBadBlock) {
|
|
HeapFree(hHeap, 0, pBadBlock);
|
|
}
|
|
|
|
if (pVersionBlock) {
|
|
HeapFree(GetProcessHeap(), 0, pVersionBlock);
|
|
}
|
|
|
|
if (hHeap) {
|
|
HeapDestroy(hHeap);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Demonstrates an AV because we used a non-growable heap
|
|
during file operations.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadCorruptHeap(
|
|
void
|
|
)
|
|
{
|
|
SYSTEM_INFO SystemInfo;
|
|
HANDLE hHeap;
|
|
HRESULT hr;
|
|
char* pszBigBlock = NULL;
|
|
char* pszBigBlock1 = NULL;
|
|
char* pszTestInputFile = NULL;
|
|
char* pszTestOutputFile = NULL;
|
|
FILE* hFile = NULL;
|
|
FILE* hFileOut = NULL;
|
|
DWORD dwCount = 0;
|
|
int nCount = 0;
|
|
const char szAlpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
|
const char szMsg[] = "This is a corrupted heap -- Heap Overflow! Bad Programming!";
|
|
|
|
//
|
|
// Get system page size.
|
|
//
|
|
GetSystemInfo(&SystemInfo);
|
|
|
|
//
|
|
// Create a local, non-growable heap.
|
|
//
|
|
hHeap = HeapCreate(0, SystemInfo.dwPageSize, 0x7FFF8);
|
|
|
|
if (!hHeap) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Allocate memory from the local heap.
|
|
//
|
|
pszBigBlock = (char*)HeapAlloc(hHeap, 0, 0x6FFF8);
|
|
pszBigBlock1 = (char*)HeapAlloc(hHeap, 0, 0xFFF);
|
|
pszTestInputFile = (char*)HeapAlloc(hHeap, 0, MAX_PATH);
|
|
pszTestOutputFile = (char*)HeapAlloc(hHeap, 0, MAX_PATH);
|
|
|
|
if (!pszBigBlock || !pszBigBlock1 || !pszTestInputFile || !pszTestOutputFile) {
|
|
HeapDestroy(hHeap);
|
|
return;
|
|
}
|
|
|
|
hr = StringCchCopy(pszBigBlock1, 0xFFF, szMsg);
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
GetCurrentDirectory(MAX_PATH, pszTestInputFile);
|
|
|
|
//
|
|
// Set up the file names.
|
|
//
|
|
hr = StringCchCopy(pszTestOutputFile, MAX_PATH, pszTestInputFile);
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
hr = StringCchCat(pszTestInputFile, MAX_PATH, "\\test.txt");
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
hr = StringCchCat(pszTestOutputFile, MAX_PATH, "\\test_out.txt");
|
|
|
|
if (FAILED(hr)) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Open the file for writing.
|
|
//
|
|
hFileOut = fopen(pszTestInputFile, "wt");
|
|
|
|
if (!hFileOut) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Put some junk data in the file - about 1.5 MBs worth.
|
|
//
|
|
for (dwCount = 0; dwCount < 0xABCD; dwCount++) {
|
|
fwrite(szAlpha, sizeof(char), 36, hFileOut);
|
|
}
|
|
|
|
fclose(hFileOut);
|
|
|
|
//
|
|
// Open the files for reading & writing.
|
|
//
|
|
hFileOut = fopen(pszTestInputFile, "r");
|
|
|
|
if (!hFileOut) {
|
|
goto exit;
|
|
}
|
|
|
|
hFile = fopen(pszTestOutputFile, "w");
|
|
|
|
if (!hFile) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Read some data from the large file.
|
|
//
|
|
nCount = fread(pszBigBlock, sizeof(char), 0x6FFFF, hFileOut);
|
|
|
|
if (!nCount) {
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Write some test data to a separate file.
|
|
//
|
|
nCount = fwrite(pszBigBlock, sizeof(char), nCount, hFile);
|
|
|
|
exit:
|
|
|
|
if (hFile) {
|
|
fclose(hFile);
|
|
}
|
|
|
|
if (hFileOut) {
|
|
fclose(hFileOut);
|
|
}
|
|
|
|
HeapFree(hHeap, 0, pszBigBlock);
|
|
HeapFree(hHeap, 0, pszBigBlock1);
|
|
HeapFree(hHeap, 0, pszTestInputFile);
|
|
HeapFree(hHeap, 0, pszTestOutputFile);
|
|
HeapDestroy(hHeap);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads a library, frees it, then tries to call an exported
|
|
function. This will cause an access violation.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
void
|
|
BadLoadLibrary(
|
|
void
|
|
)
|
|
{
|
|
char szDll[MAX_PATH];
|
|
const char szDemoDll[] = "demodll.dll";
|
|
HINSTANCE hInstance;
|
|
HRESULT hr;
|
|
|
|
hr = StringCchPrintf(szDll,
|
|
sizeof(szDll),
|
|
"%hs\\%hs",
|
|
g_ai.szCurrentDir,
|
|
szDemoDll);
|
|
|
|
if (FAILED(hr)) {
|
|
return;
|
|
}
|
|
|
|
hInstance = LoadLibrary(szDll);
|
|
|
|
if (!hInstance) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the address of the function.
|
|
//
|
|
DemoAppExpFunc = (LPFNDEMOAPPEXP)GetProcAddress(hInstance, "DemoAppExp");
|
|
|
|
FreeLibrary(hInstance);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Uses the WriteFile API and passes NULL for lpBuffer.
|
|
This is legal on Win9x, not on NT/2000/XP.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadWriteToFile(
|
|
void
|
|
)
|
|
{
|
|
//
|
|
// On Win9x/ME, applications could call the WriteFile API and pass
|
|
// a NULL for the lpBuffer argument. This would be interpreted as
|
|
// zeros. On NT/2000, this is not the case and this call will fail.
|
|
//
|
|
char szTempFile[MAX_PATH];
|
|
char szTempPath[MAX_PATH];
|
|
UINT uReturn;
|
|
HANDLE hFile;
|
|
BOOL fReturn = FALSE;
|
|
DWORD cbBytesWritten;
|
|
DWORD cchSize;
|
|
|
|
cchSize = GetTempPath(sizeof(szTempPath), szTempPath);
|
|
|
|
if (cchSize > sizeof(szTempPath) || cchSize == 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Build a path to a temp file.
|
|
//
|
|
uReturn = GetTempFileName(szTempPath, "_dem", 0, szTempFile);
|
|
|
|
if (!uReturn) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Get a handle to the newly created file.
|
|
//
|
|
hFile = CreateFile(szTempFile,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Try to write some data to the file, but pass a NULL buffer.
|
|
//
|
|
fReturn = WriteFile(hFile, NULL, 10, &cbBytesWritten, NULL);
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper function for CreateProcess. Doesn't initialize the
|
|
STARTUPINFO structure properly, causing the process not to
|
|
be launched.
|
|
|
|
Arguments:
|
|
|
|
lpApplicationName - Application name to launch.
|
|
lpCommandLine - Command line arguments to pass to the EXE.
|
|
fLaunch - A flag to indicate if we should work properly.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadCreateProcess(
|
|
IN LPSTR lpApplicationName,
|
|
IN LPSTR lpCommandLine,
|
|
IN BOOL fLaunch
|
|
)
|
|
{
|
|
//
|
|
// On Win9x/ME, the CreateProcess API isn't overly concerned with the
|
|
// members of the STARTUPINFO structure. On NT/2000/XP, more parameter
|
|
// validation is done, and if the parameters are bad or incorrect, the
|
|
// call fails. This causes an application error message to appear.
|
|
//
|
|
BOOL fReturn = FALSE;
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if (!lpApplicationName) {
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
|
ZeroMemory(&si, sizeof(STARTUPINFO));
|
|
si.cb = sizeof(STARTUPINFO);
|
|
|
|
if (fLaunch) {
|
|
fReturn = CreateProcess(lpApplicationName,
|
|
lpCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi);
|
|
|
|
} else {
|
|
//
|
|
// Provide bad values for structure members.
|
|
//
|
|
si.lpReserved = "Never store data in reserved areas...";
|
|
si.cbReserved2 = 1;
|
|
si.lpReserved2 = (LPBYTE)"Microsoft has these reserved for a reason...";
|
|
si.lpDesktop = "Bogus desktop!!!!";
|
|
|
|
fReturn = CreateProcess(lpApplicationName,
|
|
lpCommandLine,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
&si,
|
|
&pi);
|
|
}
|
|
|
|
if (pi.hThread) {
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
|
|
if (pi.hProcess) {
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
return fReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to save/retrieve our position information to
|
|
the registry. We perform this operation in HKLM, not
|
|
HKCU. This demonstrates what happens when an application
|
|
attempts to save information to the registry when the user
|
|
does not have permissions to do so because they're not an
|
|
administrator.
|
|
|
|
Arguments:
|
|
|
|
fSave - If true, indicates we're saving data.
|
|
*lppt - A POINT structure that contains/receives our data.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadSaveToRegistry(
|
|
IN BOOL fSave,
|
|
IN OUT POINT* lppt
|
|
)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
HKEY hKey;
|
|
HKEY hKeyRoot;
|
|
DWORD cbSize = 0, dwDisposition = 0;
|
|
LONG lRetVal;
|
|
char szKeyName[] = "DlgCoordinates";
|
|
|
|
//
|
|
// Initialize our coordinates in case there's no data there.
|
|
//
|
|
if (!fSave) {
|
|
lppt->x = lppt->y = 0;
|
|
}
|
|
|
|
if (g_ai.fEnableBadFunc) {
|
|
hKeyRoot = HKEY_LOCAL_MACHINE;
|
|
} else {
|
|
hKeyRoot = HKEY_CURRENT_USER;
|
|
}
|
|
|
|
//
|
|
// Open the registry key (or create it if the first time being used).
|
|
//
|
|
lRetVal = RegCreateKeyEx(hKeyRoot,
|
|
REG_APP_KEY,
|
|
0,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
0,
|
|
&hKey,
|
|
&dwDisposition);
|
|
|
|
if (ERROR_SUCCESS != lRetVal) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Save or retrieve our coordinates.
|
|
//
|
|
if (fSave) {
|
|
lRetVal = RegSetValueEx(hKey,
|
|
szKeyName,
|
|
0,
|
|
REG_BINARY,
|
|
(const BYTE*)lppt,
|
|
sizeof(*lppt));
|
|
|
|
if (ERROR_SUCCESS != lRetVal) {
|
|
goto exit;
|
|
}
|
|
|
|
} else {
|
|
cbSize = sizeof(*lppt);
|
|
lRetVal = RegQueryValueEx(hKey,
|
|
szKeyName,
|
|
0,
|
|
0,
|
|
(LPBYTE)lppt,
|
|
&cbSize);
|
|
|
|
if (ERROR_SUCCESS != lRetVal) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
bReturn = TRUE;
|
|
|
|
exit:
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempts to create a temp file (that won't be used)
|
|
in the Windows directory. We do this to demonstrate
|
|
what happens when an application attempts to write
|
|
to a directory that the user does not have access to
|
|
because they are not an administrator.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE on success, FALSE otherwise.
|
|
|
|
--*/
|
|
BOOL
|
|
BadCreateTempFile(
|
|
void
|
|
)
|
|
{
|
|
char szTempFile[MAX_PATH];
|
|
HANDLE hFile;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// We return TRUE if these functions fail because returning
|
|
// FALSE causes an error to be displayed, and Compatiblity
|
|
// Fixes will not correct the error.
|
|
//
|
|
hr = StringCchPrintf(szTempFile,
|
|
sizeof(szTempFile),
|
|
"%hs\\demotemp.tmp",
|
|
g_ai.szWinDir);
|
|
|
|
if (FAILED(hr)) {
|
|
return TRUE;
|
|
}
|
|
|
|
hFile = CreateFile(szTempFile,
|
|
GENERIC_ALL,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE == hFile) {
|
|
return FALSE;
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return TRUE;
|
|
}
|