{ HoundPE

  Simple PE Editor, will add more as I learn more :)

  By steve10120
  http://hackhound.org }

{ + Added section viewer
    + Added dump section
    + Added section editor
    + Added section hex viewer

  + Added extract overlay

  + Added compare files
    + Added save report

  + Added hex viewer

  + Data Directories Viewer
  }

unit Main;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, StrUtils, Sections, Menus, CompareFiles;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    txtEP: TEdit;
    Bevel1: TBevel;
    cmdOpen: TButton;
    Label2: TLabel;
    txtImgBase: TEdit;
    Label3: TLabel;
    txtLinker: TEdit;
    Label4: TLabel;
    txtSub: TEdit;
    Label5: TLabel;
    txtSecCount: TEdit;
    Label6: TLabel;
    txtBaseOfCode: TEdit;
    Label7: TLabel;
    txtSizeOfImage: TEdit;
    Label8: TLabel;
    txtBaseOfData: TEdit;
    Label9: TLabel;
    txtSecAlign: TEdit;
    Label10: TLabel;
    txtFileAlign: TEdit;
    Label11: TLabel;
    txtMagic: TEdit;
    Label12: TLabel;
    txtTimeDate: TEdit;
    Label13: TLabel;
    txtSizeOfHeaders: TEdit;
    label14: TLabel;
    txtSizeOfOpHeader: TEdit;
    cmdSave: TButton;
    cmdExit: TButton;
    OpenDlg: TOpenDialog;
    MainMenu: TMainMenu;
    Tools: TMenuItem;
    ExtractOverlay1: TMenuItem;
    Label15: TLabel;
    txtEPSection: TEdit;
    CompareFiles1: TMenuItem;
    CompDlg: TOpenDialog;
    HexViewer1: TMenuItem;
    DataDirs: TMenuItem;
    procedure cmdSaveClick(Sender: TObject);
    procedure cmdOpenClick(Sender: TObject);
    procedure cmdExitClick(Sender: TObject);
    procedure Label5Click(Sender: TObject);
    procedure ExtractOverlay1Click(Sender: TObject);
    procedure ToolsClick(Sender: TObject);
    procedure CompareFiles1Click(Sender: TObject);
    procedure HexViewer1Click(Sender: TObject);
    procedure DataDirsClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  IDH:    TImageDosHeader;
  INH:    TImageNtHeaders;
  ISH:    TImageSectionHeader;
  sFile:  string;
  SecName : array [0..7] of char;

implementation

uses HexViewer, Data;

{$R *.dfm}

function HexToInt(s: string): Integer;
begin
  Result := StrToInt('$' + s);
end;

