0xFF Posted October 21, 2010 Posted October 21, 2010 (edited) 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);beginif 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( '-----' ); // seperatorend;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 October 21, 2010 by rotem156 1
0xFF Posted October 21, 2010 Author Posted October 21, 2010 (edited) 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 October 21, 2010 by rotem156
IMPosTOR Posted October 21, 2010 Posted October 21, 2010 (edited) Rulessssssssss !!! Edited October 21, 2010 by IMPosTOR
Departure Posted October 22, 2010 Posted October 22, 2010 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.
0xFF Posted October 24, 2010 Author Posted October 24, 2010 (edited) Hey guys, here's an update:1) Works on all (Not Tested) versions of QUAD RegistryCleanerunit 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);beginif 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);beginif 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( '-------------' ); // seperatorend;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.pasunit 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 October 24, 2010 by rotem156
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now