Jump to content
Tuts 4 You

[SOURCE] QUAD RegistryCleaner 1.5.144 Patcher !


0xFF

Recommended Posts

It was an amazing adventure reversing QUAD RegistryCleaner 1.5.144, I would say the reversing level of it is Medium.... but you can easly reverse if you watch Lena's tuts 1~16.

I am posting this for educational purpose only, and to expose new stuff [maybe] to other members... :)

hope you'll like it, take it and modify it, do w.e you want to. you can even use the SnD function from it in your own patchers to easly make patchers, no need for stuff like dUP2, i'm just showing how easy it is to create a patcher yourself, source code is in Delphi 2010.

unit main;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, pngimage, ExtCtrls;type
TForm1 = class(TForm)
Label1: TLabel;
Memo1: TMemo;
Dlg1: TOpenDialog;
Label2: TLabel;
imgPatch1: TImage;
imgOpenFile: TImage;
procedure imgPatch1Click(Sender: TObject);
procedure imgOpenFileClick(Sender: TObject);
procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
procedure Log(const DebugInfo: string);
procedure SeeKnDestroy( liOffset: LongInt; const Buf; nCount: Integer );
public
{ Public declarations }
end;var
Form1: TForm1;
FilePath: string;
nTotalBytes: Integer;
hFile: File Of Byte; // Stores handle of file as aobimplementation{$R *.dfm}procedure TForm1.imgOpenFileClick(Sender: TObject);
begin
if Dlg1.Execute() then
begin
FilePath := Dlg1.FileName;
if FileExists( FilePath ) then
begin
if Memo1.Lines.Count > 0 then Memo1.Lines.Clear;
if imgPatch1.Enabled = False then imgPatch1.Enabled := True;
Log( 'Successfully Loaded ' + ExtractFileName(FilePath) );
AssignFile( hFile, FilePath ); // Opens file handle
Log( 'AssignFile( ' + ExtractFileName(FilePath) + ' )' );
Reset( hFile); // Opens files with Read/Write privilege
Log( 'Reset( ' + ExtractFileName(FilePath) + ' )' );
// Seperator
Log( 'File size: ' + IntToStr( FileSize( hFile ) div 1024 ) + ' Kilobytes' );
end;
end;
end;procedure TForm1.imgPatch1Click(Sender: TObject);
const
aBuffer1: array [0..1] of byte = ($A8, $00);
bBuffer2: byte = ($EB);
aBuffer3: array [0..2] of byte = ($AB, $32, $00);
aBuffer4: array [0..44] of byte = ($B0, $01, $74, $17, $68, $DC, $36, $64, $00,
$8D, $94, $24, $38, $07, $00, $00, $6A, $11,
$52, $E8, $68, $BF, $03, $00, $83, $C4, $0C,
$39, $5C, $24, $24, $EB, $04, $90, $90, $90,
$90, $3B, $FB, $EB, $04, $90, $90, $90, $90);
aBuffer5: array [0..32] of byte = ($52, $00, $65, $00, $67, $00, $69, $00, $73,
$00, $74, $00, $65, $00, $72, $00, $65, $00,
$64, $00, $00, $00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00);
ZeroMemory: array [0..26] of byte = ($00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00,
$00, $00, $00, $00, $00, $00,
$00, $00, $00);
aNop: array [0..13] of byte = ($90, $90, $90, $90, $90, $90, $90, $90,
$90, $90, $90, $90, $90, $90);
wNop: array [0..1] of byte = ($90, $90);
begin
if FilePath = '' then
begin
MessageDlg( 'No file selected!', mtError, [mbOk], 0 );
Exit;
end; SeeKnDestroy( $0000590F, aBuffer1, sizeof( aBuffer1 ) );
SeeKnDestroy( $0000596D, bBuffer2, sizeof( bBuffer2 ) );
SeeKnDestroy( $00041C12, aBuffer1, sizeof( aBuffer1 ) );
SeeKnDestroy( $00041C7F, bBuffer2, sizeof( bBuffer2 ) );
SeeKnDestroy( $0004C401, aBuffer3, sizeof( aBuffer3 ) );
SeeKnDestroy( $0005BD63, aBuffer4, sizeof( aBuffer4 ) );
SeeKnDestroy( $0005BE04, bBuffer2, sizeof( bBuffer2 ) );
SeeKnDestroy( $0025C9E4, aBuffer5, sizeof( aBuffer5 ) );
SeeKnDestroy( $0025CCDC, ZeroMemory, sizeof( ZeroMemory ) );
SeeKnDestroy( $0002C48F, bBuffer2, sizeof( bBuffer2 ) );
SeeKnDestroy( $0004470B, aNop, sizeof( aNop ) );
SeeKnDestroy( $000447CF, wNop, sizeof( wNop ) );
SeeKnDestroy( $00044800, wNop, sizeof( wNop ) ); // Of course, this could be done with a loop
// and without a new buffer CloseFile( hFile ); // Release handle from file
Log( 'CloseFile( ' + ExtractFileName(FilePath) + ' )' );
// Seperator
Log( Format( 'Successfully Patched!, Total of %d Bytes have been replaced', [nTotalBytes] ) );
imgPatch1.Enabled := False;
end;procedure TForm1.Log(const DebugInfo: string);
begin
Memo1.Lines.Add( DebugInfo );
Memo1.Lines.Add( '-----' ); // seperator
end;procedure TForm1.Memo1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if (Key = $41) and (ssCtrl in Shift) then // check for Ctrl + A
Memo1.SelectAll;
end;procedure TForm1.SeeKnDestroy( liOffset: LongInt; const Buf; nCount: Integer );
begin
seek( hFile, liOffset );
Log( 'seek offset 0x' + IntToHex( liOffset, 8 ) ); // converts from HEX to DEC
BlockWrite( hFile, Buf, nCount );
Log( 'Replaced Some Bytes' );
nTotalBytes := nTotalBytes + nCount;
end;end.

When compiled and patched, the result should be:

Successfully Loaded QUAD RegistryCleaner.exe
-----
AssignFile( QUAD RegistryCleaner.exe )
-----
Reset( QUAD RegistryCleaner.exe )
-----
File size: 17872 Kilobytes
-----
seek offset 0x0000590F
-----
Replaced Some Bytes
-----
seek offset 0x0000596D
-----
Replaced Some Bytes
-----
seek offset 0x00041C12
-----
Replaced Some Bytes
-----
seek offset 0x00041C7F
-----
Replaced Some Bytes
-----
seek offset 0x0004C401
-----
Replaced Some Bytes
-----
seek offset 0x0005BD63
-----
Replaced Some Bytes
-----
seek offset 0x0005BE04
-----
Replaced Some Bytes
-----
seek offset 0x0025C9E4
-----
Replaced Some Bytes
-----
seek offset 0x0025CCDC
-----
Replaced Some Bytes
-----
seek offset 0x0002C48F
-----
Replaced Some Bytes
-----
seek offset 0x0004470B
-----
Replaced Some Bytes
-----
seek offset 0x000447CF
-----
Replaced Some Bytes
-----
seek offset 0x00044800
-----
Replaced Some Bytes
-----
CloseFile( QUAD RegistryCleaner.exe )
-----
Successfully Patched!, Total of 134 Bytes have been replaced
-----
Edited by rotem156
  • Like 1
Link to comment

Note that this source code is hardcoded for version 1.5.144, older version are: .67, .69, .112 and .144 is current one.

I will make it a universal patcher that will work on future versions, and will update this thread when it's done and fully working.

Edited by rotem156
Link to comment

Nice work rotem156, this patches to file, Anotherway is to create a loader and patch it in memory, here is my example of how to do this. Once again nice work, I personally would think about making a Unit for the example you posted to make it more user friendly and better performance(memory map the file).


