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.
1408 lines
39 KiB
1408 lines
39 KiB
/*
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
File name:
|
|
|
|
patch.c
|
|
|
|
Author:
|
|
|
|
Adrian Marinescu (adrmarin) Wed Nov 14 2001
|
|
|
|
*/
|
|
|
|
#include "nt.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <sfc.h>
|
|
#include <psapi.h>
|
|
|
|
//
|
|
// Global constants
|
|
//
|
|
|
|
#define PATCH_OC_INSTALL 1
|
|
#define PATCH_OC_UNINSTALL 2
|
|
#define PATCH_OC_REPLACE_FILE 3
|
|
|
|
ULONG OperationCode = 0;
|
|
|
|
|
|
//
|
|
// System file protection utilities
|
|
//
|
|
|
|
typedef HANDLE (WINAPI *CONNECTTOSFCSERVER)(PCWSTR);
|
|
typedef DWORD (WINAPI *SFCFILEEXCEPTION)(HANDLE, PCWSTR, DWORD);
|
|
typedef VOID (WINAPI * SFCCLOSE)(HANDLE);
|
|
|
|
SFCFILEEXCEPTION pSfcFileException = NULL;
|
|
CONNECTTOSFCSERVER pConnectToSfcServer = NULL;
|
|
SFCCLOSE pSfcClose = NULL;
|
|
|
|
HANDLE
|
|
LoadSfcLibrary()
|
|
{
|
|
HANDLE hLibSfc;
|
|
|
|
hLibSfc = LoadLibrary("SFC.DLL");
|
|
|
|
if ( hLibSfc != NULL ) {
|
|
|
|
pConnectToSfcServer = (CONNECTTOSFCSERVER)GetProcAddress( hLibSfc, (LPCSTR)0x00000003 );
|
|
pSfcClose = (SFCCLOSE)GetProcAddress( hLibSfc, (LPCSTR)0x00000004 );
|
|
pSfcFileException = (SFCFILEEXCEPTION)GetProcAddress( hLibSfc, (LPCSTR)0x00000005 );
|
|
}
|
|
|
|
return hLibSfc;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RemoveKnownDll (
|
|
PWSTR FileName
|
|
)
|
|
{
|
|
WCHAR Buffer[DOS_MAX_PATH_LENGTH + 1];
|
|
int BytesCopied;
|
|
UNICODE_STRING Unicode;
|
|
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES Obja;
|
|
HANDLE Section;
|
|
|
|
|
|
BytesCopied = _snwprintf( Buffer,
|
|
DOS_MAX_PATH_LENGTH,
|
|
L"\\KnownDlls\\%ws",
|
|
FileName );
|
|
|
|
if ( BytesCopied < 0 ) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
RtlInitUnicodeString(&Unicode, Buffer);
|
|
|
|
//
|
|
// open the section object
|
|
//
|
|
|
|
InitializeObjectAttributes (&Obja,
|
|
&Unicode,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenSection (&Section,
|
|
SECTION_ALL_ACCESS,
|
|
&Obja);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("%ws is not a known dll\n", FileName);
|
|
|
|
return Status;
|
|
}
|
|
|
|
printf("%ws is a known dll. Deleting ...\n", FileName);
|
|
|
|
Status = NtMakeTemporaryObject(Section);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("NtMakeTemporaryObject failed with status %lx\n", Status);
|
|
|
|
}
|
|
|
|
NtClose(Section);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RemoveDelayedRename(
|
|
IN PUNICODE_STRING OldFileName,
|
|
IN PUNICODE_STRING NewFileName,
|
|
IN ULONG Index
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Appends the given delayed move file operation to the registry
|
|
value that contains the list of move file operations to be
|
|
performed on the next boot.
|
|
|
|
Arguments:
|
|
|
|
OldFileName - Supplies the old file name
|
|
|
|
NewFileName - Supplies the new file name
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING KeyName;
|
|
UNICODE_STRING ValueName;
|
|
HANDLE KeyHandle;
|
|
PWSTR ValueData, s;
|
|
PKEY_VALUE_PARTIAL_INFORMATION ValueInfo;
|
|
ULONG ValueLength = 1024;
|
|
ULONG ReturnedLength;
|
|
WCHAR ValueNameBuf[64];
|
|
NTSTATUS Status;
|
|
|
|
RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Session Manager" );
|
|
|
|
if ( Index == 1 ) {
|
|
RtlInitUnicodeString( &ValueName, L"PendingFileRenameOperations" );
|
|
} else {
|
|
swprintf(ValueNameBuf,L"PendingFileRenameOperations%d",Index);
|
|
RtlInitUnicodeString( &ValueName, ValueNameBuf );
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&KeyName,
|
|
OBJ_OPENIF | OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtCreateKey( &KeyHandle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&Obja,
|
|
0,
|
|
NULL,
|
|
0,
|
|
NULL
|
|
);
|
|
if ( Status == STATUS_ACCESS_DENIED ) {
|
|
Status = NtCreateKey( &KeyHandle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
&Obja,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_BACKUP_RESTORE,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return Status;
|
|
}
|
|
|
|
while ( TRUE ) {
|
|
ValueInfo = RtlAllocateHeap(RtlProcessHeap(),
|
|
0,
|
|
ValueLength);
|
|
|
|
if ( ValueInfo == NULL ) {
|
|
NtClose(KeyHandle);
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
//
|
|
// File rename operations are stored in the registry in a
|
|
// single MULTI_SZ value. This allows the renames to be
|
|
// performed in the same order that they were originally
|
|
// requested. Each rename operation consists of a pair of
|
|
// NULL-terminated strings.
|
|
//
|
|
|
|
Status = NtQueryValueKey(KeyHandle,
|
|
&ValueName,
|
|
KeyValuePartialInformation,
|
|
ValueInfo,
|
|
ValueLength,
|
|
&ReturnedLength);
|
|
|
|
if ( Status != STATUS_BUFFER_OVERFLOW ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The existing value is too large for our buffer.
|
|
// Retry with a larger buffer.
|
|
//
|
|
ValueLength = ReturnedLength;
|
|
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
//
|
|
// A value already exists, append our two strings to the
|
|
// MULTI_SZ.
|
|
//
|
|
ValueData = (PWSTR)(&ValueInfo->Data);
|
|
s = (PWSTR)((PCHAR)ValueData + ValueInfo->DataLength) - 1;
|
|
|
|
while ( ValueData < s ) {
|
|
|
|
UNICODE_STRING CrtString;
|
|
PWSTR Base;
|
|
ULONG RemovedBytes;
|
|
Base = ValueData;
|
|
|
|
RtlInitUnicodeString(&CrtString, ValueData);
|
|
RemovedBytes = CrtString.Length + sizeof(WCHAR);
|
|
|
|
if ( RtlEqualUnicodeString( &CrtString,
|
|
OldFileName,
|
|
TRUE ) ) {
|
|
|
|
ValueData += CrtString.Length / sizeof(WCHAR) + 1;
|
|
|
|
RtlInitUnicodeString(&CrtString, ValueData + 1);
|
|
|
|
if ( RtlEqualUnicodeString( &CrtString,
|
|
NewFileName,
|
|
TRUE ) ) {
|
|
|
|
RemovedBytes += CrtString.Length + 2 * sizeof(WCHAR); // NULL + !
|
|
printf("Removing delayed entry %ws -> %ws\n", Base, ValueData);
|
|
|
|
ValueData += CrtString.Length / sizeof(WCHAR) + 1;
|
|
MoveMemory(Base, ValueData, (ULONG_PTR)s - (ULONG_PTR)ValueData);
|
|
|
|
printf("Deleting delayed rename key (%ld bytes left)\n", (ULONG)ValueInfo->DataLength - RemovedBytes);
|
|
|
|
Status = NtSetValueKey(KeyHandle,
|
|
&ValueName,
|
|
0,
|
|
REG_MULTI_SZ,
|
|
&ValueInfo->Data,
|
|
(ULONG)ValueInfo->DataLength - RemovedBytes);
|
|
|
|
break;
|
|
}
|
|
} else {
|
|
|
|
ValueData += CrtString.Length / sizeof(WCHAR) + 1;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
NtClose(KeyHandle);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
|
return(Status);
|
|
}
|
|
|
|
NtClose(KeyHandle);
|
|
RtlFreeHeap(RtlProcessHeap(), 0, ValueInfo);
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ReplaceSystemFile(
|
|
PWSTR TargetPath,
|
|
PWSTR ReplacedFileName,
|
|
PWSTR ReplacementFile
|
|
)
|
|
{
|
|
WCHAR FullOriginalName[DOS_MAX_PATH_LENGTH + 1];
|
|
WCHAR TmpReplacementFile[DOS_MAX_PATH_LENGTH + 1];
|
|
WCHAR TmpOrigName[DOS_MAX_PATH_LENGTH + 1];
|
|
|
|
int BytesCopied;
|
|
UNICODE_STRING ReplacedUnicodeString, NewUnicodeString, TempOrigFile;
|
|
HANDLE hSfp;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatus;
|
|
HANDLE ReplacedFileHandle = NULL, NewFileHandle = NULL;
|
|
PFILE_RENAME_INFORMATION RenameInfo1 = NULL, RenameInfo2 = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD Result;
|
|
ULONG i;
|
|
int ThreadPriority;
|
|
|
|
RtlInitUnicodeString(&ReplacedUnicodeString, NULL);
|
|
RtlInitUnicodeString(&NewUnicodeString, NULL);
|
|
|
|
BytesCopied = _snwprintf( FullOriginalName,
|
|
DOS_MAX_PATH_LENGTH,
|
|
L"%ws\\%ws",
|
|
TargetPath,
|
|
ReplacedFileName );
|
|
|
|
if ( BytesCopied < 0 ) {
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
if ( !RtlDosPathNameToNtPathName_U(
|
|
FullOriginalName,
|
|
&ReplacedUnicodeString,
|
|
NULL,
|
|
NULL
|
|
) ) {
|
|
printf("RtlDosPathNameToNtPathName_U failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the target file and keep a handle opened to it
|
|
//
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&ReplacedUnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile( &ReplacedFileHandle,
|
|
SYNCHRONIZE | DELETE | FILE_GENERIC_READ,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf( "Opening the \"%ws\" file failed %lx (IOStatus = %lx)\n",
|
|
FullOriginalName,
|
|
Status,
|
|
IoStatus);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// If a knowndll file, then remove it from the system known dll directory
|
|
//
|
|
|
|
Status = RemoveKnownDll( ReplacedFileName );
|
|
|
|
if ( !NT_SUCCESS(Status) &&
|
|
(Status != STATUS_OBJECT_NAME_NOT_FOUND) ) {
|
|
|
|
printf( "Removing the KnownDll entry for \"%ws\" failed %lx\n",
|
|
ReplacedFileName,
|
|
Status);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Unprotect the replaced file
|
|
//
|
|
|
|
hSfp = (pConnectToSfcServer)( NULL );
|
|
|
|
if ( hSfp ) {
|
|
|
|
if ( SfcIsFileProtected(hSfp, FullOriginalName) ) {
|
|
|
|
printf("Replacing protected file \"%ws\"\n", FullOriginalName);
|
|
|
|
Result = (pSfcFileException)(
|
|
hSfp,
|
|
(PWSTR) FullOriginalName,
|
|
(DWORD) -1
|
|
);
|
|
if ( Result != NO_ERROR ) {
|
|
printf("Unprotect file \"%ws\" failed, ec = %d\n", FullOriginalName, Result);
|
|
|
|
(pSfcClose)(hSfp);
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
printf("Replacing unprotected file \"%ws\"\n", FullOriginalName);
|
|
}
|
|
|
|
(pSfcClose)(hSfp);
|
|
|
|
} else {
|
|
|
|
printf("SfcConnectToServer failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( GetTempFileNameW(TargetPath, L"HOTP", 0, TmpReplacementFile) == 0 ) {
|
|
|
|
printf("GetTempFileNameW failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !DeleteFileW(TmpReplacementFile) ) {
|
|
|
|
printf("DeleteFile \"%ws\" failed with error %ld\n", TmpReplacementFile, GetLastError());
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !CopyFileW ( ReplacementFile,
|
|
TmpReplacementFile,
|
|
TRUE ) ) {
|
|
|
|
printf("CopyFileW \"%ws\" failed with error %ld\n", TmpReplacementFile, GetLastError());
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !RtlDosPathNameToNtPathName_U(
|
|
TmpReplacementFile,
|
|
&NewUnicodeString,
|
|
NULL,
|
|
NULL
|
|
) ) {
|
|
|
|
printf("RtlDosPathNameToNtPathName_U failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the new file and keep a handle opened to it
|
|
//
|
|
|
|
InitializeObjectAttributes (&ObjectAttributes,
|
|
&NewUnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
Status = NtOpenFile( &NewFileHandle,
|
|
SYNCHRONIZE | DELETE | FILE_GENERIC_READ,
|
|
&ObjectAttributes,
|
|
&IoStatus,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("Opening the temporary file \"%ws\" failed %lx (IOStatus = %lx)\n", TmpReplacementFile, Status, IoStatus);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Prepare the rename info for the original file
|
|
// It will be a temporary
|
|
//
|
|
|
|
if ( GetTempFileNameW(TargetPath, L"HPO", 0, TmpOrigName) == 0 ) {
|
|
|
|
printf("GetTempFileNameW failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !DeleteFileW(TmpOrigName) ) {
|
|
|
|
printf("DeleteFile \"%ws\" failed with error %ld\n", TmpOrigName, GetLastError());
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !RtlDosPathNameToNtPathName_U(
|
|
TmpOrigName,
|
|
&TempOrigFile,
|
|
NULL,
|
|
NULL
|
|
) ) {
|
|
printf("RtlDosPathNameToNtPathName_U failed\n");
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
RenameInfo1 = RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
TempOrigFile.Length+sizeof(*RenameInfo1));
|
|
|
|
if ( RenameInfo1 == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( RenameInfo1->FileName, TempOrigFile.Buffer, TempOrigFile.Length );
|
|
|
|
RenameInfo1->ReplaceIfExists = TRUE;
|
|
RenameInfo1->RootDirectory = NULL;
|
|
RenameInfo1->FileNameLength = TempOrigFile.Length;
|
|
|
|
RenameInfo2 = RtlAllocateHeap( RtlProcessHeap(),
|
|
0,
|
|
ReplacedUnicodeString.Length+sizeof(*RenameInfo2));
|
|
|
|
if ( RenameInfo2 == NULL ) {
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( RenameInfo2->FileName, ReplacedUnicodeString.Buffer, ReplacedUnicodeString.Length );
|
|
|
|
RenameInfo2->ReplaceIfExists = TRUE;
|
|
RenameInfo2->RootDirectory = NULL;
|
|
RenameInfo2->FileNameLength = ReplacedUnicodeString.Length;
|
|
|
|
//
|
|
// We have everything set to do the two rename operations. However if
|
|
// the machine crashes before the second rename operation the system may not recover at boot
|
|
// We queue a delayed rename so smss will do the job at next boot. If we succeed,
|
|
// then smss will not find the file so it will skip that step.
|
|
//
|
|
|
|
if ( !MoveFileExW( TmpReplacementFile,
|
|
FullOriginalName,
|
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_DELAY_UNTIL_REBOOT) ) {
|
|
|
|
//
|
|
// We cannot queue the rename operation, so we cannot recover if
|
|
// the machine crashes between the two renames below. Better refuse to apply the patch
|
|
//
|
|
|
|
printf("Failed to queue the rename operation for the temporary file (%ld)\n", GetLastError());
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
|
|
ThreadPriority = GetThreadPriority(GetCurrentThread());
|
|
|
|
if ( !SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL) ) {
|
|
|
|
printf("SetThreadPriority failed\n");
|
|
}
|
|
|
|
Status = NtSetInformationFile( ReplacedFileHandle,
|
|
&IoStatus,
|
|
RenameInfo1,
|
|
TempOrigFile.Length+sizeof(*RenameInfo1),
|
|
FileRenameInformation);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("NtSetInformationFile failed for the original file %lx %lx\n", Status, IoStatus);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = NtSetInformationFile( NewFileHandle,
|
|
&IoStatus,
|
|
RenameInfo2,
|
|
ReplacedUnicodeString.Length+sizeof(*RenameInfo1),
|
|
FileRenameInformation);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("NtSetInformationFile failed for the new file %lx (IOStatus %lx). Restoring the original.\n", Status, IoStatus);
|
|
|
|
//
|
|
// Restore the original file
|
|
//
|
|
|
|
Status = NtSetInformationFile( ReplacedFileHandle,
|
|
&IoStatus,
|
|
RenameInfo2,
|
|
ReplacedUnicodeString.Length+sizeof(*RenameInfo1),
|
|
FileRenameInformation);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !SetThreadPriority(GetCurrentThread(), ThreadPriority) ) {
|
|
|
|
printf("Restoring the thread priority failed\n");
|
|
}
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
for ( i = 0; i < 3; i++ ) {
|
|
|
|
Status = RemoveDelayedRename( &NewUnicodeString,
|
|
&ReplacedUnicodeString,
|
|
i );
|
|
if ( NT_SUCCESS(Status) ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ReplacedFileHandle ) {
|
|
NtClose(ReplacedFileHandle);
|
|
}
|
|
|
|
if ( NewFileHandle ) {
|
|
NtClose(NewFileHandle);
|
|
}
|
|
|
|
if ( !DeleteFileW(TmpOrigName) ) {
|
|
|
|
printf("Queueing the temp file deletion for \"%ws\" \n", TmpOrigName);
|
|
|
|
//
|
|
// Detele the temporary file after next reboot
|
|
//
|
|
|
|
if ( !MoveFileExW( TmpOrigName,
|
|
NULL,
|
|
MOVEFILE_DELAY_UNTIL_REBOOT) ) {
|
|
|
|
//
|
|
// We cannot queue the delete operation operation
|
|
//
|
|
|
|
printf("Failed to queue the delete operation for the temporary file (%ld)\n", GetLastError());
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if ( RenameInfo1 ) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, RenameInfo1 );
|
|
}
|
|
|
|
if ( RenameInfo2 ) {
|
|
|
|
RtlFreeHeap( RtlProcessHeap(), 0, RenameInfo2 );
|
|
}
|
|
|
|
RtlFreeUnicodeString(&NewUnicodeString);
|
|
RtlFreeUnicodeString(&ReplacedUnicodeString);
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOL
|
|
PSTRToUnicodeString(
|
|
OUT PUNICODE_STRING UnicodeString,
|
|
IN LPCSTR lpSourceString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Captures and converts a 8-bit (OEM or ANSI) string into a heap-allocated
|
|
UNICODE string
|
|
|
|
Arguments:
|
|
|
|
UnicodeString - location where UNICODE_STRING is stored
|
|
|
|
lpSourceString - string in OEM or ANSI
|
|
|
|
Return Value:
|
|
|
|
TRUE if string is correctly stored, FALSE if an error occurred. In the
|
|
error case, the last error is correctly set.
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Convert input into dynamic unicode string
|
|
//
|
|
|
|
RtlInitString( &AnsiString, lpSourceString );
|
|
RtlAnsiStringToUnicodeString(UnicodeString, &AnsiString, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
InitializeAsDebugger(VOID)
|
|
{
|
|
|
|
HANDLE Token;
|
|
PTOKEN_PRIVILEGES NewPrivileges;
|
|
BYTE OldPriv[1024];
|
|
PBYTE pbOldPriv;
|
|
ULONG cbNeeded;
|
|
BOOLEAN bRet = FALSE;
|
|
LUID LuidPrivilege, LoadDriverPrivilege;
|
|
DWORD PID = 0;
|
|
|
|
//
|
|
// Make sure we have access to adjust and to get the old token privileges
|
|
//
|
|
|
|
if ( !OpenProcessToken( GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
&Token) ) {
|
|
|
|
printf("Cannot open process token %ld\n", GetLastError());
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
cbNeeded = 0;
|
|
|
|
//
|
|
// Initialize the privilege adjustment structure
|
|
//
|
|
|
|
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &LuidPrivilege );
|
|
LookupPrivilegeValue(NULL, SE_LOAD_DRIVER_NAME, &LoadDriverPrivilege );
|
|
|
|
NewPrivileges = (PTOKEN_PRIVILEGES)calloc( 1,
|
|
sizeof(TOKEN_PRIVILEGES) +
|
|
(2 - ANYSIZE_ARRAY) * sizeof(LUID_AND_ATTRIBUTES));
|
|
if ( NewPrivileges == NULL ) {
|
|
|
|
CloseHandle(Token);
|
|
return( bRet );
|
|
}
|
|
|
|
NewPrivileges->PrivilegeCount = 2;
|
|
NewPrivileges->Privileges[0].Luid = LuidPrivilege;
|
|
NewPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
NewPrivileges->Privileges[1].Luid = LoadDriverPrivilege;
|
|
NewPrivileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
//
|
|
// Enable the privilege
|
|
//
|
|
|
|
pbOldPriv = OldPriv;
|
|
bRet = (BOOLEAN)AdjustTokenPrivileges( Token,
|
|
FALSE,
|
|
NewPrivileges,
|
|
1024,
|
|
(PTOKEN_PRIVILEGES)pbOldPriv,
|
|
&cbNeeded );
|
|
|
|
if ( !bRet ) {
|
|
|
|
//
|
|
// If the stack was too small to hold the privileges
|
|
// then allocate off the heap
|
|
//
|
|
|
|
printf("AdjustTokenPrivileges returned %ld\n", GetLastError());
|
|
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) {
|
|
|
|
pbOldPriv = calloc(1,cbNeeded);
|
|
if ( pbOldPriv == NULL ) {
|
|
CloseHandle(Token);
|
|
return( bRet);
|
|
}
|
|
|
|
bRet = (BOOLEAN)AdjustTokenPrivileges( Token,
|
|
FALSE,
|
|
NewPrivileges,
|
|
cbNeeded,
|
|
(PTOKEN_PRIVILEGES)pbOldPriv,
|
|
&cbNeeded );
|
|
} else {
|
|
|
|
printf("Cannot adjust token privileges %ld\n", GetLastError());
|
|
}
|
|
}
|
|
|
|
CloseHandle( Token );
|
|
return(bRet);
|
|
}
|
|
|
|
void Usage ()
|
|
{
|
|
printf("Usage:\n");
|
|
|
|
printf(" patch -k [-i|-u] pach_file\n");
|
|
printf(" Apply a patch to a system driver.\n");
|
|
printf(" -i Enable patch\n");
|
|
printf(" -u Disable patch\n\n");
|
|
|
|
|
|
printf(" patch -i pach_file [PID|image_name]\n");
|
|
printf(" Apply a patch to a process.\n");
|
|
printf(" If Image name is missing, all existing processes will be patched.\n\n");
|
|
|
|
printf(" patch -u pach_file [PID|image_name] \n");
|
|
printf(" Uninstall an existing patch.\n");
|
|
printf(" If Image name is missing, all existing processes will be patched.\n\n");
|
|
|
|
printf(" patch -r TargetPath TargetBinary SourcePath\n");
|
|
printf(" Replaces the TargetPath\\TargetBinary with SourcePath\n");
|
|
printf(" \n");
|
|
printf(" \n");
|
|
}
|
|
|
|
|
|
PVOID
|
|
MapPatchFile(
|
|
HANDLE ProcessHandle,
|
|
LPCTSTR wPatchName,
|
|
ULONG PatchFlags
|
|
)
|
|
{
|
|
PSYSTEM_HOTPATCH_CODE_INFORMATION RemoteInfo;
|
|
SYSTEM_HOTPATCH_CODE_INFORMATION LocaLRemoteInfo;
|
|
CANSI_STRING AnsiString;
|
|
|
|
WCHAR Buffer[1024];
|
|
SIZE_T Size;
|
|
|
|
UNICODE_STRING DestinationString;
|
|
|
|
DestinationString.Buffer = Buffer;
|
|
DestinationString.Length = 0;
|
|
DestinationString.MaximumLength = sizeof(Buffer);
|
|
|
|
RtlInitAnsiString(&AnsiString, wPatchName);
|
|
|
|
RtlAnsiStringToUnicodeString(
|
|
&DestinationString,
|
|
&AnsiString,
|
|
FALSE
|
|
);
|
|
|
|
RemoteInfo = VirtualAllocEx( ProcessHandle,
|
|
NULL,
|
|
4096 * 16,
|
|
MEM_RESERVE | MEM_COMMIT,
|
|
PAGE_READWRITE);
|
|
|
|
if ( RemoteInfo == NULL ) {
|
|
|
|
printf("VirtualAllocEx failed %ld\n", GetLastError());
|
|
|
|
return NULL;
|
|
}
|
|
|
|
LocaLRemoteInfo.Flags = PatchFlags | FLG_HOTPATCH_NAME_INFO;
|
|
LocaLRemoteInfo.NameInfo.NameLength = DestinationString.Length;
|
|
LocaLRemoteInfo.NameInfo.NameOffset = sizeof(LocaLRemoteInfo);
|
|
LocaLRemoteInfo.InfoSize = LocaLRemoteInfo.NameInfo.NameLength + LocaLRemoteInfo.NameInfo.NameOffset;
|
|
|
|
|
|
if ( !WriteProcessMemory(ProcessHandle,
|
|
RemoteInfo,
|
|
&LocaLRemoteInfo,
|
|
sizeof(LocaLRemoteInfo),
|
|
&Size) ) {
|
|
|
|
printf("Write process memory failed %ld\n", GetLastError());
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
if ( !WriteProcessMemory(ProcessHandle,
|
|
(RemoteInfo + 1),
|
|
DestinationString.Buffer,
|
|
DestinationString.Length + sizeof(LocaLRemoteInfo),
|
|
&Size) ) {
|
|
|
|
printf("Write process memory failed %ld\n", GetLastError());
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return RemoteInfo;
|
|
|
|
}
|
|
|
|
|
|
PSYSTEM_HOTPATCH_CODE_INFORMATION
|
|
InitializeKernelPatchData(
|
|
LPCTSTR wPatchName,
|
|
ULONG PatchFlags
|
|
)
|
|
{
|
|
PSYSTEM_HOTPATCH_CODE_INFORMATION KernelPatch;
|
|
CANSI_STRING AnsiString;
|
|
|
|
WCHAR Buffer[1024];
|
|
SIZE_T Size;
|
|
|
|
UNICODE_STRING DestinationString;
|
|
|
|
DestinationString.Buffer = Buffer;
|
|
DestinationString.Length = 0;
|
|
DestinationString.MaximumLength = sizeof(Buffer);
|
|
|
|
RtlInitAnsiString(&AnsiString, wPatchName);
|
|
|
|
RtlAnsiStringToUnicodeString(
|
|
&DestinationString,
|
|
&AnsiString,
|
|
FALSE
|
|
);
|
|
|
|
if ( !RtlDosPathNameToNtPathName_U(
|
|
Buffer,
|
|
&DestinationString,
|
|
NULL,
|
|
NULL
|
|
) ) {
|
|
|
|
printf("RtlDosPathNameToNtPathName_U failed\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
KernelPatch = malloc(sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION) + DestinationString.Length);
|
|
|
|
if ( KernelPatch == NULL ) {
|
|
|
|
printf("Not enough memory\n");
|
|
|
|
RtlFreeUnicodeString(&DestinationString);
|
|
return NULL;
|
|
}
|
|
|
|
KernelPatch->Flags = PatchFlags | FLG_HOTPATCH_NAME_INFO;
|
|
KernelPatch->NameInfo.NameOffset = sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION);
|
|
KernelPatch->NameInfo.NameLength = DestinationString.Length;
|
|
KernelPatch->InfoSize = sizeof(SYSTEM_HOTPATCH_CODE_INFORMATION) + DestinationString.Length;
|
|
|
|
memcpy( (KernelPatch + 1), DestinationString.Buffer, DestinationString.Length);
|
|
|
|
RtlFreeUnicodeString(&DestinationString);
|
|
|
|
return KernelPatch;
|
|
}
|
|
|
|
|
|
int ApplyPatchToProcess(
|
|
DWORD PID,
|
|
PCHAR PatchFile)
|
|
{
|
|
|
|
DWORD ThreadId;
|
|
HANDLE ProcessHandle = NULL;
|
|
HANDLE RemoteThread = NULL;
|
|
PVOID ThreadParam = NULL;
|
|
HANDLE PortHandle = NULL;
|
|
HMODULE NtDllHandle;
|
|
LPTHREAD_START_ROUTINE PatchRoutine;
|
|
DWORD ExitStatus;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// User mode patch
|
|
//
|
|
|
|
ProcessHandle = OpenProcess( PROCESS_QUERY_INFORMATION |
|
|
PROCESS_VM_OPERATION |
|
|
PROCESS_CREATE_THREAD |
|
|
PROCESS_VM_WRITE |
|
|
PROCESS_VM_READ,
|
|
FALSE,
|
|
PID );
|
|
|
|
if ( ProcessHandle == NULL ) {
|
|
|
|
printf("Cannot open process. Error %ld\n", GetLastError());
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
ThreadParam = MapPatchFile( ProcessHandle,
|
|
PatchFile,
|
|
((OperationCode == PATCH_OC_INSTALL) ? 1 : 0)
|
|
);
|
|
|
|
if ( ThreadParam == NULL ) {
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
NtDllHandle = GetModuleHandle("ntdll.dll");
|
|
|
|
if ( NtDllHandle == NULL ) {
|
|
|
|
printf("Cannot get ntdll.dll handle\n");
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
PatchRoutine = (LPTHREAD_START_ROUTINE)GetProcAddress(NtDllHandle, "LdrHotPatchRoutine");
|
|
|
|
if ( PatchRoutine == NULL ) {
|
|
|
|
printf("Cannot get LdrHotPatchRoutine\n");
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
//
|
|
// Use the Rtl version of create remote thread since the win32 version
|
|
// does not work cross-session
|
|
//
|
|
|
|
Status = RtlCreateUserThread (ProcessHandle,
|
|
NULL,
|
|
FALSE,
|
|
0,
|
|
0,
|
|
0,
|
|
(PUSER_THREAD_START_ROUTINE) PatchRoutine,
|
|
ThreadParam,
|
|
&RemoteThread,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS (Status)) {
|
|
|
|
printf("Cannot create user thread. Error %ld\n", GetLastError());
|
|
|
|
VirtualFreeEx( ProcessHandle,
|
|
ThreadParam,
|
|
0,
|
|
MEM_RELEASE
|
|
);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
WaitForSingleObject(RemoteThread, INFINITE);
|
|
|
|
VirtualFreeEx( ProcessHandle,
|
|
ThreadParam,
|
|
0,
|
|
MEM_RELEASE
|
|
);
|
|
|
|
if ( GetExitCodeThread(RemoteThread, &ExitStatus) ) {
|
|
|
|
if ( ExitStatus ) {
|
|
|
|
printf("Error 0x%lx\n", ExitStatus);
|
|
} else {
|
|
|
|
printf("OK\n");
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
UpdateProcessList(PCHAR ProcName,
|
|
PCHAR PatchFile)
|
|
{
|
|
DWORD CrtSize, cbNeeded, cProcesses;
|
|
PDWORD aProcesses;
|
|
DWORD i;
|
|
|
|
CrtSize = cbNeeded = 1 * sizeof(DWORD);
|
|
|
|
aProcesses = malloc(CrtSize);
|
|
|
|
if ( aProcesses == NULL ) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for ( ;; ) {
|
|
|
|
if ( !EnumProcesses( aProcesses, CrtSize, &cbNeeded ) )
|
|
return FALSE;
|
|
|
|
if ( CrtSize > cbNeeded ) {
|
|
|
|
break;
|
|
}
|
|
|
|
free(aProcesses);
|
|
|
|
CrtSize = cbNeeded + 1024;
|
|
|
|
aProcesses = malloc(CrtSize);
|
|
|
|
if ( aProcesses == NULL ) {
|
|
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
|
|
// Calculate how many process identifiers were returned.
|
|
|
|
cProcesses = cbNeeded / sizeof(DWORD);
|
|
|
|
// Print the name and process identifier for each process.
|
|
|
|
for ( i = 0; i < cProcesses; i++ ) {
|
|
|
|
char szProcessName[MAX_PATH] = "";
|
|
DWORD processID = aProcesses[i];
|
|
|
|
|
|
// Get a handle to the process.
|
|
|
|
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
|
|
PROCESS_VM_READ,
|
|
FALSE, processID );
|
|
|
|
// Get the process name.
|
|
|
|
if ( hProcess ) {
|
|
HMODULE hMod;
|
|
DWORD cbNeeded;
|
|
|
|
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) {
|
|
|
|
GetModuleBaseName( hProcess, hMod, szProcessName,
|
|
sizeof(szProcessName) );
|
|
|
|
if ( (ProcName == NULL)
|
|
||
|
|
_stricmp(szProcessName, ProcName) == 0 ) {
|
|
|
|
printf("Patching %s : ", szProcessName);
|
|
|
|
ApplyPatchToProcess(processID, PatchFile);
|
|
|
|
}
|
|
}
|
|
|
|
CloseHandle( hProcess );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int __cdecl main (int argc, char ** argv)
|
|
{
|
|
LONG i;
|
|
DWORD id;
|
|
DWORD PID;
|
|
NTSTATUS Status;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
PCHAR PatchFile = NULL;
|
|
BOOLEAN KernelPatch = FALSE;
|
|
char * ProgramName = NULL;
|
|
|
|
//
|
|
// By default the tool instals the patch
|
|
//
|
|
|
|
OperationCode = 0;
|
|
PID = 0;
|
|
|
|
|
|
for ( i = 1; i < argc; i++ ) {
|
|
PCHAR CrtArg = argv[i];
|
|
|
|
if ( *CrtArg == '-' ) {
|
|
CrtArg++;
|
|
|
|
switch ( toupper(*CrtArg) ) {
|
|
case 'I':
|
|
if ( OperationCode != 0 ) {
|
|
printf("Invalid argument %s\n", CrtArg);
|
|
exit(0);
|
|
}
|
|
OperationCode = PATCH_OC_INSTALL;
|
|
break;
|
|
case 'U':
|
|
if ( OperationCode != 0 ) {
|
|
printf("Invalid argument %s\n", CrtArg);
|
|
exit(0);
|
|
}
|
|
OperationCode = PATCH_OC_UNINSTALL;
|
|
break;
|
|
|
|
case 'K':
|
|
KernelPatch = TRUE;
|
|
break;
|
|
|
|
case 'R':
|
|
if ( OperationCode != 0 ) {
|
|
printf("Invalid argument %s\n", CrtArg);
|
|
exit(0);
|
|
}
|
|
OperationCode = PATCH_OC_REPLACE_FILE;
|
|
break;
|
|
|
|
default:
|
|
Usage();
|
|
return EXIT_FAILURE;
|
|
}
|
|
} else {
|
|
|
|
if ( KernelPatch ) {
|
|
|
|
PatchFile = CrtArg;
|
|
|
|
} else {
|
|
|
|
if ( PatchFile == NULL ) {
|
|
|
|
PatchFile = CrtArg;
|
|
|
|
} else {
|
|
sscanf(CrtArg, "%ld", &PID);
|
|
|
|
if ( !PID ) {
|
|
|
|
ProgramName = CrtArg;
|
|
//printf("Program %s\n", CrtArg);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OperationCode == 0) {
|
|
|
|
Usage();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if ( OperationCode == PATCH_OC_REPLACE_FILE ) {
|
|
|
|
//
|
|
// Replace a binary file to a target path
|
|
//
|
|
|
|
HANDLE SfcLibrary;
|
|
|
|
|
|
if ( argc <= 4 ) {
|
|
|
|
Usage();
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
SfcLibrary = LoadSfcLibrary();
|
|
|
|
if ( SfcLibrary ) {
|
|
|
|
UNICODE_STRING p1, p2, p3;
|
|
|
|
PSTRToUnicodeString(&p1, argv[2]);
|
|
PSTRToUnicodeString(&p2, argv[3]);
|
|
PSTRToUnicodeString(&p3, argv[4]);
|
|
|
|
ReplaceSystemFile(p1.Buffer, p2.Buffer, p3.Buffer);
|
|
|
|
FreeLibrary(SfcLibrary);
|
|
|
|
if ( _stricmp(argv[3], "ntdll.dll") == 0 ) {
|
|
|
|
SYSTEM_HOTPATCH_CODE_INFORMATION KernelPatch;
|
|
NTSTATUS Status;
|
|
|
|
printf("Replacing system ntdll.dll section\n");
|
|
|
|
if ( !InitializeAsDebugger() ) {
|
|
|
|
printf("Cannot initialize as debugger\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
KernelPatch.Flags = FLG_HOTPATCH_RELOAD_NTDLL;
|
|
|
|
Status = NtSetSystemInformation( SystemHotpatchInformation,
|
|
(PVOID)&KernelPatch,
|
|
sizeof(KernelPatch) + 100
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("SystemHotpatchInformation failed with %08lx\n", Status);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if ( !InitializeAsDebugger() ) {
|
|
|
|
printf("Cannot initialize as debugger\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if ( KernelPatch ) {
|
|
|
|
PSYSTEM_HOTPATCH_CODE_INFORMATION KernelPatchData =
|
|
InitializeKernelPatchData( PatchFile,
|
|
((OperationCode == PATCH_OC_INSTALL) ? 1 : 0)
|
|
);
|
|
|
|
|
|
if ( KernelPatchData == NULL ) {
|
|
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
KernelPatchData->Flags |= FLG_HOTPATCH_KERNEL;
|
|
|
|
Status = NtSetSystemInformation( SystemHotpatchInformation,
|
|
(PVOID)KernelPatchData,
|
|
KernelPatchData->InfoSize
|
|
);
|
|
|
|
free(KernelPatchData);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
printf("Patching kernel driver failed with status 0x%lx\n", Status);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Use-mode patching.
|
|
//
|
|
|
|
if ( PID != 0 ) {
|
|
|
|
return ApplyPatchToProcess(PID, PatchFile);
|
|
}
|
|
|
|
return UpdateProcessList( ProgramName,
|
|
PatchFile);
|
|
}
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|