Ver Mensaje Individual
  #15  
Antiguo 01-03-2017
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.197
Reputación: 20
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Cita:
Empezado por dec Ver Mensaje
Hola a todos,

A ver si podéis echarme una mano con el siguiente problema. En uno de mis proyectos necesito adjuntar ciertos datos en un archivo ejecutable, esto es, en realidad adjunto un archivo a otro archivo, este último un ejecutable generado también con Delphi. Todo funciona bien, excepto si además cambio el icono de dicho archivo ejecutable. Para conseguir el tamaño original de un archivo ejecutable, me baso en el siguiente código de Angus Johnson:

Código Delphi [-]
{$IFDEF VER100}
{TImageDosHeader isn't defined in Delphi 3 so here's an an abbreviated structure definition...}
type

  PImageDosHeader = ^TImageDosHeader;
  TImageDosHeader = packed record
    e_magic         : WORD;
    e_ignore        : packed array [0..28] of WORD;
    _lfanew        : Longint;
  end;
{$ENDIF}

function GetExeSize: cardinal;
var
  p: pchar;
  i, NumSections: integer;
begin
  result := 0; // if error then result = 0
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p,sizeof(TImageFileHeader)+ sizeof(TImageOptionalHeader));
  for i := 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData+SizeOfRawData > result then
        result := PointerToRawData+SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

La idea del anterior código es determinar el tamaño original de un archivo ejecutable, sin contar con el archivo que hemos adjuntado al mismo, puesto que este se "añade" al ejecutable, pero, no forma parte de sus "cabeceras" (no sé si me explico, porque, aquí me pierdo un poco, tengo que reconocerlo). Pues bien, en teoría esto ha de funcionar, sin embargo, el anterior código siempre retorna "0" en Windows NT (Windows 10, por ejemplo). Como se trata un código para Delphi 3, yo lo he "sabido" modificar un poco de este modo:

Código Delphi [-]function GetExeSize(): Int64; var P: pchar; I, NumSections: Integer; begin Result := 0; P := pointer(HInstance); Inc(P, SizeOf(DWORD)); NumSections := PImageFileHeader(P).NumberOfSections; Inc(P, SizeOf(TImageNtHeaders)); for I := 1 to NumSections do begin if PImageSectionHeader(P)^.PointerToRawData > Result then begin Result := PImageSectionHeader(P)^.PointerToRawData + PImageSectionHeader(P)^.SizeOfRawData; end; Inc(P, SizeOf(TImageSectionHeader)); end; end;

El código funciona bien en Todos los WIndows, el problema es el compilador. En delphi 7 no hay problema pero los que priorizan unicode, la fastidian, como siempre. Al usar PCHAR el compilados asume (malamente) un PWCHAR con lo que al incrementar el puntero se hace en múltiplos de 2 bytes. Esta es la modificación que debes hacer:

Código Delphi [-]
function GetExeSize: cardinal;
var
  p: PBYTE;  // USAR PBYTE EN LUGAR DE PCHAR (PWCHAR)
  i, NumSections: integer;
begin
  Result := 0; // if error then result = 0
  p := pointer(hinstance);
  inc(p, PImageDosHeader(p)._lfanew + sizeof(dword));
  NumSections := PImageFileHeader(p).NumberOfSections;
  inc(p, sizeof(TImageFileHeader) + sizeof(TImageOptionalHeader));
  for i:= 1 to NumSections do
  begin
    with PImageSectionHeader(p)^ do
      if PointerToRawData+SizeOfRawData > Result then
        Result := PointerToRawData + SizeOfRawData;
    inc(p, sizeof(TImageSectionHeader));
  end;
end;

El código expuesto calcula el tamaño del ejecutable actual, pero puede modificarse para calcular el tamaño teórico de cualquier fichero ejecutable conociendo su ruta.

El formato PE de los ejecutables de Windows, agrandes rasgos consta de:

- Cabecera DOS 'MZ' : IMAGE_DOS_HEADE: 64 bytes
- DOS Stub o subprograma encargado de avisar del error si no se ejecuta en Windows: 448 bytes
- Firma PE: 'PE00': 4 Bytes.
- Cabecera PE: IMAGE_FILE_HEADER: 20 Bytes.
- Cabecera opcional: IMAGE_OPTIONAL_HEADER: 224 Bytes. (tamaño variable)
- Cabeceras de sección: IMAGE_SECTION_HEADER: 40 Bytes cada una, número variable
- Cuerpos de sección: código, datos importados y exportados, relocations, recursos... Tamaño indeterminado.

Además, todo esto lleva una alineación con lo que la afirmación TamFile + TamIcono = NewTamfile es falsa.

la siguiente clase es un fragmento de otra más completa para el manejo del PE que escribí hace ya algunos años con la funcionalidad de eliminar secciones de un ejecutable. El tema está aquí. Dado que es un foro restringido, voy a publicar un fragmento que permite calcular el tamaño teórico de un archivo ejecutable que no se está ejecutando. Si no coincide con el real, "algo se ha añadido sin permiso oficial al ejecutable" (aunque un buen "añadidor", ya se encargará de modificar todo el PE para oficializarlo).

Código Delphi [-]
unit PEFile_;

interface

uses
  Windows;

type
  PIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;
  PIMAGE_FILE_HEADER = ^IMAGE_FILE_HEADER;
  PIMAGE_OPTIONAL_HEADER = ^IMAGE_OPTIONAL_HEADER;
  PIMAGE_SECTION_HEADER = ^IMAGE_SECTION_HEADER;

  ASectionHeaders = array [0..0] of IMAGE_SECTION_HEADER;
  PASectionHeaders = ^ASectionHeaders;

  TPEFile = class;
  PTPEFile = ^TPEFile;

  TPEFile = class
  private
   pImageFile: Pointer;
   SizeOfFile: integer;

   procedure IniPE;
  public
   DosHeader:      PIMAGE_DOS_HEADER;
   DosStub:        Pointer;
   PESignature:    PDWORD;
   PEHeader:       PIMAGE_FILE_HEADER;
   OptionalHeader: PIMAGE_OPTIONAL_HEADER;
   SectionHeaders: PASectionHeaders; //PIMAGE_SECTION_HEADER;

   constructor  Create;
   constructor  CreateFromFile(const FileName: PCHAR);
   destructor   Destroy; override;
   function     GetSizeOfFile: DWORD;

   function  ReadFile(FileName: PCHAR): boolean;

   function  ToOffset(pData: Pointer): Cardinal;
   function  ToPointer(Offset: Cardinal): Pointer;

   function  Signature(): boolean;
  end;

implementation


//------------------------------------------------------------------------------
//  El constructor por defecto.
constructor TPEFile.Create;
begin
 inherited Create;
 pImageFile:= nil;
 DosHeader:= nil;
 PESignature:= nil;
 PEHeader:= nil;
 OptionalHeader:= nil;
 SectionHeaders:= nil;
end;

constructor TPEFile.CreateFromFile(const FileName: PCHAR);
begin
 inherited Create;
 pImageFile:= nil;
 ReadFile(FileName);
end;

destructor  TPEFile.Destroy;
begin
  if pImageFile <> nil then VirtualFree(pImageFile, 0, MEM_RELEASE);
  inherited Destroy;
end;

// Inicializa los punteros de un objeto
procedure TPEFile.IniPE;
begin
   if pImageFile = nil then exit;

   DosHeader      := PIMAGE_DOS_HEADER(pImageFile);
   PESignature    := PDWORD(DosHeader._lfanew + DWORD(pImageFile));
   PEHeader       := PIMAGE_FILE_HEADER(DosHeader._lfanew + 4 + DWORD(pImageFile));
   OptionalHeader := PIMAGE_OPTIONAL_HEADER(DWORD(PEHeader) + sizeof(IMAGE_FILE_HEADER));
   SectionHeaders := PASectionHeaders(DWORD(OptionalHeader) + PEHeader.SizeOfOptionalHeader);
end;

// Lee un archivo PE e inicializa el objeto TPEFile
function TPEFile.ReadFile(FileName: PCHAR): boolean;
var
   Hi: DWORD;
   hFile: THandle;
   BytesRead: integer;
begin
   if pImageFile <> nil then VirtualFree(pImageFile, 0, MEM_RELEASE);

   hFile      := CreateFile(FileName, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
   SizeOfFile := GetFileSize(hFile, @Hi);
   pImageFile := VirtualAlloc(nil, SizeOfFile, MEM_COMMIT, PAGE_READWRITE);
   BytesRead  := _lread(hFile, pImageFile, SizeOfFile);
   CloseHandle(hFile);

   IniPE;

   Result:= BytesRead = SizeOfFile;
end;

function TPEFile.Signature: boolean;
begin
  Result:= false;
  if (DosHeader = nil) or (PESignature = nil) then exit;
  Result:= (DosHeader.e_magic = (PWORD(PAnsiCHAR('MZ')))^)
   and ((PWORD(PESignature))^ = (PWORD(PAnsiCHAR('PE')))^);
end;

// Calcula el tamaño del archivo PE
function TPEFile.GetSizeOfFile: DWORD;
var
   MaxPointerToRawData: DWORD;
   n: integer;
begin
   Result:= 0;
   if not Signature then exit;

   // Busco la última sección
   MaxPointerToRawData:= 0;
   for n:=0 to PEHeader.NumberOfSections-1 do
   begin
     if (MaxPointerToRawData = 0) or (MaxPointerToRawData <= SectionHeaders[n].PointerToRawData) then
     begin
        MaxPointerToRawData:= SectionHeaders[n].PointerToRawData;
        Result:= MaxPointerToRawData + SectionHeaders[n].SizeOfRawData;
     end;
   end;

   if Result = 0 then Result:= ToOffset(SectionHeaders);
   SizeOfFile:= Result;
end;

function  TPEFile.ToOffset(pData: Pointer): Cardinal;
begin
  Result:= DWORD(pData)-DWORD(pImageFile);
end;

function  TPEFile.ToPointer(Offset: Cardinal): Pointer;
begin
  Result:= Pointer(Offset + DWORD(pImageFile));
end;

end.

El código no funcionará compilado a 64bits.

Por otro lado, una forna de encontrar fácilmente el fin de fichero sin usar el PE, es adherirle al final una firma fija compuesta de una cadena conocida. Una búsqueda nos dará el tamaño de archivo, y a partir de esa firma podemos añadir más cosas al archivo inicial.

Código:
Stub + Firma + DatosVarios.
El mismo Stub se encargaría de localizar la firma y liberar el resto del archivo tranquilamente.


Saludos.

Última edición por escafandra fecha: 01-03-2017 a las 22:31:48.
Responder Con Cita