Skip to content

Commit

Permalink
wip on detecting windows editions with magic numbers.
Browse files Browse the repository at this point in the history
  • Loading branch information
bengarrett committed Aug 7, 2024
1 parent 27eb0a8 commit e7649bf
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 9 deletions.
3 changes: 3 additions & 0 deletions docs/todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- [ ] If a #hash is appended to a /f/<id> URL while signed out, then return a 404 or a redirect to the sign in page. Post signing should return to the #hash URL?
- [ ] Delete all previews that are unused, such as textfiles that are displayed as a readme.

- [ ] On Demozoo or Pouet upload or reach, locally cache the JSON to the temp directory.

- [ ] - http://www.platohistory.org/
- [ ] - https://portcommodore.com/dokuwiki/doku.php?id=larry:comp:bbs:about_cbbs
- [ ] - 8BBS https://everything2.com/title/8BBS
Expand All @@ -29,6 +31,7 @@ Magic files to add:
- Excel, http://localhost:1323/f/b02fcc
- Multipart zip, http://localhost:1323/f/a9247de, http://localhost:1323/f/a619619
- Convert ms-dos filenames to valid utf-8 names, see http://localhost:1323/f/b323bda
- Windows binaries

### Bug fixes

Expand Down
Binary file added internal/magicnumber/TGDEMO.EXE
Binary file not shown.
131 changes: 131 additions & 0 deletions internal/magicnumber/magicnumber.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ package magicnumber

import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"math/bits"
"net/http"
"path/filepath"
"slices"
Expand Down Expand Up @@ -791,6 +793,135 @@ func FindBytes512B(p []byte) Signature {
}
}

// NewExe represents the New Executable file type, a format used by Microsoft and IBM
// to improve on the limitations of the MS-DOS MZ executable format.
type NewExe int

const (
NotNewExe NewExe = iota - 1 // Not a New Executable
UnknownExe // 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
)

// NE returns the New Executable file type from the byte slice
// or NotNewExe if the file is not a New Executable.
//
// Windows programs that are New Executables are usually for the ancient Windows 2 or 3.x versions.
// Windows v2 came in two versions, Windows 2 (for the 286 CPU) and Windows/386,
// while Windows v3 unified support for both CPUs.
// The New Executable format was replaced by the Portable Executable format in Windows 95.
//
// 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) (NewExe, int, int) {
const min = 64
if len(p) < min {
return NotNewExe, 0, 0
}
if p[0] != 'M' || p[1] != 'Z' {
return NotNewExe, 0, 0
}
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
location := int16(binary.LittleEndian.Uint16(p[segmentedHeaderIndex:]))
if len(p) < int(location)+int(winMajorIndex) {
return NotNewExe, 0, 0
}
segmentedHeader := [2]byte{
p[location],
p[location+1],
}
if segmentedHeader != [2]byte{'N', 'E'} {
return NotNewExe, 0, 0
}
minor := int(p[location+winMinorIndex])
major := int(p[location+winMajorIndex])
newType := NewExe(p[location+executableTypeIndex])
switch newType {
case Windows286Exe, Windows386Exe, OS2Exe, DOSv4Exe:
return newType, major, minor
case UnknownExe:
return newType, 0, 0
default:
return NotNewExe, 0, 0
}
}

type PortableExecutable uint16

const (
UnknownPE PortableExecutable = 0x0 // Unknown Portable Executable
Intel386PE PortableExecutable = 0x14c // Intel 386 Portable Executable
AMD64PE PortableExecutable = 0x8664 // AMD64 Portable Executable
ARMPE PortableExecutable = 0x1c0 // ARM Portable Executable
ARM64PE PortableExecutable = 0xaa64 // ARM64 Portable Executable
ItaniumPE PortableExecutable = 0x200 // Itanium Portable Executable
NotPE PortableExecutable = 0xffff // Not a Portable Executable
)

func PE(p []byte) (PortableExecutable, int, int) {
const min = 64
if len(p) < min {
return NotPE, 0, 0
}
if p[0] != 'M' || p[1] != 'Z' {
return NotPE, 0, 0
}
const segmentedHeaderIndex = 0x3c // the location of the segmented header
location := int16(binary.LittleEndian.Uint16(p[segmentedHeaderIndex:]))
if len(p) < int(location) {
return NotPE, 0, 0
}
signature := [4]byte{
p[location+0],
p[location+1],
p[location+2],
p[location+3],
}
if signature != [4]byte{'P', 'E', 0, 0} {
return NotPE, 0, 0
}
const coffLen = 20
coffHeaderIndex := location + int16(len(signature))
if len(p) < int(coffHeaderIndex)+coffLen {
return NotPE, 0, 0
}
machine := [2]byte{
p[coffHeaderIndex],
p[coffHeaderIndex+1],
}
pem := binary.LittleEndian.Uint16(machine[:])
switch PortableExecutable(pem) {
case AMD64PE:
return AMD64PE, 0, 0
case ARMPE:
return ARMPE, 0, 0
case ARM64PE:
return ARM64PE, 0, 0
case Intel386PE:
return Intel386PE, 0, 0
case ItaniumPE:
return ItaniumPE, 0, 0
}
return UnknownPE, 0, 0
}

func Indexes(x uint8) []int {
var ind = make([]int, bits.OnesCount8(x))
pos := 0
for x != 0 {
ind[pos] = bits.TrailingZeros8(x)
x &= x - 1
pos += 1
}
return ind
}

// Iff matches the Interchange File Format image in the byte slice.
// This is a generic wrapper format originally created by Electronic Arts
// for storing data in chunks.
Expand Down
33 changes: 33 additions & 0 deletions internal/magicnumber/magicnumber_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,39 @@ func tduncompress(name string) string {
return x
}

func TestXXX(t *testing.T) {
t.Parallel()
// test the test data paths
p, err := os.ReadFile("TGDEMO.EXE")
require.NoError(t, err)
b, maj, min := magicnumber.NE(p)
assert.Equal(t, b, magicnumber.Windows286Exe)
assert.Equal(t, 3, maj)
assert.Equal(t, 0, min)

p, err = os.ReadFile("XXX.exe")
require.NoError(t, err)
pe, _, _ := magicnumber.PE(p)
assert.Equal(t, pe, magicnumber.Intel386PE)

p, err = os.ReadFile("7z.exe")
require.NoError(t, err)
pe, _, _ = magicnumber.PE(p)
assert.Equal(t, pe, magicnumber.Intel386PE)

p, err = os.ReadFile("7za.exe")
require.NoError(t, err)
pe, _, _ = magicnumber.PE(p)
assert.Equal(t, pe, magicnumber.AMD64PE)

var x = uint8(2)
for _, v := range magicnumber.Indexes(x) {
fmt.Println(v)
}
fmt.Printf("%08b\n", x)

}

func TestFinds(t *testing.T) {
t.Parallel()
f, err := os.Open(td("PKZ204EX.TXT"))
Expand Down
9 changes: 0 additions & 9 deletions view/app/artifact.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,6 @@
</div>
</div>
{{- end}}
{{/* TODO:
<iframe
src="/v/ad3075"
loading="lazy"
allow="fullscreen"
sandbox="allow-same-origin"
height="480"
title="iframe Example 1">
</iframe> */}}
{{- if or ($readmeL) ($readmeC)}}
<div class="col-12">
{{- if not $screenNone}}
Expand Down

0 comments on commit e7649bf

Please sign in to comment.