delphi 给EXE文件增加区段

学习 PE 可执行文件格式,用 delphi 实现给 EXE 文件增加区段
源码下载(StudyPE.zip)
unit uStudyPE;

interface
uses
  Classes, SysUtils, Windows, uPERec;

type

  TImage_Section_headerList = class;

  TStudyPE = class
  private
    FFileStream: TFileStream;
    FNewStream: TMemoryStream;
    FImage_Dos_Header: PImage_DOS_Header;
    FImage_NT_Headers: PImage_NT_Headers;
    FImage_Section_HeaderList: TImage_Section_headerList;
  public
    constructor Create;
    destructor Destroy; override;
  public
    procedure LoadPE(AFileName: string);
    property Image_Dos_Header: PImage_DOS_Header read FImage_Dos_Header;
    property Image_NT_Headers: PImage_NT_Headers read FImage_NT_Headers;
    property Image_Section_HeaderList: TImage_Section_headerList read FImage_Section_HeaderList;
  end;

  TImage_Section_headerList = class(TList)
  public
    procedure FreeAllItems();
    destructor Destroy; override;
  end;

implementation
{ TStudyPE }
const
  Image_Dos_Header_len = SizeOf(TImage_dos_header);
  Image_NT_Headers_len = SizeOf(TImage_NT_Headers);
  Image_Section_Header_len = SizeOf(TImage_Section_Header);

constructor TStudyPE.Create;
begin
  inherited Create;
  New(FImage_Dos_Header);
  New(FImage_NT_Headers);
  FImage_Section_HeaderList := TImage_Section_headerList.Create;
  FNewStream := TMemoryStream.Create;
end;

destructor TStudyPE.Destroy;
begin
  Dispose(FImage_Dos_Header);
  Dispose(FImage_NT_Headers);
  FImage_Section_HeaderList.Free;
  FNewStream.Free;
  inherited;
end;

procedure TStudyPE.LoadPE(AFileName: string);
var

  e_lfanew: DWORD;
  pSecHeader: PImage_Section_header;
  nNumberOfSections: word;
  i: integer;
  nDosStubLen: integer;
  nSizeOfImage: DWORD;
  LImage_Section_header: TImage_Section_Header;
  Rva: DWORD;
  vaSize: DWORD;

  SizeOfHeaders: DWORD; // 一般是 $400
  NewSectionSize: DWORD; // 新增区段大小