(* *****************************************************
Unit : uMultiMemPatch
Author : Departure
Url : forum.im-integrations.com Info:
An Easy way to patch your Target in memory,
Just add your VA and your array of bytes,
it will load the target in suspended mode
write the bytes to address(s) given and then
resume the thread with new Patches. Usage:
uses
uMultiMemPatch;
Var
MyArrayOfPatches: APatchRec;
Being
AddPatch($0040107B,[$90,$90,$90],MyArrayOfPatches);
AddPatch($004010B0,[$90,$EB],MyArrayOfPatches);
WritePatches('CrackMe.exe', MyArrayOfPatches);
MyArrayOfPatches:= Nil;
end;
***************************************************** *)unit uMultiMemPatch;interfaceuses
Windows;type
TPatchRec = Record
dwAddress: Dword;
baPatches: Array of Byte;
end;type
APatchRec = Array of TPatchRec; Procedure AddPatch(dwAddress: Dword; aPatches: Array of byte; var ArrayRecord: APatchRec);implementationProcedure AddPatch(dwAddress: Dword; aPatches: Array of byte; var ArrayRecord: APatchRec);
var
Patch: TPatchRec;
i: Integer;
begin
{Set the Address}
Patch.dwAddress:= dwAddress;
{Set the Length of Patches}
SetLength(Patch.baPatches, Length(aPatches));
{Add the Patches}
for i:= low(aPatches) to high(aPatches) do
Patch.baPatches[i]:= aPatches[i]; {Check if Array is already Built}
if ArrayRecord = Nil then
SetLength(ArrayRecord,1)
else
SetLength(ArrayRecord,Length(ArrayRecord) +1);
{Add out Patches to the Array}
ArrayRecord[high(ArrayRecord)]:= Patch;
end;Function WritePatches(sFileName: String; ArrayPatches: APatchRec):Boolean;
var
{ Startup and variables used in procedure }
StartInfo : TStartupInfo;
ProcInfo : TProcessInformation;
CreateOK : Boolean;
Write: Cardinal;
i: Integer;
begin
Result:= False;
{ Intinilize }
FillChar(StartInfo,SizeOf(TStartupInfo),#0);
FillChar(ProcInfo,SizeOf(TProcessInformation),#0);
StartInfo.cb := SizeOf(TStartupInfo); { Create Process in a suspended state }
CreateOK := CreateProcess(PChar(sFileName),nil, nil, nil,False,CREATE_SUSPENDED,nil, nil, StartInfo, ProcInfo); { check to see if successful }
if CreateOK then
try
Begin
for i:= low(ArrayPatches) to high(ArrayPatches) do
{ Write MultiPatch to memory }
WriteProcessMemory(ProcInfo.hProcess,ptr(ArrayPatches[i].dwAddress),@ArrayPatches[i].baPatches,Length(ArrayPatches[i].baPatches),Write);
{ Resume the thread }
ResumeThread(ProcInfo.hThread);
CloseHandle(ProcInfo.hProcess);
Result:= True;
end;
except
Result:= False;
end;
end;end.
Link to comment

Hey guys, here's an update:

1) Works on all (Not Tested) versions of QUAD RegistryCleaner

unit main;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, pngimage, ExtCtrls, ComCtrls, Buffers;type
TStaticArray = Array [0..6] of byte;
TDynamicArray = Array of byte;type
TMainForm = class(TForm)
Label1: TLabel;
Memo1: TMemo;
Label2: TLabel;
imgPatch1: TImage;
imgOpenFile: TImage;
Dlg1: TOpenDialog;
ProgressBar1: TProgressBar;
procedure imgPatch1Click(Sender: TObject);
procedure imgOpenFileClick(Sender: TObject);
procedure Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
private
{ Private declarations }
procedure Log(const DebugInfo: string);
function RetOffset(const bSrc: Array Of Byte): DWORD;
procedure SeeKnDestroy( const aobDest: array of byte; const Buf; nCount: Integer );
public
{ Public declarations }
end;var
MainForm: TMainForm;
FilePath: string;
FileSize: DWORD;
nTotalBytes: Integer;
hFile: THandle; // Stores file handleimplementation{$R *.dfm}procedure TMainForm.imgOpenFileClick(Sender: TObject);
begin
if Memo1.Lines.Count > 0 then Memo1.Lines.Clear; if Dlg1.Execute() then
begin
FilePath := Dlg1.FileName;
hFile := CreateFile( PChar(FilePath), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
Log( 'CreateFile( ' + ExtractFileName(FilePath) + ' )' );
if (hFile = INVALID_HANDLE_VALUE) then
begin
Log( Format( 'Error @ CreateFile, Exit Code (%d)', [GetLastError] ) );
Exit;
end;
begin
Log( 'Successfully Loaded ' + ExtractFileName(FilePath) );
FileSize := GetFileSize( hFile, nil ); // Opens files with Read/Write privilege
Log( 'GetFileSize( ' + ExtractFileName(FilePath) + ' )' );
if imgPatch1.Enabled = False then imgPatch1.Enabled := True;
Log( 'File size: ' + IntToStr( FileSize {div 1024} ) + ' bytes' );
end;
end;
end;function TMainForm.RetOffset(const bSrc: Array Of Byte): DWORD;
var
CompareArray : Array Of Byte;
FileLength : DWORD;
Pos : DWORD;
BytesRead : DWORD;
begin
Result := 0;
Pos := 0;
SetLength(CompareArray, Length(bSrc));
Try
FileLength := SetFilePointer(hFile, 0, nil, FILE_END) - Length(bSrc);
SetFilePointer(hFile, Pos, nil, FILE_BEGIN);
While Pos <= FileLength Do
begin
If ReadFile(hFile, CompareArray[0], Length(CompareArray), BytesRead, nil) Then
begin
If CompareMem(PByteArray(@bSrc[0]), PByteArray(@CompareArray[0]), Length(bSrc)) Then
begin
Result := Pos;
Break;
end;
Inc(Pos, 1);
SetFilePointer(hFile, Pos, nil, FILE_BEGIN);
Application.ProcessMessages;
end
Else
begin
Break;
end;
end;
Finally
CompareArray := nil;
end;
end;procedure TMainForm.imgPatch1Click(Sender: TObject);
begin
if FilePath = '' then
begin
MessageDlg( 'No file selected!', mtError, [mbOk], 0 );
Exit;
end; SeeKnDestroy( [$84, $C0, $75, $5C, $DD, $44, $24, $5C, $8D, $44, $24, $4C, $50, $8D], wException, sizeof( wException ) );
SeeKnDestroy( [$74, $07, $55, $55, $E8, $A2, $5B, $09, $00, $8B, $87, $58, $01, $00], bConditionJmp, sizeof( bConditionJmp ) );
SeeKnDestroy( [$84, $C0, $0F, $85, $39, $08, $00, $00, $DD, $84, $24, $9C, $00, $00], wException, sizeof( wException ) );
SeeKnDestroy( [$74, $06, $53, $E8, $91, $98, $05, $00, $8B, $55, $74, $8B, $02, $8D], bConditionJmp, sizeof( bConditionJmp ) );
SeeKnDestroy( [$A1, $89, $03, $00, $8D, $4C, $24, $34, $89, $44, $24, $14, $88, $9C], dwRepairRegistry, sizeof( dwRepairRegistry ) );
SeeKnDestroy( [$DF, $FB, $FF, $83, $C4, $08, $84, $C0, $74, $17, $68, $DC, $36, $64], aRestoreBackup, sizeof( aRestoreBackup ) );
SeeKnDestroy( [$75, $1D, $8B, $96, $08, $09, $00, $00, $56, $52, $53, $8D, $44 ,$24], bConditionJmp, sizeof( bConditionJmp ) );
SeeKnDestroy( [$55, $00, $6E, $00, $72, $00, $65, $00, $67, $00, $69, $00, $73 ,$00, $74, $00, $65, $00, $72, $00, $65, $00, $64, $00, $20, $00, $63], aLabelUnregist, sizeof( aLabelUnregist ) );
SeeKnDestroy( [$43, $00, $68, $00, $61, $00, $6E, $00, $67 ,$00 ,$65 ,$00 ,$20, $00], LabelChangeLic, sizeof( LabelChangeLic ) );
SeeKnDestroy( [$75, $62, $8B, $4E, $44, $6A, $00, $68, $D0, $E8, $6A, $00, $68, $EC], bConditionJmp, sizeof( bConditionJmp ) );
SeeKnDestroy( [$0F, $85, $29, $01, $00, $00, $3B, $EF, $0F, $85, $21, $01, $00, $00], IsRegistered, sizeof( IsRegistered ) );
SeeKnDestroy( [$75, $2A, $6A, $05, $68, $3A, $01, $00, $00, $8B, $CE, $E8, $97, $F3], IsRegistered2, sizeof( IsRegistered2 ) );
SeeKnDestroy( [$75, $62, $57, $68, $3A, $01, $00, $00, $8B, $CE, $E8, $67, $F3, $03], IsRegistered2, sizeof( IsRegistered2 ) ); CloseHandle( hFile ); // Release handle from file
Log( 'CloseHandle( ' + ExtractFileName(FilePath) + ' )' );
// seperator
Log( Format( 'Successfully Patched!, Total of %d Bytes have been replaced', [nTotalBytes] ) );
imgPatch1.Enabled := False;
end;procedure TMainForm.Log(const DebugInfo: string);
begin
Memo1.Lines.Add( DebugInfo );
Memo1.Lines.Add( '-------------' ); // seperator
end;procedure TMainForm.Memo1KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if (Key = $41) and (ssCtrl in Shift) then // check for Ctrl + A
Memo1.SelectAll;
end;procedure TMainForm.SeeKnDestroy(const aobDest: array of byte; const Buf; nCount: Integer );
var
dwBytesWritten: DWORD;
dwOffset: DWORD;
begin
dwOffset := RetOffset(aobDest);
SetFilePointer( hFile, dwOffset, nil, FILE_BEGIN );
Log( 'move to offset 0x' + IntToHex( dwOffset, 8 ) ); // converts from HEX to DEC
WriteFile( hFile, Buf, nCount, dwBytesWritten, nil );
Log( 'Replaced Some Bytes' );
inc(nTotalBytes, dwBytesWritten); // := nTotalBytes + nCount;
ProgressBar1.Position := nTotalBytes;
end;end.

buffers.pas

unit Buffers;interfaceuses Windows;const
wException: WORD = ($00A8); // Endian
bConditionJmp: BYTE = ($EB);
dwRepairRegistry: DWORD = ($000032AB); // Endian
aRestoreBackup: Array [0..50] Of BYTE = ($DF, $FB, $FF,
$83, $C4, $08,
$B0, $01, $74,
$17, $68, $DC,
$36, $64, $00,
$8D, $94, $24,
$38, $07, $00,
$00, $6A, $11,
$52, $E8, $68,
$BF, $03, $00,
$83, $C4, $0C,
$39, $5C, $24,
$24, $EB, $04,
$90, $90, $90,
$90, $3B, $FB,
$EB, $04, $90,
$90, $90, $90);
aLabelUnregist: Array [0..32] Of BYTE = ($52, $00, $65,
$00, $67, $00,
$69, $00, $73,
$00, $74, $00,
$65, $00, $72,
$00, $65, $00,
$64, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00);
LabelChangeLic: Array [0..26] Of BYTE = ($00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00,
$00, $00, $00);
IsRegistered: Array [0..13] Of BYTE = ($90, $90,
$90, $90,
$90, $90,
$90, $90,
$90, $90,
$90, $90,
$90, $90);
IsRegistered2: WORD = ($9090);implementationend.
Edited by rotem156
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...