Skip to content

Commit

Permalink
tests and bug fixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
bengarrett committed Aug 8, 2024
1 parent 6b13014 commit 5057122
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 27 deletions.
Binary file added assets/testdata/binaries/windows/hello.com
Binary file not shown.
Binary file added assets/testdata/binaries/windows/hellojs.com
Binary file not shown.
Binary file added assets/testdata/binaries/windows/life.com
Binary file not shown.
3 changes: 3 additions & 0 deletions assets/testdata/binaries/windows/readme.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
These 3 tiny applications, created by Justine Tunney, run on modern Windows using Portable Executable format.

https://justine.lol/ape.html
Binary file removed internal/magicnumber/TGDEMO.EXE
Binary file not shown.
78 changes: 51 additions & 27 deletions internal/magicnumber/magicnumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ func FindBytes512B(p []byte) Signature {
// If the file is not a known executable format, an empty Windows struct is returned.
func FindExecutable(p []byte) Windows {
win := NE(p)
if win == (Windows{}) {
if win.NE == NoneNE {
win = PE(p)
}
return win
Expand Down Expand Up @@ -845,6 +845,17 @@ type Windows struct {
NE NewExecutable // The New Executable, a legacy format replaced by the Portable Executable format
}

func Default() Windows {
return Windows{
Major: 0,
Minor: 0,
TimeDateStamp: time.Time{},
PE64: false,
PE: UnknownPE,
NE: NoneNE,
}
}

func (w Windows) String() string {
const (
Windows2x = 2
Expand Down Expand Up @@ -872,7 +883,7 @@ func (w Windows) String() string {
case w.PE == Intel386PE && w.Major <= WindowsNT:
return fmt.Sprintf("Windows NT v%d.%d", w.Major, w.Minor)
}
os := ""
os := fmt.Sprintf("Windows NT v%d.%d", w.Major, w.Minor)
for name, ver := range WindowsNames() {
if w.Major == ver[0] && w.Minor == ver[1] {
os = name
Expand All @@ -884,8 +895,6 @@ func (w Windows) String() string {
return "Unknown PE+ executable"
case w.PE == UnknownPE:
return "Unknown PE executable"
case os == "":
return "Unknown Windows version"
case w.PE == Intel386PE:
return fmt.Sprintf("%s 32-bit", os)
case w.PE == AMD64PE:
Expand All @@ -905,22 +914,30 @@ func (w Windows) String() string {
type NewExecutable int

const (
NoneNE NewExecutable = iota // Not a New Executable
UnknownNE // Unknown New Executable
OS2Exe // Microsoft IBM OS/2 New Executable
Windows286Exe // Windows requiring an Intel 286 CPU New Executable
DOSv4Exe // MS-DOS v4 New Executable
Windows386Exe // Windows requiring an Intel 386 CPU New Executable
NoneNE NewExecutable = iota - 1 // Not a New Executable
UnknownNE // Unknown New Executable
OS2Exe // Microsoft IBM OS/2 New Executable
Windows286Exe // Windows requiring an Intel 286 CPU New Executable
DOSv4Exe // MS-DOS v4 New Executable
Windows386Exe // Windows requiring an Intel 386 CPU New Executable
)

func (ne NewExecutable) String() string {
return [...]string{
"Unknown NE executable",
"OS/2",
"Windows for 286",
"MS-DOS",
"Windows for 386+",
}[ne]
switch ne {
case NoneNE:
return "Not a New Executable"
case UnknownNE:
return "Unknown New Executable"
case OS2Exe:
return "OS/2 New Executable"
case Windows286Exe:
return "Windows for 286 New Executable"
case DOSv4Exe:
return "MS-DOS v4 New Executable"
case Windows386Exe:
return "Windows for 386+ New Executable"
}
return ""
}

// NE returns the New Executable file type from the byte slice.
Expand All @@ -933,27 +950,31 @@ func (ne NewExecutable) String() string {
// If a Windows program is detected, the major and minor version numbers are returned,
// for example, a Windows 3.0 requirement would return 3 and 0.
func NE(p []byte) Windows {
none := Default()
fmt.Println("MZ", none.NE)
const min = 64
if len(p) < min {
return Windows{}
fmt.Println("???")
return none
}
fmt.Println("MZ", none)
if p[0] != 'M' || p[1] != 'Z' {
return Windows{}
return none
}
const segmentedHeaderIndex = 0x3c // the location of the segmented header
const executableTypeIndex = 0x36 // the executable type aka the operating system
const winMinorIndex = 0x3e // the location of the Windows minor version
const winMajorIndex = 0x3f // the location of the Windows major version
offset := int16(binary.LittleEndian.Uint16(p[segmentedHeaderIndex:]))
if len(p) < int(offset)+int(winMajorIndex) {
return Windows{}
return none
}
segmentedHeader := [2]byte{
p[offset+0],
p[offset+1],
}
if segmentedHeader != [2]byte{'N', 'E'} {
return Windows{}
return none
}
minor := int(p[offset+winMinorIndex])
major := int(p[offset+winMajorIndex])
Expand All @@ -964,8 +985,9 @@ func NE(p []byte) Windows {
w.Major = major
w.Minor = minor
w.NE = newType
return w
}
return w
return none
}

// PortableExecutable represents the Portable Executable file type, a format used by Microsoft
Expand Down Expand Up @@ -1012,18 +1034,19 @@ func (pe PortableExecutable) String() string {
//
// [Portable Executable format]: https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
func PE(p []byte) Windows {
none := Default()
const min = 64
if len(p) < min {
return Windows{}
return none
}
if p[0] != 'M' || p[1] != 'Z' {
return Windows{}
return none
}
// the location of the portable executable header
const peHeaderIndex = 0x3c
offset := int16(binary.LittleEndian.Uint16(p[peHeaderIndex:]))
if len(p) < int(offset) {
return Windows{}
return none
}
signature := [4]byte{
p[offset+0],
Expand All @@ -1032,13 +1055,13 @@ func PE(p []byte) Windows {
p[offset+3],
}
if signature != [4]byte{'P', 'E', 0, 0} {
return Windows{}
return none
}
// the location of the COFF (Common Object File Format) header
coffHeaderIndex := offset + int16(len(signature))
const coffLen = 20
if len(p) < int(coffHeaderIndex)+coffLen {
return Windows{}
return none
}
machine := [2]byte{
p[coffHeaderIndex],
Expand Down Expand Up @@ -1072,6 +1095,7 @@ func PE(p []byte) Windows {
Minor: osMinor,
TimeDateStamp: compiled,
PE64: magic == [2]byte{0x0b, 0x02},
NE: NoneNE,
}
pem := binary.LittleEndian.Uint16(machine[:])
switch PortableExecutable(pem) {
Expand Down
122 changes: 122 additions & 0 deletions internal/magicnumber/magicnumber_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,113 @@ func tduncompress(name string) string {
return x
}

func TestFindExecutable(t *testing.T) {
t.Parallel()

w := magicnumber.FindExecutable([]byte{})
assert.Equal(t, magicnumber.UnknownPE, w.PE)
assert.Equal(t, magicnumber.NoneNE, w.NE)

freedos := []string{"/exe/EXE.EXE", "/exemenu/exemenu.exe", "/press/PRESS.EXE", "/rread/rread.exe"}
for _, v := range freedos {
p, err := os.ReadFile(td("binaries/freedos" + v))
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.UnknownPE, w.PE)
assert.Equal(t, magicnumber.NoneNE, w.NE)
}

vista := []string{"/hello.com", "/hellojs.com", "/life.com"}
for _, v := range vista {
p, err := os.ReadFile(td("binaries/windows" + v))
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.AMD64PE, w.PE)
assert.Equal(t, 6, w.Major)
assert.Equal(t, 0, w.Minor)
assert.Equal(t, 2019, w.TimeDateStamp.Year())
assert.Equal(t, "Windows Vista 64-bit", fmt.Sprint(w))
assert.Equal(t, magicnumber.NoneNE, w.NE)
}

winv3 := []string{"/calmir10/CALMIRA.EXE", "/calmir10/TASKBAR.EXE", "/dskutl21/DISKUTIL.EXE"}
for _, v := range winv3 {
p, err := os.ReadFile(td("binaries/windows3x" + v))
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.UnknownPE, w.PE)
assert.Equal(t, magicnumber.Windows286Exe, w.NE)
assert.Equal(t, 3, w.Major)
assert.Equal(t, 10, w.Minor)
assert.Equal(t, "Windows v3.10 for 286", fmt.Sprint(w))
}

p, err := os.ReadFile(td("binaries/windowsXP/CoreTempv13/32bit/Core Temp.exe"))
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.Intel386PE, w.PE)
assert.Equal(t, magicnumber.NoneNE, w.NE)
assert.Equal(t, 5, w.Major)
assert.Equal(t, 0, w.Minor)
assert.Equal(t, "Windows 2000 32-bit", fmt.Sprint(w))

p, err = os.ReadFile(td("binaries/windowsXP/CoreTempv13/64bit/Core Temp.exe"))
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.AMD64PE, w.PE)
assert.Equal(t, magicnumber.NoneNE, w.NE)
assert.Equal(t, 5, w.Major)
assert.Equal(t, 2, w.Minor)
assert.Equal(t, "Windows XP Professional x64 Edition 64-bit", fmt.Sprint(w))
}

func TestFindExecutableWinNT(t *testing.T) {
win9x := []string{
"/rlowe-encrypt/DEMOCD.EXE",
"/rlowe-encrypt/DISKDVR.EXE",
"/rlowe-cdrools/DEMOCD.EXE",
"/7za920/7za.exe",
"/7z1604-extra/7za.exe",
}
for _, v := range win9x {
p, err := os.ReadFile(td("binaries/windows9x" + v))
require.NoError(t, err)
w := magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.Intel386PE, w.PE)
assert.Equal(t, 4, w.Major)
assert.Equal(t, 0, w.Minor)
assert.Greater(t, w.TimeDateStamp.Year(), 2000)
assert.Equal(t, "Windows NT v4.0", fmt.Sprint(w))
assert.Equal(t, magicnumber.NoneNE, w.NE)
}
unknown := []string{
"/rlowe-rformat/RFORMATD.EXE",
"/rlowe-encrypt/DFMINST.COM",
"/rlowe-encrypt/UNINST.COM",
}
for _, v := range unknown {
p, err := os.ReadFile(td("binaries/windows9x" + v))
require.NoError(t, err)
w := magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.UnknownPE, w.PE)
assert.Equal(t, 0, w.Major)
assert.Equal(t, 0, w.Minor)
assert.Equal(t, w.TimeDateStamp.Year(), 1)
assert.Equal(t, "Unknown PE executable", fmt.Sprint(w))
assert.Equal(t, magicnumber.NoneNE, w.NE)
}

p, err := os.ReadFile(td("binaries/windows9x/7z1604-extra/x64/7za.exe"))
require.NoError(t, err)
w := magicnumber.FindExecutable(p)
assert.Equal(t, magicnumber.AMD64PE, w.PE)
assert.Equal(t, 4, w.Major)
assert.Equal(t, 0, w.Minor)
assert.Equal(t, w.TimeDateStamp.Year(), 2016)
assert.Equal(t, "Windows NT v4.0 64-bit", fmt.Sprint(w))
assert.Equal(t, magicnumber.NoneNE, w.NE)
}

func TestXXX(t *testing.T) {
t.Parallel()
// test the test data paths
Expand Down Expand Up @@ -62,6 +169,21 @@ func TestXXX(t *testing.T) {
assert.NotEqual(t, magicnumber.AMD64PE, w.PE)
fmt.Printf("PE: %+v\n", w)

p, err = os.ReadFile("life.com")
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
fmt.Printf(">>%+v\n", w)

p, err = os.ReadFile("hello.com")
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
fmt.Printf(">>%+v\n", w)

p, err = os.ReadFile("hellojs.com")
require.NoError(t, err)
w = magicnumber.FindExecutable(p)
fmt.Printf(">>%+v\n", w)

x := uint8(2)
for _, v := range magicnumber.Flags(x) {
fmt.Println(v)
Expand Down

0 comments on commit 5057122

Please sign in to comment.