Files
go-obfuscation/pkg/pclntab/rtcorrupt_darwin.go
T
2026-05-31 19:48:56 +01:00

117 lines
2.5 KiB
Go

//go:build darwin
package pclntab
import (
"debug/macho"
"fmt"
"os"
"syscall"
)
func runtimeCorrupt(binaryPath string, key byte) error {
self, err := os.ReadFile(binaryPath)
if err != nil {
return fmt.Errorf("pclntab: cannot read binary: %w", err)
}
f, err := macho.NewFile(bytesReader(self))
if err != nil {
return fmt.Errorf("pclntab: cannot parse Mach-O from memory: %w", err)
}
sectionData, sectionAddr, sectionSize, err := findPclntabMacho(f)
if err != nil {
return err
}
nameStart, nameEnd := findMemoryNameRegion(sectionData)
if nameStart == 0 || nameEnd <= nameStart {
return fmt.Errorf("pclntab: could not locate name region")
}
pageSize := syscall.Getpagesize()
baseAddr := sectionAddr &^ uintptr(pageSize-1)
protSize := sectionSize + (sectionAddr - baseAddr)
if err := mprotectRW(baseAddr, protSize); err != nil {
return fmt.Errorf("pclntab: mprotect failed: %w", err)
}
nameSlice := ptrToSlice(sectionAddr+uintptr(nameStart), uintptr(nameEnd-nameStart))
for i := range nameSlice {
if nameSlice[i] != 0 {
nameSlice[i] ^= key
}
}
mprotectRO(baseAddr, protSize)
return nil
}
func findPclntabMacho(f *macho.File) ([]byte, uintptr, uintptr, error) {
sectionNames := []string{
"__gopclntab",
"__pclntab",
}
for _, sec := range f.Sections {
for _, name := range sectionNames {
if sec.Name == name {
data, err := sec.Data()
if err != nil {
continue
}
buf := make([]byte, len(data))
copy(buf, data)
return buf, uintptr(sec.Addr), uintptr(sec.Size), nil
}
}
}
for _, sec := range f.Sections {
data, err := sec.Data()
if err != nil {
continue
}
if len(data) >= 8 {
for _, magic := range pclntabMagics {
if len(data) >= len(magic) && bytesEqual(data[:len(magic)], magic) {
buf := make([]byte, len(data))
copy(buf, data)
return buf, uintptr(sec.Addr), uintptr(sec.Size), nil
}
}
}
}
return nil, 0, 0, fmt.Errorf("pclntab: __gopclntab section not found in memory")
}
func findMemoryNameRegion(data []byte) (uint32, uint32) {
tab := &pclntabParser{data: data}
if err := tab.parse(); err != nil {
return 0, 0
}
return findNameRegion(tab)
}
type bytesReaderWrapper struct {
data []byte
}
func bytesReader(data []byte) *bytesReaderWrapper {
return &bytesReaderWrapper{data: data}
}
func (r *bytesReaderWrapper) ReadAt(p []byte, off int64) (int, error) {
if off < 0 || int(off) >= len(r.data) {
return 0, fmt.Errorf("invalid offset")
}
n := copy(p, r.data[int(off):])
if n < len(p) {
return n, fmt.Errorf("EOF")
}
return n, nil
}