begin

  if Assigned(FFileStream) then
  begin
    FFileStream.Free;
  end;

  NewSectionSize := $200;

  FFileStream := TFileStream.Create(AFileName, fmOpenRead);
  FFileStream.Read(FImage_Dos_Header^, Image_Dos_Header_len); // 读 DOS 头,64字节

  FNewStream.Size := FFileStream.Size + NewSectionSize; // 新文件大小
  FNewStream.Write(FImage_Dos_Header^, Image_Dos_Header_len); // 将 DOS 头写入新文件

  e_lfanew := FImage_Dos_Header.e_lfanew; // 获取 Image_NT_Headers 的位置
  nDosStubLen := e_lfanew - Image_Dos_Header_len; // DOS 汇编代码区域长度

  // 将 DOS 汇编代码写入新文件,写完后 FFileStream.Position 正好在 Image_NT_Header 的位置上
  FFileStream.Read((Pbyte(FNewStream.Memory) + Image_Dos_Header_len)^, nDosStubLen);
  FNewStream.Position := Image_Dos_Header_len + nDosStubLen;

  FFileStream.Read(FImage_NT_Headers^, Image_NT_Headers_len); // 获取 Image_NT_Headers
  SizeOfHeaders := FImage_NT_Headers.Image_Optional_Header32.SizeOfHeaders;

  nNumberOfSections := FImage_NT_Headers.Image_File_Header.NumberOfSections; // 区段数量
  FImage_NT_Headers.Image_File_Header.NumberOfSections := nNumberOfSections + 1; // 增加区段数量

  nSizeOfImage := FImage_NT_Headers.Image_Optional_Header32.SizeOfImage;
  FImage_NT_Headers.Image_Optional_Header32.SizeOfImage := nSizeOfImage + NewSectionSize; // 修改 EXE 所占内存大小
  FNewStream.Write(FImage_NT_Headers^, Image_NT_Headers_len); // 将 Image_NT_Headers 写入新文件

  FImage_Section_HeaderList.FreeAllItems;

  for i := 0 to nNumberOfSections - 1 do
  begin
    New(pSecHeader);
    FImage_Section_HeaderList.Add(pSecHeader);
    FFileStream.Read(pSecHeader^, Image_Section_Header_len);
    FNewStream.Write(pSecHeader^, Image_Section_Header_len); // 将各个 Image_Section_Header 写入新文件
  end;

  LImage_Section_header := pSecHeader^;

  // 给新区段命名
  LImage_Section_header.Name[0] := ord('.');
  LImage_Section_header.Name[1] := ord('N');
  LImage_Section_header.Name[2] := ord('E');
  LImage_Section_header.Name[3] := ord('W');
  LImage_Section_header.Name[4] := ord('T');
  LImage_Section_header.Name[5] := ord('E');
  LImage_Section_header.Name[6] := ord('S');
  LImage_Section_header.Name[7] := ord('T');

  // 用最后一个区段作参考计算新增区段的 VirtualAddress,PointerToRawData 的值
  // VirtualAddress 是在EXE中虚拟地址
  // PointerToRawData 新区段在文件中的位置
  Rva := pSecHeader.VirtualAddress;
  vaSize := ((pSecHeader.Misc.VirtualSize + $FFF) div $1000) * $1000;
  LImage_Section_header.VirtualAddress := Rva + vaSize; // 新区段的虚拟地址

  Rva := pSecHeader.PointerToRawData;
  vaSize := ((pSecHeader.Misc.VirtualSize + $1FF) div $200) * $200;
  LImage_Section_header.PointerToRawData := Rva + vaSize; // 新区段的文件中的位置

  LImage_Section_header.Misc.VirtualSize := NewSectionSize;
  LImage_Section_header.SizeOfRawData := NewSectionSize;
  LImage_Section_header.Characteristics := $50000040; // 区段的属性(读,写,执行)

  // 要计算位置是否够放新 Section, 未完成,本例所改的 exe 是可以的。

  if ((SizeOfHeaders - FFileStream.Position) < Image_Section_Header_len) then
  begin
    raise Exception.Create('区段位置不够,本Demo无法操作!');
    // 为了简单,所以这样操作了。实际上是可以的,只是每个区段都得重新计算虚拟地址与在文件中的地址
  end;

  FNewStream.Write(LImage_Section_header, Image_Section_Header_len); // 将新增 Image_Section_Header 写入新文件

  // 将源文件剩下的数据写入新文件
  FFileStream.Seek(SizeOfHeaders, soBeginning);
  FFileStream.Read((Pbyte(FNewStream.Memory) + SizeOfHeaders)^, FFileStream.Size - SizeOfHeaders);
  FNewStream.SaveToFile('new.exe');

end;

{ TImage_Section_headerList }

destructor TImage_Section_headerList.Destroy;
begin
  FreeAllItems;
  inherited;
end;

procedure TImage_Section_headerList.FreeAllItems;
var
  p: PImage_Section_header;
begin
  for p in self do
    Dispose(p);
  Clear;
end;

end.
uStudyPE.pas
unit uPERec;

interface
uses
  Classes, SysUtils, Windows;

type
 // Windows 单元中有这些结构,而我为了学习,从书上重新操作了一遍

  PImage_DOS_Header = ^TImage_DOS_Header;
  TImage_DOS_Header = packed record
    e_magic: WORD; // DOS signature:4D5A ('MZ')
    e_cblp: WORD;
    e_cp: WORD;
    e_crlc: WORD;
    e_cparhdr: WORD;
    e_minalloc: WORD;
    e_maxalloc: WORD;
    e_ss: WORD;
    e_sp: WORD;
    e_csum: WORD;
    e_ip: WORD;
    e_cs: WORD;
    e_lfarlc: WORD;
    e_ovno: WORD;
    e_res: array [0 .. 3] of WORD;
    e_oemid: WORD;
    e_oeminfo: WORD;
    e_res2: array [0 .. 9] of WORD;
    e_lfanew: DWORD; // offset to NT hearder
  end;

  TImage_Optional_Header32 = _Image_Optional_Header32;
  TImage_File_Header = _Image_File_Header;

  PImage_NT_Headers = ^TImage_NT_Headers;
  TImage_NT_Headers = packed record
    Signature: DWORD; // PE Signature: 50450000 ('PE'00)
    Image_File_Header: TImage_File_Header;
    Image_Optional_Header32: TImage_Optional_Header32;
  end;

  PImage_File_Header = ^TImage_File_Header;
  _Image_File_Header = packed record
    Machine: WORD;
    NumberOfSections: WORD;
    TimeDateStamp: DWORD;
    PointerToSymbolTable: DWORD;
    NumberOfSymbols: DWORD;
    SizeOfOptionalHeader: WORD;
    Characteristics: WORD;
  end;

const
  Image_NumberOf_Directory_Entries = 16;

type

  TImage_Data_Directory = _Image_Data_Directory;

  PImage_Optional_Header32 = ^TImage_Optional_Header32;
  _Image_Optional_Header32 = packed record
    Magic: WORD;
    MajorLinkerVersion: BYTE;
    MinorLinkerVersion: BYTE;
    SizeOfCode: DWORD;
    SizeOfInitializedData: DWORD;
    SizeOfUninitializedData: DWORD;
    AddressOfEntryPoint: DWORD;
    BaseOfCode: DWORD;
    BaseOfData: DWORD;
    ImageBase: DWORD;
    SectionAlignment: DWORD;
    fileAlignment: DWORD;
    MajorOperatingSystemVersion: WORD;
    MinorOperatingSystemVersion: WORD;
    MajorImageVersion: WORD;
    minorImageVersion: WORD;
    MajorSubsystemVersion: WORD;
    MinorSubsystemVersion: WORD;
    Win32VersionValue: DWORD;
    SizeOfImage: DWORD;
    SizeOfHeaders: DWORD;
    checkSum: DWORD;
    Subsystem: WORD;
    DllCharacteristics: WORD;
    SizeOfStackReserve: DWORD;
    SizeOfStackCommit: DWORD;
    sizeOfHeapReserve: DWORD;
    sizeofHeapcommit: DWORD;
    LoaderFlags: DWORD;
    NumberOfRvaAndSizes: DWORD;
    Image_Data_Directory: array [0 .. Image_NumberOf_Directory_Entries - 1] of TImage_Data_Directory;
  end;

  _Image_Data_Directory = packed record
    VirtualAddress: DWORD;
    Size: DWORD;
  end;
const
  Image_SizeOf_Short_Name = 8;

type

  TMisc = packed record
    case integer of
      0:(PhysicalAddress: DWORD);
      1:(VirtualSize: DWORD);
  end;
  // 此为 C 语言 union 格式 改写而成
  //
  //  union {
  //         DWORD PhysicalAddress;
  //         DWORD VirtualSize;
  //  } Misc;
  //
  //

  PImage_Section_Header = ^TImage_Section_Header;
  TImage_Section_Header = packed record
    Name: array [0 .. Image_SizeOf_Short_Name - 1] of BYTE;
    Misc: TMisc;
    VirtualAddress: DWORD;
    SizeOfRawData: DWORD;
    PointerToRawData: DWORD;
    PointerToRelocations: DWORD;
    PointerToLineNumbers: DWORD;
    NumberOfRelocations: WORD;
    NumberofLineNumbers: WORD;
    Characteristics: DWORD;
  end;

implementation
end.
uPERec.pas

 

posted on 2018-01-08 08:40  晓不得2013  阅读(839)  评论(0编辑  收藏  举报

导航