Skip to content

Commit 4ed35a3

Browse files
committed
init
1 parent bb3bf34 commit 4ed35a3

13 files changed

+538
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@ go.work.sum
2323

2424
# env file
2525
.env
26+
27+
tests/embedemo_*
28+
.DS_Store

.vscode/launch.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
// 使用 IntelliSense 了解相关属性。
3+
// 悬停以查看现有属性的描述。
4+
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "main",
9+
"type": "go",
10+
"request": "launch",
11+
"mode": "auto",
12+
"program": "${workspaceFolder}/main.go",
13+
"args": [
14+
"-target",
15+
"./tests/embedemo_pe",
16+
"-vaddr",
17+
"0x00000001000F3B00",
18+
]
19+
},
20+
{
21+
"name": "embedemo",
22+
"type": "go",
23+
"request": "launch",
24+
"mode": "auto",
25+
"program": "${workspaceFolder}/tests/embedemo.go",
26+
}
27+
]
28+
}

Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
build:
2+
@CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath -o bin/gee main.go
3+
4+
test:
5+
@cd tests/
6+
@CGO_ENABLED=0 GOOS=darwin go build -ldflags "-s -w" -trimpath -o tests/embedemo_macho tests/embedemo.go
7+
@CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -trimpath -o tests/embedemo_elf tests/embedemo.go
8+
@CGO_ENABLED=0 GOOS=windows go build -ldflags "-s -w" -trimpath -o tests/embedemo_pe tests/embedemo.go
9+
10+
@echo "unit test"
11+
@go run main.go -target ./tests/embedemo_elf -vaddr 0x00000000000F8F60
12+
@go run main.go -target ./tests/embedemo_macho -vaddr 0x00000001000EAA00
13+
@go run main.go -target ./tests/embedemo_pe -vaddr 0x00000001000F3B00

extractor/extractor.go

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package extractor
2+
3+
import (
4+
"encoding/binary"
5+
"errors"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
"sync"
11+
)
12+
13+
type EmbedFile struct {
14+
Name string `json:"name,omitempty"`
15+
Data []byte `json:"data,omitempty"`
16+
Hash []byte `json:"hash,omitempty"`
17+
}
18+
19+
func Extract(fpath string, vaddr uint64) (ret []EmbedFile, err error) {
20+
parser, err := NewParser(fpath)
21+
if err != nil {
22+
return nil, err
23+
}
24+
defer parser.Close()
25+
26+
fr, err := NewFileReader(fpath)
27+
if err != nil {
28+
return nil, err
29+
}
30+
defer fr.Close()
31+
32+
// parse embed header
33+
embedHeaderOff, err := parser.ConvRelativeOffset(vaddr)
34+
if err != nil {
35+
return nil, err
36+
}
37+
header, err := fr.ReadUint64(3, embedHeaderOff)
38+
if err != nil {
39+
return nil, err
40+
}
41+
if header[1] != header[2] { // length and capacity
42+
return nil, errors.New("invalid embed header")
43+
}
44+
45+
filesOff, err := parser.ConvRelativeOffset(header[0])
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
// parse embed files
51+
for i := header[1]; i > 0; i-- {
52+
// parse file name
53+
name, err := fr.ReadUint64(2, filesOff)
54+
if err != nil {
55+
return nil, err
56+
}
57+
offset, err := parser.ConvRelativeOffset(name[0])
58+
if err != nil {
59+
return nil, err
60+
}
61+
fname, err := fr.ReadBytes(int(name[1]), offset)
62+
if err != nil {
63+
return nil, err
64+
}
65+
66+
// parse file content
67+
filesOff += 16
68+
var fcontent []byte
69+
content, err := fr.ReadUint64(2, filesOff)
70+
if err != nil {
71+
return nil, err
72+
}
73+
if content[0] != 0 {
74+
offset, err = parser.ConvRelativeOffset(content[0])
75+
if err != nil {
76+
return nil, err
77+
}
78+
fcontent, err = fr.ReadBytes(int(content[1]), offset)
79+
if err != nil {
80+
return nil, err
81+
}
82+
}
83+
84+
// parse file hash
85+
filesOff += 16
86+
hash, err := fr.ReadBytes(16, filesOff)
87+
if err != nil {
88+
return nil, err
89+
}
90+
91+
// create embed file
92+
ret = append(ret, EmbedFile{Name: string(fname), Data: fcontent, Hash: hash})
93+
filesOff += 16
94+
}
95+
96+
return ret, nil
97+
}
98+
99+
type FileReader struct {
100+
f *os.File
101+
}
102+
103+
func NewFileReader(fpath string) (*FileReader, error) {
104+
file, err := os.Open(fpath)
105+
if err != nil {
106+
return nil, err
107+
}
108+
109+
return &FileReader{f: file}, nil
110+
}
111+
112+
func (fr *FileReader) Close() error {
113+
if fr.f != nil {
114+
return fr.f.Close()
115+
}
116+
117+
return nil
118+
}
119+
120+
var bufpool = sync.Pool{
121+
New: func() interface{} {
122+
buf := make([]byte, 8)
123+
return &buf
124+
},
125+
}
126+
127+
func (fr *FileReader) ReadUint64(size int, off uint64) ([]uint64, error) {
128+
if fr.f == nil {
129+
return nil, os.ErrClosed
130+
}
131+
132+
ret := make([]uint64, 0)
133+
for i := 0; i < size; i++ {
134+
bufptr := bufpool.Get().(*[]byte)
135+
buf := *bufptr
136+
defer bufpool.Put(bufptr)
137+
138+
n, err := fr.f.ReadAt(buf, int64(off))
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
if n != 8 {
144+
return nil, errors.New("read size mismatch")
145+
}
146+
147+
off += 8
148+
ret = append(ret, binary.LittleEndian.Uint64(buf))
149+
}
150+
151+
if len(ret) != size {
152+
return nil, errors.New("read size mismatch")
153+
}
154+
155+
return ret, nil
156+
}
157+
158+
func (fr *FileReader) ReadBytes(size int, off uint64) ([]byte, error) {
159+
if fr.f == nil {
160+
return nil, os.ErrClosed
161+
}
162+
163+
ret := make([]byte, size)
164+
n, err := fr.f.ReadAt(ret, int64(off))
165+
if err != nil {
166+
return nil, err
167+
}
168+
169+
if n != size {
170+
return nil, errors.New("read size mismatch")
171+
}
172+
173+
return ret, nil
174+
}
175+
176+
func Save(files []EmbedFile, output string) error {
177+
if err := os.MkdirAll(output, 0755); err != nil {
178+
return err
179+
}
180+
181+
for _, ef := range files {
182+
fullPath := filepath.Join(output, ef.Name)
183+
184+
if strings.HasSuffix(ef.Name, "/") {
185+
if err := os.MkdirAll(fullPath, 0755); err != nil {
186+
return err
187+
}
188+
}
189+
190+
parentDir := filepath.Dir(fullPath)
191+
if err := os.MkdirAll(parentDir, 0755); err != nil {
192+
return err
193+
}
194+
195+
if ef.Data != nil {
196+
if err := os.WriteFile(fullPath, ef.Data, 0644); err != nil {
197+
return fmt.Errorf("failed to write file %s: %v", fullPath, err)
198+
}
199+
}
200+
}
201+
202+
return nil
203+
}

extractor/filetype.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package extractor
2+
3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
8+
type Filetype string
9+
10+
const (
11+
FiletypeELF Filetype = "ELF"
12+
FiletypePE Filetype = "PE"
13+
FiletypeMachO Filetype = "Mach-O"
14+
)
15+
16+
func GetFileType(filePath string) (Filetype, error) {
17+
// Check the file type using the magic number
18+
file, err := os.Open(filePath)
19+
if err != nil {
20+
return "", err
21+
}
22+
defer file.Close()
23+
24+
// Read the first few bytes to determine the file type
25+
header := make([]byte, 4)
26+
if _, err := file.Read(header); err != nil {
27+
return "", err
28+
}
29+
30+
switch {
31+
case header[0] == 0x7F && header[1] == 'E' && header[2] == 'L' && header[3] == 'F':
32+
return FiletypeELF, nil
33+
case header[0] == 'M' && header[1] == 'Z':
34+
return FiletypePE, nil
35+
case header[0] == 0xCF && header[1] == 0xFA && header[2] == 0xED:
36+
return FiletypeMachO, nil
37+
}
38+
39+
return "", fmt.Errorf("unknown file type")
40+
}

extractor/filetype_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package extractor
2+
3+
import "testing"
4+
5+
func TestGetFileType(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
filePath string
9+
expected Filetype
10+
}{
11+
{"ELF", "testdata/embedemo_elf", FiletypeELF},
12+
{"PE", "testdata/embedemo_pe", FiletypePE},
13+
{"Mach-O", "testdata/embedemo_macho", FiletypeMachO},
14+
}
15+
16+
for _, test := range tests {
17+
t.Run(test.name, func(t *testing.T) {
18+
fileType, err := GetFileType(test.filePath)
19+
if err != nil {
20+
t.Fatalf("expected no error, got %v", err)
21+
}
22+
if fileType != test.expected {
23+
t.Fatalf("expected %v, got %v", test.expected, fileType)
24+
}
25+
})
26+
}
27+
}

0 commit comments

Comments
 (0)