Add ability to create a git repository from a BG3 install
Add library to parse version info from a windows PE executable There are issues with go-git, will change to a wrapper library
This commit is contained in:
parent
59f8bdcc54
commit
b5d007c865
19
cmd/lsconvert/fileversion.go
Normal file
19
cmd/lsconvert/fileversion.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jdferrell3/peinfo-go/peinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPEFileVersion(filepath string) string {
|
||||||
|
f, err := peinfo.Initialize(filepath, false, "", false)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer f.OSFile.Close()
|
||||||
|
defer f.PEFile.Close()
|
||||||
|
v, _, err := f.GetVersionInfo()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v["FileVersion"]
|
||||||
|
}
|
@ -1,15 +1,21 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/go-git/go-git/v5"
|
"github.com/go-git/go-git/v5"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
|
"git.narnian.us/lordwelch/lsgo/gog"
|
||||||
ie "git.narnian.us/lordwelch/lsgo/internal/exec"
|
ie "git.narnian.us/lordwelch/lsgo/internal/exec"
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
)
|
)
|
||||||
@ -19,26 +25,202 @@ var (
|
|||||||
initFlags struct {
|
initFlags struct {
|
||||||
BG3Path string
|
BG3Path string
|
||||||
divinePath string
|
divinePath string
|
||||||
|
Repo string
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func initRepository(arguments []string) {
|
const ignore = `# ignores all binary files
|
||||||
|
*.wem
|
||||||
|
*.dds
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.gr2
|
||||||
|
*.bin
|
||||||
|
*.patch
|
||||||
|
*.bshd
|
||||||
|
*.tga
|
||||||
|
*.ttf
|
||||||
|
*.fnt
|
||||||
|
Assets_pak/Public/Shared/Assets/Characters/_Anims/Humans/_Female/HUM_F_Rig/HUM_F_Rig_DFLT_SPL_Somatic_MimeTalk_01
|
||||||
|
Assets_pak/Public/Shared/Assets/Characters/_Models/_Creatures/Beholder/BEH_Spectator_A_GM_DDS
|
||||||
|
Assets_pak/Public/Shared/Assets/Consumables/CONS_GEN_Food_Turkey_ABC/CONS_GEN_Food_Turkey_ABC.ma
|
||||||
|
Assets_pak/Public/Shared/Assets/Decoration/Harbour/DEC_Harbour_Shell_ABC/DEC_Harbour_Shell_Venus_A
|
||||||
|
*.bnk
|
||||||
|
*.pak
|
||||||
|
*.data
|
||||||
|
*.osi
|
||||||
|
*.cur
|
||||||
|
*.gtp
|
||||||
|
*.gts
|
||||||
|
*.ffxanim
|
||||||
|
*.ffxactor
|
||||||
|
*.ffxbones
|
||||||
|
*.bk2
|
||||||
|
`
|
||||||
|
|
||||||
|
type BG3Repo struct {
|
||||||
|
git *git.Repository
|
||||||
|
gitPath string
|
||||||
|
gog gog.GOGalaxy
|
||||||
|
BG3Path string
|
||||||
|
divinePath string
|
||||||
|
Empty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func openRepository(path string) (BG3Repo, error) {
|
||||||
var (
|
var (
|
||||||
BG3Path, divinePath string
|
b BG3Repo
|
||||||
repo *git.Repository
|
err error
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
BG3Path, divinePath = preRequisites()
|
b.gitPath = path
|
||||||
if BG3Path == "" {
|
b.BG3Path, b.divinePath = preRequisites()
|
||||||
|
if b.BG3Path == "" {
|
||||||
panic("Could not locate Baldur's Gate 3")
|
panic("Could not locate Baldur's Gate 3")
|
||||||
}
|
}
|
||||||
fmt.Println(divinePath)
|
|
||||||
panic("Baldur's Gate 3 located " + BG3Path)
|
b.git, err = git.PlainOpen(b.gitPath)
|
||||||
repo, err = git.PlainInit(".", false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
b.Empty = true
|
||||||
}
|
}
|
||||||
repo.Fetch(nil)
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bgr *BG3Repo) Init() error {
|
||||||
|
var err error
|
||||||
|
if bgr.Empty || bgr.git == nil {
|
||||||
|
bgr.git, err = git.PlainInit(bgr.gitPath, false)
|
||||||
|
if err == git.ErrRepositoryAlreadyExists {
|
||||||
|
bgr.git, err = git.PlainOpen(bgr.gitPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if citer, err := bgr.git.CommitObjects(); err == nil {
|
||||||
|
if _, err := citer.Next(); err == nil {
|
||||||
|
// Repo contains commits, nothing to do
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bgr.AddNewVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bgr *BG3Repo) RetrieveGOGalaxy() error {
|
||||||
|
var err error
|
||||||
|
bgr.gog, err = gog.RetrieveGOGInfo("1456460669")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bgr *BG3Repo) AddNewVersion() error {
|
||||||
|
var (
|
||||||
|
version string
|
||||||
|
changelog gog.Change
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
version = getPEFileVersion(filepath.Join(bgr.BG3Path, "bin/bg3.exe"))
|
||||||
|
err = bgr.RetrieveGOGalaxy()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
changelog, err = gog.ParseChangelog(bgr.gog.Changelog, bgr.gog.Title)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
versionChanges := changelog.FindChange(version)
|
||||||
|
if versionChanges == nil {
|
||||||
|
return fmt.Errorf("no version information found for %q", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace with custom errors
|
||||||
|
t, err := bgr.git.Worktree()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var stat git.Status
|
||||||
|
stat, err = t.Status()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !stat.IsClean() {
|
||||||
|
return errors.New("git working directory must be clean before adding a new version, please save your work:\n" + stat.String())
|
||||||
|
}
|
||||||
|
if _, err := t.Filesystem.Stat(".gitignore"); err != nil {
|
||||||
|
gitignore, err := t.Filesystem.Create(".gitignore")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = gitignore.Write([]byte(ignore))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
gitignore.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Add(".gitignore")
|
||||||
|
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
mut := &sync.Mutex{}
|
||||||
|
errs := new(multierror.Error)
|
||||||
|
paths := []string{}
|
||||||
|
|
||||||
|
err = filepath.Walk(filepath.Join(bgr.BG3Path, "Data"), func(path string, info os.FileInfo, err error) error {
|
||||||
|
rpath := strings.TrimPrefix(path, filepath.Join(bgr.BG3Path, "Data"))
|
||||||
|
fmt.Println("bg3 ", filepath.Join(bgr.BG3Path, "Data"))
|
||||||
|
fmt.Println("path ", path)
|
||||||
|
fmt.Println("rpath", rpath)
|
||||||
|
repoPath := filepath.Join(filepath.Dir(rpath), strings.TrimSuffix(filepath.Base(rpath), filepath.Ext(rpath))+strings.ReplaceAll(filepath.Ext(rpath), ".", "_"))
|
||||||
|
fmt.Println("repopath", repoPath)
|
||||||
|
if filepath.Ext(info.Name()) != ".pak" || info.IsDir() || strings.Contains(info.Name(), "Textures") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = t.Filesystem.MkdirAll(repoPath, 0o660)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
paths = append(paths, repoPath)
|
||||||
|
divine := exec.Command(bgr.divinePath, "-g", "bg3", "-s", path, "-d", filepath.Join(bgr.gitPath, repoPath), "-a", "extract-package")
|
||||||
|
var buf bytes.Buffer
|
||||||
|
divine.Stderr = &buf
|
||||||
|
divine.Stdout = &buf
|
||||||
|
err = divine.Run()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("an error occurred running divine.exe: %s", buf.String())
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
if err := convert("-w", "-r", filepath.Join(bgr.gitPath, repoPath)); err != nil {
|
||||||
|
mut.Lock()
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
mut.Unlock()
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
if errs.ErrorOrNil() != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, v := range paths {
|
||||||
|
_, err = t.Add(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = t.Commit(strings.Replace(versionChanges.String(), versionChanges.Title, versionChanges.Title+"\n", 1), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func preRequisites() (BG3Path, divinePath string) {
|
func preRequisites() (BG3Path, divinePath string) {
|
||||||
|
@ -1,15 +1,43 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "flag"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
// flag.Parse()
|
||||||
switch flag.Arg(0) {
|
arg := ""
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
arg = os.Args[1]
|
||||||
|
}
|
||||||
|
switch arg {
|
||||||
case "convert":
|
case "convert":
|
||||||
convert(flag.Args()[1:])
|
err := convert(os.Args[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
case "init":
|
case "init":
|
||||||
initRepository(flag.Args()[1:])
|
p := "."
|
||||||
|
if len(os.Args) > 2 {
|
||||||
|
p = os.Args[2]
|
||||||
|
}
|
||||||
|
repo, err := openRepository(p)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
err = repo.Init()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
convert(flag.Args())
|
err := convert(os.Args[1:]...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
4
go.mod
4
go.mod
@ -4,10 +4,14 @@ go 1.15
|
|||||||
|
|
||||||
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
|
replace github.com/pierrec/lz4/v4 v4.1.3 => ./third_party/lz4
|
||||||
|
|
||||||
|
replace github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1 => ./third_party/peinfo-go
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/go-git/go-git/v5 v5.2.0
|
github.com/go-git/go-git/v5 v5.2.0
|
||||||
github.com/go-kit/kit v0.10.0
|
github.com/go-kit/kit v0.10.0
|
||||||
github.com/google/uuid v1.1.4
|
github.com/google/uuid v1.1.4
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0
|
||||||
|
github.com/jdferrell3/peinfo-go v0.0.0-20191128021257-cff0d8506fb1
|
||||||
github.com/kr/pretty v0.2.1
|
github.com/kr/pretty v0.2.1
|
||||||
github.com/pierrec/lz4/v4 v4.1.3
|
github.com/pierrec/lz4/v4 v4.1.3
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||||
|
8
go.sum
8
go.sum
@ -121,11 +121,14 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf
|
|||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
|
||||||
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||||
|
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI=
|
||||||
|
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||||
@ -300,6 +303,8 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
|
|||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@ -354,10 +359,13 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363 h1:wHn06sgWHMO1VsQ8F+KzDJx/JzqfsNLnc+oEi07qD7s=
|
||||||
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210108172913-0df2131ae363/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
15
third_party/peinfo-go/.gitignore
vendored
Normal file
15
third_party/peinfo-go/.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, build with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Mac files
|
||||||
|
.DS_Store
|
49
third_party/peinfo-go/LICENSE
vendored
Normal file
49
third_party/peinfo-go/LICENSE
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 John Ferrell
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
peinfo/revoked.go
|
||||||
|
Copyright (c) 2014 CloudFlare Inc.
|
||||||
|
Copyright (c) 2019 John Ferrell
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
62
third_party/peinfo-go/README.md
vendored
Normal file
62
third_party/peinfo-go/README.md
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# peinfo-go
|
||||||
|
|
||||||
|
This is a PE (Portable Executable) parser written in GoLang. I wanted to learn more about the PE format, specifically how the certificates were stored. What better way is there than to write some code?
|
||||||
|
|
||||||
|
_This is a work in progress and will continue to change._
|
||||||
|
|
||||||
|
This leverages the `debug/pe` package for parsing of the common headers/sections.
|
||||||
|
|
||||||
|
Current state:
|
||||||
|
- Displays some PE details
|
||||||
|
- Validates certificate, verifies certificate chain, checks against CRL
|
||||||
|
- Parses Version Info struct
|
||||||
|
- Displays imports
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
- ~~Actually Parse Version Info struct (currently displayed as raw binary)~~
|
||||||
|
- Re-write function for finding Version Info (currently written so I could better understand the structure)
|
||||||
|
- ~~Custom certificate stores~~
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```
|
||||||
|
[user:~/peinfo-go\ > go run cmd/main.go -certdir ~/RootCerts -versioninfo ~/Downloads/PsExec.exe
|
||||||
|
type: pe32
|
||||||
|
TimeDateStamp: 2016-06-28 18:43:09 +0000 UTC
|
||||||
|
Characteristics: [Executable 32bit]
|
||||||
|
Subsystem: IMAGE_SUBSYSTEM_WINDOWS_CUI
|
||||||
|
|
||||||
|
Cert:
|
||||||
|
subject: CN=Microsoft Corporation,OU=MOPR,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US
|
||||||
|
issuer: CN=Microsoft Code Signing PCA,O=Microsoft Corporation,L=Redmond,ST=Washington,C=US
|
||||||
|
not before: 2015-06-04 17:42:45 +0000 UTC
|
||||||
|
not after: 2016-09-04 17:42:45 +0000 UTC
|
||||||
|
CRL: [http://crl.microsoft.com/pki/crl/products/MicCodSigPCA_08-31-2010.crl]
|
||||||
|
verified: true (chain expired: true)
|
||||||
|
|
||||||
|
Version Info:
|
||||||
|
BuildDate :
|
||||||
|
BuildVersion :
|
||||||
|
Comments :
|
||||||
|
CompanyName : Sysinternals - www.sysinternals.com
|
||||||
|
Copyright :
|
||||||
|
FileDescription : Execute processes remotely
|
||||||
|
FileVersion : 2.2
|
||||||
|
InternalName : PsExec
|
||||||
|
LegalCopyright : Copyright (C) 2001-2016 Mark Russinovich
|
||||||
|
LegalTrademarks :
|
||||||
|
OriginalFilename : psexec.c
|
||||||
|
PrivateBuild :
|
||||||
|
ProductName : Sysinternals PsExec
|
||||||
|
ProductVersion : 2.2
|
||||||
|
SpecialBuild :
|
||||||
|
langCharSet : 040904b0h$
|
||||||
|
```
|
||||||
|
|
||||||
|
## References
|
||||||
|
- https://golang.org/pkg/debug/pe/
|
||||||
|
- http://www.pelib.com/resources/luevel.txt
|
||||||
|
- https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/EXE.pm
|
||||||
|
- https://github.com/deptofdefense/SalSA/blob/master/pe.py
|
||||||
|
- https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#resource-directory-entries
|
||||||
|
- https://github.com/quarkslab/dreamboot/blob/31e155b06802dce94367c38ea93316f7cb86cb15/QuarksUBootkit/PeCoffLib.c
|
||||||
|
- https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#the-attribute-certificate-table-image-only
|
7
third_party/peinfo-go/go.mod
vendored
Normal file
7
third_party/peinfo-go/go.mod
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module github.com/jdferrell3/peinfo-go
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||||
|
)
|
||||||
|
|
||||||
|
go 1.13
|
10
third_party/peinfo-go/go.sum
vendored
Normal file
10
third_party/peinfo-go/go.sum
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
17
third_party/peinfo-go/peinfo/consts.go
vendored
Normal file
17
third_party/peinfo-go/peinfo/consts.go
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package peinfo
|
||||||
|
|
||||||
|
var FileOS = map[uint32]string{
|
||||||
|
0x00001: "Win16",
|
||||||
|
0x00002: "PM-16",
|
||||||
|
0x00003: "PM-32",
|
||||||
|
0x00004: "Win32",
|
||||||
|
0x10000: "DOS",
|
||||||
|
0x20000: "OS/2 16-bit",
|
||||||
|
0x30000: "OS/2 32-bit",
|
||||||
|
0x40000: "Windows NT",
|
||||||
|
0x10001: "Windows 16-bit",
|
||||||
|
0x10004: "Windows 32-bit",
|
||||||
|
0x20002: "OS/2 16-bit PM-16",
|
||||||
|
0x30003: "OS/2 32-bit PM-32",
|
||||||
|
0x40004: "Windows NT 32-bit",
|
||||||
|
}
|
31
third_party/peinfo-go/peinfo/initialize.go
vendored
Normal file
31
third_party/peinfo-go/peinfo/initialize.go
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package peinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/pe"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Initialize returns the config for execution
|
||||||
|
func Initialize(filePath string, verbose bool, rootCertDir string, extractCert bool) (ConfigT, error) {
|
||||||
|
fh, err := os.Open(filePath)
|
||||||
|
if nil != err {
|
||||||
|
return ConfigT{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tempPE, err := pe.NewFile(fh)
|
||||||
|
if nil != err {
|
||||||
|
return ConfigT{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
file := ConfigT{
|
||||||
|
FileName: filepath.Base(filePath),
|
||||||
|
OSFile: fh,
|
||||||
|
PEFile: tempPE,
|
||||||
|
Verbose: verbose,
|
||||||
|
ExtractCert: extractCert,
|
||||||
|
RootCertDir: rootCertDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
return file, nil
|
||||||
|
}
|
124
third_party/peinfo-go/peinfo/peinfo.go
vendored
Normal file
124
third_party/peinfo-go/peinfo/peinfo.go
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
package peinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/pe"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http://www.pelib.com/resources/luevel.txt
|
||||||
|
// https://github.com/exiftool/exiftool/blob/master/lib/Image/ExifTool/EXE.pm
|
||||||
|
// https://github.com/deptofdefense/SalSA/blob/master/pe.py
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#resource-directory-entries
|
||||||
|
// https://github.com/quarkslab/dreamboot/blob/31e155b06802dce94367c38ea93316f7cb86cb15/QuarksUBootkit/PeCoffLib.c
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#the-attribute-certificate-table-image-only
|
||||||
|
// https://docs.microsoft.com/en-us/windows/desktop/menurc/vs-versioninfo
|
||||||
|
|
||||||
|
var (
|
||||||
|
sizeofOptionalHeader32 = uint16(binary.Size(pe.OptionalHeader32{}))
|
||||||
|
sizeofOptionalHeader64 = uint16(binary.Size(pe.OptionalHeader64{}))
|
||||||
|
)
|
||||||
|
|
||||||
|
type ImageDirectoryT struct {
|
||||||
|
Type int
|
||||||
|
VirtualAddress uint32
|
||||||
|
Size uint32
|
||||||
|
ImageBase uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ConfigT) HeaderMagic() uint16 {
|
||||||
|
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
|
||||||
|
|
||||||
|
if pe64 {
|
||||||
|
return cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).Magic
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).Magic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ConfigT) GetPEType() string {
|
||||||
|
t := "pe32"
|
||||||
|
if cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||||
|
t = "pe32+"
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ConfigT) GetImageSubSystem() string {
|
||||||
|
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
|
||||||
|
|
||||||
|
subsystem := map[uint16]string{
|
||||||
|
0: "IMAGE_SUBSYSTEM_UNKNOWN",
|
||||||
|
1: "IMAGE_SUBSYSTEM_NATIVE",
|
||||||
|
2: "IMAGE_SUBSYSTEM_WINDOWS_GUI",
|
||||||
|
3: "IMAGE_SUBSYSTEM_WINDOWS_CUI",
|
||||||
|
4: "IMAGE_SUBSYSTEM_OS2_CUI",
|
||||||
|
5: "IMAGE_SUBSYSTEM_POSIX_CUI",
|
||||||
|
9: "IMAGE_SUBSYSTEM_WINDOWS_CE_GUI",
|
||||||
|
10: "IMAGE_SUBSYSTEM_EFI_APPLICATION",
|
||||||
|
11: "IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER",
|
||||||
|
12: "IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER",
|
||||||
|
13: "IMAGE_SUBSYSTEM_EFI_ROM",
|
||||||
|
14: "IMAGE_SUBSYSTEM_XBOX",
|
||||||
|
15: "IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION",
|
||||||
|
}
|
||||||
|
|
||||||
|
if pe64 {
|
||||||
|
return subsystem[cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).Subsystem]
|
||||||
|
}
|
||||||
|
|
||||||
|
return subsystem[cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).Subsystem]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCharacteristics returns a list of PE characteristics
|
||||||
|
func (cfg *ConfigT) GetCharacteristics() []string {
|
||||||
|
characteristics := []string{}
|
||||||
|
|
||||||
|
if (cfg.PEFile.FileHeader.Characteristics & 0x0002) > 1 {
|
||||||
|
characteristics = append(characteristics, "Executable")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.PEFile.FileHeader.Characteristics & 0x0100) > 1 {
|
||||||
|
characteristics = append(characteristics, "32bit")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg.PEFile.FileHeader.Characteristics & 0x2000) > 1 {
|
||||||
|
characteristics = append(characteristics, "DLL")
|
||||||
|
}
|
||||||
|
|
||||||
|
return characteristics
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTimeDateStamp returns the date-time stamp in the PE's header
|
||||||
|
func (cfg *ConfigT) GetTimeDateStamp() string {
|
||||||
|
tm := time.Unix(int64(cfg.PEFile.FileHeader.TimeDateStamp), 0)
|
||||||
|
return fmt.Sprintf("%s", tm.UTC())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindDataDirectory
|
||||||
|
func (cfg *ConfigT) FindDataDirectory(imageDirectoryEntryType int) (idd ImageDirectoryT) {
|
||||||
|
pe64 := cfg.PEFile.Machine == pe.IMAGE_FILE_MACHINE_AMD64
|
||||||
|
|
||||||
|
var dd pe.DataDirectory
|
||||||
|
if pe64 {
|
||||||
|
dd = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).DataDirectory[imageDirectoryEntryType]
|
||||||
|
idd.ImageBase = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader64).ImageBase
|
||||||
|
} else {
|
||||||
|
dd = cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[imageDirectoryEntryType]
|
||||||
|
idd.ImageBase = uint64(cfg.PEFile.OptionalHeader.(*pe.OptionalHeader32).ImageBase)
|
||||||
|
}
|
||||||
|
|
||||||
|
idd.VirtualAddress = dd.VirtualAddress
|
||||||
|
idd.Size = dd.Size
|
||||||
|
idd.Type = imageDirectoryEntryType
|
||||||
|
|
||||||
|
return idd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell is a wrapper for Seek()
|
||||||
|
func (cfg *ConfigT) Tell() int64 {
|
||||||
|
pos, _ := cfg.OSFile.Seek(0, os.SEEK_CUR)
|
||||||
|
return pos
|
||||||
|
}
|
90
third_party/peinfo-go/peinfo/structs.go
vendored
Normal file
90
third_party/peinfo-go/peinfo/structs.go
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package peinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/pe"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConfigT struct {
|
||||||
|
FileName string
|
||||||
|
OSFile *os.File
|
||||||
|
PEFile *pe.File
|
||||||
|
ExtractCert bool
|
||||||
|
Verbose bool
|
||||||
|
RootCertDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResourceDirectoryD struct {
|
||||||
|
Characteristics uint32
|
||||||
|
TimeDateStamp uint32
|
||||||
|
MajorVersion uint16
|
||||||
|
MinorVersion uint16
|
||||||
|
NumberOfNamedEntries uint16
|
||||||
|
NumberOfIdEntries uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type CertDetails struct {
|
||||||
|
Length uint32
|
||||||
|
Revision uint16
|
||||||
|
CertificateType uint16
|
||||||
|
DER []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
|
||||||
|
// union {
|
||||||
|
// struct {
|
||||||
|
// DWORD NameOffset:31;
|
||||||
|
// DWORD NameIsString:1;
|
||||||
|
// };
|
||||||
|
// DWORD Name;
|
||||||
|
// WORD Id;
|
||||||
|
// };
|
||||||
|
// union {
|
||||||
|
// DWORD OffsetToData;
|
||||||
|
// struct {
|
||||||
|
// DWORD OffsetToDirectory:31;
|
||||||
|
// DWORD DataIsDirectory:1;
|
||||||
|
// };
|
||||||
|
// };
|
||||||
|
// } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
|
||||||
|
|
||||||
|
type ResourceDirectoryEntry struct {
|
||||||
|
Name uint32
|
||||||
|
OffsetToData uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResourceDirectoryEntryNamed struct {
|
||||||
|
Name uint32
|
||||||
|
OffsetToData uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Resource Directory Entry */
|
||||||
|
// type ResourceDirectoryEntryT struct {
|
||||||
|
// ResourceDirectoryEntry ResourceDirectoryEntry
|
||||||
|
// FileOffset uint32
|
||||||
|
// Size uint32
|
||||||
|
// DataIsDirectory bool
|
||||||
|
// }
|
||||||
|
|
||||||
|
type _IMAGE_RESOURCE_DATA_ENTRY struct {
|
||||||
|
OffsetToData uint32
|
||||||
|
Size uint32
|
||||||
|
CodePage uint32
|
||||||
|
Reserved uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type VS_FIXEDFILEINFO struct {
|
||||||
|
DwSignature uint32
|
||||||
|
DwStrucVersion uint32
|
||||||
|
DwFileVersionMS uint32
|
||||||
|
DwFileVersionLS uint32
|
||||||
|
DwProductVersionMS uint32
|
||||||
|
DwProductVersionLS uint32
|
||||||
|
DwFileFlagsMask uint32
|
||||||
|
DwFileFlags uint32
|
||||||
|
DwFileOS uint32
|
||||||
|
DwFileType uint32
|
||||||
|
DwFileSubtype uint32
|
||||||
|
DwFileDateMS uint32
|
||||||
|
DwFileDateLS uint32
|
||||||
|
}
|
267
third_party/peinfo-go/peinfo/verinfo.go
vendored
Normal file
267
third_party/peinfo-go/peinfo/verinfo.go
vendored
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
package peinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"debug/pe"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RT_VERSION = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cfg *ConfigT) FindVerInfoOffset(fileOffset int64, sectionOffset uint32, sectionVirtualAddress uint32) (verInfoOffset int64, len uint32, err error) {
|
||||||
|
pos, _ := cfg.OSFile.Seek(fileOffset, os.SEEK_SET)
|
||||||
|
if pos != fileOffset {
|
||||||
|
return 0, 0, fmt.Errorf("did not seek to offset")
|
||||||
|
}
|
||||||
|
type VerInfoDetailsT struct {
|
||||||
|
Off uint32
|
||||||
|
Len uint32
|
||||||
|
D1 uint32
|
||||||
|
D2 uint32
|
||||||
|
}
|
||||||
|
var peoff VerInfoDetailsT
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &peoff)
|
||||||
|
if nil != err {
|
||||||
|
return verInfoOffset, len, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// $filePos = $off + $$section{Base} - $$section{VirtualAddress};
|
||||||
|
verInfoOffset = int64(peoff.Off + sectionOffset - sectionVirtualAddress)
|
||||||
|
return verInfoOffset, peoff.Len, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ConfigT) GetVersionInfo() (vi map[string]string, keys []string, err error) {
|
||||||
|
vi = map[string]string{
|
||||||
|
"BuildDate": "",
|
||||||
|
"BuildVersion": "",
|
||||||
|
"Comments": "",
|
||||||
|
"CompanyName": "",
|
||||||
|
"Copyright": "",
|
||||||
|
"FileDescription": "",
|
||||||
|
"FileVersion": "",
|
||||||
|
"InternalName": "",
|
||||||
|
"LegalCopyright": "",
|
||||||
|
"LegalTrademarks": "",
|
||||||
|
"OriginalFilename": "",
|
||||||
|
"PrivateBuild": "",
|
||||||
|
"ProductName": "",
|
||||||
|
"ProductVersion": "",
|
||||||
|
"SpecialBuild": "",
|
||||||
|
"langCharSet": "",
|
||||||
|
// "varFileInfo": "",
|
||||||
|
}
|
||||||
|
keys = []string{
|
||||||
|
"BuildDate",
|
||||||
|
"BuildVersion",
|
||||||
|
"Comments",
|
||||||
|
"CompanyName",
|
||||||
|
"Copyright",
|
||||||
|
"FileDescription",
|
||||||
|
"FileVersion",
|
||||||
|
"InternalName",
|
||||||
|
"LegalCopyright",
|
||||||
|
"LegalTrademarks",
|
||||||
|
"OriginalFilename",
|
||||||
|
"PrivateBuild",
|
||||||
|
"ProductName",
|
||||||
|
"ProductVersion",
|
||||||
|
"SpecialBuild",
|
||||||
|
"langCharSet",
|
||||||
|
// "varFileInfo"
|
||||||
|
}
|
||||||
|
|
||||||
|
section := cfg.PEFile.Section(".rsrc")
|
||||||
|
if section == nil {
|
||||||
|
return vi, keys, fmt.Errorf("resource section not found")
|
||||||
|
}
|
||||||
|
// fmt.Printf("%+v\n", section)
|
||||||
|
|
||||||
|
// Resource
|
||||||
|
_, err = cfg.OSFile.Seek(int64(0), os.SEEK_SET)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
idd := cfg.FindDataDirectory(pe.IMAGE_DIRECTORY_ENTRY_RESOURCE)
|
||||||
|
idd.VirtualAddress -= (section.VirtualAddress - section.Offset)
|
||||||
|
if cfg.Verbose {
|
||||||
|
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE virtual address: %d\n", idd.VirtualAddress)
|
||||||
|
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE size: %d\n", idd.Size)
|
||||||
|
fmt.Printf("IMAGE_DIRECTORY_ENTRY_RESOURCE image base: %d\n", idd.ImageBase)
|
||||||
|
}
|
||||||
|
|
||||||
|
pos, err := cfg.OSFile.Seek(int64(idd.VirtualAddress), os.SEEK_SET)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
if pos != int64(idd.VirtualAddress) {
|
||||||
|
fmt.Errorf("did not seek to VirtualAddress")
|
||||||
|
}
|
||||||
|
|
||||||
|
var table ResourceDirectoryD
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &table)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// fmt.Printf("table %+v\n", table)
|
||||||
|
|
||||||
|
x := 0
|
||||||
|
for x < int(table.NumberOfNamedEntries+table.NumberOfIdEntries) {
|
||||||
|
var entry ResourceDirectoryEntry
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Name == RT_VERSION {
|
||||||
|
// Directory
|
||||||
|
if (entry.OffsetToData&0x80000000)>>31 == 1 {
|
||||||
|
new := entry.OffsetToData&0x7fffffff + idd.VirtualAddress
|
||||||
|
cfg.OSFile.Seek(int64(new), os.SEEK_SET)
|
||||||
|
|
||||||
|
var innerDir ResourceDirectoryD
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &innerDir)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// pos := f.Tell()
|
||||||
|
// fmt.Printf("level 1 innerDir %+v (file offset=%d)\n", innerDir, pos)
|
||||||
|
|
||||||
|
y := 0
|
||||||
|
for y < int(innerDir.NumberOfNamedEntries+innerDir.NumberOfIdEntries) {
|
||||||
|
var entry ResourceDirectoryEntry
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// pos := f.Tell()
|
||||||
|
// fmt.Printf("item %d - level 2 buff %s (file offset=%d)\n", y, entry, pos)
|
||||||
|
|
||||||
|
if (entry.OffsetToData&0x80000000)>>31 == 1 {
|
||||||
|
new := entry.OffsetToData&0x7fffffff + idd.VirtualAddress
|
||||||
|
// fmt.Printf("level 2 DirStart 0x%x (%d)\n", new, new)
|
||||||
|
cfg.OSFile.Seek(int64(new), os.SEEK_SET)
|
||||||
|
}
|
||||||
|
|
||||||
|
var innerDir ResourceDirectoryD
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &innerDir)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// pos = f.Tell()
|
||||||
|
// fmt.Printf("level 3 innerDir %+v (file offset=%d)\n", innerDir, pos)
|
||||||
|
|
||||||
|
z := 0
|
||||||
|
for z < int(innerDir.NumberOfNamedEntries+innerDir.NumberOfIdEntries) {
|
||||||
|
var entry ResourceDirectoryEntry
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &entry)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// pos := f.Tell()
|
||||||
|
// fmt.Printf("item %d - level 3 buff %s (file offset=%d)\n", y, entry, pos)
|
||||||
|
// fmt.Printf("ver: 0x%x\n", entry.OffsetToData+idd.VirtualAddress)
|
||||||
|
|
||||||
|
// find offset of VS_VERSION_INFO
|
||||||
|
off := int64(entry.OffsetToData + idd.VirtualAddress)
|
||||||
|
viPos, viLen, err := cfg.FindVerInfoOffset(off, section.SectionHeader.Offset, section.SectionHeader.VirtualAddress)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// fmt.Printf("VerInfo Struct filePos: 0x%x (%d)\n", viPos, viPos)
|
||||||
|
|
||||||
|
cfg.OSFile.Seek(viPos, os.SEEK_SET)
|
||||||
|
b := make([]byte, viLen)
|
||||||
|
err = binary.Read(cfg.OSFile, binary.LittleEndian, &b)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
// fmt.Printf("%s\n", b)
|
||||||
|
|
||||||
|
if cfg.Verbose {
|
||||||
|
fmt.Printf(" %s\n", hex.Dump(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
vi, err = parseVersionInfo(b, vi)
|
||||||
|
if nil != err {
|
||||||
|
return vi, keys, err
|
||||||
|
}
|
||||||
|
return vi, keys, nil
|
||||||
|
}
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
|
||||||
|
return vi, keys, fmt.Errorf("no version info found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseVersionInfo(vi []byte, versionInfo map[string]string) (map[string]string, error) {
|
||||||
|
// Grab everything after "StringFileInfo"
|
||||||
|
stringFileInfoTemp := bytes.Split(vi, []byte{0x53, 0x0, 0x74, 0x0, 0x72, 0x0, 0x69, 0x0, 0x6e, 0x0, 0x67, 0x0, 0x46, 0x0, 0x69, 0x0, 0x6c, 0x0, 0x65, 0x0, 0x49, 0x0, 0x6e, 0x0, 0x66, 0x0, 0x6f})
|
||||||
|
|
||||||
|
if len(stringFileInfoTemp) < 2 {
|
||||||
|
err := fmt.Errorf("can't find StringFileInfo bytes")
|
||||||
|
return versionInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stringFileInfo := stringFileInfoTemp[1]
|
||||||
|
|
||||||
|
divide := bytes.Split(stringFileInfo, []byte{0x0, 0x1, 0x0})
|
||||||
|
|
||||||
|
langCharSet := trimSlice(divide[1])
|
||||||
|
versionInfo["langCharSet"] = string(langCharSet)
|
||||||
|
|
||||||
|
// check for slice out of bounds
|
||||||
|
if len(divide) < 3 {
|
||||||
|
err := fmt.Errorf("VersionInfo slice too small")
|
||||||
|
return versionInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
end := len(divide) - 1
|
||||||
|
if end < 2 {
|
||||||
|
err := fmt.Errorf("slice end less than start")
|
||||||
|
return versionInfo, err
|
||||||
|
}
|
||||||
|
|
||||||
|
values := divide[2:end]
|
||||||
|
|
||||||
|
// TODO: handle varFileInfo, currently contains binary information which chrome does not display
|
||||||
|
// varFileInfo := divide[len(divide)-1]
|
||||||
|
// versionInfo["varFileInfo"] = string(trimSlice(varFileInfo))
|
||||||
|
|
||||||
|
for _, element := range values {
|
||||||
|
temp := bytes.Split(element, []byte{0x0, 0x0, 0x0})
|
||||||
|
valueInfo := temp[:len(temp)-1]
|
||||||
|
|
||||||
|
if len(valueInfo) > 1 {
|
||||||
|
name := string(trimSlice(valueInfo[0]))
|
||||||
|
value := string(trimSlice(valueInfo[1]))
|
||||||
|
|
||||||
|
versionInfo[name] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimSlice(nonTrimmed []byte) (trimmed []byte) {
|
||||||
|
for bytes.HasPrefix(nonTrimmed, []byte{0x0}) {
|
||||||
|
nonTrimmed = nonTrimmed[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, val := range nonTrimmed {
|
||||||
|
if i%2 == 0 && val != 0x0 {
|
||||||
|
trimmed = append(trimmed, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return trimmed
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user