6

Is there a method to determine if an exe file has been compressed with UPX?

The function to determine if an exe file has been compressed is excellent except I found a problem with the code. If the function IsUPXCompressed is called then you try to run upx, upx can not save the file it modifies. There is something not sharing rights correctly in the function. I have tested this for several hours. If I do not call the method then UPX can write the files with no problem. You you call it then try to run UPX it will not save the file. UPX reports an IOException Permission denied error when trying to write the file.

Can anyone spot something wrong in the code that would cause this problem?

Thank-you


The function to determine if an exe file has been compressed is excellent except I found a problem with the code. If the function IsUPXCompressed is called then you try to run upx, upx can not save the file it modifies. There is something not sharing rights correctly in the function. I have tested this for several hours. If I do not call the method then UPX can write the files with no problem. You you call it then try to run UPX it will not save the file. UPX reports an IOException Permission denied error when trying to write the file.

Can anyone spot something wrong in the code that would cause this problem?

Thank-you

Gordon
  • 312,688
  • 75
  • 539
  • 559
Bill
  • 2,993
  • 5
  • 37
  • 71

5 Answers5

11

Another Method, when a exe is packed with the UPX tool, the section of the PE header contains sections called UPX0,UPX1, etc. so if read these sections and compare the name with the string UPX you can determine if the exe was compressed using the UPX packer.

enter image description here

check this function

uses 
Windows;

function IsUPXCompressed(const Filename:TFileName): Boolean;
var
  i             : integer;
  pBaseAddress  : PByte;
  pDosHeader    : PImageDosHeader;
  pNtHeaders    : PImageNtHeaders;
  hFile         : Cardinal;
  hFileMap      : Cardinal;
  pSectionHeader: PImageSectionHeader;
  dwOffset      : Cardinal;
  SectName      : AnsiString;
begin
  Result:=False;

  hFile := CreateFile(PChar(Filename), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  if (hFile = INVALID_HANDLE_VALUE) then Exit;

  hFileMap := CreateFileMapping(hFile, nil, PAGE_READONLY or SEC_IMAGE,  0, 0, nil);
  if (hFileMap = 0) then
  begin
    CloseHandle(hFile);
    Exit;
  end;

  pBaseAddress := MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
  if (pBaseAddress = nil) then
  begin
    CloseHandle(hFileMap);
    CloseHandle(hFile);
    Exit;
  end;

  try
      dwOffset   := Cardinal(pBaseAddress);
      pDosHeader := PImageDosHeader(pBaseAddress);
      pNtHeaders := PImageNtHeaders(dwOffset + Cardinal(pDosHeader._lfanew));
      pSectionHeader := pImageSectionHeader(Cardinal(pNtHeaders) + SizeOf(TImageNtHeaders));
      for i := 0 to pNtHeaders.FileHeader.NumberOfSections-1 do
      begin
        SetString(SectName, PAnsiChar(@pSectionHeader.Name), SizeOf(pSectionHeader.Name));
        Result:=Pos('UPX',SectName)>0;
        If Result then break;
        Inc(pSectionHeader);
      end;

  finally
    UnmapViewOfFile(pBaseAddress);
    CloseHandle(hFileMap);
    CloseHandle(hFile);
  end;

end;
RRUZ
  • 134,889
  • 20
  • 356
  • 483
  • Unfortunately the cool function IsUPXCompressed always returns false here. i tested this on 3 newly compressed files and the function always returns false. I am running Delphi 2010 on Win 7. Anyone know why the function does not work? – Bill Feb 25 '11 at 19:36
  • Declare `SectName` as `AnsiString` (or `UTF8String`, if you have it), and type-cast `@pSectionHeader.Name` to `PAnsiChar`. Image header names are UTF-8, not UCS-2. Or even replace `SetString` and `Pos` with `StrLComp` to compare the first three characters. – Rob Kennedy Feb 25 '11 at 21:21
  • @Bill the function was uptated to work with unicode versions of Delphi. (>=2009) – RRUZ Feb 25 '11 at 21:59
  • @RRUZ: I'd probably change the test in the for.. loop to: `Result := Pos('UPX', SectName) > 0; if Result then Break;` This eliminates an exit point (making the code a little easier to follow IMO). – Ken White Mar 01 '11 at 23:08
  • Thanks to all. I can now test for compression and UPX still works. – Bill Mar 02 '11 at 01:24
4

UPX itself does it like this:

if (memcmp(isection[0].name,"UPX",3) == 0)
    throwAlreadyPackedByUPX();

This is the implementation for 32-bit PEs; 64-bit PEs need different offsets, and other executable formats have to be handled separately.

#include <stdio.h>

typedef unsigned int uint;

uint peek_d( FILE* f, uint offs ) {
  fseek( f, offs, SEEK_SET );
  uint a = 0;
  fread( &a, 1,sizeof(a), f );
  return a;
}

int main( int argc, char** argv ) {

  FILE* f = fopen( argv[1], "rb" ); if( f==0 ) return 1;

  uint p,n,x,y;

  p = peek_d( f, 0x3C ); // PE header offset
  n = peek_d( f, p+0x74 ); // pointer table size
  x = p + 0x78 + n*8;
  y = peek_d( f, x+0*0x28+0 ); // 1st section name

  if( (y&0xFFFFFF) == ('U'+('P'<<8)+('X'<<16)) ) {
    printf( "UPX detected!\n" );
  } else {
    printf( "No UPX!\n" );
  }

  return 0;
}
Shelwien
  • 2,160
  • 15
  • 17
  • This is what I call a light-weight approach! +1 (But maybe you should elaborate a bit: what is `isection`, for instance?) – Andreas Rejbrand Feb 25 '11 at 17:10
  • 3
    Holy magic numbers, Batman! The ImageHlp library could be of some assistance in parsing the contents of the binary. – Rob Kennedy Feb 25 '11 at 17:25
  • 1
    without imagehlp and windows.h its at least portable. and magic numbers are cool :) – Shelwien Feb 25 '11 at 17:36
  • @Shelwien, when you're reading a Win32 executable header to see if it's been compressed by UPX, why does portability matter? It's a WINDOWS executable. And magic numbers stink for maintenance. (And BTW, can you explain the 'y = peek_d(f, x+0*0x28+0)'? Zero + 28h + 0 = 28h, unless I've forgotten my basic math skills, so 'y = peek_d(f, x + 0x28)' should be equivalent and easier to read.) – Ken White Feb 25 '11 at 19:46
  • Of course, my question about the math is wrong. What I should say is 'x + (0 * 0x28) + 0 = x', so unless I remember my precedence order wrong, it's easier to say 'peek_d(f, x)'. (Edit window ran out before I caught it. ;-) – Ken White Feb 25 '11 at 19:57
  • So only a windows-based app may need to check whether a windows executable is compressed with upx? Why can't it be some web service for example? Also my script can be modified to check x64 executables on 32-bit system, unlike others. – Shelwien Feb 26 '11 at 03:14
  • And as to math, the difference is that in that kind of expression (x+(0*0x28)+0) you can modify 0s to access different section structures and different fields. – Shelwien Feb 26 '11 at 03:16
  • 1
    @Shelwien: Why would anything but a Windows app care if a Win32 app was compressed w/UPX? And as far as the math, you can modify 'peek_d(f, x)' more easily than '(x+(0*0x28)+0)'. – Ken White Feb 26 '11 at 05:21
  • So you think that, for example, an antivirus for windows exes only can be windows-based? As to math, you can't modify (x) to access 2nd section, because you won't know the record size. – Shelwien Feb 26 '11 at 05:35
  • @Shelwien, you can modify `x + RecSizeCalcuatedVar` more easily than you can modify `x + ((0*0x28)+0)`, and the code is much more readable and maintainable. – Ken White Mar 01 '11 at 23:32
2

try to uncompress it with upx?

oddi
  • 387
  • 1
  • 3
  • 14
  • I think the OP is seeking a slightly more light-weight approach. – Andreas Rejbrand Feb 25 '11 at 16:52
  • That doesn't sound like a particularly heavy-weight approach, plus OP made no indication. In the absence of detail, I consider this an excellent answer! – David Heffernan Feb 25 '11 at 16:59
  • OK. So, how do you do that? (And programmatically, remember.) – Rob Kennedy Feb 25 '11 at 17:05
  • @Rob Nobody said anything about programmatically. – David Heffernan Feb 25 '11 at 17:18
  • 4
    @David, it's a programming site. It's implied in every question. If Bill doesn't want a programmatic solution, then this question belongs on Super User, not Stack Overflow. – Rob Kennedy Feb 25 '11 at 17:20
  • 3
    @David: The fact that the question was posted at SO strongly suggests that it is to be done programmatically. If the OP doesn't wish to do it programmatically, IMO the question should be closed (or moved to SU). – Andreas Rejbrand Feb 25 '11 at 17:21
  • @Rob @Andreas Plenty of people post here when it should go elsewhere. You know that. I'm sure you vote to move! – David Heffernan Feb 25 '11 at 17:33
  • I am a delphi developer. The programmatic solution posted by RRUZ is exactly what I am looking for... but unfortunately the function IsUPXCompressed always returns false. Any help would be appreciated. – Bill Feb 25 '11 at 20:05
  • @David, then you shouldn't answer them if they're non-programming questions (or you can't tell); instead you should be voting to move them so they end up in the right place and the poster learns where to post similar questions. :) – Ken White Mar 01 '11 at 23:29
2

// Returns IsUPXCompressed - Modified for Delphi 2010

function IsUPXCompressed( const Filename: TFileName ): Boolean;
var
  i: integer;
  pBaseAddress: PByte;
  pDosHeader: PImageDosHeader;
  pNtHeaders: PImageNtHeaders;
  hFile: Cardinal;
  hFileMap: Cardinal;
  pSectionHeader: PImageSectionHeader;
  dwOffset: Cardinal;
  SectName: AnsiString;
begin
  Result := False;
  hFile := CreateFile( PChar( Filename ), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
  if ( hFile = INVALID_HANDLE_VALUE ) then
    Exit;
  hFileMap := CreateFileMapping( hFile, nil, PAGE_READONLY or SEC_IMAGE, 0, 0, nil );
  if ( hFileMap = 0 ) then
  begin
    CloseHandle( hFile );
    Exit;
  end;
  pBaseAddress := MapViewOfFile( hFileMap, FILE_MAP_READ, 0, 0, 0 );
  if ( pBaseAddress = nil ) then
  begin
    CloseHandle( hFileMap );
    CloseHandle( hFile );
    Exit;
  end;
  dwOffset := Cardinal( pBaseAddress );
  pDosHeader := PImageDosHeader( pBaseAddress );
  pNtHeaders := PImageNtHeaders( dwOffset + Cardinal( pDosHeader._lfanew ) );
  pSectionHeader := pImageSectionHeader( Cardinal( pNtHeaders ) + SizeOf( TImageNtHeaders ) );
  for i := 0 to pNtHeaders.FileHeader.NumberOfSections - 1 do
  begin
    SetString( SectName, PAnsiChar( @pSectionHeader.name ), SizeOf( pSectionHeader.name ) );
    if Pos( 'UPX', SectName ) > 0 then
    begin
      Result := True;
      exit;
    end;
    Inc( pSectionHeader );
  end;
end;

Thanks Rob for the pointers.

Bill
  • 2,993
  • 5
  • 37
  • 71
1

The section names are not included the UPX word always. It mabebe contain another name changed by user. For certain. Ypu must search for UPX copmpressor signature in the whole file.

drjackool
  • 483
  • 1
  • 4
  • 9