function FileToStr(sPath:string):AnsiString;
var
hFile:  THandle;
dSize:  DWORD;
dRead:  DWORD;
sBuff:  AnsiString;
begin
  hFile := CreateFile(PChar(sPath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    dSize := GetFileSize(hFile, nil);
    SetLength(sBuff, dSize);
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    ReadFile(hFile, sBuff[1], dSize, dRead, nil);
    if dRead = 0 then
    begin
      MessageBox(Application.Handle, PChar('Error reading file.'), PChar('Error'), MB_ICONEXCLAMATION);
      Exit;
    end;
    Result := sBuff
  end;
  CloseHandle(hFile);
end;

function GetEOFPos(sPath:string):DWORD;
var
hFile:  THandle;
dRead:  DWORD;
IDH:    TImageDosHeader;
INH:    TImageNtHeaders;
ISH:    TImageSectionHeader;
begin
  hFile := CreateFile(PChar(sPath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    ReadFile(hFile, IDH, SizeOf(IDH), dRead, nil);
    if dRead = SizeOf(IDH) then
    begin
      SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN);
      ReadFile(hFile, INH, SizeOf(INH), dRead, nil);
      if dRead = SizeOf(INH) then
      begin
        SetFilePointer(hFile, IDH._lfanew + 248 + (INH.FileHeader.NumberOfSections - 1) * 40, nil, FILE_BEGIN);
        ReadFile(hFile, ISH, SizeOf(ISH), dRead, nil);
        Result := ISH.PointerToRawData + ISH.SizeOfRawData;
      end;
    end;
  end;
  CloseHandle(hFile);
end;

procedure SaveOverlay(sPath:string);
var
hFile:    THandle;
dSize:    DWORD;
dRead:    DWORD;
dWritten: DWORD;
pData:    Pointer;
EOFPos:   DWORD;
begin
  hFile := CreateFile(PChar(sPath), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    dSize := GetFileSize(hFile, nil);
    EOFPos := GetEOFPos(sPath);
    if EOFPos = dSize then
    begin
      MessageBox(Application.Handle, PChar('No overlay found.'), PChar('Error'), MB_ICONEXCLAMATION);
      Exit;
    end;
    SetFilePointer(hFile, EOFPos, nil, FILE_BEGIN);
    GetMem(pData, dSize - EOFPos);
    ReadFile(hFile, pData^,  dSize - EOFPos, dRead, nil);
    CloseHandle(hFile);

    hFile := CreateFile(PChar(ExtractFileName(sPath) + '.ovr'), GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
    if hFile <> INVALID_HANDLE_VALUE then
    begin
      SetFilePointer(hFile, 0, nil, FILE_BEGIN);
      WriteFile(hFile, pData^, dSize - EOFPos, dWritten, nil);
      CloseHandle(hFile);
    end;
    FreeMem(pData, dSize - EOFPos);
  end;
end;

function GetInfo():boolean;
var
hFile:  THandle;
dRead:  DWORD;
i:      integer;
begin
  hFile := CreateFile(PChar(sFile), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    ReadFile(hFile, IDH, SizeOf(IDH), dRead, nil); // Read first 64 bytes into IMAGE_DOS_HEADER struct
    if IDH.e_magic <> 23117 then // Check for valid MZ header
    begin
      MessageBox(Application.Handle, PChar('Invalid PE File'), PChar('Error'), MB_ICONEXCLAMATION);
      Exit;
    end;
    SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN); // Setting file pointer to the beginning IMAGE_NT_HEADERS struct
    ReadFile(hFile, INH, SizeOf(INH), dRead, nil); // Read 248 bytes into IMAGE_NT_HEADERS struct
    /// Data Directories
    frmData.txtETRVA.Text  := IntToHex(INH.OptionalHeader.DataDirectory[0].VirtualAddress, 8);
    frmData.txtETSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[0].Size, 8);
    frmData.txtITRVA.Text  := IntToHex(INH.OptionalHeader.DataDirectory[1].VirtualAddress, 8);
    frmData.txtITSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[1].Size, 8);
    frmData.txtResRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[2].VirtualAddress, 8);
    frmData.txtResSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[2].Size, 8);
    frmData.txtExRVA.Text  := IntToHex(INH.OptionalHeader.DataDirectory[3].VirtualAddress, 8);
    frmData.txtExSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[3].Size, 8);
    frmData.txtRelRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[4].VirtualAddress, 8);
    frmData.txtRelSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[4].Size, 8);
    frmData.txtSecRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[5].VirtualAddress, 8);
    frmData.txtSecSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[5].Size, 8);
    frmData.txtDeb.Text     := IntToHex(INH.OptionalHeader.DataDirectory[6].VirtualAddress, 8);
    frmData.txtDebSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[6].Size, 8);
    frmData.txtCopyRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[7].VirtualAddress, 8);
    frmData.txtCopySize.Text := IntToHex(INH.OptionalHeader.DataDirectory[7].Size, 8);
    frmData.txtGlobalRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[8].VirtualAddress, 8);
    frmData.txtGlobalSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[8].Size, 8);
    frmData.txtTlsRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[9].VirtualAddress, 8);
    frmData.txtTlsSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[9].Size, 8);
    frmData.txtLConRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[10].VirtualAddress, 8);
    frmData.txtLConSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[10].Size, 8);
    frmData.txtBIRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[11].VirtualAddress, 8);
    frmData.txtBISize.Text := IntToHex(INH.OptionalHeader.DataDirectory[11].Size, 8);
    frmData.txtIATRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[12].VirtualAddress, 8);
    frmData.txtIATSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[12].Size, 8);
    frmData.txtDIRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[13].VirtualAddress, 8);
    frmData.txtDISize.Text := IntToHex(INH.OptionalHeader.DataDirectory[13].Size, 8);
    frmData.txtCOMRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[14].VirtualAddress, 8);
    frmData.txtCOMSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[14].Size, 8);
    frmData.txtReseRVA.Text := IntToHex(INH.OptionalHeader.DataDirectory[15].VirtualAddress, 8);
    frmData.txtReseSize.Text := IntToHex(INH.OptionalHeader.DataDirectory[15].Size, 8);
    /// End Data Directories
    frmSections.lsSections.Clear;
    For i := 0 to INH.FileHeader.NumberOfSections - 1 do // Loop thru sections
    begin
      SetFilePointer(hFile, IDH._lfanew + 248 + (i) * 40, nil, FILE_BEGIN); // Set file pointer to the beggining of each section
      ReadFile(hFile, ISH, SizeOf(ISH), dRead, nil); // Read 40 bytes into IMAGE_SECTION_HEADER struct
      Move(ISH.Name, SecName, 8);
      if (INH.OptionalHeader.AddressOfEntryPoint >= ISH.VirtualAddress) and (INH.OptionalHeader.AddressOfEntryPoint <= ISH.VirtualAddress + ISH.Misc.VirtualSize) then // Check if EP Section is the first section
      begin
        if i <> 0 then
        begin
          Form1.txtEPSection.Font.Color := clRed;
          Form1.txtEPSection.Hint := 'EP section is not first section, file possibly packed/crypted.';
        end
        else
        begin
          Form1.txtEPSection.Font.Color := clBlack;
          Form1.txtEPSection.Hint := '';
        end;
        Form1.txtEPSection.Text := SecName;
      end;

      with frmSections.lsSections.Items.Add do
      begin
        Caption:= SecName;
        SubItems.Add(IntToHex(ISH.VirtualAddress, 8));
        SubItems.Add(IntToHex(ISH.Misc.VirtualSize, 8));
        SubItems.Add(IntToHex(ISH.PointerToRawData, 8));
        SubItems.Add(IntToHex(ISH.SizeOfRawData, 8));
        SubItems.Add(IntToHex(ISH.Characteristics, 8));
      end;
    end;
    ///
    Form1.txtEP.Text := IntToHex(INH.OptionalHeader.AddressOfEntryPoint, 8);
    Form1.txtImgBase.Text := IntToHex(INH.OptionalHeader.ImageBase, 8); //
    Form1.txtSizeOfImage.Text := IntToHex(INH.OptionalHeader.SizeOfImage, 8);
    Form1.txtBaseOfCode.Text := IntToHex(INH.OptionalHeader.BaseOfCode, 8);
    Form1.txtBaseOfData.Text := IntToHex(INH.OptionalHeader.BaseOfData, 8);
    Form1.txtSecAlign.Text := IntToHex(INH.OptionalHeader.SectionAlignment, 8);
    Form1.txtFileAlign.Text := IntToHex(INH.OptionalHeader.FileAlignment, 8);
    Form1.txtMagic.Text := IntToHex(INH.OptionalHeader.Magic, 4);
    Form1.txtTimeDate.Text := IntToHex(INH.FileHeader.TimeDateStamp, 8);
    Form1.txtSizeOfHeaders.Text := IntToHex(INH.OptionalHeader.SizeOfHeaders, 8);
    Form1.txtSizeOfOpHeader.Text := IntToHex(INH.FileHeader.SizeOfOptionalHeader, 4);
    Form1.txtLinker.Text := IntToStr(INH.OptionalHeader.MajorLinkerVersion) + '.' + IntToStr(INH.OptionalHeader.MinorLinkerVersion);
    Form1.txtSub.Text := IntToHex(INH.OptionalHeader.Subsystem, 0);
    Form1.txtSecCount.Text := IntToHex(INH.FileHeader.NumberOfSections, 4);
  end;
  CloseHandle(hFile);
end;

function WriteInfo():boolean;
var
hFile:    THandle;
dRead:    DWORD;
dWritten: DWORD;
Minor, Major: string;
begin
  hFile := CreateFile(PChar(sFile), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    SetFilePointer(hFile, 0, nil, FILE_BEGIN);
    ReadFile(hFile, IDH, SizeOf(IDH), dRead, nil);  // Again reading the IMAGE_DOS_HEADER so we have the start pointer to write to
  end;
  CloseHandle(hFile);

  hFile := CreateFile(PChar(sFile), GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
  if hFile <> INVALID_HANDLE_VALUE then
  begin
    INH.OptionalHeader.AddressOfEntryPoint := HexToInt(Form1.txtEP.Text);
    INH.OptionalHeader.ImageBase := HexToInt(Form1.txtImgBase.Text);
    INH.OptionalHeader.SizeOfImage := HexToInt(Form1.txtSizeOfImage.Text);
    INH.OptionalHeader.BaseOfCode := HexToInt(Form1.txtBaseOfCode.Text);
    INH.OptionalHeader.BaseOfData := HexToInt(Form1.txtBaseOfData.Text);
    INH.OptionalHeader.SectionAlignment := HexToInt(Form1.txtSecAlign.Text);
    INH.OptionalHeader.FileAlignment := HexToInt(Form1.txtFileAlign.Text);
    INH.OptionalHeader.Magic := HexToInt(Form1.txtMagic.Text);
    INH.FileHeader.TimeDateStamp := HexToInt(Form1.txtTimeDate.Text);
    INH.OptionalHeader.SizeOfHeaders := HexToInt(Form1.txtSizeOfHeaders.Text);
    INH.FileHeader.SizeOfOptionalHeader := HexToInt(Form1.txtSizeOfOpHeader.Text);
    INH.OptionalHeader.Subsystem := HexToInt(Form1.txtSub.Text);
    INH.FileHeader.NumberOfSections := HexToInt(Form1.txtSecCount.Text);
    Major := LeftStr(Form1.txtLinker.Text, Pos('.', Form1.txtLinker.Text) - 1);
    Minor := RightStr(Form1.txtLinker.Text, Length(Form1.txtLinker.Text) - Pos('.', Form1.txtLinker.Text));
    INH.OptionalHeader.MajorLinkerVersion := StrToInt(Major);
    INH.OptionalHeader.MinorLinkerVersion := StrToInt(Minor);
    SetFilePointer(hFile, IDH._lfanew, nil, FILE_BEGIN); // Again setting file pointer to the beginning of IMAGE_NT_HEADERS struct
    if WriteFile(hFile, INH, SizeOf(INH), dWritten, nil) then  // Finally write our modified IMAGE_NT_HEADERS struct
      Result := TRUE
    else
      Result := FALSE;

  end;
  CloseHandle(hFile);
end;

procedure TForm1.cmdSaveClick(Sender: TObject);
begin
 if WriteInfo = FALSE then
    MessageBox(Application.Handle, PChar('Failed to write data.'), PChar('Error'), MB_ICONEXCLAMATION);
end;

procedure TForm1.cmdOpenClick(Sender: TObject);
begin
  OpenDlg.Filter := 'Executable Files | *.exe';
  OpenDlg.Title  := 'Please select a file...';
  OpenDlg.Execute;

  if FileExists(OpenDlg.FileName) then
  begin
    sFile := OpenDlg.FileName;
    Form1.Caption := 'HoundPE - ' + ExtractFileName(OpenDlg.FileName);
    GetInfo;
  end;
end;

procedure TForm1.cmdExitClick(Sender: TObject);
begin
Halt(0);
end;

procedure TForm1.Label5Click(Sender: TObject);
begin
  if FileExists(sFile) then
    frmSections.Show;
end;

procedure TForm1.ExtractOverlay1Click(Sender: TObject);
begin
  SaveOverlay(sFile)
end;

procedure TForm1.ToolsClick(Sender: TObject);
begin
  if FileExists(sFile) then
  begin
    ExtractOverlay1.Enabled := TRUE;
    CompareFiles1.Enabled   := TRUE;
    HexViewer1.Enabled      := TRUE;
  end
  else
  begin
    ExtractOverlay1.Enabled := FALSE;
    CompareFiles1.Enabled   := FALSE;
    HexViewer1.Enabled      := FALSE;
  end;
end;

procedure TForm1.CompareFiles1Click(Sender: TObject);
begin
  CompDlg.Filter := 'Executable Files | *.exe';
  CompDlg.Title  := 'Please select a file...';
  CompDlg.Execute;

  if FileExists(CompDlg.FileName) then
  begin
    GetInfo_2(CompDlg.FileName);
    frmCompare.lsInfo.Columns[1].Caption := ExtractFileName(CompDlg.FileName);
    frmCompare.lsInfo.Columns[2].Caption := ExtractFileName(sFile);
    frmCompare.Show;
  end;
end;

procedure TForm1.HexViewer1Click(Sender: TObject);
var
sBuff:  string;
begin
frmHex.Hex1.Undo;
sBuff := FileToStr(sFile);
frmHex.Hex1.InsertBuffer(PChar(sBuff), Length(sBuff), 0, '');
frmHex.Show;
frmHex.Hex1.SelStart := 0;
end;

procedure TForm1.DataDirsClick(Sender: TObject);
begin
  frmData.Show
end;

end.
