8000 compiler: rename Go conflictful symbols by NSEcho · Pull Request #1178 · frida/frida-core · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 35 additions & 45 deletions src/compiler/build-backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,41 @@ def run(*args):
)

if mode == "c-archive":
symbols_to_scramble = detect_conflictful_symbols_in_archive(backend_a, config["nm"])
scramble_symbols_in_archive(backend_a, symbols_to_scramble, config["ranlib"])
if config.get("mingw") is None:
replacer_inputs: List[Path] = [
Path(priv_dir / "symbol-replacer" / "main.go"),
Path(priv_dir / "symbol-replacer" / "trie.go"),
]

symbol_dest = priv_dir / "symbol-replacer"
symbol_dest.mkdir(exist_ok=True)
shutil.copy(base_dir / "symbol-replacer" / "main.go", symbol_dest)
shutil.copy(base_dir / "symbol-replacer" / "trie.go", symbol_dest)
shutil.copy(base_dir / "symbol-replacer" / "go.mod", symbol_dest)

symbol_replacer_sources = [str("symbol-replacer" / f.relative_to(symbol_dest)) for f in replacer_inputs if f.suffix == ".go"]

# when we are cross-compiling we want to unset GOOS and GOARCH
env_copy = config["env"].copy()
env_copy["GOOS"] = ""
env_copy["GOARCH"] = ""

symbol_replacer = symbol_dest / "frida-symbol-replacer"

subprocess.run([go,
"build",
"-o",
symbol_replacer.name,
*symbol_replacer_sources],
cwd=priv_dir,
env=env_copy,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding="utf-8",
check=True,
)

run(priv_dir / symbol_replacer.name, backend_a.name, *config["nm"], *config["ranlib"])

if (mingw := config.get("mingw")) is not None and (abi := config["abi"]) in {
"x86",
Expand Down Expand Up @@ -133,49 +166,6 @@ def run(*args):
shutil.copy(priv_dir / backend_h.name, output_dir / backend_h.name)


def detect_conflictful_symbols_in_archive(
archive: Path, nm_cmd_array: List[str]
) -> List[str]:
result = []
for line in subprocess.run(
[*nm_cmd_array, str(archive)], capture_output=True, encoding="utf-8", check=True
).stdout.splitlines():
tokens = line.lstrip().split(" ")
if len(tokens) >= 3 and tokens[1] in {"S", "T"}:
symbol = tokens[2]
if all(sub not in symbol for sub in ("/", ".", ":", "frida")):
result.append(symbol)
return result


def scramble_symbols_in_archive(
archive: Path, symbols: List[str], ranlib_cmd_array: List[str]
):
data = archive.read_bytes()

for old_sym in symbols:
new_sym = roll_first_alpha(old_sym)
data = data.replace(old_sym.encode("ascii"), new_sym.encode("ascii"))

archive.write_bytes(data)

subprocess.run([*ranlib_cmd_array, str(archive)], check=True)


def roll_first_alpha(s: str) -> str:
chars = list(s)
for i, c in enumerate(chars):
if c.isalpha():
if c == "z":
chars[i] = "a"
elif c == "Z":
chars[i] = "A"
else:
chars[i] = chr(ord(c) + 1)
break
return "".join(chars)


# Work around the Go toolchain's almost-MSVC-compatible object files
# until our patch is merged upstream:
# https://go-review.googlesource.com/c/go/+/678795
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/symbol-replacer/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/frida/frida-symbol-replacer

go 1.24.4
115 changes: 115 additions & 0 deletions src/compiler/symbol-replacer/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package main

import (
"bytes"
"io"
"os"
"os/exec"
"unicode"
)

var (
excludeSymbols = [][]byte{
[]byte("fprintf"),
[]byte("pthread"),
[]byte("st0_"),
[]byte("rt0_"),
[]byte("cas"),
}
)

func main() {
archive := os.Args[1]
nm := os.Args[2]
ranlib := os.Args[3]

buf := new(bytes.Buffer)
c := exec.Command(nm, archive)
c.Stdout = buf
if err := c.Run(); err != nil {
panic(err)
}

t := newTrie()
t.insert([]byte("internal/"), flipAlpha([]byte("internal/")))

content := bytes.Split(buf.Bytes(), []byte{'\n'})
for _, line := range content {
splitted := bytes.Split(line, []byte{' '})
if len(splitted) >= 3 {
if bytes.ContainsAny(splitted[1], "TSt") {
symbol := splitted[2]
symbolLower := bytes.ToLower(symbol)
if symbolIsOkay(symbolLower) {
t.insert(symbol, flipAlpha(symbol))
}
}
}
}

f, err := os.OpenFile(archive, os.O_RDWR, 0755)
if err != nil {
panic(err)
}

data, _ := io.ReadAll(f)
modifiedData := make([]byte, len(data))

for i := 0; i < len(data); {
if l, replacement, ok := t.search(data, i); ok {
copy(modifiedData[i:], replacement)
i += l
} else {
modifiedData[i] = data[i]
i++
}
}

f.Truncate(0)
f.Seek(0, 0)
f.Write(modifiedData)

f.Close()

ranl := exec.Command(ranlib, archive)
if err := ranl.Run(); err != nil {
panic(err)
}
}

func flipAlpha(s []byte) []byte {
dt := make([]byte, len(s))
copy(dt, s)

for i, r := range s {
if unicode.IsLetter(rune(r)) || unicode.IsNumber(rune(r)) {
if r == 'z' {
dt[i] = 'a'
} else if r == 'Z' {
dt[i] = 'A'
} else {
dt[i] = r + 1
}
break
}
}
return dt
}

func symbolIsOkay(symbol []byte) bool {
switch {
case bytes.Contains(symbol, []byte("frida")):
return false
case bytes.Contains(symbol, []byte("cgo")):
return true
case bytes.ContainsAny(symbol, "./"):
return true
default:
for _, s := range excludeSymbols {
if bytes.Contains(symbol, s) {
return false
}
}
return true
}
}
52 changes: 52 additions & 0 deletions src/compiler/symbol-replacer/trie.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

type trieNode struct {
children map[byte]*trieNode
isEnd bool
replacement []byte
}

type trie struct {
root *trieNode
}

func newTrie() *trie {
return &trie{
root: &trieNode{
children: make(map[byte]*trieNode),
},
}
}

func (t *trie) insert(pattern, value []byte) {
node := t.root
for _, b := range pattern {
if node.children[b] == nil {
node.children[b] = &trieNode{children: make(map[byte]*trieNode)}
}
node = node.children[b]
}
node.isEnd = true
node.replacement = value
}

func (t *trie) search(data []byte, start int) (int, []byte, bool) {
node := t.root
maxLen := 0
var match []byte
for i := start; i < len(data); i++ {
b := data[i]
node = node.children[b]
if node == nil {
break
}
if node.isEnd {
maxLen = i - start + 1
match = node.replacement
}
}
if maxLen > 0 {
return maxLen, match, true
}
return 0, nil, false
}
Loading
0