format code
This commit is contained in:
parent
4016abf40d
commit
f88861a87e
@ -4,13 +4,13 @@ import (
|
|||||||
. "ios-wrapper/internal/wrapper/ofile"
|
. "ios-wrapper/internal/wrapper/ofile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type rewriteImports struct{
|
type rewriteImports struct {
|
||||||
symbols []string
|
symbols []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *rewriteImports) withMacho(mf *MachoFile) error {
|
func (action *rewriteImports) withMacho(mf *MachoFile) error {
|
||||||
mc := mf.Context()
|
mc := mf.Context()
|
||||||
mc.RewriteImportsTable(action.symbols)
|
mc.RewriteImportsTable(action.symbols)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,7 +20,6 @@ func (action *rewriteImports) withFat(ff *FatFile) error {
|
|||||||
|
|
||||||
func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports {
|
func NewRewriteImportsWithKeepSymbolsAction(symbols []string) *rewriteImports {
|
||||||
return &rewriteImports{
|
return &rewriteImports{
|
||||||
symbols,
|
symbols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,24 +3,24 @@ package action
|
|||||||
import (
|
import (
|
||||||
// "fmt"
|
// "fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
// log "github.com/sirupsen/logrus"
|
// log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
. "ios-wrapper/internal/wrapper/ofile"
|
. "ios-wrapper/internal/wrapper/ofile"
|
||||||
"ios-wrapper/pkg/protomodel"
|
"ios-wrapper/pkg/protomodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
type saveImports struct{
|
type saveImports struct {
|
||||||
keepSymbols []string
|
keepSymbols []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *saveImports) withMacho(mf *MachoFile) error {
|
func (action *saveImports) withMacho(mf *MachoFile) error {
|
||||||
action.saveToInfo(mf)
|
action.saveToInfo(mf)
|
||||||
mc := mf.Context()
|
mc := mf.Context()
|
||||||
if mc.Header().IsDylib() {
|
if mc.Header().IsDylib() {
|
||||||
mc.WriteInfoToData(mf.Info())
|
mc.WriteInfoToData(mf.Info())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
||||||
@ -64,28 +64,28 @@ func (action *saveImports) saveToInfo(mf *MachoFile) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
skip := false
|
skip := false
|
||||||
for _, keep := range action.keepSymbols {
|
for _, keep := range action.keepSymbols {
|
||||||
name := keep
|
name := keep
|
||||||
lib := ""
|
lib := ""
|
||||||
parts := strings.Split(keep, ",")
|
parts := strings.Split(keep, ",")
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
name = parts[0]
|
name = parts[0]
|
||||||
lib = parts[1]
|
lib = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
if symbol.Name() != name {
|
if symbol.Name() != name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if lib == "" || lib == symbol.Dylib() {
|
if lib == "" || lib == symbol.Dylib() {
|
||||||
skip = true
|
skip = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if skip {
|
if skip {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// dylib_hash := calculateHash(symbol.Dylib())
|
// dylib_hash := calculateHash(symbol.Dylib())
|
||||||
seg := mc.Segments()[symbol.Segment()]
|
seg := mc.Segments()[symbol.Segment()]
|
||||||
@ -139,6 +139,6 @@ func (action *saveImports) withFat(ff *FatFile) error {
|
|||||||
|
|
||||||
func NewSaveImportsAction(keepSymbols []string) *saveImports {
|
func NewSaveImportsAction(keepSymbols []string) *saveImports {
|
||||||
return &saveImports{
|
return &saveImports{
|
||||||
keepSymbols,
|
keepSymbols,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ func Cli() {
|
|||||||
pc.rpath_to_add = arg.Rpath
|
pc.rpath_to_add = arg.Rpath
|
||||||
pc.outfile = arg.Out
|
pc.outfile = arg.Out
|
||||||
pc.bcellfile = arg.Bcell
|
pc.bcellfile = arg.Bcell
|
||||||
pc.symbols_keep = arg.KeepImports
|
pc.symbols_keep = arg.KeepImports
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
|
@ -72,7 +72,7 @@ type PepeArgument struct {
|
|||||||
RebuildLinkEdit bool `default:"false" negatable:"" help:"(TODO) Rebuild the LINKEDIT section"`
|
RebuildLinkEdit bool `default:"false" negatable:"" help:"(TODO) Rebuild the LINKEDIT section"`
|
||||||
FullRemoval bool `default:"false" help:"Apply every removal possible"`
|
FullRemoval bool `default:"false" help:"Apply every removal possible"`
|
||||||
|
|
||||||
KeepImports []string `short:"keep" help:"Do not remove import symbols and allow dyld resolve. If symbols is found with more than 1 occurrances, specify the library with a comma or else all occurrances will be kept. e.g., _symbol,libLIB.dylib"`
|
KeepImports []string `short:"keep" help:"Do not remove import symbols and allow dyld resolve. If symbols is found with more than 1 occurrances, specify the library with a comma or else all occurrances will be kept. e.g., _symbol,libLIB.dylib"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BcellToHeaderArgument struct {
|
type BcellToHeaderArgument struct {
|
||||||
|
@ -46,7 +46,7 @@ type ProgramContext struct {
|
|||||||
remove_symbol_table bool
|
remove_symbol_table bool
|
||||||
dylib_to_add []string
|
dylib_to_add []string
|
||||||
rpath_to_add []string
|
rpath_to_add []string
|
||||||
symbols_keep []string
|
symbols_keep []string
|
||||||
|
|
||||||
outfile string
|
outfile string
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ func (pc *ProgramContext) Process(ofile OFile) {
|
|||||||
if pc.remove_imports {
|
if pc.remove_imports {
|
||||||
pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
|
pc.AddAction(NewSaveImportsAction(pc.symbols_keep))
|
||||||
pc.AddAction(NewRemoveImportsAction())
|
pc.AddAction(NewRemoveImportsAction())
|
||||||
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
pc.AddAction(NewRewriteImportsWithKeepSymbolsAction(pc.symbols_keep))
|
||||||
}
|
}
|
||||||
if pc.remove_symbol_table {
|
if pc.remove_symbol_table {
|
||||||
pc.AddAction(NewRemoveClassicSymbolAction())
|
pc.AddAction(NewRemoveClassicSymbolAction())
|
||||||
|
@ -25,7 +25,7 @@ type ImportSymbol struct {
|
|||||||
segment_offset uint32
|
segment_offset uint32
|
||||||
address uint64
|
address uint64
|
||||||
file_address uint64
|
file_address uint64
|
||||||
lib_ordinal uint32
|
lib_ordinal uint32
|
||||||
|
|
||||||
// push number
|
// push number
|
||||||
pnum uint32
|
pnum uint32
|
||||||
@ -121,7 +121,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
// fmt.Printf("segment=%x format=%x page_count=%d\n", fix.segment, fix.format, fix.page_count)
|
||||||
// fmt.Printf("pages=%x\n", fix.pages)
|
// fmt.Printf("pages=%x\n", fix.pages)
|
||||||
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
||||||
reader := bytes.NewReader(mc.buf)
|
reader := bytes.NewReader(mc.buf)
|
||||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
|||||||
sym.name = name
|
sym.name = name
|
||||||
sym.dylib = dylib
|
sym.dylib = dylib
|
||||||
sym.typ = "lazy"
|
sym.typ = "lazy"
|
||||||
sym.lib_ordinal = uint32(s.lib_ordinal)
|
sym.lib_ordinal = uint32(s.lib_ordinal)
|
||||||
|
|
||||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||||
sym.file_address = uint64(address)
|
sym.file_address = uint64(address)
|
||||||
|
@ -2,17 +2,17 @@ package macho
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"strings"
|
|
||||||
"encoding/binary"
|
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"ios-wrapper/pkg/protomodel"
|
|
||||||
. "ios-wrapper/pkg/ios"
|
. "ios-wrapper/pkg/ios"
|
||||||
|
"ios-wrapper/pkg/protomodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
// #include "fixups.h"
|
// #include "fixups.h"
|
||||||
@ -285,10 +285,10 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
|||||||
} else {
|
} else {
|
||||||
mc.removeBindSymbolsLegacy()
|
mc.removeBindSymbolsLegacy()
|
||||||
}
|
}
|
||||||
// Objective-C stub replaces main which can only appears in executable
|
// Objective-C stub replaces main which can only appears in executable
|
||||||
if mc.Header().IsExecutable() {
|
if mc.Header().IsExecutable() {
|
||||||
mc.ReworkForObjc()
|
mc.ReworkForObjc()
|
||||||
}
|
}
|
||||||
|
|
||||||
// due to some limitations when design this tool
|
// due to some limitations when design this tool
|
||||||
// we write the c code to stdout lol
|
// we write the c code to stdout lol
|
||||||
@ -445,27 +445,27 @@ func (mc *MachoContext) ReworkForObjc() {
|
|||||||
// arm also has different indexing instruction, so be careful
|
// arm also has different indexing instruction, so be careful
|
||||||
// https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing
|
// https://developer.arm.com/documentation/102374/0101/Loads-and-stores---addressing
|
||||||
/*
|
/*
|
||||||
adr x8, 0
|
adr x8, 0
|
||||||
# x9 = (offset end of __DATA) - (offset shellcode)
|
# x9 = (offset end of __DATA) - (offset shellcode)
|
||||||
movz x9, #0x9999
|
movz x9, #0x9999
|
||||||
add x8, x8, x9
|
add x8, x8, x9
|
||||||
|
|
||||||
stp x30, x8, [sp], #-0x10
|
stp x30, x8, [sp], #-0x10
|
||||||
stp x3, x2, [sp], #-0x10
|
stp x3, x2, [sp], #-0x10
|
||||||
stp x1, x0, [sp], #-0x10
|
stp x1, x0, [sp], #-0x10
|
||||||
|
|
||||||
# custom intializer
|
# custom intializer
|
||||||
ldr x9, [x8]
|
ldr x9, [x8]
|
||||||
blr x9
|
blr x9
|
||||||
|
|
||||||
ldp x1, x0, [sp, #0x10]!
|
ldp x1, x0, [sp, #0x10]!
|
||||||
ldp x3, x2, [sp, #0x10]!
|
ldp x3, x2, [sp, #0x10]!
|
||||||
ldp x30, x8, [sp, #0x10]!
|
ldp x30, x8, [sp, #0x10]!
|
||||||
|
|
||||||
# original main
|
# original main
|
||||||
# link register is set so jump only
|
# link register is set so jump only
|
||||||
ldr x9, [x8, #8]
|
ldr x9, [x8, #8]
|
||||||
br x9
|
br x9
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: fix to work with offset larger than 0xffff
|
// TODO: fix to work with offset larger than 0xffff
|
||||||
@ -529,72 +529,72 @@ func (mc *MachoContext) ReworkForObjc() {
|
|||||||
|
|
||||||
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
|
func (mc *MachoContext) RewriteImportsTable(keepSymbols []string) {
|
||||||
allSymbols := mc.CollectBindSymbols()
|
allSymbols := mc.CollectBindSymbols()
|
||||||
fixups, fixupsOffset := mc.Fixups()
|
fixups, fixupsOffset := mc.Fixups()
|
||||||
|
|
||||||
importTable := fixups.ImportsOffset(uint32(fixupsOffset))
|
importTable := fixups.ImportsOffset(uint32(fixupsOffset))
|
||||||
symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset))
|
symbolTable := fixups.SymbolsOffset(uint32(fixupsOffset))
|
||||||
symbolTablePtr := symbolTable
|
symbolTablePtr := symbolTable
|
||||||
|
|
||||||
// in removeBindSymbolsModern, we erase these pointers in file
|
// in removeBindSymbolsModern, we erase these pointers in file
|
||||||
// but because we keep a few symbols, we need to rewrite the pointers
|
// but because we keep a few symbols, we need to rewrite the pointers
|
||||||
// as well as rebuild the import table and strings table, and bind values
|
// as well as rebuild the import table and strings table, and bind values
|
||||||
|
|
||||||
keepCount := uint32(0)
|
keepCount := uint32(0)
|
||||||
for _, symbol := range keepSymbols {
|
for _, symbol := range keepSymbols {
|
||||||
name := symbol
|
name := symbol
|
||||||
lib := ""
|
lib := ""
|
||||||
parts := strings.Split(symbol, ",")
|
parts := strings.Split(symbol, ",")
|
||||||
if len(parts) == 2 {
|
if len(parts) == 2 {
|
||||||
name = parts[0]
|
name = parts[0]
|
||||||
lib = parts[1]
|
lib = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
symbolInfo := (*ImportSymbol)(nil)
|
symbolInfo := (*ImportSymbol)(nil)
|
||||||
for _, s := range allSymbols {
|
for _, s := range allSymbols {
|
||||||
if s.Name() != name {
|
if s.Name() != name {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if lib == "" || lib == s.Dylib() {
|
if lib == "" || lib == s.Dylib() {
|
||||||
symbolInfo = s
|
symbolInfo = s
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if symbolInfo == nil {
|
if symbolInfo == nil {
|
||||||
// symbol not found
|
// symbol not found
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("keep symbol %s\n", name)
|
fmt.Printf("keep symbol %s\n", name)
|
||||||
fmt.Printf("importTable at 0x%x; stringTable at 0x%x\n", importTable, symbolTablePtr)
|
fmt.Printf("importTable at 0x%x; stringTable at 0x%x\n", importTable, symbolTablePtr)
|
||||||
fmt.Printf("bind value at 0x%x\n", symbolInfo.file_address)
|
fmt.Printf("bind value at 0x%x\n", symbolInfo.file_address)
|
||||||
|
|
||||||
// write string to string table
|
// write string to string table
|
||||||
mc.file.WriteAt([]byte(name), int64(symbolTablePtr))
|
mc.file.WriteAt([]byte(name), int64(symbolTablePtr))
|
||||||
// fix bind value
|
// fix bind value
|
||||||
rebaseOpcodeBytes := make([]byte, 8)
|
rebaseOpcodeBytes := make([]byte, 8)
|
||||||
mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address))
|
mc.file.ReadAt(rebaseOpcodeBytes, int64(symbolInfo.file_address))
|
||||||
rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes)
|
rebaseOpcode := mc.byteorder.Uint64(rebaseOpcodeBytes)
|
||||||
bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount))
|
bindOpcode := C.MakeBindFixupOpcodeFromRebase(C.uint64_t(rebaseOpcode), C.uint(keepCount))
|
||||||
|
|
||||||
{
|
{
|
||||||
v := make([]byte, 8)
|
v := make([]byte, 8)
|
||||||
mc.byteorder.PutUint64(v, uint64(bindOpcode))
|
mc.byteorder.PutUint64(v, uint64(bindOpcode))
|
||||||
mc.file.WriteAt(v, int64(symbolInfo.file_address))
|
mc.file.WriteAt(v, int64(symbolInfo.file_address))
|
||||||
}
|
}
|
||||||
// write import data to import table
|
// write import data to import table
|
||||||
entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr - symbolTable))
|
entry := C.MakeImportTableEntry(C.uint(symbolInfo.LibOrdinal()), C.uint(symbolTablePtr-symbolTable))
|
||||||
{
|
{
|
||||||
v := make([]byte, 4)
|
v := make([]byte, 4)
|
||||||
mc.byteorder.PutUint32(v, uint32(entry))
|
mc.byteorder.PutUint32(v, uint32(entry))
|
||||||
mc.file.WriteAt(v, int64(importTable))
|
mc.file.WriteAt(v, int64(importTable))
|
||||||
}
|
}
|
||||||
|
|
||||||
keepCount += 1
|
keepCount += 1
|
||||||
importTable += 4
|
importTable += 4
|
||||||
symbolTablePtr += uint32(len(name)) + 1
|
symbolTablePtr += uint32(len(name)) + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
fixups.imports_count = keepCount
|
fixups.imports_count = keepCount
|
||||||
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
mc.file.WriteAt(fixups.Serialize(mc), int64(mc.fixups.dataoff))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,128 +685,128 @@ func (mc *MachoContext) RemoveExportTrie() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
||||||
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||||
var ret Section
|
var ret Section
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for _, command := range mc.commands {
|
for _, command := range mc.commands {
|
||||||
switch command.(type) {
|
switch command.(type) {
|
||||||
case *Segment64:
|
case *Segment64:
|
||||||
var virtualAddr uint64
|
var virtualAddr uint64
|
||||||
var fileOffset uint64
|
var fileOffset uint64
|
||||||
|
|
||||||
var segment = command.(*Segment64)
|
var segment = command.(*Segment64)
|
||||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 {
|
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte(segname)) != 0 {
|
||||||
buffer.Write(segment.Serialize(mc))
|
buffer.Write(segment.Serialize(mc))
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
virtualAddr = segment.Vmaddr()
|
virtualAddr = segment.Vmaddr()
|
||||||
fileOffset = segment.Fileoff()
|
fileOffset = segment.Fileoff()
|
||||||
for _, section := range segment.Sections() {
|
for _, section := range segment.Sections() {
|
||||||
virtualAddr += section.Size()
|
virtualAddr += section.Size()
|
||||||
// if section.Offset() != 0 {
|
// if section.Offset() != 0 {
|
||||||
fileOffset += section.Size()
|
fileOffset += section.Size()
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
align := uint64(4)
|
align := uint64(4)
|
||||||
alignment := align - (fileOffset % align)
|
alignment := align - (fileOffset % align)
|
||||||
fileOffset += alignment
|
fileOffset += alignment
|
||||||
virtualAddr += alignment
|
virtualAddr += alignment
|
||||||
|
|
||||||
enoughSpace := segment.Fileoff() + segment.Filesize() >= fileOffset + uint64(size)
|
enoughSpace := segment.Fileoff()+segment.Filesize() >= fileOffset+uint64(size)
|
||||||
if !enoughSpace {
|
if !enoughSpace {
|
||||||
fmt.Println("Not enough space to store saved info in __DATA segment, need resize (not supported now)")
|
fmt.Println("Not enough space to store saved info in __DATA segment, need resize (not supported now)")
|
||||||
panic("Not enough space to store saved info in __DATA segment, need resize (not supported now)")
|
panic("Not enough space to store saved info in __DATA segment, need resize (not supported now)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var section Section64
|
var section Section64
|
||||||
section.sectname = make([]byte, 16)
|
section.sectname = make([]byte, 16)
|
||||||
copy(section.sectname, name)
|
copy(section.sectname, name)
|
||||||
section.segname = make([]byte, 16)
|
section.segname = make([]byte, 16)
|
||||||
copy(section.segname, segname)
|
copy(section.segname, segname)
|
||||||
section.size = uint64(size)
|
section.size = uint64(size)
|
||||||
section.reloff = 0
|
section.reloff = 0
|
||||||
section.nreloc = 0
|
section.nreloc = 0
|
||||||
section.flags = 0
|
section.flags = 0
|
||||||
section.align = 3
|
section.align = 3
|
||||||
section.reserved1 = 0
|
section.reserved1 = 0
|
||||||
section.reserved2 = 0
|
section.reserved2 = 0
|
||||||
section.reserved3 = 0
|
section.reserved3 = 0
|
||||||
|
|
||||||
// addr will increment from the last section
|
// addr will increment from the last section
|
||||||
// offset will increment from the last section + size
|
// offset will increment from the last section + size
|
||||||
// be careful of Virtual section (bss)
|
// be careful of Virtual section (bss)
|
||||||
section.addr = virtualAddr
|
section.addr = virtualAddr
|
||||||
section.offset = uint32(fileOffset)
|
section.offset = uint32(fileOffset)
|
||||||
segment.nsects += 1
|
segment.nsects += 1
|
||||||
buffer.Write(segment.Serialize(mc))
|
buffer.Write(segment.Serialize(mc))
|
||||||
buffer.Write(section.Serialize(mc))
|
buffer.Write(section.Serialize(mc))
|
||||||
|
|
||||||
fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset)
|
fmt.Printf("Add a new section with addr=0x%x, fileoffset=0x%x\n", section.addr, section.offset)
|
||||||
|
|
||||||
ret = §ion
|
ret = §ion
|
||||||
continue
|
continue
|
||||||
|
|
||||||
default:
|
default:
|
||||||
buffer.Write(command.Serialize(mc))
|
buffer.Write(command.Serialize(mc))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mc.header.sizeofcmds = uint32(buffer.Len())
|
mc.header.sizeofcmds = uint32(buffer.Len())
|
||||||
header := mc.header.Serialize(mc)
|
header := mc.header.Serialize(mc)
|
||||||
mc.file.WriteAt(header, 0)
|
mc.file.WriteAt(header, 0)
|
||||||
mc.file.WriteAt(buffer.Bytes(), int64(len(header)))
|
mc.file.WriteAt(buffer.Bytes(), int64(len(header)))
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) {
|
func (mc *MachoContext) WriteInfoToData(info *protomodel.MachoInfo) {
|
||||||
encode := func () []byte {
|
encode := func() []byte {
|
||||||
buffer := new(bytes.Buffer)
|
buffer := new(bytes.Buffer)
|
||||||
for _, table := range info.Symbols.Tables {
|
for _, table := range info.Symbols.Tables {
|
||||||
binary.Write(buffer, mc.byteorder, table.LibIndex)
|
binary.Write(buffer, mc.byteorder, table.LibIndex)
|
||||||
binary.Write(buffer, mc.byteorder, table.Nsymbols)
|
binary.Write(buffer, mc.byteorder, table.Nsymbols)
|
||||||
for _, symbol := range table.Symbols {
|
for _, symbol := range table.Symbols {
|
||||||
binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex)
|
binary.Write(buffer, mc.byteorder, (symbol.SymbolIndex<<8)|symbol.SegmentIndex)
|
||||||
binary.Write(buffer, mc.byteorder, symbol.Offset)
|
binary.Write(buffer, mc.byteorder, symbol.Offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instructions := buffer.Bytes()
|
instructions := buffer.Bytes()
|
||||||
|
|
||||||
buffer = new(bytes.Buffer)
|
buffer = new(bytes.Buffer)
|
||||||
for _, lib := range info.Symbols.Libs {
|
for _, lib := range info.Symbols.Libs {
|
||||||
buffer.WriteString(lib)
|
buffer.WriteString(lib)
|
||||||
buffer.WriteByte(0)
|
buffer.WriteByte(0)
|
||||||
}
|
}
|
||||||
liblist := buffer.Bytes()
|
liblist := buffer.Bytes()
|
||||||
|
|
||||||
buffer = new(bytes.Buffer)
|
buffer = new(bytes.Buffer)
|
||||||
for _, symbol := range info.Symbols.Symbols {
|
for _, symbol := range info.Symbols.Symbols {
|
||||||
buffer.WriteString(symbol)
|
buffer.WriteString(symbol)
|
||||||
buffer.WriteByte(0)
|
buffer.WriteByte(0)
|
||||||
}
|
}
|
||||||
symbollist := buffer.Bytes()
|
symbollist := buffer.Bytes()
|
||||||
|
|
||||||
buffer = new(bytes.Buffer)
|
buffer = new(bytes.Buffer)
|
||||||
// offset to liblist
|
// offset to liblist
|
||||||
binary.Write(buffer, mc.byteorder, uint32(len(instructions)))
|
binary.Write(buffer, mc.byteorder, uint32(len(instructions)))
|
||||||
// offset to symbollist
|
// offset to symbollist
|
||||||
binary.Write(buffer, mc.byteorder, uint32(len(instructions) + len(liblist)))
|
binary.Write(buffer, mc.byteorder, uint32(len(instructions)+len(liblist)))
|
||||||
buffer.Write(instructions)
|
buffer.Write(instructions)
|
||||||
buffer.Write(liblist)
|
buffer.Write(liblist)
|
||||||
buffer.Write(symbollist)
|
buffer.Write(symbollist)
|
||||||
|
|
||||||
return buffer.Bytes()
|
return buffer.Bytes()
|
||||||
}
|
}
|
||||||
encoded := encode()
|
encoded := encode()
|
||||||
// encoded := []byte{0x11,0x22,0x33, 0x44}
|
// encoded := []byte{0x11,0x22,0x33, 0x44}
|
||||||
section := mc.AddSection("__DATA", "selfbind", len(encoded))
|
section := mc.AddSection("__DATA", "selfbind", len(encoded))
|
||||||
if mc.Is64bit() {
|
if mc.Is64bit() {
|
||||||
var s *Section64 = section.(*Section64)
|
var s *Section64 = section.(*Section64)
|
||||||
mc.file.WriteAt(encoded, int64(s.Offset()))
|
mc.file.WriteAt(encoded, int64(s.Offset()))
|
||||||
} else {
|
} else {
|
||||||
var s *Section32 = section.(*Section32)
|
var s *Section32 = section.(*Section32)
|
||||||
mc.file.WriteAt(encoded, int64(s.Offset()))
|
mc.file.WriteAt(encoded, int64(s.Offset()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,11 @@ func (h *Header) Cpusubtype() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) IsExecutable() bool {
|
func (h *Header) IsExecutable() bool {
|
||||||
return h.filetype == 0x2
|
return h.filetype == 0x2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) IsDylib() bool {
|
func (h *Header) IsDylib() bool {
|
||||||
return h.filetype == 0x6
|
return h.filetype == 0x6
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) Serialize(mc *MachoContext) []byte {
|
func (h *Header) Serialize(mc *MachoContext) []byte {
|
||||||
@ -135,7 +135,7 @@ func (lcmd *LoadCmd) Cmdname() string {
|
|||||||
return "LC_DATA_IN_CODE"
|
return "LC_DATA_IN_CODE"
|
||||||
case LC_SYMTAB:
|
case LC_SYMTAB:
|
||||||
return "LC_SYMTAB"
|
return "LC_SYMTAB"
|
||||||
case LC_BUILD_VERSION:
|
case LC_BUILD_VERSION:
|
||||||
return "LC_BUILD_VERSION"
|
return "LC_BUILD_VERSION"
|
||||||
case LC_UUID:
|
case LC_UUID:
|
||||||
return "LC_UUID"
|
return "LC_UUID"
|
||||||
@ -425,11 +425,11 @@ type Fixups struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 {
|
func (lcmd *Fixups) ImportsOffset(fixups_offset uint32) uint32 {
|
||||||
return lcmd.imports_offset + fixups_offset
|
return lcmd.imports_offset + fixups_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lcmd *Fixups) SymbolsOffset(fixups_offset uint32) uint32 {
|
func (lcmd *Fixups) SymbolsOffset(fixups_offset uint32) uint32 {
|
||||||
return lcmd.symbols_offset + fixups_offset
|
return lcmd.symbols_offset + fixups_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {
|
func (lcmd *Fixups) Serialize(mc *MachoContext) []byte {
|
||||||
|
@ -73,7 +73,7 @@ func (mc *MachoContext) Fixups() (*Fixups, uint64) {
|
|||||||
size := mc.fixups.datasize
|
size := mc.fixups.datasize
|
||||||
fixups := new(Fixups)
|
fixups := new(Fixups)
|
||||||
fixups.Deserialize(mc, mc.buf[start:start+size])
|
fixups.Deserialize(mc, mc.buf[start:start+size])
|
||||||
return fixups, uint64(mc.fixups.dataoff)
|
return fixups, uint64(mc.fixups.dataoff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *MachoContext) WriteEnabled() bool {
|
func (mc *MachoContext) WriteEnabled() bool {
|
||||||
|
@ -22,19 +22,17 @@ int custom_strcmp(const char *p1, const char *p2) {
|
|||||||
return c1 - c2;
|
return c1 - c2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int custom_strncmp(const char *s1, const char *s2, register size_t n)
|
int custom_strncmp(const char *s1, const char *s2, register size_t n) {
|
||||||
{
|
|
||||||
register unsigned char u1, u2;
|
register unsigned char u1, u2;
|
||||||
|
|
||||||
while (n-- > 0)
|
while (n-- > 0) {
|
||||||
{
|
u1 = (unsigned char)*s1++;
|
||||||
u1 = (unsigned char) *s1++;
|
u2 = (unsigned char)*s2++;
|
||||||
u2 = (unsigned char) *s2++;
|
if (u1 != u2)
|
||||||
if (u1 != u2)
|
return u1 - u2;
|
||||||
return u1 - u2;
|
if (u1 == '\0')
|
||||||
if (u1 == '\0')
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +62,7 @@ struct libcache_item {
|
|||||||
|
|
||||||
// pointer to segment address
|
// pointer to segment address
|
||||||
uint32_t nsegment;
|
uint32_t nsegment;
|
||||||
uint64_t* segment;
|
uint64_t *segment;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct libcache {
|
struct libcache {
|
||||||
@ -216,7 +214,7 @@ uint64_t get_slide(const void *header) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* get_selfbind(const void *header) {
|
void *get_selfbind(const void *header) {
|
||||||
const uint32_t magic = *(uint32_t *)header;
|
const uint32_t magic = *(uint32_t *)header;
|
||||||
char *ptr = (char *)header;
|
char *ptr = (char *)header;
|
||||||
if (magic == magic64) {
|
if (magic == magic64) {
|
||||||
@ -226,7 +224,7 @@ void* get_selfbind(const void *header) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||||
char* command_ptr = ptr;
|
char *command_ptr = ptr;
|
||||||
|
|
||||||
uint64_t slide;
|
uint64_t slide;
|
||||||
for (int i = 0; i < ncmds; i++) {
|
for (int i = 0; i < ncmds; i++) {
|
||||||
@ -240,17 +238,17 @@ void* get_selfbind(const void *header) {
|
|||||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||||
slide = (uint64_t)header - vmaddr;
|
slide = (uint64_t)header - vmaddr;
|
||||||
} else if (custom_strcmp(name, "__DATA") == 0) {
|
} else if (custom_strcmp(name, "__DATA") == 0) {
|
||||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||||
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
||||||
|
|
||||||
for (int sec = 0; sec < nsect; sec++) {
|
for (int sec = 0; sec < nsect; sec++) {
|
||||||
char* secname = sections_ptr;
|
char *secname = sections_ptr;
|
||||||
if (custom_strcmp(secname, "selfbind") == 0) {
|
if (custom_strcmp(secname, "selfbind") == 0) {
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
uint32_t *data_ptr = (uint32_t*)(addr + slide);
|
uint32_t *data_ptr = (uint32_t *)(addr + slide);
|
||||||
return (void*)data_ptr;
|
return (void *)data_ptr;
|
||||||
}
|
}
|
||||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||||
}
|
}
|
||||||
@ -302,15 +300,16 @@ void print_macho_summary(const void *header) {
|
|||||||
printf(" vmaddr=0x%llx fileoffset=0x%llx\n", vmaddr, fileoffset);
|
printf(" vmaddr=0x%llx fileoffset=0x%llx\n", vmaddr, fileoffset);
|
||||||
printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize);
|
printf(" vmsize=0x%llx filesize=0x%llx\n", vmsize, filesize);
|
||||||
|
|
||||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||||
for (int sec = 0; sec < nsect; sec++) {
|
for (int sec = 0; sec < nsect; sec++) {
|
||||||
char* secname = sections_ptr;
|
char *secname = sections_ptr;
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
uint32_t fileoffset = *((uint32_t*)sections_ptr + 6*2);
|
uint32_t fileoffset = *((uint32_t *)sections_ptr + 6 * 2);
|
||||||
printf(" Section %s\n", sections_ptr);
|
printf(" Section %s\n", sections_ptr);
|
||||||
printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size, fileoffset);
|
printf(" addr=0x%llx size=0x%llx fileoffset=0x%x\n", addr, size,
|
||||||
|
fileoffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd == LC_SYMTAB) {
|
if (cmd == LC_SYMTAB) {
|
||||||
@ -327,15 +326,18 @@ void print_macho_summary(const void *header) {
|
|||||||
uint64_t value;
|
uint64_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
uint64_t symtab_start =
|
||||||
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
(uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||||
|
uint64_t stroff_start =
|
||||||
|
(uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||||
|
|
||||||
printf(" symtab: offset=0x%x nsym=0x%x\n", symoff, nsym);
|
printf(" symtab: offset=0x%x nsym=0x%x\n", symoff, nsym);
|
||||||
for (int j = 0; j < nsym; j++) {
|
for (int j = 0; j < nsym; j++) {
|
||||||
struct symbol_t* symtab = (struct symbol_t*)symtab_start;
|
struct symbol_t *symtab = (struct symbol_t *)symtab_start;
|
||||||
struct symbol_t symbol = symtab[j];
|
struct symbol_t symbol = symtab[j];
|
||||||
char* name = (char*)stroff_start + symbol.strx;
|
char *name = (char *)stroff_start + symbol.strx;
|
||||||
printf(" %s %llx => %p\n", name, symbol.value, (void*)(symbol.value + slide));
|
printf(" %s %llx => %p\n", name, symbol.value,
|
||||||
|
(void *)(symbol.value + slide));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cmd == LC_REEXPORT_DYLIB) {
|
if (cmd == LC_REEXPORT_DYLIB) {
|
||||||
@ -495,7 +497,7 @@ void *find_in_reexport(struct libcache *cache, struct libcache_item *lib,
|
|||||||
char *name = (char *)ptr + name_offset;
|
char *name = (char *)ptr + name_offset;
|
||||||
uint32_t hash = calculate_libname_hash(cache, name);
|
uint32_t hash = calculate_libname_hash(cache, name);
|
||||||
for (int j = 0; j < cache->size; j++) {
|
for (int j = 0; j < cache->size; j++) {
|
||||||
struct libcache_item* reexport = &cache->libs[j];
|
struct libcache_item *reexport = &cache->libs[j];
|
||||||
if (reexport->hash != hash) {
|
if (reexport->hash != hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -520,7 +522,7 @@ void *find_in_lib(struct libcache *cache, struct libcache_item *lib,
|
|||||||
|
|
||||||
void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) {
|
void *custom_dlsym(struct libcache *cache, uint32_t hash, const char *symbol) {
|
||||||
for (size_t i = 0; i < cache->size; i++) {
|
for (size_t i = 0; i < cache->size; i++) {
|
||||||
struct libcache_item* cache_lib = &cache->libs[i];
|
struct libcache_item *cache_lib = &cache->libs[i];
|
||||||
if (cache_lib->hash == hash) {
|
if (cache_lib->hash == hash) {
|
||||||
return find_in_lib(cache, cache_lib, symbol);
|
return find_in_lib(cache, cache_lib, symbol);
|
||||||
}
|
}
|
||||||
@ -534,8 +536,9 @@ void *custom_dlsym(struct libcache *cache, const char *libname,
|
|||||||
return custom_dlsym(cache, hash, symbol);
|
return custom_dlsym(cache, hash, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bootstrap_libcache_item(struct libcache_item* item, const void* header, const char* name) {
|
void bootstrap_libcache_item(struct libcache_item *item, const void *header,
|
||||||
item->header = (void*)header;
|
const char *name) {
|
||||||
|
item->header = (void *)header;
|
||||||
item->trie = get_export_trie(header, item->trie_size);
|
item->trie = get_export_trie(header, item->trie_size);
|
||||||
|
|
||||||
const uint32_t magic = *(uint32_t *)header;
|
const uint32_t magic = *(uint32_t *)header;
|
||||||
@ -547,7 +550,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||||
char* command_ptr = ptr;
|
char *command_ptr = ptr;
|
||||||
|
|
||||||
for (int i = 0; i < ncmds; i++) {
|
for (int i = 0; i < ncmds; i++) {
|
||||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||||
@ -564,7 +567,7 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
|||||||
}
|
}
|
||||||
|
|
||||||
ptr = command_ptr;
|
ptr = command_ptr;
|
||||||
item->segment = (uint64_t*)malloc(sizeof(uint64_t) * item->nsegment);
|
item->segment = (uint64_t *)malloc(sizeof(uint64_t) * item->nsegment);
|
||||||
for (int i = 0, segment_i = 0; i < ncmds; i++) {
|
for (int i = 0, segment_i = 0; i < ncmds; i++) {
|
||||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||||
@ -577,8 +580,9 @@ void bootstrap_libcache_item(struct libcache_item* item, const void* header, con
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct libcache_item* get_libcache_with_name(struct libcache* cache, const char* name) {
|
struct libcache_item *get_libcache_with_name(struct libcache *cache,
|
||||||
void* to_find = 0;
|
const char *name) {
|
||||||
|
void *to_find = 0;
|
||||||
if (custom_strcmp(name, "main") == 0) {
|
if (custom_strcmp(name, "main") == 0) {
|
||||||
to_find = cache->main;
|
to_find = cache->main;
|
||||||
} else if (custom_strcmp(name, "thislib") == 0) {
|
} else if (custom_strcmp(name, "thislib") == 0) {
|
||||||
@ -586,7 +590,7 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char*
|
|||||||
}
|
}
|
||||||
uint32_t hash = calculate_libname_hash(cache, name);
|
uint32_t hash = calculate_libname_hash(cache, name);
|
||||||
for (int i = 0; i < cache->size; i++) {
|
for (int i = 0; i < cache->size; i++) {
|
||||||
struct libcache_item* cache_lib = &cache->libs[i];
|
struct libcache_item *cache_lib = &cache->libs[i];
|
||||||
// search by hash or by pointer for special case
|
// search by hash or by pointer for special case
|
||||||
if (cache_lib->hash == hash || cache_lib->header == to_find) {
|
if (cache_lib->hash == hash || cache_lib->header == to_find) {
|
||||||
return cache_lib;
|
return cache_lib;
|
||||||
@ -595,13 +599,14 @@ struct libcache_item* get_libcache_with_name(struct libcache* cache, const char*
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_export_trie(const void* trie, uint32_t size, const char* filename) {
|
void dump_export_trie(const void *trie, uint32_t size, const char *filename) {
|
||||||
FILE *outfile = fopen(filename, "wb");
|
FILE *outfile = fopen(filename, "wb");
|
||||||
fwrite((char*)trie, size, 1, outfile);
|
fwrite((char *)trie, size, 1, outfile);
|
||||||
fclose(outfile);
|
fclose(outfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_export_trie_of(const char* libname, const libcache* cache, const char* filename) {
|
void dump_export_trie_of(const char *libname, const libcache *cache,
|
||||||
|
const char *filename) {
|
||||||
uint32_t hash = calculate_libname_hash(cache, libname);
|
uint32_t hash = calculate_libname_hash(cache, libname);
|
||||||
for (int i = 0; i < cache->size; i++) {
|
for (int i = 0; i < cache->size; i++) {
|
||||||
struct libcache_item cache_lib = cache->libs[i];
|
struct libcache_item cache_lib = cache->libs[i];
|
||||||
@ -611,8 +616,8 @@ void dump_export_trie_of(const char* libname, const libcache* cache, const char*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* find_in_symtab(const libcache_item* lib, const char* find) {
|
void *find_in_symtab(const libcache_item *lib, const char *find) {
|
||||||
void* header = lib->header;
|
void *header = lib->header;
|
||||||
const uint32_t magic = *(uint32_t *)header;
|
const uint32_t magic = *(uint32_t *)header;
|
||||||
char *ptr = (char *)header;
|
char *ptr = (char *)header;
|
||||||
if (magic == magic64) {
|
if (magic == magic64) {
|
||||||
@ -622,7 +627,7 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||||
char* command_ptr = ptr;
|
char *command_ptr = ptr;
|
||||||
|
|
||||||
uint64_t linkedit_vmaddr;
|
uint64_t linkedit_vmaddr;
|
||||||
uint64_t linkedit_fileoffset;
|
uint64_t linkedit_fileoffset;
|
||||||
@ -644,15 +649,17 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
|||||||
uint64_t value;
|
uint64_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint64_t symtab_start = (uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
uint64_t symtab_start =
|
||||||
uint64_t stroff_start = (uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
(uint64_t)symoff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||||
|
uint64_t stroff_start =
|
||||||
|
(uint64_t)stroff - linkedit_fileoffset + slide + linkedit_vmaddr;
|
||||||
|
|
||||||
for (int j = 0; j < nsym; j++) {
|
for (int j = 0; j < nsym; j++) {
|
||||||
struct symbol_t* symtab = (struct symbol_t*)symtab_start;
|
struct symbol_t *symtab = (struct symbol_t *)symtab_start;
|
||||||
struct symbol_t symbol = symtab[j];
|
struct symbol_t symbol = symtab[j];
|
||||||
char* name = (char*)stroff_start + symbol.strx;
|
char *name = (char *)stroff_start + symbol.strx;
|
||||||
if (custom_strcmp(name, find) == 0) {
|
if (custom_strcmp(name, find) == 0) {
|
||||||
return (void*)(symbol.value + slide);
|
return (void *)(symbol.value + slide);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -673,7 +680,8 @@ void* find_in_symtab(const libcache_item* lib, const char* find) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* find_in_symtab(const char* libname, const libcache* cache, const char* find) {
|
void *find_in_symtab(const char *libname, const libcache *cache,
|
||||||
|
const char *find) {
|
||||||
uint32_t hash = calculate_libname_hash(cache, libname);
|
uint32_t hash = calculate_libname_hash(cache, libname);
|
||||||
struct libcache_item *cache_lib = 0;
|
struct libcache_item *cache_lib = 0;
|
||||||
for (int i = 0; i < cache->size; i++) {
|
for (int i = 0; i < cache->size; i++) {
|
||||||
@ -697,10 +705,10 @@ int hook_printf(const char *format, ...) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef void *(*readClass_t)(void *, bool, bool);
|
typedef void *(*readClass_t)(void *, bool, bool);
|
||||||
typedef void *(*realizeClassWithoutSwift_t)(void *, void*);
|
typedef void *(*realizeClassWithoutSwift_t)(void *, void *);
|
||||||
typedef void *(*remapClass_t)(void *);
|
typedef void *(*remapClass_t)(void *);
|
||||||
typedef void *(*load_method_t)(void*, void*);
|
typedef void *(*load_method_t)(void *, void *);
|
||||||
typedef void *(*sel_lookUpByName_t)(const char*);
|
typedef void *(*sel_lookUpByName_t)(const char *);
|
||||||
typedef void (*addClassTableEntry_t)(void *);
|
typedef void (*addClassTableEntry_t)(void *);
|
||||||
typedef void (*schedule_class_load_t)(void *);
|
typedef void (*schedule_class_load_t)(void *);
|
||||||
|
|
||||||
@ -709,24 +717,23 @@ typedef void (*objc_autoreleasePoolPop_t)(void *);
|
|||||||
|
|
||||||
struct custom_initializer_t {
|
struct custom_initializer_t {
|
||||||
// used for Objective-C load methods
|
// used for Objective-C load methods
|
||||||
uint64_t* loadable_classes;
|
uint64_t *loadable_classes;
|
||||||
uint32_t* loadable_classes_used;
|
uint32_t *loadable_classes_used;
|
||||||
sel_lookUpByName_t sel_lookUpByName;
|
sel_lookUpByName_t sel_lookUpByName;
|
||||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush;
|
objc_autoreleasePoolPush_t objc_autoreleasePoolPush;
|
||||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop;
|
objc_autoreleasePoolPop_t objc_autoreleasePoolPop;
|
||||||
remapClass_t remapClass;
|
remapClass_t remapClass;
|
||||||
schedule_class_load_t schedule_class_load;
|
schedule_class_load_t schedule_class_load;
|
||||||
uint64_t* cls;
|
uint64_t *cls;
|
||||||
size_t ncls;
|
size_t ncls;
|
||||||
// used for constructors
|
// used for constructors
|
||||||
void* programvars;
|
void *programvars;
|
||||||
uint64_t* constructors;
|
uint64_t *constructors;
|
||||||
size_t nconstructors;
|
size_t nconstructors;
|
||||||
};
|
};
|
||||||
|
|
||||||
// global variable for PoC
|
// global variable for PoC
|
||||||
struct custom_initializer_t* custom_initializer_i;
|
struct custom_initializer_t *custom_initializer_i;
|
||||||
|
|
||||||
|
|
||||||
struct ProgramVars {
|
struct ProgramVars {
|
||||||
void *mh; // mach_header or mach_header64
|
void *mh; // mach_header or mach_header64
|
||||||
@ -736,10 +743,10 @@ struct ProgramVars {
|
|||||||
const char **__prognamePtr;
|
const char **__prognamePtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void build_cache(struct libcache& cache, void* main);
|
void build_cache(struct libcache &cache, void *main);
|
||||||
void fix(struct libcache& cache);
|
void fix(struct libcache &cache);
|
||||||
|
|
||||||
void test(struct libcache& cache);
|
void test(struct libcache &cache);
|
||||||
|
|
||||||
__attribute__((constructor)) static void
|
__attribute__((constructor)) static void
|
||||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||||
@ -760,8 +767,8 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
|||||||
// "/usr/lib/libobjc.A.dylib", &cache,
|
// "/usr/lib/libobjc.A.dylib", &cache,
|
||||||
// "../scripts/lib_objc_symtab.bin");
|
// "../scripts/lib_objc_symtab.bin");
|
||||||
|
|
||||||
// struct libcache_item* objc = get_libcache_with_name(&cache, "/usr/lib/libobjc.A.dylib");
|
// struct libcache_item* objc = get_libcache_with_name(&cache,
|
||||||
// print_macho_summary(objc->header);
|
// "/usr/lib/libobjc.A.dylib"); print_macho_summary(objc->header);
|
||||||
|
|
||||||
// test(cache);
|
// test(cache);
|
||||||
|
|
||||||
@ -773,8 +780,9 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
|||||||
// (we do not remove them for **our lib**)
|
// (we do not remove them for **our lib**)
|
||||||
// - malloc
|
// - malloc
|
||||||
// - free
|
// - free
|
||||||
custom_initializer_i = (custom_initializer_t*)malloc(sizeof(custom_initializer_t));
|
custom_initializer_i =
|
||||||
custom_initializer_i->programvars = (void*)vars;
|
(custom_initializer_t *)malloc(sizeof(custom_initializer_t));
|
||||||
|
custom_initializer_i->programvars = (void *)vars;
|
||||||
custom_initializer_i->cls = 0;
|
custom_initializer_i->cls = 0;
|
||||||
custom_initializer_i->constructors = 0;
|
custom_initializer_i->constructors = 0;
|
||||||
fix(cache);
|
fix(cache);
|
||||||
@ -786,16 +794,16 @@ bruh(int argc, const char *const argv[], const char *const envp[],
|
|||||||
printf("=== manual symbol bind completes ===\n");
|
printf("=== manual symbol bind completes ===\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void build_cache(struct libcache& cache, void* main) {
|
void build_cache(struct libcache &cache, void *main) {
|
||||||
const uint64_t main_slide = get_slide(main);
|
const uint64_t main_slide = get_slide(main);
|
||||||
// Find our lib (mapped) file
|
// Find our lib (mapped) file
|
||||||
const void *thislib = find_header((void *)bruh);
|
const void *thislib = find_header((void *)bruh);
|
||||||
// Find dyld lib (mapped) file using a no-sus function
|
// Find dyld lib (mapped) file using a no-sus function
|
||||||
const void *libdyld = find_header((void *)dyld_get_sdk_version);
|
const void *libdyld = find_header((void *)dyld_get_sdk_version);
|
||||||
|
|
||||||
cache.main = (void*)main;
|
cache.main = (void *)main;
|
||||||
cache.thislib = (void*)thislib;
|
cache.thislib = (void *)thislib;
|
||||||
cache.libdyld = (void*)libdyld;
|
cache.libdyld = (void *)libdyld;
|
||||||
uint32_t libsystem_hash =
|
uint32_t libsystem_hash =
|
||||||
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
||||||
|
|
||||||
@ -855,8 +863,8 @@ void build_cache(struct libcache& cache, void* main) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
void fix_binds(struct libcache_item *libfixing, struct libcache *cache,
|
||||||
int n_ins, uint32_t* instructions, char* libs, char* symbols) {
|
int n_ins, uint32_t *instructions, char *libs, char *symbols) {
|
||||||
uint32_t libsystem_hash =
|
uint32_t libsystem_hash =
|
||||||
calculate_libname_hash(cache, "/usr/lib/libSystem.B.dylib");
|
calculate_libname_hash(cache, "/usr/lib/libSystem.B.dylib");
|
||||||
|
|
||||||
@ -871,12 +879,12 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
|||||||
uint64_t page_rw_fixed[10]; // should be dynamic, but works for now
|
uint64_t page_rw_fixed[10]; // should be dynamic, but works for now
|
||||||
|
|
||||||
int pc = 0;
|
int pc = 0;
|
||||||
for (;pc != n_ins;) {
|
for (; pc != n_ins;) {
|
||||||
uint32_t libidx = instructions[pc];
|
uint32_t libidx = instructions[pc];
|
||||||
uint32_t nsym = instructions[pc + 1];
|
uint32_t nsym = instructions[pc + 1];
|
||||||
pc += 2;
|
pc += 2;
|
||||||
|
|
||||||
char* lib = libs + libidx;
|
char *lib = libs + libidx;
|
||||||
for (int i = 0; i < nsym; i++) {
|
for (int i = 0; i < nsym; i++) {
|
||||||
uint32_t op = instructions[pc];
|
uint32_t op = instructions[pc];
|
||||||
uint32_t offset = instructions[pc + 1];
|
uint32_t offset = instructions[pc + 1];
|
||||||
@ -884,22 +892,21 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
|||||||
|
|
||||||
uint32_t symidx = op >> 8;
|
uint32_t symidx = op >> 8;
|
||||||
uint32_t segment = op & 0xff;
|
uint32_t segment = op & 0xff;
|
||||||
char* sym = symbols + symidx;
|
char *sym = symbols + symidx;
|
||||||
|
|
||||||
uint64_t fix_at = offset + libfixing->segment[segment];
|
uint64_t fix_at = offset + libfixing->segment[segment];
|
||||||
|
|
||||||
// enable WRITE protection for this data segment
|
// enable WRITE protection for this data segment
|
||||||
int need_rw_fix = true;
|
int need_rw_fix = true;
|
||||||
for (int j = 0; j < npage_rw_fixed; j++) {
|
for (int j = 0; j < npage_rw_fixed; j++) {
|
||||||
if (page_rw_fixed[j] <= fix_at &&
|
if (page_rw_fixed[j] <= fix_at && page_rw_fixed[j] + 0x1000 > fix_at) {
|
||||||
page_rw_fixed[j] + 0x1000 > fix_at) {
|
|
||||||
need_rw_fix = false;
|
need_rw_fix = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (need_rw_fix) {
|
if (need_rw_fix) {
|
||||||
uint64_t start_page = fix_at - (fix_at % 0x1000);
|
uint64_t start_page = fix_at - (fix_at % 0x1000);
|
||||||
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0,
|
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0,
|
||||||
VM_PROT_READ | VM_PROT_WRITE);
|
VM_PROT_READ | VM_PROT_WRITE);
|
||||||
page_rw_fixed[npage_rw_fixed++] = start_page;
|
page_rw_fixed[npage_rw_fixed++] = start_page;
|
||||||
printf("modify page starts at 0x%llx to RW\n", start_page);
|
printf("modify page starts at 0x%llx to RW\n", start_page);
|
||||||
}
|
}
|
||||||
@ -916,15 +923,14 @@ void fix_binds(struct libcache_item* libfixing, struct libcache* cache,
|
|||||||
printf("imports need to fix: %s at 0x%llx\n", sym, fix_at);
|
printf("imports need to fix: %s at 0x%llx\n", sym, fix_at);
|
||||||
printf(" from=%s\n", lib);
|
printf(" from=%s\n", lib);
|
||||||
printf(" segment id=%d; offset=0x%x;", segment, offset);
|
printf(" segment id=%d; offset=0x%x;", segment, offset);
|
||||||
printf(" resolved=%llx(%p)\n", *(uint64_t*)fix_at, resolved);
|
printf(" resolved=%llx(%p)\n", *(uint64_t *)fix_at, resolved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_objc(struct libcache_item* libfixing, struct libcache& cache);
|
void fix_objc(struct libcache_item *libfixing, struct libcache &cache);
|
||||||
void fix_initializer(struct libcache_item* libfixing, struct libcache& cache);
|
void fix_initializer(struct libcache_item *libfixing, struct libcache &cache);
|
||||||
void fix(struct libcache& cache) {
|
void fix(struct libcache &cache) {
|
||||||
// now we have function to find exported symbols
|
// now we have function to find exported symbols
|
||||||
// it supports full name search or hash search
|
// it supports full name search or hash search
|
||||||
// to reserve space, we use the hash search
|
// to reserve space, we use the hash search
|
||||||
@ -960,20 +966,23 @@ void fix(struct libcache& cache) {
|
|||||||
// OBJC:
|
// OBJC:
|
||||||
// In Objective-C, the binary is loaded with the Objective-C runtime
|
// In Objective-C, the binary is loaded with the Objective-C runtime
|
||||||
// This runtime (a library) install a hook on dyld for all images
|
// This runtime (a library) install a hook on dyld for all images
|
||||||
// And because this runtime is a system runtime, the bootstrap step is already prepared
|
// And because this runtime is a system runtime, the bootstrap step is already
|
||||||
// The details on this runtime will be in a seperated document, below are some basics
|
// prepared The details on this runtime will be in a seperated document, below
|
||||||
|
// are some basics
|
||||||
//
|
//
|
||||||
// The compiler for Objective-C emits a bunch of details for the runtime in the binary itself
|
// The compiler for Objective-C emits a bunch of details for the runtime in
|
||||||
// These information are stored in sections with prefix name __objc, namely
|
// the binary itself These information are stored in sections with prefix name
|
||||||
|
// __objc, namely
|
||||||
// - __objc_classlist
|
// - __objc_classlist
|
||||||
// - __objc_clssrefs
|
// - __objc_clssrefs
|
||||||
// - __objc_selref
|
// - __objc_selref
|
||||||
// - __objc_const
|
// - __objc_const
|
||||||
// - __objc_data
|
// - __objc_data
|
||||||
//
|
//
|
||||||
// Objective-C stores the class interface in the binary particulary in __objc_data
|
// Objective-C stores the class interface in the binary particulary in
|
||||||
// This interface contains the superclass, metaclass, and a cache to methods pointers
|
// __objc_data This interface contains the superclass, metaclass, and a cache
|
||||||
// These information are either bound (by dyld) or built (by Objective-C runtime)
|
// to methods pointers These information are either bound (by dyld) or built
|
||||||
|
// (by Objective-C runtime)
|
||||||
//
|
//
|
||||||
// One of the important routine in the Objective-C runtime is readClass.
|
// One of the important routine in the Objective-C runtime is readClass.
|
||||||
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3385
|
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3385
|
||||||
@ -983,18 +992,21 @@ void fix(struct libcache& cache) {
|
|||||||
//
|
//
|
||||||
// Because __objc_data contains to-be-bound values,
|
// Because __objc_data contains to-be-bound values,
|
||||||
// which will be resolved by dyld and referenced by Objective-C runtime later
|
// which will be resolved by dyld and referenced by Objective-C runtime later
|
||||||
// if we simply erase this value, reference(s) read by Objective-C runtime ensues a crash
|
// if we simply erase this value, reference(s) read by Objective-C runtime
|
||||||
// (through debugging, we know that the crash happens in readClass, realizeClassWithoutSwift)
|
// ensues a crash (through debugging, we know that the crash happens in
|
||||||
|
// readClass, realizeClassWithoutSwift)
|
||||||
//
|
//
|
||||||
// However, we can evade this by making the runtime thinks there is no class needs setup
|
// However, we can evade this by making the runtime thinks there is no class
|
||||||
// This can be done by changing the __objc_classlist to some other name or remove this section
|
// needs setup This can be done by changing the __objc_classlist to some other
|
||||||
// Because the runtime find the __objc_classlist section by name, and the size of the section
|
// name or remove this section Because the runtime find the __objc_classlist
|
||||||
// is used to iterate through pointers.
|
// section by name, and the size of the section is used to iterate through
|
||||||
// So if we change the name, the runtime will have no class to run setup.
|
// pointers. So if we change the name, the runtime will have no class to run
|
||||||
// Or complete removal and call the setup by ourselves, because we know where the data is
|
// setup. Or complete removal and call the setup by ourselves, because we know
|
||||||
|
// where the data is
|
||||||
//
|
//
|
||||||
// The setup is done through readClass function, as said above, its address can be found
|
// The setup is done through readClass function, as said above, its address
|
||||||
// This function is pure C function so call into this function is easy
|
// can be found This function is pure C function so call into this function is
|
||||||
|
// easy
|
||||||
//
|
//
|
||||||
// Important function with their names:
|
// Important function with their names:
|
||||||
// _readClass(objc_class*, bool, bool)
|
// _readClass(objc_class*, bool, bool)
|
||||||
@ -1022,37 +1034,38 @@ void fix(struct libcache& cache) {
|
|||||||
|
|
||||||
// resolve selfbind if exist
|
// resolve selfbind if exist
|
||||||
{ // stored inside __DATA,selfbind
|
{ // stored inside __DATA,selfbind
|
||||||
struct libcache_item* libfixing = get_libcache_with_name(&cache, "thislib");
|
struct libcache_item *libfixing = get_libcache_with_name(&cache, "thislib");
|
||||||
struct selfbind_t {
|
struct selfbind_t {
|
||||||
uint32_t liblist_offset;
|
uint32_t liblist_offset;
|
||||||
uint32_t symbollist_offset;
|
uint32_t symbollist_offset;
|
||||||
};
|
};
|
||||||
struct selfbind_t* selfbind = (struct selfbind_t*)get_selfbind(libfixing->header);
|
struct selfbind_t *selfbind =
|
||||||
|
(struct selfbind_t *)get_selfbind(libfixing->header);
|
||||||
|
|
||||||
if (selfbind) {
|
if (selfbind) {
|
||||||
char* libs = (char*)(selfbind+1) + selfbind->liblist_offset;
|
char *libs = (char *)(selfbind + 1) + selfbind->liblist_offset;
|
||||||
char* symbols = (char*)(selfbind+1) + selfbind->symbollist_offset;
|
char *symbols = (char *)(selfbind + 1) + selfbind->symbollist_offset;
|
||||||
uint64_t n_instructions = ((uint64_t)libs - (uint64_t)(selfbind + 1)) / 4;
|
uint64_t n_instructions = ((uint64_t)libs - (uint64_t)(selfbind + 1)) / 4;
|
||||||
uint32_t* encoded_table = (uint32_t*)(selfbind+1);
|
uint32_t *encoded_table = (uint32_t *)(selfbind + 1);
|
||||||
|
|
||||||
printf("[*] performing selfbind (instructions=%p)\n", selfbind);
|
printf("[*] performing selfbind (instructions=%p)\n", selfbind);
|
||||||
fix_binds(libfixing, &cache,
|
fix_binds(libfixing, &cache, n_instructions, encoded_table, libs,
|
||||||
n_instructions, encoded_table,
|
symbols);
|
||||||
libs, symbols);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the rest of the fixes are in main executable
|
// the rest of the fixes are in main executable
|
||||||
printf("[*] performing bind for main executable\n");
|
printf("[*] performing bind for main executable\n");
|
||||||
struct libcache_item* libfixing = get_libcache_with_name(&cache, "main");
|
struct libcache_item *libfixing = get_libcache_with_name(&cache, "main");
|
||||||
fix_binds(libfixing, &cache,
|
fix_binds(libfixing, &cache, bshield_data::n_instructions,
|
||||||
bshield_data::n_instructions, bshield_data::encoded_table,
|
bshield_data::encoded_table, bshield_data::libs,
|
||||||
bshield_data::libs, bshield_data::symbols);
|
bshield_data::symbols);
|
||||||
|
|
||||||
// TODO: Reformat the region as per before, or leave as it
|
// TODO: Reformat the region as per before, or leave as it
|
||||||
// for (int j = 0; j < npage_rw_fixed; j++) {
|
// for (int j = 0; j < npage_rw_fixed; j++) {
|
||||||
// uint64_t start_page = page_rw_fixed[j];
|
// uint64_t start_page = page_rw_fixed[j];
|
||||||
// vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0, VM_PROT_READ);
|
// vm_protect_func(mach_task_self_func(), start_page, 0x4000, 0,
|
||||||
|
// VM_PROT_READ);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Encrypted __TEXT segment
|
// Encrypted __TEXT segment
|
||||||
@ -1070,61 +1083,74 @@ void fix(struct libcache& cache) {
|
|||||||
fix_initializer(libfixing, cache);
|
fix_initializer(libfixing, cache);
|
||||||
}
|
}
|
||||||
|
|
||||||
void volatile
|
void volatile custom_initializer(int argc, const char *const argv[],
|
||||||
custom_initializer(int argc, const char *const argv[], const char *const envp[], const char *const apple[]) {
|
const char *const envp[],
|
||||||
|
const char *const apple[]) {
|
||||||
printf("run custom initializers %p\n", custom_initializer_i);
|
printf("run custom initializers %p\n", custom_initializer_i);
|
||||||
|
|
||||||
if (custom_initializer_i->cls != 0) {
|
if (custom_initializer_i->cls != 0) {
|
||||||
// for Objective-C load
|
// for Objective-C load
|
||||||
uint64_t* loadable_classes = custom_initializer_i->loadable_classes;
|
uint64_t *loadable_classes = custom_initializer_i->loadable_classes;
|
||||||
uint32_t* loadable_classes_used = custom_initializer_i->loadable_classes_used;
|
uint32_t *loadable_classes_used =
|
||||||
sel_lookUpByName_t sel_lookUpByName = custom_initializer_i->sel_lookUpByName;
|
custom_initializer_i->loadable_classes_used;
|
||||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = custom_initializer_i->objc_autoreleasePoolPop;
|
sel_lookUpByName_t sel_lookUpByName =
|
||||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = custom_initializer_i->objc_autoreleasePoolPush;
|
custom_initializer_i->sel_lookUpByName;
|
||||||
|
objc_autoreleasePoolPop_t objc_autoreleasePoolPop =
|
||||||
|
custom_initializer_i->objc_autoreleasePoolPop;
|
||||||
|
objc_autoreleasePoolPush_t objc_autoreleasePoolPush =
|
||||||
|
custom_initializer_i->objc_autoreleasePoolPush;
|
||||||
remapClass_t remapClass = custom_initializer_i->remapClass;
|
remapClass_t remapClass = custom_initializer_i->remapClass;
|
||||||
schedule_class_load_t schedule_class_load = custom_initializer_i->schedule_class_load;
|
schedule_class_load_t schedule_class_load =
|
||||||
|
custom_initializer_i->schedule_class_load;
|
||||||
|
|
||||||
for (int i = 0; i < custom_initializer_i->ncls; i++) {
|
for (int i = 0; i < custom_initializer_i->ncls; i++) {
|
||||||
void* cls0 = (void*)custom_initializer_i->cls[i];
|
void *cls0 = (void *)custom_initializer_i->cls[i];
|
||||||
void* cls = remapClass(cls0);
|
void *cls = remapClass(cls0);
|
||||||
if (!cls) continue;
|
if (!cls)
|
||||||
|
continue;
|
||||||
schedule_class_load(cls);
|
schedule_class_load(cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("loadable_classes %llx %x\n", *loadable_classes, *loadable_classes_used);
|
printf("loadable_classes %llx %x\n", *loadable_classes,
|
||||||
|
*loadable_classes_used);
|
||||||
|
|
||||||
struct loadable_class_t {
|
struct loadable_class_t {
|
||||||
void* cls;
|
void *cls;
|
||||||
void* method;
|
void *method;
|
||||||
};
|
};
|
||||||
struct loadable_class_t *classes = (struct loadable_class_t*)*loadable_classes;
|
struct loadable_class_t *classes =
|
||||||
|
(struct loadable_class_t *)*loadable_classes;
|
||||||
int used = *loadable_classes_used;
|
int used = *loadable_classes_used;
|
||||||
*loadable_classes = 0;
|
*loadable_classes = 0;
|
||||||
// *loadable_classes_allocated = 0;
|
// *loadable_classes_allocated = 0;
|
||||||
*loadable_classes_used = 0;
|
*loadable_classes_used = 0;
|
||||||
void* sel = sel_lookUpByName("load");
|
void *sel = sel_lookUpByName("load");
|
||||||
// Call all +loads for the detached list.
|
// Call all +loads for the detached list.
|
||||||
void* pool = objc_autoreleasePoolPush();
|
void *pool = objc_autoreleasePoolPush();
|
||||||
for (int i = 0; i < used; i++) {
|
for (int i = 0; i < used; i++) {
|
||||||
void* cls = classes[i].cls;
|
void *cls = classes[i].cls;
|
||||||
load_method_t load_method = (load_method_t)classes[i].method;
|
load_method_t load_method = (load_method_t)classes[i].method;
|
||||||
printf("call load of class %p %p\n", cls, load_method);
|
printf("call load of class %p %p\n", cls, load_method);
|
||||||
if (!cls) continue;
|
if (!cls)
|
||||||
|
continue;
|
||||||
(load_method)(cls, sel);
|
(load_method)(cls, sel);
|
||||||
}
|
}
|
||||||
// Destroy the detached list.
|
// Destroy the detached list.
|
||||||
if (classes) free(classes);
|
if (classes)
|
||||||
|
free(classes);
|
||||||
objc_autoreleasePoolPop(pool);
|
objc_autoreleasePoolPop(pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
// for constructors
|
// for constructors
|
||||||
if (custom_initializer_i->constructors) {
|
if (custom_initializer_i->constructors) {
|
||||||
typedef void *(*constructors_t)(int, void*, void*, void*, void*);
|
typedef void *(*constructors_t)(int, void *, void *, void *, void *);
|
||||||
uint32_t nconst = custom_initializer_i->nconstructors;
|
uint32_t nconst = custom_initializer_i->nconstructors;
|
||||||
for (int i = 0; i < nconst; i++) {
|
for (int i = 0; i < nconst; i++) {
|
||||||
constructors_t cons = (constructors_t)custom_initializer_i->constructors[i];
|
constructors_t cons =
|
||||||
|
(constructors_t)custom_initializer_i->constructors[i];
|
||||||
printf("call initializer at %p\n", cons);
|
printf("call initializer at %p\n", cons);
|
||||||
cons(argc, (void*)argv, (void*)envp, (void*)apple, custom_initializer_i->programvars);
|
cons(argc, (void *)argv, (void *)envp, (void *)apple,
|
||||||
|
custom_initializer_i->programvars);
|
||||||
}
|
}
|
||||||
free(custom_initializer_i->constructors);
|
free(custom_initializer_i->constructors);
|
||||||
}
|
}
|
||||||
@ -1132,7 +1158,7 @@ custom_initializer(int argc, const char *const argv[], const char *const envp[],
|
|||||||
free(custom_initializer_i);
|
free(custom_initializer_i);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
|
||||||
// Manually run the Objective-C runtime for each class
|
// Manually run the Objective-C runtime for each class
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -1146,7 +1172,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
// "mov rcx, 123;"
|
// "mov rcx, 123;"
|
||||||
// "call r12;");
|
// "call r12;");
|
||||||
|
|
||||||
void* header = libfixing->header;
|
void *header = libfixing->header;
|
||||||
const uint32_t magic = *(uint32_t *)header;
|
const uint32_t magic = *(uint32_t *)header;
|
||||||
char *ptr = (char *)header;
|
char *ptr = (char *)header;
|
||||||
if (magic == magic64) {
|
if (magic == magic64) {
|
||||||
@ -1156,7 +1182,7 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||||
char* command_ptr = ptr;
|
char *command_ptr = ptr;
|
||||||
|
|
||||||
uint64_t linkedit_vmaddr;
|
uint64_t linkedit_vmaddr;
|
||||||
uint64_t linkedit_fileoffset;
|
uint64_t linkedit_fileoffset;
|
||||||
@ -1173,62 +1199,80 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||||
slide = (uint64_t)header - vmaddr;
|
slide = (uint64_t)header - vmaddr;
|
||||||
} else if (custom_strcmp(name, "__DATA_CONST") == 0) {
|
} else if (custom_strcmp(name, "__DATA_CONST") == 0) {
|
||||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||||
for (int sec = 0; sec < nsect; sec++) {
|
for (int sec = 0; sec < nsect; sec++) {
|
||||||
char* secname = sections_ptr;
|
char *secname = sections_ptr;
|
||||||
printf("section %s\n", secname);
|
printf("section %s\n", secname);
|
||||||
if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) {
|
if (custom_strncmp(secname, "__objc_classbruh", 16) == 0) {
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
uint64_t *data_ptr = (uint64_t*)(addr + slide);
|
uint64_t *data_ptr = (uint64_t *)(addr + slide);
|
||||||
|
|
||||||
readClass_t readClass = (readClass_t)find_in_symtab(
|
readClass_t readClass =
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL9readClassP10objc_classbb");
|
(readClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||||
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab(
|
"__ZL9readClassP10objc_classbb");
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
realizeClassWithoutSwift_t realizeClassWithoutSwift =
|
||||||
|
(realizeClassWithoutSwift_t)find_in_symtab(
|
||||||
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
|
"__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||||
|
|
||||||
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
||||||
// this pointer is rebased by dyld and points to the correct class interface
|
// this pointer is rebased by dyld and points to the correct class
|
||||||
// for some reason, we can skip this and it should still work
|
// interface for some reason, we can skip this and it should still
|
||||||
void* newCls = readClass((void*)data_ptr[ptr_i], false, false);
|
// work
|
||||||
if (newCls != (void*)data_ptr[ptr_i]) {
|
void *newCls = readClass((void *)data_ptr[ptr_i], false, false);
|
||||||
|
if (newCls != (void *)data_ptr[ptr_i]) {
|
||||||
realizeClassWithoutSwift(newCls, 0);
|
realizeClassWithoutSwift(newCls, 0);
|
||||||
}
|
}
|
||||||
printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls);
|
printf("add class init (%llx)%p\n", data_ptr[ptr_i], newCls);
|
||||||
}
|
}
|
||||||
}
|
} else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) {
|
||||||
else if (custom_strncmp(secname, "__objc_nlclsbruh", 16) == 0) {
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t *data_ptr = (uint64_t *)(addr + slide);
|
||||||
uint64_t *data_ptr = (uint64_t*)(addr + slide);
|
|
||||||
|
|
||||||
uint64_t* loadable_classes = (uint64_t*)find_in_symtab(
|
uint64_t *loadable_classes = (uint64_t *)find_in_symtab(
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes");
|
"/usr/lib/libobjc.A.dylib", &cache, "__ZL16loadable_classes");
|
||||||
uint32_t* loadable_classes_allocated = (uint32_t*)find_in_symtab(
|
uint32_t *loadable_classes_allocated =
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL26loadable_classes_allocated");
|
(uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||||
uint32_t* loadable_classes_used = (uint32_t*)find_in_symtab(
|
"__ZL26loadable_classes_allocated");
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL21loadable_classes_used");
|
uint32_t *loadable_classes_used =
|
||||||
|
(uint32_t *)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||||
|
"__ZL21loadable_classes_used");
|
||||||
|
|
||||||
remapClass_t remapClass = (remapClass_t)find_in_symtab(
|
remapClass_t remapClass =
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL10remapClassP10objc_class");
|
(remapClass_t)find_in_symtab("/usr/lib/libobjc.A.dylib", &cache,
|
||||||
schedule_class_load_t schedule_class_load = (schedule_class_load_t)find_in_symtab(
|
"__ZL10remapClassP10objc_class");
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL19schedule_class_loadP10objc_class");
|
schedule_class_load_t schedule_class_load =
|
||||||
realizeClassWithoutSwift_t realizeClassWithoutSwift = (realizeClassWithoutSwift_t)find_in_symtab(
|
(schedule_class_load_t)find_in_symtab(
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
addClassTableEntry_t addClassTableEntry = (addClassTableEntry_t)find_in_symtab(
|
"__ZL19schedule_class_loadP10objc_class");
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__ZL18addClassTableEntryP10objc_classb");
|
realizeClassWithoutSwift_t realizeClassWithoutSwift =
|
||||||
sel_lookUpByName_t sel_lookUpByName = (sel_lookUpByName_t)find_in_symtab(
|
(realizeClassWithoutSwift_t)find_in_symtab(
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "_sel_lookUpByName");
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
objc_autoreleasePoolPush_t objc_autoreleasePoolPush = (objc_autoreleasePoolPush_t)find_in_symtab(
|
"__ZL24realizeClassWithoutSwiftP10objc_classS0_");
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPush");
|
addClassTableEntry_t addClassTableEntry =
|
||||||
objc_autoreleasePoolPop_t objc_autoreleasePoolPop = (objc_autoreleasePoolPop_t)find_in_symtab(
|
(addClassTableEntry_t)find_in_symtab(
|
||||||
"/usr/lib/libobjc.A.dylib", &cache, "__objc_autoreleasePoolPop");
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
|
"__ZL18addClassTableEntryP10objc_classb");
|
||||||
|
sel_lookUpByName_t sel_lookUpByName =
|
||||||
|
(sel_lookUpByName_t)find_in_symtab("/usr/lib/libobjc.A.dylib",
|
||||||
|
&cache, "_sel_lookUpByName");
|
||||||
|
objc_autoreleasePoolPush_t objc_autoreleasePoolPush =
|
||||||
|
(objc_autoreleasePoolPush_t)find_in_symtab(
|
||||||
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
|
"__objc_autoreleasePoolPush");
|
||||||
|
objc_autoreleasePoolPop_t objc_autoreleasePoolPop =
|
||||||
|
(objc_autoreleasePoolPop_t)find_in_symtab(
|
||||||
|
"/usr/lib/libobjc.A.dylib", &cache,
|
||||||
|
"__objc_autoreleasePoolPop");
|
||||||
|
|
||||||
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3822
|
// https://github.com/apple-oss-distributions/objc4/blob/689525d556eb3dee1ffb700423bccf5ecc501dbf/runtime/objc-runtime-new.mm#L3822
|
||||||
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
for (int ptr_i = 0; ptr_i < size / 8; ptr_i++) {
|
||||||
void* cls = remapClass((void*)data_ptr[ptr_i]);
|
void *cls = remapClass((void *)data_ptr[ptr_i]);
|
||||||
if (!cls) continue;
|
if (!cls)
|
||||||
|
continue;
|
||||||
addClassTableEntry(cls);
|
addClassTableEntry(cls);
|
||||||
realizeClassWithoutSwift(cls, 0);
|
realizeClassWithoutSwift(cls, 0);
|
||||||
printf("build nonlazy class at (%llx)%p\n", data_ptr[ptr_i], cls);
|
printf("build nonlazy class at (%llx)%p\n", data_ptr[ptr_i], cls);
|
||||||
@ -1237,8 +1281,10 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
custom_initializer_i->sel_lookUpByName = sel_lookUpByName;
|
custom_initializer_i->sel_lookUpByName = sel_lookUpByName;
|
||||||
custom_initializer_i->loadable_classes = loadable_classes;
|
custom_initializer_i->loadable_classes = loadable_classes;
|
||||||
custom_initializer_i->loadable_classes_used = loadable_classes_used;
|
custom_initializer_i->loadable_classes_used = loadable_classes_used;
|
||||||
custom_initializer_i->objc_autoreleasePoolPush = objc_autoreleasePoolPush;
|
custom_initializer_i->objc_autoreleasePoolPush =
|
||||||
custom_initializer_i->objc_autoreleasePoolPop = objc_autoreleasePoolPop;
|
objc_autoreleasePoolPush;
|
||||||
|
custom_initializer_i->objc_autoreleasePoolPop =
|
||||||
|
objc_autoreleasePoolPop;
|
||||||
custom_initializer_i->schedule_class_load = schedule_class_load;
|
custom_initializer_i->schedule_class_load = schedule_class_load;
|
||||||
custom_initializer_i->remapClass = remapClass;
|
custom_initializer_i->remapClass = remapClass;
|
||||||
custom_initializer_i->cls = data_ptr;
|
custom_initializer_i->cls = data_ptr;
|
||||||
@ -1256,13 +1302,14 @@ void fix_objc(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
void fix_initializer(struct libcache_item *libfixing, struct libcache &cache) {
|
||||||
// fix the initializers
|
// fix the initializers
|
||||||
// The Objective-C runtime loads the NSObject after this lib booted
|
// The Objective-C runtime loads the NSObject after this lib booted
|
||||||
// So all calls to NSObject (and its children classes) will segfault/throw error
|
// So all calls to NSObject (and its children classes) will segfault/throw
|
||||||
|
// error
|
||||||
//
|
//
|
||||||
// So we will fix the main initializers, which runs after all Objective-C setup
|
// So we will fix the main initializers, which runs after all Objective-C
|
||||||
// The initializers will run these Objective-C classes' load methods
|
// setup The initializers will run these Objective-C classes' load methods
|
||||||
//
|
//
|
||||||
// (THIS IDEA IS TESTED AND WILL NOT WORK)
|
// (THIS IDEA IS TESTED AND WILL NOT WORK)
|
||||||
// As of now, we assume the main executable has a __mod_init_func section
|
// As of now, we assume the main executable has a __mod_init_func section
|
||||||
@ -1281,29 +1328,32 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
//
|
//
|
||||||
// There could be many ways to do this. I discovered 1 method of doing this.
|
// There could be many ways to do this. I discovered 1 method of doing this.
|
||||||
//
|
//
|
||||||
// The idea is to hijack the main function to do the rest of the initalizations.
|
// The idea is to hijack the main function to do the rest of the
|
||||||
// By fixing the LC_MAIN command, we can make dyld jump to anywhere we want as main.
|
// initalizations. By fixing the LC_MAIN command, we can make dyld jump to
|
||||||
// But the command can't be edited at runtime.
|
// anywhere we want as main. But the command can't be edited at runtime. And
|
||||||
// And pointing to the function we want needs a workaround.
|
// pointing to the function we want needs a workaround.
|
||||||
//
|
//
|
||||||
// So we will write a shellcode in the binary and point main to that shellcode.
|
// So we will write a shellcode in the binary and point main to that
|
||||||
// The shellcode basically loads the address of the initalization function,
|
// shellcode. The shellcode basically loads the address of the initalization
|
||||||
// call it, then call main, and return.
|
// function, call it, then call main, and return.
|
||||||
//
|
//
|
||||||
// The shellcode must be able ot get the current pc address to correctly calculate
|
// The shellcode must be able ot get the current pc address to correctly
|
||||||
// address from any offset. In arm64, we can use `adr x8, 0`.
|
// calculate address from any offset. In arm64, we can use `adr x8, 0`. If we
|
||||||
// If we know where the shellcode is, we can effectively calculate the header of main.
|
// know where the shellcode is, we can effectively calculate the header of
|
||||||
// Now, everything is easy, just need offsets to anywhere we want and we can get it.
|
// main. Now, everything is easy, just need offsets to anywhere we want and we
|
||||||
|
// can get it.
|
||||||
//
|
//
|
||||||
// Now the address of the initalization function can be fetched using many methods,
|
// Now the address of the initalization function can be fetched using many
|
||||||
// but it resides inside this library. To reduce redundance work, we can write the
|
// methods, but it resides inside this library. To reduce redundance work, we
|
||||||
// address of this function somewhere inside main, which will then be easily found.
|
// can write the address of this function somewhere inside main, which will
|
||||||
|
// then be easily found.
|
||||||
//
|
//
|
||||||
// As a result, we choose the space before __text to write the shellcode,
|
// As a result, we choose the space before __text to write the shellcode,
|
||||||
// the space after __DATA to write the address for initalization function.
|
// the space after __DATA to write the address for initalization function.
|
||||||
// Because all segment is allocated/pre-allocated with page alignement,
|
// Because all segment is allocated/pre-allocated with page alignement,
|
||||||
// we can be pretty sure that there are free space.
|
// we can be pretty sure that there are free space.
|
||||||
// (note: __TEXT segment is aligned to the end of the page, free space in the middle)
|
// (note: __TEXT segment is aligned to the end of the page, free space in the
|
||||||
|
// middle)
|
||||||
//
|
//
|
||||||
// Below is the shellcode.
|
// Below is the shellcode.
|
||||||
/*
|
/*
|
||||||
@ -1330,7 +1380,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
br x9
|
br x9
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void* header = libfixing->header;
|
void *header = libfixing->header;
|
||||||
const uint32_t magic = *(uint32_t *)header;
|
const uint32_t magic = *(uint32_t *)header;
|
||||||
char *ptr = (char *)header;
|
char *ptr = (char *)header;
|
||||||
if (magic == magic64) {
|
if (magic == magic64) {
|
||||||
@ -1340,7 +1390,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||||
char* command_ptr = ptr;
|
char *command_ptr = ptr;
|
||||||
|
|
||||||
uint64_t linkedit_vmaddr;
|
uint64_t linkedit_vmaddr;
|
||||||
uint64_t linkedit_fileoffset;
|
uint64_t linkedit_fileoffset;
|
||||||
@ -1356,23 +1406,26 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
printf("segment %s\n", name);
|
printf("segment %s\n", name);
|
||||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||||
slide = (uint64_t)header - vmaddr;
|
slide = (uint64_t)header - vmaddr;
|
||||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||||
for (int sec = 0; sec < nsect; sec++) {
|
for (int sec = 0; sec < nsect; sec++) {
|
||||||
char* secname = sections_ptr;
|
char *secname = sections_ptr;
|
||||||
printf("section %s\n", secname);
|
printf("section %s\n", secname);
|
||||||
if (custom_strcmp(secname, "__init_offsets") == 0) {
|
if (custom_strcmp(secname, "__init_offsets") == 0) {
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
uint32_t *data_ptr = (uint32_t*)(addr + slide);
|
uint32_t *data_ptr = (uint32_t *)(addr + slide);
|
||||||
|
|
||||||
printf("found initializer at %p\n", data_ptr);
|
printf("found initializer at %p\n", data_ptr);
|
||||||
|
|
||||||
custom_initializer_i->nconstructors = size / 4;
|
custom_initializer_i->nconstructors = size / 4;
|
||||||
custom_initializer_i->constructors = (uint64_t*)malloc(sizeof(uint64_t) * size/4);
|
custom_initializer_i->constructors =
|
||||||
|
(uint64_t *)malloc(sizeof(uint64_t) * size / 4);
|
||||||
for (int j = 0; j < size / 4; j++) {
|
for (int j = 0; j < size / 4; j++) {
|
||||||
custom_initializer_i->constructors[j] = (uint64_t)header + data_ptr[j];
|
custom_initializer_i->constructors[j] =
|
||||||
printf("registered initializer at %llx\n", custom_initializer_i->constructors[j]);
|
(uint64_t)header + data_ptr[j];
|
||||||
|
printf("registered initializer at %llx\n",
|
||||||
|
custom_initializer_i->constructors[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (custom_strcmp(secname, "__mod_init_func") == 0) {
|
if (custom_strcmp(secname, "__mod_init_func") == 0) {
|
||||||
@ -1382,14 +1435,14 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||||
}
|
}
|
||||||
} else if (custom_strcmp(name, "__DATA") == 0) {
|
} else if (custom_strcmp(name, "__DATA") == 0) {
|
||||||
uint64_t nsect = *((uint32_t *)ptr + 8*2);
|
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||||
char* sections_ptr = (char*)((uint32_t*)ptr + 18);
|
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||||
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
sections_ptr += (16 * 2 + 8 * 2 + 4 * 8) * (nsect - 1);
|
||||||
|
|
||||||
uint64_t addr = *((uint64_t*)sections_ptr + 4);
|
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||||
uint64_t size = *((uint64_t*)sections_ptr + 5);
|
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||||
|
|
||||||
uint64_t* dummy = (uint64_t*)(addr + slide + size);
|
uint64_t *dummy = (uint64_t *)(addr + slide + size);
|
||||||
dummy[0] = (uint64_t)custom_initializer;
|
dummy[0] = (uint64_t)custom_initializer;
|
||||||
dummy[1] = (uint64_t)(header) + bshield_data::main;
|
dummy[1] = (uint64_t)(header) + bshield_data::main;
|
||||||
printf("add custom main-peg at %p\n", dummy);
|
printf("add custom main-peg at %p\n", dummy);
|
||||||
@ -1402,7 +1455,7 @@ void fix_initializer(struct libcache_item* libfixing, struct libcache& cache) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test(struct libcache& cache) {
|
void test(struct libcache &cache) {
|
||||||
uint32_t libsystem_hash =
|
uint32_t libsystem_hash =
|
||||||
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
|
||||||
if (false) { // test search using name
|
if (false) { // test search using name
|
||||||
@ -1436,7 +1489,8 @@ void test(struct libcache& cache) {
|
|||||||
printf("Indirect search: Found=%p Expected=%p\n", vm_protect_func,
|
printf("Indirect search: Found=%p Expected=%p\n", vm_protect_func,
|
||||||
vm_protect);
|
vm_protect);
|
||||||
|
|
||||||
void *realpath_func = custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN");
|
void *realpath_func =
|
||||||
|
custom_dlsym(&cache, libsystem_hash, "_realpath$DARWIN_EXTSN");
|
||||||
printf("Indirect search: Found=%p Expected=%p\n", realpath_func, realpath);
|
printf("Indirect search: Found=%p Expected=%p\n", realpath_func, realpath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user