判断exe文件是否使用UPX压缩的方法

6
有没有一种方法可以确定一个exe文件是否被UPX压缩了?
除此之外,用于确定一个exe文件是否已经被压缩的函数非常出色。但是我发现代码存在问题。如果调用IsUPXCompressed方法,然后尝试运行upx,upx无法保存修改后的文件。在该函数中,有些共享权限不正确。我已经测试了几个小时。如果我不调用这个方法,那么UPX就可以毫无问题地写入文件。如果你调用它,然后尝试运行UPX,它将无法保存文件。当尝试写入文件时,UPX会报告一个IOException权限被拒绝的错误。
请问有人能够发现导致这个问题的代码错误吗?
谢谢。
5个回答

11

另一种方法是,当一个可执行文件使用UPX工具进行打包时,PE头的节区中包含名为UPX0UPX1等的节区。因此,如果读取这些节区并将名称与字符串UPX进行比较,则可以确定该可执行文件是否使用了UPX压缩器进行压缩。

enter image description here

检查以下函数:

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;

很不幸,这里的酷炫功能IsUPXCompressed总是返回false。我在三个新压缩文件上测试了它,但该函数总是返回false。我正在Win 7上运行Delphi 2010。有人知道为什么这个函数不起作用吗? - Bill
声明SectNameAnsiString(或者如果有的话,可以为UTF8String),并将@pSectionHeader.Name类型转换为PAnsiChar。图像头名称采用UTF-8编码,而不是UCS-2。甚至可以使用StrLComp来替换SetStringPos函数,以便比较前三个字符。 - Rob Kennedy
@Bill 函数已更新,可与 Delphi 的 Unicode 版本(>=2009)一起使用。 - RRUZ
@RRUZ: 我可能会更改for循环中的测试为:Result := Pos('UPX',SectName)> 0; if Result then Break; 这样可以消除一个退出点(在我看来使代码更容易理解一些)。 - Ken White
感谢大家。我现在可以测试压缩和 UPX 仍然有效。 - Bill

4

UPX本身是这样做的:

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

这是32位PE的实现;64位PE需要不同的偏移量,其他可执行文件格式需要单独处理。
#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;
}

3
神奇的幻数,蝙蝠侠!ImageHlp库可能有助于解析二进制内容。 - Rob Kennedy
1
没有使用imagehlp和windows.h,代码至少是可移植的。神奇数字很酷 :) - Shelwien
当然,我关于数学的问题是错误的。我应该说的是“x +(0 * 0x28)+ 0 = x”,所以除非我记错了优先顺序,否则更容易说“peek_d(f,x)”。(编辑窗口在我注意到之前就关闭了。;-) - Ken White
至于数学方面,不同之处在于在这种表达式(x +(0 * 0x28)+ 0)中,您可以修改0以访问不同的部分结构和不同的字段。 - Shelwien
1
@Shelwien:除了 Windows 应用程序,为什么其他应用程序会关心 Win32 应用程序是否使用 UPX 压缩?至于数学方面,你可以更容易地修改“peek_d(f,x)”而不是“(x +(0 * 0x28)+ 0)”。 - Ken White
显示剩余5条评论

2

// 返回 IsUPXCompressed - 适用于 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;

感谢 Rob 提供的指引。


2
尝试使用upx解压它?

我认为原帖作者正在寻求一种稍微轻量级的方法。 - Andreas Rejbrand
@Rob 没有人提到编程。 - David Heffernan
4
@David,这是一个编程网站。每个问题都暗示了这一点。如果Bill不想要一个编程解决方案,那么这个问题就应该放在Super User而不是Stack Overflow上。 - Rob Kennedy
3
@David: 这个问题被发布在SO上,这表明它需要以编程的方式解决。如果OP不想以编程的方式解决,我认为这个问题应该被关闭(或移动到SU)。 - Andreas Rejbrand
@Rob @Andreas 很多人在这里发布了不该发的东西。你们知道的。我相信你们会投票搬移吧! - David Heffernan
显示剩余4条评论

1
部分名称通常不包含UPX单词。它可能包含用户更改的另一个名称。确定必须在整个文件中搜索UPX压缩器签名。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接