mirror of
https://github.com/lukaszraczylo/go-obfuscation.git
synced 2026-10-14 05:48:22 +00:00
.
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var pclntabMagics = [][]byte{
|
||||
{0xF1, 0xFF, 0xFF, 0xFA}, // Go 1.2 - 1.15
|
||||
{0xF1, 0xFF, 0xFF, 0xF0}, // Go 1.16 - 1.17
|
||||
{0xF1, 0xFF, 0xFF, 0xF1}, // Go 1.18+
|
||||
}
|
||||
|
||||
type CorruptOptions struct {
|
||||
EncryptNames bool
|
||||
ScrambleMagic bool
|
||||
InjectFake bool
|
||||
Key byte
|
||||
}
|
||||
|
||||
func CorruptPclntab(binaryPath string, opts CorruptOptions) ([]byte, error) {
|
||||
if opts.Key == 0 {
|
||||
var k [1]byte
|
||||
if _, err := rand.Read(k[:]); err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to generate key: %w", err)
|
||||
}
|
||||
if k[0] == 0 {
|
||||
k[0] = 0x42
|
||||
}
|
||||
opts.Key = k[0]
|
||||
}
|
||||
|
||||
data, err := readPclntabSection(binaryPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
return nil, fmt.Errorf("pclntab: .gopclntab section not found in %s", binaryPath)
|
||||
}
|
||||
|
||||
corrupted := make([]byte, len(data))
|
||||
copy(corrupted, data)
|
||||
|
||||
tab := &pclntabParser{data: corrupted}
|
||||
|
||||
if opts.ScrambleMagic {
|
||||
scrambleMagic(corrupted)
|
||||
}
|
||||
|
||||
if opts.EncryptNames {
|
||||
if err := encryptNames(tab, opts.Key); err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to encrypt names: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if opts.InjectFake {
|
||||
corrupted = injectFakeEntries(corrupted)
|
||||
}
|
||||
|
||||
return corrupted, nil
|
||||
}
|
||||
|
||||
type pclntabParser struct {
|
||||
data []byte
|
||||
magic []byte
|
||||
quantum uint8
|
||||
ptrSize uint8
|
||||
nfiles uint32
|
||||
nameOff uint32
|
||||
strtabOff uint32
|
||||
valid bool
|
||||
}
|
||||
|
||||
func (p *pclntabParser) parse() error {
|
||||
if len(p.data) < 16 {
|
||||
return fmt.Errorf("pclntab: data too small (%d bytes)", len(p.data))
|
||||
}
|
||||
|
||||
p.magic = p.data[:4]
|
||||
found := false
|
||||
for _, m := range pclntabMagics {
|
||||
if bytesEqual(p.magic, m) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("pclntab: unrecognized magic: %x", p.magic)
|
||||
}
|
||||
|
||||
p.ptrSize = p.data[6]
|
||||
p.quantum = p.data[7]
|
||||
|
||||
if p.ptrSize == 0 || (p.ptrSize != 4 && p.ptrSize != 8) {
|
||||
p.ptrSize = 8
|
||||
}
|
||||
if p.quantum == 0 {
|
||||
p.quantum = 1
|
||||
}
|
||||
|
||||
if bytesEqual(p.magic, pclntabMagics[2]) {
|
||||
if len(p.data) >= 20 {
|
||||
p.nfiles = binary.LittleEndian.Uint32(p.data[16:20])
|
||||
}
|
||||
}
|
||||
|
||||
p.valid = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func scrambleMagic(data []byte) {
|
||||
if len(data) < 4 {
|
||||
return
|
||||
}
|
||||
var rnd [4]byte
|
||||
_, _ = rand.Read(rnd[:])
|
||||
for i := 0; i < 4; i++ {
|
||||
data[i] = rnd[i]
|
||||
}
|
||||
}
|
||||
|
||||
func encryptNames(tab *pclntabParser, key byte) error {
|
||||
if err := tab.parse(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameStart, nameEnd := findNameRegion(tab)
|
||||
if nameStart == 0 || nameEnd == 0 || nameStart >= nameEnd {
|
||||
return fmt.Errorf("pclntab: could not locate name region")
|
||||
}
|
||||
|
||||
if nameEnd > uint32(len(tab.data)) {
|
||||
nameEnd = uint32(len(tab.data))
|
||||
}
|
||||
|
||||
for i := nameStart; i < nameEnd; i++ {
|
||||
if tab.data[i] != 0 {
|
||||
tab.data[i] ^= key
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findNameRegion(tab *pclntabParser) (uint32, uint32) {
|
||||
data := tab.data
|
||||
ptrSize := int(tab.ptrSize)
|
||||
|
||||
if bytesEqual(tab.magic, pclntabMagics[2]) {
|
||||
if len(data) < 16+4+4*int(ptrSize) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
offset := 16 + 4
|
||||
_ = readUint(data, offset, ptrSize) // nfunc
|
||||
offset += ptrSize
|
||||
offset += ptrSize // nfiles (Go 1.18 already read above)
|
||||
|
||||
_ = readUint(data, offset, ptrSize) // textStart
|
||||
offset += ptrSize
|
||||
|
||||
funcnametab := readUint(data, offset, ptrSize)
|
||||
offset += ptrSize
|
||||
_ = readUint(data, offset, ptrSize) // cutab
|
||||
offset += ptrSize
|
||||
_ = readUint(data, offset, ptrSize) // filetab
|
||||
offset += ptrSize
|
||||
_ = readUint(data, offset, ptrSize) // pctab
|
||||
offset += ptrSize
|
||||
pclntab := readUint(data, offset, ptrSize)
|
||||
|
||||
_ = pclntab
|
||||
|
||||
nameStart := uint32(funcnametab)
|
||||
nameEnd := nameStart
|
||||
for i := nameStart; i < uint32(len(data)); i++ {
|
||||
if data[i] == 0 {
|
||||
if i+1 < uint32(len(data)) && data[i+1] == 0 {
|
||||
nameEnd = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if nameEnd == nameStart {
|
||||
nameEnd = uint32(len(data))
|
||||
}
|
||||
|
||||
return nameStart, nameEnd
|
||||
}
|
||||
|
||||
return findNameRegionLegacy(tab)
|
||||
}
|
||||
|
||||
func findNameRegionLegacy(tab *pclntabParser) (uint32, uint32) {
|
||||
data := tab.data
|
||||
offset := 8 + 4 + 4
|
||||
|
||||
if bytesEqual(tab.magic, pclntabMagics[1]) {
|
||||
offset += 4
|
||||
}
|
||||
|
||||
if offset+8 > len(data) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
nameOff := binary.LittleEndian.Uint32(data[offset : offset+4])
|
||||
_ = binary.LittleEndian.Uint32(data[offset+4 : offset+8])
|
||||
|
||||
nameStart := nameOff
|
||||
nameEnd := nameStart
|
||||
for i := nameStart; i < uint32(len(data)); i++ {
|
||||
if data[i] == 0 {
|
||||
if i+1 < uint32(len(data)) && data[i+1] == 0 {
|
||||
nameEnd = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if nameEnd == nameStart {
|
||||
nameEnd = uint32(len(data))
|
||||
}
|
||||
|
||||
return nameStart, nameEnd
|
||||
}
|
||||
|
||||
func injectFakeEntries(data []byte) []byte {
|
||||
fakeEntries := make([]byte, 256)
|
||||
_, _ = rand.Read(fakeEntries)
|
||||
|
||||
fakeNames := []string{
|
||||
"runtime.fakeGoroutine",
|
||||
"crypto/internal.init",
|
||||
"main.processRequest",
|
||||
"net/http.handleConn",
|
||||
"google.golang.org/grpc.recvMsg",
|
||||
"github.com/user/repo.processData",
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for _, name := range fakeNames {
|
||||
if offset+len(name)+1 > len(fakeEntries) {
|
||||
break
|
||||
}
|
||||
copy(fakeEntries[offset:], name)
|
||||
offset += len(name) + 1
|
||||
}
|
||||
|
||||
return append(data, fakeEntries...)
|
||||
}
|
||||
|
||||
func readUint(data []byte, offset int, size int) uint64 {
|
||||
if offset+size > len(data) {
|
||||
return 0
|
||||
}
|
||||
switch size {
|
||||
case 4:
|
||||
return uint64(binary.LittleEndian.Uint32(data[offset : offset+4]))
|
||||
case 8:
|
||||
return binary.LittleEndian.Uint64(data[offset : offset+8])
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func bytesEqual(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//go:build linux
|
||||
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func readPclntabSection(binaryPath string) ([]byte, error) {
|
||||
f, err := elf.Open(binaryPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to open ELF: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sectionNames := []string{
|
||||
".gopclntab",
|
||||
"runtime.pclntab",
|
||||
"runtime.pclntab_v1",
|
||||
}
|
||||
|
||||
for _, name := range sectionNames {
|
||||
section := f.Section(name)
|
||||
if section != nil {
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to read section %s: %w", name, err)
|
||||
}
|
||||
buf := make([]byte, len(data))
|
||||
copy(buf, data)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, section := range f.Sections {
|
||||
data, err := section.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, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func WriteCorruptedPclntab(binaryPath string, corrupted []byte, outputPath string) error {
|
||||
return fmt.Errorf("pclntab: offline ELF patching not yet implemented; use runtime corruption instead")
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//go:build darwin
|
||||
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"debug/macho"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func readPclntabSection(binaryPath string) ([]byte, error) {
|
||||
f, err := macho.Open(binaryPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to open Mach-O: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sectionNames := []string{
|
||||
"__gopclntab",
|
||||
"__pclntab",
|
||||
}
|
||||
|
||||
for _, section := range f.Sections {
|
||||
for _, name := range sectionNames {
|
||||
if section.Name == name {
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to read section %s: %w", name, err)
|
||||
}
|
||||
buf := make([]byte, len(data))
|
||||
copy(buf, data)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, section := range f.Sections {
|
||||
data, err := section.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, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func WriteCorruptedPclntab(binaryPath string, corrupted []byte, outputPath string) error {
|
||||
return fmt.Errorf("pclntab: offline Mach-O patching not yet implemented; use runtime corruption instead")
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
//go:build windows
|
||||
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func readPclntabSection(binaryPath string) ([]byte, error) {
|
||||
f, err := pe.Open(binaryPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to open PE: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
sectionNames := []string{
|
||||
".gopclntab",
|
||||
"runtime.pclntab",
|
||||
"runtime.pclntab_v1",
|
||||
}
|
||||
|
||||
for _, section := range f.Sections {
|
||||
for _, name := range sectionNames {
|
||||
if section.Name == name {
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("pclntab: failed to read section %s: %w", name, err)
|
||||
}
|
||||
buf := make([]byte, len(data))
|
||||
copy(buf, data)
|
||||
return buf, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, section := range f.Sections {
|
||||
data, err := section.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, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func WriteCorruptedPclntab(binaryPath string, corrupted []byte, outputPath string) error {
|
||||
return fmt.Errorf("pclntab: offline PE patching not yet implemented; use runtime corruption instead")
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// RuntimeCorrupt finds the running binary's pclntab in memory and XOR-encrypts
|
||||
// all name strings. Call this during init() — the Go runtime has already used
|
||||
// pclntab to resolve function pointers, so the program keeps working but
|
||||
// post-init reverse engineering tools see garbage names.
|
||||
//
|
||||
// WARNING: Degrades panic stack traces (function names become unreadable).
|
||||
func RuntimeCorrupt(key byte) error {
|
||||
if key == 0 {
|
||||
var k [1]byte
|
||||
if _, err := rand.Read(k[:]); err != nil {
|
||||
return fmt.Errorf("pclntab: failed to generate key: %w", err)
|
||||
}
|
||||
if k[0] == 0 {
|
||||
k[0] = 0x42
|
||||
}
|
||||
key = k[0]
|
||||
}
|
||||
|
||||
selfPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return fmt.Errorf("pclntab: cannot determine executable: %w", err)
|
||||
}
|
||||
|
||||
return runtimeCorrupt(selfPath, key)
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//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
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//go:build linux
|
||||
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func runtimeCorrupt(binaryPath string, key byte) error {
|
||||
self, err := os.ReadFile("/proc/self/exe")
|
||||
if err != nil {
|
||||
return fmt.Errorf("pclntab: cannot read /proc/self/exe: %w", err)
|
||||
}
|
||||
|
||||
f, err := elf.NewFile(bytesReader(self))
|
||||
if err != nil {
|
||||
return fmt.Errorf("pclntab: cannot parse ELF from memory: %w", err)
|
||||
}
|
||||
|
||||
sectionData, sectionAddr, sectionSize, err := findPclntabELF(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nameStart, nameEnd := findMemoryNameRegion(sectionData)
|
||||
if nameStart == 0 || nameEnd <= nameStart {
|
||||
return fmt.Errorf("pclntab: could not locate name region")
|
||||
}
|
||||
|
||||
baseAddr := sectionAddr &^ uintptr(syscall.Getpagesize()-1)
|
||||
protSize := sectionSize + (sectionAddr - baseAddr)
|
||||
|
||||
if err := mprotectRW(baseAddr, protSize); err != nil {
|
||||
return fmt.Errorf("pclntab: mprotect failed: %w", err)
|
||||
}
|
||||
|
||||
basePtr := unsafe.Pointer(sectionAddr)
|
||||
namePtr := unsafe.Add(basePtr, nameStart)
|
||||
nameSlice := (*[1 << 30]byte)(namePtr)[: nameEnd-nameStart : nameEnd-nameStart]
|
||||
for i := range nameSlice {
|
||||
if nameSlice[i] != 0 {
|
||||
nameSlice[i] ^= key
|
||||
}
|
||||
}
|
||||
|
||||
mprotectRO(baseAddr, protSize)
|
||||
return nil
|
||||
}
|
||||
|
||||
func findPclntabELF(f *elf.File) ([]byte, uintptr, uintptr, error) {
|
||||
sectionNames := []string{
|
||||
".gopclntab",
|
||||
"runtime.pclntab",
|
||||
"runtime.pclntab_v1",
|
||||
}
|
||||
|
||||
for _, name := range sectionNames {
|
||||
sec := f.Section(name)
|
||||
if sec == nil {
|
||||
continue
|
||||
}
|
||||
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 mprotectRW(addr uintptr, size uintptr) error {
|
||||
return syscall.Mprotect(
|
||||
(*[1]byte)(unsafe.Pointer(addr))[:size:size],
|
||||
syscall.PROT_READ|syscall.PROT_WRITE,
|
||||
)
|
||||
}
|
||||
|
||||
func mprotectRO(addr uintptr, size uintptr) {
|
||||
_ = syscall.Mprotect(
|
||||
(*[1]byte)(unsafe.Pointer(addr))[:size:size],
|
||||
syscall.PROT_READ,
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//go:build darwin || linux
|
||||
|
||||
package pclntab
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// mprotectRW makes a memory region read-write using an unsafe.Pointer conversion
|
||||
// from a page-aligned uintptr. This is intentional for runtime memory manipulation.
|
||||
//
|
||||
//go:nosplit
|
||||
func mprotectRW(addr uintptr, size uintptr) error {
|
||||
p := *(*unsafe.Pointer)(unsafe.Pointer(&addr))
|
||||
return syscall.Mprotect(
|
||||
unsafe.Slice((*byte)(p), size),
|
||||
syscall.PROT_READ|syscall.PROT_WRITE,
|
||||
)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func mprotectRO(addr uintptr, size uintptr) {
|
||||
p := *(*unsafe.Pointer)(unsafe.Pointer(&addr))
|
||||
_ = syscall.Mprotect(
|
||||
unsafe.Slice((*byte)(p), size),
|
||||
syscall.PROT_READ,
|
||||
)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func ptrToSlice(addr uintptr, size uintptr) []byte {
|
||||
p := *(*unsafe.Pointer)(unsafe.Pointer(&addr))
|
||||
return unsafe.Slice((*byte)(p), size)
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build windows
|
||||
|
||||
package pclntab
|
||||
|
||||
import "fmt"
|
||||
|
||||
func runtimeCorrupt(binaryPath string, key byte) error {
|
||||
return fmt.Errorf("pclntab: runtime corruption not supported on windows")
|
||||
}
|
||||
Reference in New Issue
Block a user