2005. február 12., szombat

How to embed binary data in an executable


Problem/Question/Abstract:

I have a very specialized script language which requires an executable program (The Engine) to load and process scripts. This works on the basis that each script requires its own copy of the executable processing engine (for reasons about to be explained). I want to take this a step further by embedding a script inside the executable at runtime. This will be done from my existing script editor. Something like a compile script function. How can I open an executable file, safely add a block of binary data which can then be read when the executable is running? I know this can be done. If virus writers can put additional executable code into an *.exe file, then I must be able to put binary data in.

Answer:

I wrote this component to embed data in forms or datamodules. Drop the component in the form, double click it and select the file to embed. I have written a version that compresses the data also, but I lost it, anyway, is not complicated to do so if you want compressed data.

unit uBinaryData;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DsgnIntf;

type
  TBinaryData = class(TComponent)
  private
    MemStream: TStream;
    TempFileName: string;
    procedure WriteData(Stream: TStream);
    procedure ReadData(Stream: TStream);
    procedure SetStream(Stream: TStream);
    function GetDataSize: Longint;
    procedure SetDataSize(ASize: Longint);
  protected
    procedure DefineProperties(Filer: TFiler); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetTempFile(const Ext: string): string;
    procedure DeleteTempFile;
    procedure SaveToFile(const FName: string);
    property Stream: TStream read MemStream write SetStream;
  published
    property DataSize: Longint read GetDataSize write SetDataSize;
  end;

  TBinaryDataEditor = class(TComponentEditor)
  protected
    function GetVerbCount: Integer; override;
    function GetVerb(index: Integer): string; override;
    procedure ExecuteVerb(index: Integer); override;
    procedure Edit; override;
  end;

procedure Register;

implementation

{ TBinaryData }

constructor TBinaryData.Create;
begin
  inherited;
  MemStream := TMemoryStream.Create;
end;

destructor TBinaryData.Destroy;
begin
  MemStream.Free;
  if TempFileName <> '' then
    DeleteTempFile;
  inherited;
end;

function TBinaryData.GetDataSize;
begin
  Result := MemStream.Size;
end;

procedure TBinaryData.SetDataSize;
begin
  (MemStream as TMemoryStream).SetSize(ASize);
end;

procedure TBinaryData.DefineProperties;
begin
  inherited;
  Filer.DefineBinaryProperty('TheData', ReadData, WriteData, true);
end;

procedure TBinaryData.ReadData;
var
  ASize: Longint;
begin
  Stream.Read(ASize, sizeof(ASize));
  if ASize > 0 then
  begin
    (MemStream as TMemoryStream).SetSize(ASize);
    Stream.Read((MemStream as TMemoryStream).Memory^, ASize);
  end;
end;

procedure TBinaryData.WriteData;
var
  ASize: Longint;
begin
  ASize := MemStream.Size;
  Stream.Write(ASize, sizeof(ASize));
  if ASize > 0 then
    Stream.Write((MemStream as TMemoryStream).Memory^, ASize);
end;

procedure TBinaryData.SetStream;
begin
  if Stream <> nil then
    (MemStream as TMemoryStream).LoadFromStream(Stream)
  else
    (MemStream as TMemoryStream).SetSize(0);
end;

function TBinaryData.GetTempFile;
const
  FirstChars: PChar = 'AAA';
var
  PathBuffer: array[0..255] of char;
  FileName: array[0..MAX_PATH] of char;
  FileStream: TFileStream;
begin
  GetTempPath(256, PathBuffer);
  if GetTempFileName(PathBuffer, FirstChars, 0, FileName) = 0 then
    raise Exception.Create('No se pudo crear el archivo temporal');
  Result := StrPas(FileName);
  DeleteFile(Result);
  Result := ChangeFileExt(Result, Ext);
  TempFileName := Result;
  FileStream := TFileStream.Create(Result, fmCreate);
  try
    MemStream.Seek(0, 0);
    FileStream.CopyFrom(MemStream, MemStream.Size);
  finally
    FileStream.Free;
  end;
end;

procedure TBinaryData.DeleteTempFile;
begin
  DeleteFile(TempFileName);
  TempFileName := '';
end;

procedure TBinaryData.SaveToFile;
var
  s: TFileStream;
begin
  s := TFileStream.Create(FName, fmCreate);
  try
    Stream.Seek(0, 0);
    s.CopyFrom(Stream, Stream.Size);
  finally
    s.Free;
  end;
end;

{ TBinaryDataEditor }

function TBinaryDataEditor.GetVerbCount;
begin
  Result := 1;
end;

function TBinaryDataEditor.GetVerb;
begin
  Result := 'Load File...';
end;

procedure TBinaryDataEditor.ExecuteVerb;
begin
  Edit;
end;

procedure TBinaryDataEditor.Edit;
var
  OpenDialog: TOpenDialog;
  FileStream: TFileStream;
begin
  OpenDialog := TOpenDialog.Create(Application);
  try
    OpenDialog.Filter := '*.*';
    if OpenDialog.Execute then
      if FileExists(OpenDialog.Filename) then
      begin
        FileStream := TFileStream.Create(OpenDialog.Filename, fmOpenRead);
        try
          (Component as TBinaryData).Stream := FileStream;
          Designer.Modified;
        finally
          FileStream.Free;
        end;
      end;
  finally
    OpenDialog.Free;
  end;
end;

procedure Register;
begin
  RegisterComponents('Misc', [TBinaryData]);
  RegisterComponentEditor(TBinaryData, TBinaryDataEditor);
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése