Abhishek Sharma
asked on
Find and Replace Stream with 0s
Greetings Sir,
I want to search the contents of a stream in a binary file and replace all the bytes present in it with 0s. I tried to implement it by taking the first 10 bytes of the stream and searching them in the file, then if >1 matches are found, confirmed it by SHA1. But it seems to be bugged and slow. Can ya please tell me how to do it?
I want to search the contents of a stream in a binary file and replace all the bytes present in it with 0s. I tried to implement it by taking the first 10 bytes of the stream and searching them in the file, then if >1 matches are found, confirmed it by SHA1. But it seems to be bugged and slow. Can ya please tell me how to do it?
ASKER
I'm trying to replace all the data in b/w two offsets with 0s, i.e., if I had data = "ABCDEFGHIJKLM" and I give offset of 4 and size 2, then it should become like this "ABCD00GHIJKLM". Just the binary version of it.
What code have you got so far ?
I would use memory mapped file and search through it with CompareMem function, replace with CopyMemory.... This is possible because with memory mapped file - you get pointer to "memory" which is great for CompareMem.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I ended up writing one code too, but it's giving StackOverflow on big scan inputs:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
{$optimization on}
uses
WinAPI.Windows,
System.SysUtils,
System.Classes,
System.Math;
type
TByteArray = Array of Byte;
function StreamToByteArray(Stream: TStream): TByteArray;
begin
if Assigned(Stream) then
begin
Stream.Position := 0;
SetLength(result, Stream.Size);
Stream.Read(result[0], Stream.Size);
end
else
SetLength(result, 0);
end;
procedure FindHeader(FileName: string; Sequence: array of Byte;
var List: TStringList);
var
Buffer: array of Byte;
Fs: TBufferedFileStream;
l, n, sl: Int64;
ScanStopped: boolean;
begin
List.Clear;
sl := length(Sequence);
if sl = 0 then
Exit;
Fs := TBufferedFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite,
4 * 1024 * 1024);
try
SetLength(Buffer, Fs.Size);
Fs.Read(Buffer[0], Fs.Size);
for l := 0 to Fs.Size - 1 do
begin
if Buffer[l] = Sequence[0] then
begin
if sl = 1 then
List.Add(IntToStr(l))
else
begin
n := 1;
ScanStopped := false;
while n < sl do
begin
if Buffer[l + n] <> Sequence[n] then
begin
ScanStopped := true;
Break;
end;
Inc(n);
end;
if not ScanStopped then
List.Add(IntToStr(l));
end;
end;
end;
finally
Finalize(Buffer);
Fs.Free;
end;
end;
var
raw: TMemoryStream;
null: array [0 .. 0] of Byte = (
$00
);
pos: TStringList;
I, j: Int64;
start, stop, elapsed: cardinal;
infile, scanfile: TBufferedFileStream;
begin
start := GetTickCount;
pos := TStringList.Create;
j := 0;
try
scanfile := TBufferedFileStream.Create(paramstr(2), fmOpenReadWrite);
raw := TMemoryStream.Create;
if scanfile.Size > 1024 * 1024 then
raw.CopyFrom(scanfile, 1024 * 1024)
else
raw.CopyFrom(scanfile, 0);
scanfile.Free;
FindHeader(paramstr(1), StreamToByteArray(raw), pos);
writeln('Possible offsets found: ' + IntToStr(pos.Count));
infile := TBufferedFileStream.Create(paramstr(1), fmOpenReadWrite);
if pos.Count <> 0 then
begin
infile.Seek(strtoint(pos[0]), soFromBeginning);
for I := 0 to raw.Size - 1 do
begin
infile.Write(null, 1);
end;
end;
finally
pos.Free;
raw.Free;
infile.Free;
end;
stop := GetTickCount;
elapsed := stop - start;
writeln('Time Taken: ' + IntToStr(elapsed) + ' ms');
end.
well yeah, you put the whole stream in memory with this function StreamToByteArray
why ?
read a chunk, analyze it for the items, update the positions on file, read next check,
repeat until Jezus Christ returns or EOF
why ?
read a chunk, analyze it for the items, update the positions on file, read next check,
repeat until Jezus Christ returns or EOF
Try my piece of code for finding first position (with a replacing):
function MMFReplace(FileName: String; pFindWhat: TByteDynArray; pReplaceWith: TByteDynArray): Boolean;
function MakeInt64 (H,L: DWord): Int64;
begin
Result := H or (L shl 32);
end;
var
hFile: THandle;
hFileMap: THandle;
hiSize, loSize: DWORD;
pFileView: Pointer;
lFileSz, lFind, lReplace: Int64;
i: Cardinal;
begin
Result := False;
if FileName = '' then Exit;
lFind := Length(pFindWhat);
lReplace := Length(pReplaceWith);
//check if parameter are same length
//if Length(pFindWhat) <> Length(pReplaceWith) then Exit;
//open the file...
hFile := CreateFile(
PChar(FileName), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if hFile <> INVALID_HANDLE_VALUE then
begin
try
loSize := GetFileSize(hFile, @hiSize);
lFileSz := MakeInt64(hiSize, loSize);
//create file mapping....
hFileMap := CreateFileMapping(hFile, nil, PAGE_READWRITE, hiSize, loSize, 'Find_And_Replace_MMF');
if hFileMap <> 0 then
begin
try
//create map view....
pFileView := MapViewOfFile(hFileMap, FILE_MAP_READ or FILE_MAP_WRITE, 0, 0, 0);
if pFileView <> nil then
begin
try
//try to find ..
for i := 0 to lFileSz - lFind - 1 do
begin
//compare bytes...
if CompareMem(Pointer(Cardinal(pFileView)+i), @pFindWhat[0], lFind) then
begin
//found it... replace
if lReplace>0 then
MoveMemory(Pointer(Cardinal(pFileView)+i), @pReplaceWith[0], lReplace);
Result := True;
Break;
end;
end;
finally
UnmapViewOfFile(pFileView);
end;
end;
finally
CloseHandle(hFileMap);
end;
end;
finally
CloseHandle(hFile);
end;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
sFind, sReplace: TByteDynArray;
begin
SetLength(sFind, 4);
sFind[0] := Ord('5');
sFind[1] := Ord('0');
sFind[2] := Ord('0');
sFind[3] := Ord('2');
SetLength(sReplace, 4);
sReplace[0] := Ord('5');
sReplace[1] := Ord('0');
sReplace[2] := Ord('0');
sReplace[3] := Ord('3');
if MMFReplace('C:\Temp\myfile.txt', sFind, sReplace) then
ShowMessage('Found!');
end;
mlmcc