Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

752 lines
25 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
init.c
Abstract:
This is the initialization module for the INSTALER program.
Author:
Steve Wood (stevewo) 09-Aug-1994
Revision History:
--*/
#include "instaler.h"
#if TRACE_ENABLED
ULONG EnabledTraceEvents = DBG_MASK_INTERNALERROR |
DBG_MASK_MEMORYERROR;
#endif
MODULE_INFO ModuleInfo[ MAXIMUM_MODULE_INDEX ] = {
{L"NTDLL", NULL},
{L"KERNEL32", NULL},
{L"ADVAPI32", NULL},
{L"USER32", NULL}
};
API_INFO ApiInfo[ MAXIMUM_API_INDEX ] = {
{NTDLL_MODULE_INDEX, "NtCreateFile", NULL, NtCreateFileHandler , sizeof( CREATEFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtOpenFile", NULL, NtOpenFileHandler , sizeof( OPENFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtDeleteFile", NULL, NtDeleteFileHandler , sizeof( DELETEFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtSetInformationFile", NULL, NtSetInformationFileHandler , sizeof( SETINFORMATIONFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtQueryAttributesFile", NULL, NtQueryAttributesFileHandler , sizeof( QUERYATTRIBUTESFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtQueryDirectoryFile", NULL, NtQueryDirectoryFileHandler , sizeof( QUERYDIRECTORYFILE_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtCreateKey", NULL, NtCreateKeyHandler , sizeof( CREATEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtOpenKey", NULL, NtOpenKeyHandler , sizeof( OPENKEY_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtDeleteKey", NULL, NtDeleteKeyHandler , sizeof( DELETEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtSetValueKey", NULL, NtSetValueKeyHandler , sizeof( SETVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtDeleteValueKey", NULL, NtDeleteValueKeyHandler , sizeof( DELETEVALUEKEY_PARAMETERS ) , sizeof( NTSTATUS )},
{NTDLL_MODULE_INDEX, "NtClose", NULL, NtCloseHandleHandler , sizeof( CLOSEHANDLE_PARAMETERS ) , sizeof( NTSTATUS )},
{KERNEL32_MODULE_INDEX, "GetVersion", NULL, GetVersionHandler , 0 , sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "GetVersionExW", NULL, GetVersionExWHandler , sizeof( GETVERSIONEXW_PARAMETERS ) , sizeof( BOOL )},
{KERNEL32_MODULE_INDEX, "SetCurrentDirectoryA", NULL, SetCurrentDirectoryAHandler , sizeof( SETCURRENTDIRECTORYA_PARAMETERS ) , sizeof( BOOL )},
{KERNEL32_MODULE_INDEX, "SetCurrentDirectoryW", NULL, SetCurrentDirectoryWHandler , sizeof( SETCURRENTDIRECTORYW_PARAMETERS ) , sizeof( BOOL )},
{KERNEL32_MODULE_INDEX, "WritePrivateProfileStringA", NULL, WritePrivateProfileStringAHandler , sizeof( WRITEPRIVATEPROFILESTRINGA_PARAMETERS ) , sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "WritePrivateProfileStringW", NULL, WritePrivateProfileStringWHandler , sizeof( WRITEPRIVATEPROFILESTRINGW_PARAMETERS ) , sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionA", NULL, WritePrivateProfileSectionAHandler, sizeof( WRITEPRIVATEPROFILESECTIONA_PARAMETERS ) , sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "WritePrivateProfileSectionW", NULL, WritePrivateProfileSectionWHandler, sizeof( WRITEPRIVATEPROFILESECTIONW_PARAMETERS ) , sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "GetPrivateProfileStringW", NULL, NULL, 0, sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "GetPrivateProfileStringA", NULL, NULL, 0, sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionW", NULL, NULL, 0, sizeof( DWORD )},
{KERNEL32_MODULE_INDEX, "GetPrivateProfileSectionA", NULL, NULL, 0, sizeof( DWORD )},
{ADVAPI32_MODULE_INDEX, "RegConnectRegistryW", NULL, RegConnectRegistryWHandler , sizeof( REGCONNECTREGISTRYW_PARAMETERS ) , sizeof( DWORD )},
{USER32_MODULE_INDEX, "ExitWindowsEx", NULL, ExitWindowsExHandler , sizeof( EXITWINDOWSEX_PARAMETERS ) , sizeof( BOOL )}
};
BOOLEAN
LoadApplicationForDebug(
PWSTR CommandLine
);
BOOLEAN
LoadDriveLetterDefinitions( VOID );
WCHAR WindowsDirectoryBuffer[ MAX_PATH ];
UNICODE_STRING WindowsDirectory;
BOOLEAN
InitializeInstaler(
int argc,
char *argv[]
)
{
UINT ModuleIndex, ApiIndex;
PWSTR CommandLine;
UINT i;
LPSTR s;
UCHAR c;
SYSTEM_INFO SystemInfo;
BOOLEAN ProcessingDebugSwitch;
InitCommonCode( "INSTALER",
"[-9] [-r] [-dAE] CommandLine...",
"-9 specifies that GetVersion should lie and tell application\n"
" that it is running on Windows 95\n"
"-r specifies that attempts to do a wildcard scan of the root\n"
" directory of a drive should fail.\n"
"-d specifies one or more debugging message options\n"
" A - shows all errors\n"
" E - shows all debug events\n"
" C - shows all create API calls\n"
);
AppHeap = GetProcessHeap();
InstalerModuleHandle = GetModuleHandle( NULL );
if (GetWindowsDirectoryW( WindowsDirectoryBuffer, MAX_PATH ) == 0) {
return FALSE;
}
RtlInitUnicodeString( &WindowsDirectory, WindowsDirectoryBuffer );
GetSystemInfo( &SystemInfo );
TemporaryBufferLengthGrowth = SystemInfo.dwPageSize;
TemporaryBufferMaximumLength = ((128 * 1024) + SystemInfo.dwAllocationGranularity - 1) &
~SystemInfo.dwAllocationGranularity;
TemporaryBuffer = VirtualAlloc( NULL,
TemporaryBufferMaximumLength,
MEM_RESERVE,
PAGE_READWRITE
);
if (TemporaryBuffer == NULL) {
return FALSE;
}
InitializeListHead( &ProcessListHead );
InitializeListHead( &FileReferenceListHead );
InitializeListHead( &KeyReferenceListHead );
InitializeListHead( &IniReferenceListHead );
InitializeCriticalSection( &BreakTable );
//
// Loop over the table of modules and make sure each is loaded in our
// address space so we can access their export tables.
//
for (ModuleIndex=0; ModuleIndex<MAXIMUM_MODULE_INDEX; ModuleIndex++) {
ModuleInfo[ ModuleIndex ].ModuleHandle =
GetModuleHandle( ModuleInfo[ ModuleIndex ].ModuleName );
if (ModuleInfo[ ModuleIndex ].ModuleHandle == NULL) {
//
// Bail if any are missing.
//
DeclareError( INSTALER_MISSING_MODULE,
GetLastError(),
ModuleInfo[ ModuleIndex ].ModuleName
);
return FALSE;
}
}
//
// Now loop over the table of entry points that we care about and
// get the address of each entry point from the
for (ApiIndex=0; ApiIndex<MAXIMUM_API_INDEX; ApiIndex++) {
ModuleIndex = ApiInfo[ ApiIndex ].ModuleIndex;
ApiInfo[ ApiIndex ].EntryPointAddress =
LookupEntryPointInIAT( ModuleInfo[ ModuleIndex ].ModuleHandle,
ApiInfo[ ApiIndex ].EntryPointName
);
if (ApiInfo[ ApiIndex ].EntryPointAddress == NULL) {
//
// Bail if we are unable to get the address of any of them
//
DeclareError( INSTALER_MISSING_ENTRYPOINT,
GetLastError(),
ApiInfo[ ApiIndex ].EntryPointName,
ModuleInfo[ ModuleIndex ].ModuleName
);
return FALSE;
}
}
CommandLine = NULL;
ProcessingDebugSwitch = FALSE;
AskUserOnce = FALSE;
while (--argc) {
s = *++argv;
if (*s == '-' || *s == '/') {
while (*++s) {
switch( tolower( *s ) ) {
case '9':
DefaultGetVersionToWin95 = TRUE;
break;
case 'r':
FailAllScansOfRootDirectories = TRUE;
break;
#if TRACE_ENABLED
case 'd':
ProcessingDebugSwitch = TRUE;
break;
case 'a':
if (ProcessingDebugSwitch) {
EnabledTraceEvents = 0xFFFFFFFF;
break;
}
case 'e':
if (ProcessingDebugSwitch) {
EnabledTraceEvents |= DBG_MASK_DBGEVENT;
break;
}
case 'c':
if (ProcessingDebugSwitch) {
EnabledTraceEvents |= DBG_MASK_CREATEEVENT;
break;
}
#endif
default:
if (!ProcessingDebugSwitch) {
CommonSwitchProcessing( &argc, &argv, *s );
} else {
Usage( "Invalid debug switch (-%c)", (ULONG)*s );
}
break;
}
}
ProcessingDebugSwitch = FALSE;
} else if (!CommonArgProcessing( &argc, &argv )) {
CommandLine = GetCommandLine();
while (*CommandLine && *CommandLine <= L' ') {
CommandLine += 1;
}
while (*CommandLine && *CommandLine > L' ') {
CommandLine += 1;
}
CommandLine = wcsstr( CommandLine, GetArgAsUnicode( s ) );
break;
}
}
if (ImlPath == NULL) {
Usage( "Must specify an installation name as first argument", 0 );
}
if (CommandLine == NULL) {
Usage( "Command line missing", 0 );
}
pImlNew = CreateIml( ImlPath, FALSE );
if (pImlNew == NULL) {
FatalError( "Unable to create %ws (%u)\n",
(ULONG)ImlPath,
GetLastError()
);
}
//
// Ready to roll. Cache the drive letter information and then load
// the application with the DEBUG option so we can watch what it does
//
if (!LoadDriveLetterDefinitions() ||
!LoadApplicationForDebug( CommandLine )
) {
return FALSE;
}
return TRUE;
}
BOOLEAN
LoadApplicationForDebug(
PWSTR CommandLine
)
{
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
PWSTR ImageFilePath;
PWSTR ImageFileName;
PWSTR CurrentDirectory;
PWSTR TemporaryNull;
WCHAR TemporaryChar;
ULONG Length;
WCHAR PathBuffer[ MAX_PATH ];
TemporaryNull = CommandLine;
while(TemporaryChar = *TemporaryNull) {
if (TemporaryChar == L' ' || TemporaryChar == L'\t') {
*TemporaryNull = UNICODE_NULL;
break;
}
TemporaryNull += 1;
}
ImageFilePath = AllocMem( MAX_PATH * sizeof( WCHAR ) );
Length = SearchPath( NULL,
CommandLine,
L".exe",
MAX_PATH,
ImageFilePath,
&ImageFileName
);
*TemporaryNull = TemporaryChar;
if (!Length || Length >= MAX_PATH) {
DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
GetLastError(),
CommandLine
);
return FALSE;
}
if (ImageFileName != NULL &&
ImageFileName > ImageFilePath &&
ImageFileName[ -1 ] == L'\\'
) {
ImageFileName[ -1 ] = UNICODE_NULL;
CurrentDirectory = wcscpy( PathBuffer, ImageFilePath );
ImageFileName[ -1 ] = L'\\';
SetCurrentDirectory( CurrentDirectory );
} else {
CurrentDirectory = NULL;
}
memset( &StartupInfo, 0, sizeof( StartupInfo ) );
StartupInfo.cb = sizeof(StartupInfo);
//
// Create the process with DEBUG option, as a separate WOW so we only see our
// application's damage.
//
if (!CreateProcess( ImageFilePath,
CommandLine,
NULL,
NULL,
FALSE, // No handles to inherit
DEBUG_PROCESS |
CREATE_SEPARATE_WOW_VDM, // Ignored for 32 bit apps
NULL,
CurrentDirectory,
&StartupInfo,
&ProcessInformation
) ) {
DeclareError( INSTALER_CANT_DEBUG_PROGRAM,
GetLastError(),
CommandLine
);
return FALSE;
}
sprintf( (LPSTR)PathBuffer, "%ws%ws.log", InstalerDirectory, InstallationName );
InstalerLogFile = fopen( (LPSTR)PathBuffer, "w" );
//
// Will pick up the process and thread handles with the
// CREATE_PROCESS_DEBUG_EVENT and CREATE_PROCESS_THREAD_EVENT
//
return TRUE;
}
PVOID
LookupEntryPointInIAT(
HMODULE ModuleHandle,
PCHAR EntryPointName
)
{
PIMAGE_EXPORT_DIRECTORY Exports;
PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries;
DWORD NumberOfFunctionTableEntries;
ULONG ExportSize, FunctionTableSize;
PCHAR *EntryPointNames;
PULONG EntryPointAddresses;
PUSHORT EntryPointOrdinals;
PVOID EntryPointAddress;
ULONG i;
Exports = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData( ModuleHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize
);
if (Exports == NULL) {
return NULL;
}
FunctionTableEntries = (PIMAGE_RUNTIME_FUNCTION_ENTRY)
RtlImageDirectoryEntryToData( ModuleHandle,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
&FunctionTableSize
);
if (FunctionTableEntries != NULL) {
NumberOfFunctionTableEntries = FunctionTableSize / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY );
} else {
NumberOfFunctionTableEntries = 0;
}
EntryPointNames = (PCHAR *)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNames);
EntryPointOrdinals = (PUSHORT)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfNameOrdinals);
EntryPointAddresses = (PULONG)((PCHAR)ModuleHandle + (ULONG)Exports->AddressOfFunctions);
for (i=0; i<Exports->NumberOfNames; i++) {
EntryPointAddress = (PVOID)((PCHAR)ModuleHandle +
EntryPointAddresses[ EntryPointOrdinals[ i ] ]
);
if ((ULONG)EntryPointAddress > (ULONG)Exports &&
(ULONG)EntryPointAddress < ((ULONG)Exports + ExportSize) ) {
//
// Skip this... It's a forwarded reference
//
} else if (!_stricmp( EntryPointName, (PCHAR)ModuleHandle + (ULONG)EntryPointNames[ i ] )) {
return GetAddressForEntryPointBreakpoint( EntryPointAddress,
NumberOfFunctionTableEntries,
FunctionTableEntries
);
}
}
DbgEvent( INTERNALERROR, ( "Unable to find entry point '%s' in module at %x\n", EntryPointName, ModuleHandle ) );
return NULL;
}
PVOID
GetAddressForEntryPointBreakpoint(
PVOID EntryPointAddress,
DWORD NumberOfFunctionTableEntries OPTIONAL,
PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTableEntries OPTIONAL
)
{
PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionEntry;
PIMAGE_RUNTIME_FUNCTION_ENTRY FunctionTable;
LONG High;
LONG Low;
LONG Middle;
//
// If no function table, then okay to set breakpoint at exported
// address.
//
if (NumberOfFunctionTableEntries == 0) {
return EntryPointAddress;
}
//
// Initialize search indicies.
//
Low = 0;
High = NumberOfFunctionTableEntries - 1;
FunctionTable = FunctionTableEntries;
//
// Perform binary search on the function table for a function table
// entry that subsumes the specified PC.
//
while (High >= Low) {
//
// Compute next probe index and test entry. If the specified PC
// is greater than of equal to the beginning address and less
// than the ending address of the function table entry, then
// return the address of the function table entry. Otherwise,
// continue the search.
//
Middle = (Low + High) >> 1;
FunctionEntry = &FunctionTable[Middle];
if ((ULONG)EntryPointAddress < FunctionEntry->BeginAddress) {
High = Middle - 1;
} else if ((ULONG)EntryPointAddress >= FunctionEntry->EndAddress) {
Low = Middle + 1;
} else {
break;
}
}
//
// A function table entry for the specified PC was not found. Just use
// the exported address and hope for the best.
//
return EntryPointAddress;
}
BOOLEAN
LoadDriveLetterDefinitions( VOID )
{
PVOID Buffer;
ULONG BufferSize;
ULONG cchDeviceName;
ULONG cch;
ULONG cchTargetPath;
PWSTR DeviceName;
PWSTR TargetPath;
ULONG DriveLetterIndex;
ULONG cb;
PDRIVE_LETTER_INFO p;
RtlInitUnicodeString( &AltDriveLetterPrefix, L"\\DosDevices\\" );
RtlInitUnicodeString( &DriveLetterPrefix, L"\\??\\" );
RtlInitUnicodeString( &UNCPrefix, L"UNC\\" );
BufferSize = 0x1000;
Buffer = VirtualAlloc( NULL, BufferSize, MEM_COMMIT, PAGE_READWRITE );
if (Buffer == NULL) {
DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n", BufferSize, GetLastError() ) );
return FALSE;
}
cchTargetPath = BufferSize / sizeof( WCHAR );
cchDeviceName = QueryDosDeviceW( NULL,
Buffer,
cchTargetPath
);
if (cchDeviceName == 0) {
DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( NULL ) failed (%u)\n", GetLastError() ) );
return FALSE;
}
cchTargetPath -= (cchDeviceName + 2);
DeviceName = Buffer;
TargetPath = DeviceName + 2 + cchDeviceName;
while (*DeviceName) {
if (wcslen( DeviceName ) == 2 &&
DeviceName[ 1 ] == L':' &&
_wcsupr( DeviceName ) &&
DeviceName[ 0 ] >= L'@' &&
DeviceName[ 0 ] <= L'_'
) {
cch = QueryDosDeviceW( DeviceName, TargetPath, cchTargetPath );
if (cch == 0) {
DbgEvent( INTERNALERROR, ( "QueryDosDeviceW( %ws ) failed (%u)\n", DeviceName, GetLastError() ) );
return FALSE;
}
DriveLetterIndex = DeviceName[ 0 ] - L'@';
p = &DriveLetters[ DriveLetterIndex ];
p->DriveLetter = (WCHAR)(L'@' + DriveLetterIndex);
cb = (cch+1) * sizeof( WCHAR );
p->NtLinkTarget.Buffer = AllocMem( cb );
if (p->NtLinkTarget.Buffer == NULL) {
return FALSE;
}
MoveMemory( p->NtLinkTarget.Buffer, TargetPath, cb );
p->NtLinkTarget.Length = (USHORT)( cb - sizeof( WCHAR ));
p->NtLinkTarget.MaximumLength = (USHORT)cb;
p->DriveLetterValid = TRUE;
}
while (*DeviceName++) {}
}
return TRUE;
}
BOOLEAN
IsDriveLetterPath(
PUNICODE_STRING Path
)
{
ULONG DriveLetterIndex;
PDRIVE_LETTER_INFO p;
PUNICODE_STRING Prefix;
if (RtlPrefixUnicodeString( Prefix = &DriveLetterPrefix, Path, TRUE ) ||
RtlPrefixUnicodeString( Prefix = &AltDriveLetterPrefix, Path, TRUE ) ) {
Path->Length -= Prefix->Length;
RtlMoveMemory( Path->Buffer,
(PCHAR)(Path->Buffer) + Prefix->Length,
Path->Length + sizeof( UNICODE_NULL )
);
if (RtlPrefixUnicodeString( &UNCPrefix, Path, TRUE )) {
Path->Length -= UNCPrefix.Length;
Path->Buffer[0] = L'\\';
Path->Buffer[1] = L'\\';
RtlMoveMemory( &Path->Buffer[2],
(PCHAR)(Path->Buffer) + UNCPrefix.Length,
Path->Length + sizeof( UNICODE_NULL )
);
Path->Length += 2 * sizeof( WCHAR );
}
return TRUE;
}
for (DriveLetterIndex=0, p = &DriveLetters[ DriveLetterIndex ];
DriveLetterIndex<32;
DriveLetterIndex++, p++
)
{
if (p->DriveLetterValid &&
RtlPrefixUnicodeString( &p->NtLinkTarget, Path, TRUE )
)
{
Path->Length -= p->NtLinkTarget.Length;
RtlMoveMemory( &Path->Buffer[ 2 ],
(PCHAR)(Path->Buffer) + p->NtLinkTarget.Length,
Path->Length + sizeof( UNICODE_NULL )
);
Path->Buffer[ 0 ] = p->DriveLetter;
Path->Buffer[ 1 ] = L':';
Path->Length += 2;
return TRUE;
}
}
return FALSE;
}
VOID
TrimTemporaryBuffer( VOID )
{
PVOID CommitAddress;
if (TemporaryBufferLength == 0) {
return;
}
if (VirtualFree( (PCHAR)TemporaryBuffer,
TemporaryBufferLength,
MEM_DECOMMIT
)
)
{
TemporaryBufferLength = 0;
}
return;
}
BOOLEAN
GrowTemporaryBuffer(
ULONG NeededSize
)
{
PVOID CommitAddress;
if (NeededSize <= TemporaryBufferLength) {
return TRUE;
}
if (TemporaryBufferLength == TemporaryBufferMaximumLength) {
return FALSE;
}
CommitAddress = VirtualAlloc( (PCHAR)TemporaryBuffer + TemporaryBufferLength,
NeededSize - TemporaryBufferLength,
MEM_COMMIT,
PAGE_READWRITE
);
if (CommitAddress == NULL) {
DbgEvent( INTERNALERROR, ( "VirtualAlloc( %0x8 ) failed (%u)\n",
NeededSize - TemporaryBufferLength,
GetLastError()
)
);
return FALSE;
}
TemporaryBufferLength += TemporaryBufferLengthGrowth;
return TRUE;
}
ULONG
FillTemporaryBuffer(
PPROCESS_INFO Process,
PVOID Address,
BOOLEAN Unicode,
BOOLEAN DoubleNullTermination
)
{
ULONG BytesRead;
ULONG TotalBytesRead;
PVOID Source;
PVOID Destination;
LPSTR s1;
PWSTR s2;
BOOLEAN MoreToRead;
BOOLEAN SeenOneNull;
TotalBytesRead = 0;
Destination = (PCHAR)TemporaryBuffer;
MoreToRead = TRUE;
SeenOneNull = FALSE;
while (MoreToRead) {
TotalBytesRead += TemporaryBufferLengthGrowth;
if (!GrowTemporaryBuffer( TotalBytesRead )) {
return 0;
}
Source = Destination;
BytesRead = 0;
if (!ReadProcessMemory( Process->Handle,
Address,
Destination,
TemporaryBufferLengthGrowth,
&BytesRead
)
)
{
if (BytesRead == 0) {
MoreToRead = FALSE;
} else {
return 0;
}
}
Destination = (PCHAR)Destination + TotalBytesRead;
if (Unicode) {
s2 = (PWSTR)Source;
while (s2 < (PWSTR)Destination) {
if (*s2 == UNICODE_NULL) {
if (SeenOneNull || !DoubleNullTermination) {
return (PCHAR)s2 - (PCHAR)TemporaryBuffer;
} else {
SeenOneNull = TRUE;
}
} else {
SeenOneNull = FALSE;
}
s2++;
}
} else {
s1 = (LPSTR)Source;
while (s1 < (LPSTR)Destination) {
if (*s1 == '\0') {
if (SeenOneNull || !DoubleNullTermination) {
return (PCHAR)s1 - (PCHAR)TemporaryBuffer;
} else {
SeenOneNull = TRUE;
}
} else {
SeenOneNull = FALSE;
}
s1++;
}
}
}
return 0;
}