Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debug/pe: panic when parsing UPX compressed pe file #33901

Closed
BruceWang666 opened this issue Aug 28, 2019 · 8 comments
Closed

debug/pe: panic when parsing UPX compressed pe file #33901

BruceWang666 opened this issue Aug 28, 2019 · 8 comments
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Comments

@BruceWang666
Copy link

https://github.com/golang/go/blob/master/src/debug/pe/file.go
line 102 and line 108

f.StringTable, err = readStringTable(&f.FileHeader, sr)
f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)

when PE file compressed by UPX, this line code will panic .

@smasher164 smasher164 changed the title panic when parse UPX compressed pe file debug/pe: panic when parsing UPX compressed pe file Aug 28, 2019
@smasher164 smasher164 added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Aug 28, 2019
@smasher164
Copy link
Member

/cc @alexbrainman

@ALTree
Copy link
Member

ALTree commented Aug 28, 2019

Instructions on how to reproduce this and a code snippet using debug/pe that triggers the panic would definitely speed-up the investigation, @BruceWang666

@alexbrainman
Copy link
Member

Yes. Please, provide instructions on how to reproduce this. Thank you.

Alex

@ALTree
Copy link
Member

ALTree commented Aug 29, 2019

I think in general the debug/pe package has trouble understanding upx-compressed binaries. Here's a very small reproducer:

Write and compile simple Go hello world:

$ cat hello.go
package main

import "fmt"

func main() {
        fmt.Println("hi!")
}

$ go version
go version go1.12.5 windows/amd64

$ go build hello.go

$ ./hello.exe
hi!

Compress it with the latest upx release:

$ ../Downloads/upx-3.95-win64/upx.exe hello.exe
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95w       Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
   2063872 ->   1026560   49.74%    win64/pe     hello.exe

Packed 1 file.

The executable itself still works properly:

$ ./hello.exe
hi!

But trying to load it with debug/pe, like this:

package main

import (
        "debug/pe"
        "os"
)

func main() {
        file, _ := os.Open("hello.exe")
        _, err := pe.NewFile(file)
        if err != nil {
                panic(err)
        }
}

will print this error:

panic: fail to read string table length: EOF

goroutine 1 [running]:
main.main()
        D:/users/ZZZZZ/alberto/test.go:12 +0x9a
exit status 2

While the same snippet works fine when loading an uncompressed executable.

The error above comes from debug/pe.readStringTable which is not the same place in the OP report but it's still clear that loading upx-compressed windows executables doesn't currently work.

@alexbrainman
Copy link
Member

@ALTree thank you for the repro. I will try it, when I have time.

If you have time, please, try and use current tip. I just submitted 3b92f36 - maybe it fixes the problem.

Thank you.

Alex

@ALTree
Copy link
Member

ALTree commented Aug 29, 2019

If you have time, please, try and use current tip. I just submitted 3b92f36 - maybe it fixes the problem.

@alexbrainman I tested it, it's still failing on current tip, same error:

$ ~/other/temp/go/bin/go version
go version devel +5cf5a6fc5e Thu Aug 29 09:53:56 2019 +0000 windows/amd64

$ ~/other/temp/go/bin/go run test.go
panic: fail to read string table length: EOF

goroutine 1 [running]:
main.main()
        D:/users/ZZZZZ/alberto/test.go:12 +0x99
exit status 2

@alexbrainman
Copy link
Member

I tested it, it's still failing on current tip, same error:

@ALTree I built https://go-review.googlesource.com/c/go/+/193819 to play with this issue. And the problem here is that PE file header points beyond the end of file.

In the CL, I adjusted string table and symbol table reading code to ignore any EOF. This allows me to complete debug/pe.Open without any errors, and I can print debug/pe.File that debug/pe.Open returns.

=== RUN   TestUPXed
--- PASS: TestUPXed (0.00s)
    file_test.go:775: testdata/gcc-386-mingw-exec-upx size is 21237
    file_test.go:781: &{FileHeader:{Machine:332 NumberOfSections:3 TimeDateStamp:1282022240 PointerToSymbolTable:15360 NumberOfSymbols:642 SizeOfOptionalHeader:224 Characteristics:263} OptionalHeader:0xc0000961c0 Sections:[0xc0000562a0 0xc000056300 0xc000056360] Symbols:[] COFFSymbols:[] StringTable:[] closer:<nil>}
PASS
ok      debug/pe        0.003s

According to https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#coff-string-table

... the position of this table is found by taking the symbol table address in the COFF header and adding the number of symbols multiplied by the size of a symbol. ...

So, it means that string table should start at 26916 = 15360 (PointerToSymbolTable) + 642 (NumberOfSymbols) * 18 (symbol table record size). But the file size is only 21237.

I don't think we should use CL 193819 to allow for UPXed executables. These executables as far as I am concerned violate PE Format description (as I explained above).

In fact objdump agrees with me:

$ objdump -t testdata/gcc-386-mingw-exec | head -n 8

testdata/gcc-386-mingw-exec:     file format pei-i386

SYMBOL TABLE:
[  0](sec -2)(fl 0x00)(ty   0)(scl 103) (nx 1) 0x0000000f crt1.c
File
[  2](sec  1)(fl 0x00)(ty  20)(scl   2) (nx 1) 0x00000000 _atexit
AUX tagndx 0 ttlsiz 0x0 lnnos 0 next 0
$ objdump -t testdata/gcc-386-mingw-exec-upx | head -n 8
objdump:
testdata/gcc-386-mingw-exec-upx:     file format pei-i386

failed to read symbol table from: testdata/gcc-386-mingw-exec-upx
objdump: error message was: File truncated
$

The only problem I see here is that objdump can at least display sections of the file:

$ objdump -h testdata/gcc-386-mingw-exec-upx

testdata/gcc-386-mingw-exec-upx:     file format pei-i386

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 UPX0          0000f000  00401000  00401000  00000200  2**2
                  CONTENTS, ALLOC, CODE
  1 UPX1          00001600  00410000  00410000  00000200  2**2
                  CONTENTS, ALLOC, LOAD, CODE, DATA
  2 UPX2          00000200  00412000  00412000  00001800  2**2
                  CONTENTS, ALLOC, LOAD, DATA
$

but debug/pe.Open fails, because it reads sections alright, but then it fails reading string table. But that is by design - debug/pe.Open does everything. I don't see way around it.

I think the problem is that UPX does not adjust pointer to symbol table. I think both symbol and string tables are also compressed by UPX (I did not check that). So they are not accessible. UPX should set both PointerToSymbolTable and NumberOfSymbols to 0. So it does not confuse all tools that read the file.

I don't think there is anything to do here. Let me know, if you disagree.

Thank you.

Alex

@ALTree
Copy link
Member

ALTree commented Sep 7, 2019

I don't think there is anything to do here. Let me know, if you disagree.

I know nothing about PE : ) but if your analysis is correct and the executable generated by UPX are not strictly standard, I agree that the final verdict here could be "the debug/pe package does not support loading UPX-compressed executables".

We could maybe add a note about this at the top of the package, but leaving this for you to decide. Closing here, since it appears that this is not a debug/pe bug.

@ALTree ALTree closed this as completed Sep 7, 2019
@golang golang locked and limited conversation to collaborators Sep 6, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants