Leaked source code of windows server 2003
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

/*
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;
}