Last active
January 1, 2016 19:29
-
-
Save Anaminus/8190330 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Thanks: | |
// http://stackoverflow.com/questions/12396665/c-library-to-read-exe-version-from-linux#answer-12486703 | |
package main | |
import ( | |
"encoding/binary" | |
"errors" | |
"os" | |
) | |
func readWord(file *os.File, off int64) int64 { | |
file.Seek(off, 0) | |
var n uint16 | |
binary.Read(file, binary.LittleEndian, &n) | |
return int64(n) | |
} | |
func readDword(file *os.File, off int64) int64 { | |
file.Seek(off, 0) | |
var n uint32 | |
binary.Read(file, binary.LittleEndian, &n) | |
return int64(n) | |
} | |
func pad(x int64) int64 { | |
return (x + 3) & 0xFFFFFFFC | |
} | |
func equals(file *os.File, off int64, value string) bool { | |
b := make([]byte, len(value)) | |
file.ReadAt(b, off) | |
return string(b) == value | |
} | |
func findVersion(file *os.File) (ptr int64, err error) { | |
if !equals(file, 0, "MZ") { | |
return 0, errors.New("No MZ_HEADER") | |
} | |
pe := readWord(file, 0x3C) | |
if !equals(file, pe, "PE") { | |
return 0, errors.New("No PE_HEADER") | |
} | |
coff := pe + 4 | |
numSections := readWord(file, coff+2) | |
optHeaderSize := readWord(file, coff+16) | |
if numSections == 0 || optHeaderSize == 0 { | |
return 0, errors.New("No opt header") | |
} | |
optHeader := coff + 20 | |
if readWord(file, optHeader) != 0x10b { | |
return 0, errors.New("no optional header magic") | |
} | |
dataDir := optHeader + 96 | |
vaRes := readDword(file, dataDir+8*2) | |
secTable := optHeader + optHeaderSize | |
for i := 0; i < int(numSections); i++ { | |
sec := secTable + 40*int64(i) | |
if !equals(file, sec, ".rsrc") { | |
continue | |
} | |
vaSec := readDword(file, sec+12) | |
raw := readDword(file, sec+20) | |
resSec := raw + (vaRes - vaSec) | |
numNamed := readWord(file, resSec+12) | |
numId := readWord(file, resSec+14) | |
// print("CHECK ", numNamed, " ", numId, "\n") | |
for j := 0; j < int(numNamed+numId); j++ { | |
// resSec is a IMAGE_RESOURCE_DIRECTORY followed by an array of | |
// IMAGE_RESOURCE_DIRECTORY_ENTRY | |
res := resSec + 16 + 8*int64(j) | |
name := readDword(file, res) | |
if name != 16 { | |
continue | |
} | |
offs := readDword(file, res+4) | |
if offs&0x80000000 == 0 { | |
return 0, errors.New("no dir resource") | |
} | |
// verDir is another IMAGE_RESOURCE_DIRECTORY and | |
// IMAGE_RESOURCE_DIRECTORY_ENTRY array | |
verDir := resSec + (offs & 0x7FFFFFFF) | |
numNamed = readWord(file, verDir+12) | |
numId = readWord(file, verDir+14) | |
if numNamed == 0 && numId == 0 { | |
return 0, errors.New("resource empty") | |
} | |
res = verDir + 16 | |
offs = readDword(file, res+4) | |
if offs&0x80000000 == 0 { | |
return 0, errors.New("no dir ressource") | |
} | |
// and yet another IMAGE_RESOURCE_DIRECTORY, etc. | |
verDir = resSec + (offs & 0x7FFFFFFF) | |
numNamed = readWord(file, verDir+12) | |
numId = readWord(file, verDir+14) | |
if numNamed == 0 && numId == 0 { | |
return 0, errors.New("resource empty") | |
} | |
res = verDir + 16 | |
offs = readDword(file, res+4) | |
if offs&0x80000000 != 0 { | |
return 0, errors.New("no dir resource") | |
} | |
verDir = resSec + offs | |
verVa := readDword(file, verDir) | |
//verSize := readDword(file, verDir+4) | |
verPtr := raw + (verVa - vaSec) | |
return verPtr, nil | |
} | |
return 0, errors.New("") | |
} | |
return 0, errors.New("") | |
} | |
func recParse(file *os.File, ptr int64, offs int64, version *uint64) int64 { | |
len := readWord(file, ptr+offs) | |
offs += 2 | |
valLen := readWord(file, ptr+offs) | |
offs += 2 | |
vtype := readWord(file, ptr+offs) | |
offs += 2 | |
info := []byte{} | |
for i := 0; i < 200; i++ { | |
c := readWord(file, ptr+offs) | |
if c == 0 { | |
break | |
} | |
offs += 2 | |
info = append(info, byte(c)) | |
} | |
offs = pad(offs) | |
if vtype != 0 { // TEXT | |
value := []byte{} | |
for i := 0; i < int(valLen); i++ { | |
c := readWord(file, ptr+offs) | |
offs += 2 | |
value = append(value, byte(c)) | |
} | |
} else { | |
if string(info) == "VS_VERSION_INFO" { | |
fixed := ptr + offs | |
//dwSignature := readDword(file, fixed+4) | |
//dwStrucVersion := readDword(file, fixed+8) | |
//dwFileVersionMS := readDword(file, fixed+12) | |
//dwFileVersionLS := readDword(file, fixed+16) | |
dwProductVersionMS := readDword(file, fixed+12) | |
dwProductVersionLS := readDword(file, fixed+16) | |
//dwFileFlagsMask := readDword(file, fixed+28) | |
//dwFileFlags := readDword(file, fixed+32) | |
//dwFileOS := readDword(file, fixed+36) | |
//dwFileType := readDword(file, fixed+40) | |
//dwFileSubtype := readDword(file, fixed+44) | |
//dwFileDateMS := readDword(file, fixed+48) | |
//dwFileDateLS := readDword(file, fixed+52) | |
*version = uint64(dwProductVersionMS<<32) | uint64(dwProductVersionLS) | |
} | |
offs += valLen | |
} | |
for offs < len { | |
offs = recParse(file, ptr, offs, version) | |
} | |
return pad(offs) | |
} | |
func parseVersion(file *os.File, ptr int64) (version uint64) { | |
recParse(file, ptr, 0, &version) | |
return version | |
} | |
func ReadVersion(path string) (version Version) { | |
file, _ := os.Open(path) | |
defer file.Close() | |
ptr, err := findVersion(file) | |
if err != nil { | |
return | |
} | |
ver := parseVersion(file, ptr) | |
version.Major = uint16(ver >> 0x30) | |
version.Minor = uint16(ver >> 0x20) | |
version.Release = uint16(ver >> 0x10) | |
version.Build = uint16(ver >> 0x00) | |
return | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment