Compare commits
9 Commits
remove-str
...
benchmark
Author | SHA1 | Date | |
---|---|---|---|
465aed7ba1 | |||
998d5844e7 | |||
78a8ca45d5 | |||
3e99eff22d | |||
8be97742c9 | |||
792316f4ea | |||
62fa58f039 | |||
62daeb1c52 | |||
37c2f93383 |
@ -1,6 +1,6 @@
|
||||
module ios-wrapper
|
||||
|
||||
go 1.18
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kong v0.2.16
|
||||
|
@ -1,20 +0,0 @@
|
||||
package action
|
||||
|
||||
import (
|
||||
. "ios-wrapper/internal/wrapper/ofile"
|
||||
)
|
||||
|
||||
type removeStrings struct{}
|
||||
|
||||
func (action *removeStrings) withMacho(mf *MachoFile) error {
|
||||
mf.Context().RemoveStrings()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (action *removeStrings) withFat(ff *FatFile) error {
|
||||
return defaultWithFat(action, ff)
|
||||
}
|
||||
|
||||
func NewRemoveStringsAction() *removeStrings {
|
||||
return &removeStrings{}
|
||||
}
|
@ -117,7 +117,6 @@ func Cli() {
|
||||
pc.remove_inits = true
|
||||
pc.remove_codesign = true
|
||||
pc.remove_others = true
|
||||
pc.remove_string = true
|
||||
}
|
||||
pc.remove_imports = arg.RemoveBindSymbols
|
||||
pc.remove_codesign = arg.RemoveCodeSign
|
||||
@ -125,7 +124,6 @@ func Cli() {
|
||||
pc.remove_others = arg.RemoveOthers
|
||||
pc.remove_exports = arg.RemoveExports
|
||||
pc.remove_symbol_table = arg.RemoveSymbolTable
|
||||
pc.remove_string = arg.RemoveStrings
|
||||
pc.dylib_to_add = arg.Dylibs
|
||||
pc.rpath_to_add = arg.Rpath
|
||||
pc.outfile = arg.Out
|
||||
@ -266,54 +264,41 @@ func bcell2header(bfile string, header string) {
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
|
||||
if info.Symbols != nil {
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] =\n")
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", lib)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] =\n")
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", symbol)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "// very compact symbol table,\n")
|
||||
fmt.Fprintf(w, "// [lib idx/*4 bytes*/, nsymbol/*4 byte*/]\n")
|
||||
fmt.Fprintf(w, "// repeat nsymbol times [name offset/*3 bytes*/, segment idx/**/, offset /*4 btyes*/]\n")
|
||||
fmt.Fprintf(w, "// name offset is 3 bytes because we don't think we should have a table size > 2^(3 * 8)\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {\n")
|
||||
n_instructions := 0
|
||||
for i, table := range info.Symbols.Tables {
|
||||
fmt.Fprintf(w, " // %s\n", info.Symbols.Libs[i])
|
||||
fmt.Fprintf(w, " %d/*lib offset*/,\n", table.LibIndex)
|
||||
fmt.Fprintf(w, " %d/*nsymbols*/,\n", table.Nsymbols)
|
||||
n_instructions += 2
|
||||
for _, symbol := range table.Symbols {
|
||||
fmt.Fprintf(w, " %d, 0x%x,\n", (symbol.SymbolIndex<<8)|symbol.SegmentIndex, symbol.Offset)
|
||||
n_instructions += 2
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", n_instructions)
|
||||
} else {
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {};\n")
|
||||
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", 0)
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char libs[] =\n")
|
||||
for _, lib := range info.Symbols.Libs {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", lib)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "char symbols[] =\n")
|
||||
for _, symbol := range info.Symbols.Symbols {
|
||||
fmt.Fprintf(w, " \"%s\\0\"\n", symbol)
|
||||
}
|
||||
fmt.Fprintf(w, ";\n")
|
||||
|
||||
fmt.Fprintf(w, "// very compact symbol table,\n")
|
||||
fmt.Fprintf(w, "// [lib idx/*4 bytes*/, nsymbol/*4 byte*/]\n")
|
||||
fmt.Fprintf(w, "// repeat nsymbol times [name offset/*3 bytes*/, segment idx/**/, offset /*4 btyes*/]\n")
|
||||
fmt.Fprintf(w, "// name offset is 3 bytes because we don't think we should have a table size > 2^(3 * 8)\n")
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t encoded_table[] = {\n")
|
||||
n_instructions := 0
|
||||
for i, table := range info.Symbols.Tables {
|
||||
fmt.Fprintf(w, " // %s\n", info.Symbols.Libs[i])
|
||||
fmt.Fprintf(w, " %d/*lib offset*/,\n", table.LibIndex)
|
||||
fmt.Fprintf(w, " %d/*nsymbols*/,\n", table.Nsymbols)
|
||||
n_instructions += 2
|
||||
for _, symbol := range table.Symbols {
|
||||
fmt.Fprintf(w, " %d, 0x%x,\n", (symbol.SymbolIndex<<8)|symbol.SegmentIndex, symbol.Offset)
|
||||
n_instructions += 2
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "};\n")
|
||||
fmt.Fprintf(w, "uint32_t n_instructions = %d;\n", n_instructions)
|
||||
|
||||
fmt.Fprintf(w, "__attribute__((section(\"__DATA,bshield\")))\n")
|
||||
fmt.Fprintf(w, "uint32_t special_selectors_idx[] = {\n")
|
||||
|
@ -61,7 +61,6 @@ type PepeArgument struct {
|
||||
OFile string `arg help:"Path to Mach-O/Fat binary file" type:"existingfile"`
|
||||
Dylibs []string `short:"l" help:"Add more LC_DYLIB"`
|
||||
Rpath []string `short:"r" help:"Add more LC_RPATH"`
|
||||
RemoveStrings bool `default:"false" negatable:"" help:"Remove static strings found in DATA and DATA_CONST section"`
|
||||
RemoveCodeSign bool `default:"false" negatable:"" help:"Remove LC_CODE_SIGNATURE"`
|
||||
RemoveExports bool `default:"false" negatable:"" help:"Clear the export table/trie"`
|
||||
RemoveSymbolTable bool `default:"false" negatable:"" help:"Remove LC_SYMTAB and LC_DYSYMTAB"`
|
||||
|
@ -46,7 +46,7 @@ func (printer *InfoPrinter) Print() {
|
||||
)
|
||||
}
|
||||
|
||||
mc.CollectObjectiveCClasses()
|
||||
mc.CollectObjectiveCClasses()
|
||||
|
||||
fmt.Println("======")
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ type ProgramContext struct {
|
||||
remove_others bool
|
||||
remove_exports bool
|
||||
remove_symbol_table bool
|
||||
remove_string bool
|
||||
dylib_to_add []string
|
||||
rpath_to_add []string
|
||||
symbols_keep []string
|
||||
@ -107,9 +106,6 @@ func (pc *ProgramContext) Process(ofile OFile) {
|
||||
if pc.remove_exports {
|
||||
pc.AddAction(NewRemoveExportsAction())
|
||||
}
|
||||
if pc.remove_string {
|
||||
pc.AddAction(NewRemoveStringsAction())
|
||||
}
|
||||
ExperimentalFeature("Remove Unnecessary Info", func() {
|
||||
if pc.remove_others {
|
||||
pc.AddAction(NewRemoveUnnecessaryInfoAction())
|
||||
|
@ -1,174 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
|
||||
// this file encodes and decodes arm instructions
|
||||
// only support for ARMv8 or (aarch64) because
|
||||
// it is the default ISA in Apple devices
|
||||
// https://yurichev.com/mirrors/ARMv8-A_Architecture_Reference_Manual_(Issue_A.a).pdf
|
||||
|
||||
// using bit field in little endian is crazy
|
||||
// but clang does not support switching endianess (fuck u llvm)
|
||||
// gcc does, but default on Apple compiler is clang
|
||||
// TODO: default to using big endian for bit field parsing
|
||||
|
||||
// this file assumes everything is LITTLE ENDIAN
|
||||
// the struct is assigned as bit field, and parsed using get_bits
|
||||
// to encode the instruction, can use the bit field and load each bits
|
||||
// through shifting, by default it has 0 padded so should work
|
||||
|
||||
// mask to delete last 12 bits
|
||||
uint32_t ZEROS_12_LOWER = ~0xFFF;
|
||||
|
||||
uint32_t get_bits(uint32_t value, uint32_t from, uint32_t to) {
|
||||
// should assert compiler error
|
||||
if (to < from) return false;
|
||||
if (from == to) {
|
||||
// bit at position is set
|
||||
return (value & (1 << from)) != 0;
|
||||
}
|
||||
|
||||
return (value << (31 - to)) >> (from + (31 - to));
|
||||
}
|
||||
|
||||
int is_bit_set(uint32_t value, uint32_t at) {
|
||||
return (value & (1 << at)) != 0;
|
||||
}
|
||||
|
||||
struct add {
|
||||
uint32_t sf : 1;
|
||||
uint32_t op : 1;
|
||||
uint32_t s : 1;
|
||||
uint32_t sig : 5;
|
||||
uint32_t shift : 2;
|
||||
uint32_t imm : 12;
|
||||
uint32_t rn : 5;
|
||||
uint32_t rd : 5;
|
||||
};
|
||||
|
||||
struct add to_add(uint32_t inst) {
|
||||
struct add parsed;
|
||||
parsed.sf = is_bit_set(inst, 31);
|
||||
parsed.op = is_bit_set(inst, 30);
|
||||
parsed.s = is_bit_set(inst, 29);
|
||||
parsed.sig = get_bits(inst, 24, 28);
|
||||
parsed.shift = get_bits(inst, 22, 23);
|
||||
parsed.imm = get_bits(inst, 10, 21);
|
||||
parsed.rn = get_bits(inst, 5, 9);
|
||||
parsed.rd = get_bits(inst, 0, 4);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
void from_add(struct add parsed, uint32_t *inst) {
|
||||
*inst = 0;
|
||||
*inst |= parsed.sf << 31;
|
||||
*inst |= parsed.op << 30;
|
||||
*inst |= parsed.s << 29;
|
||||
*inst |= parsed.sig << 24;
|
||||
*inst |= parsed.shift << 22;
|
||||
*inst |= parsed.imm << 10;
|
||||
*inst |= parsed.rn << 5;
|
||||
*inst |= parsed.rd;
|
||||
}
|
||||
|
||||
int add_imm_set(uint32_t *inst, uint32_t offset) {
|
||||
struct add parsed = to_add(*inst);
|
||||
if (parsed.op != 0 || parsed.sig != 0b10001) {
|
||||
return false;
|
||||
}
|
||||
parsed.imm = offset; // auto truncate?
|
||||
from_add(parsed, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t add_imm_get(uint32_t inst) {
|
||||
struct add parsed = to_add(inst);
|
||||
if (parsed.shift != 0) {
|
||||
printf("add instruction shift != 0 is not supported\n");
|
||||
*(char*)0 = 0;
|
||||
}
|
||||
return parsed.imm;
|
||||
}
|
||||
|
||||
int is_add(uint32_t inst) {
|
||||
struct add parsed = to_add(inst);
|
||||
if (parsed.op != 0 || parsed.sig != 0b10001) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct adrp {
|
||||
uint32_t op : 1;
|
||||
uint32_t immlo : 2;
|
||||
uint32_t sig : 5;
|
||||
uint32_t immhi : 19;
|
||||
uint32_t rd : 5;
|
||||
};
|
||||
|
||||
struct adrp to_adrp(uint32_t inst) {
|
||||
struct adrp parsed;
|
||||
parsed.op = is_bit_set(inst, 31);
|
||||
parsed.immlo = get_bits(inst, 29, 30);
|
||||
parsed.sig = get_bits(inst, 24, 28);
|
||||
parsed.immhi = get_bits(inst, 5, 23);
|
||||
parsed.rd = get_bits(inst, 0, 4);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
void from_adrp(struct adrp parsed, uint32_t *inst) {
|
||||
*inst = 0;
|
||||
*inst |= parsed.op << 31;
|
||||
*inst |= parsed.immlo << 29;
|
||||
*inst |= parsed.sig << 24;
|
||||
*inst |= parsed.immhi << 5;
|
||||
*inst |= parsed.rd;
|
||||
}
|
||||
|
||||
int is_adrp(uint32_t inst) {
|
||||
struct adrp parsed = to_adrp(inst);
|
||||
if (parsed.op != 1 || parsed.sig != 0b10000) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// change adrp imm to something else
|
||||
int adrp_imm_set(uint32_t *inst, uint32_t offset) {
|
||||
struct adrp parsed = to_adrp(*inst);
|
||||
if (parsed.op != 1 || parsed.sig != 0b10000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// uint32_t imm = 0;
|
||||
|
||||
// imm = parsed.immhi << 14;
|
||||
// imm |= parsed.immlo << 12;
|
||||
// printf("old adrp is %x\n", imm);
|
||||
// printf(" immlo %x\n", parsed.immlo);
|
||||
// printf(" immhi %x\n", parsed.immhi);
|
||||
|
||||
// adrp: register = (base masked lower 12-bit) + imm
|
||||
parsed.immlo = get_bits(offset >> 12, 0, 1);
|
||||
parsed.immhi = offset >> 14;
|
||||
|
||||
// imm = parsed.immhi << 14;
|
||||
// imm |= parsed.immlo << 12;
|
||||
// printf("new adrp is %x\n", imm);
|
||||
// printf(" immlo %x\n", parsed.immlo);
|
||||
// printf(" immhi %x\n", parsed.immhi);
|
||||
|
||||
from_adrp(parsed, inst);
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t adrp_imm_get(uint32_t inst) {
|
||||
struct adrp parsed = to_adrp(inst);
|
||||
uint32_t imm = 0;
|
||||
imm = parsed.immhi << 14;
|
||||
imm |= parsed.immlo << 12;
|
||||
return imm;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
int is_add(uint32_t inst);
|
||||
int add_imm_set(uint32_t *inst, uint32_t offset);
|
||||
uint32_t add_imm_get(uint32_t inst);
|
||||
|
||||
int is_adrp(uint32_t inst);
|
||||
int adrp_imm_set(uint32_t *inst, uint32_t offset);
|
||||
uint32_t adrp_imm_get(uint32_t inst);
|
@ -27,9 +27,6 @@ type ImportSymbol struct {
|
||||
file_address uint64
|
||||
lib_ordinal uint32
|
||||
|
||||
target uint32
|
||||
high8 uint32
|
||||
|
||||
// push number
|
||||
pnum uint32
|
||||
stub uint64
|
||||
@ -125,18 +122,15 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
if status == 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
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)
|
||||
pages := ([]C.ushort)(unsafe.Slice(fix.pages, fix.page_count))
|
||||
reader := bytes.NewReader(mc.buf)
|
||||
for page_i := 0; page_i < int(fix.page_count); page_i++ {
|
||||
// loop through each page in segment, each page has size fix.page_size
|
||||
// the first item in page is offset through pages[page_i]
|
||||
address := int64(fix.segment) + int64(page_i)*int64(fix.page_size) + int64(pages[page_i])
|
||||
reader.Seek(address, io.SeekStart)
|
||||
// fmt.Printf(" page offset=%x\n", pages[page_i])
|
||||
|
||||
fmt.Printf(" page %d offset=%x\n", page_i, address)
|
||||
address := int64(fix.segment) + int64(pages[page_i])
|
||||
reader.Seek(address, io.SeekStart)
|
||||
|
||||
code := make([]byte, 8)
|
||||
|
||||
@ -147,10 +141,6 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
var bind C.int
|
||||
var ret1 C.ulonglong
|
||||
var ret2 C.ulonglong
|
||||
if fix.format != 2 && fix.format != 6 {
|
||||
fmt.Printf("format is %d\n", fix.format)
|
||||
}
|
||||
|
||||
next := C.ParseFixValue(C.int(fix.format), C.ulonglong(v),
|
||||
&bind, &ret1, &ret2)
|
||||
|
||||
@ -174,14 +164,6 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
syms = append(syms, &new_sym)
|
||||
} else {
|
||||
fmt.Printf("// 0x%x rebase=%d target=0x%x high8=0x%x\n", address, bind, ret1, ret2)
|
||||
sym.typ = "rebase"
|
||||
sym.target = uint32(ret1)
|
||||
sym.high8 = uint32(ret2)
|
||||
sym.segment = uint32(mc.findSegmentIndexAt(uint64(address)))
|
||||
sym.file_address = uint64(address)
|
||||
sym.next = int(next)
|
||||
new_sym := sym
|
||||
syms = append(syms, &new_sym)
|
||||
}
|
||||
|
||||
if int(next) == 0 {
|
||||
@ -194,7 +176,6 @@ func (mc *MachoContext) CollectBindSymbolsModern() []*ImportSymbol {
|
||||
reader.Seek(0, io.SeekStart)
|
||||
}
|
||||
}
|
||||
fmt.Printf("number of imports %d\n", len(syms))
|
||||
return syms
|
||||
}
|
||||
|
||||
|
@ -6,12 +6,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"unsafe"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
. "ios-wrapper/pkg/ios"
|
||||
@ -19,7 +16,6 @@ import (
|
||||
)
|
||||
|
||||
// #include "fixups.h"
|
||||
// #include "arm.h"
|
||||
import "C"
|
||||
|
||||
func rewriteLoadcommandsWithoutCodesignature(mc *MachoContext) {
|
||||
@ -198,29 +194,6 @@ func (mc *MachoContext) RemoveUnnecessaryInfo() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (mc *MachoContext) RewriteHeader() {
|
||||
var offset uint64
|
||||
var start uint64
|
||||
|
||||
if mc.Is64bit() {
|
||||
start = Header_size_64
|
||||
} else {
|
||||
start = Header_size
|
||||
}
|
||||
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
|
||||
offset = start
|
||||
for _, cmd := range mc.commands {
|
||||
nwrite, _ := mc.file.WriteAt(cmd.Serialize(mc), int64(offset))
|
||||
offset += uint64(nwrite)
|
||||
}
|
||||
|
||||
mc.header.ncmds = uint32(len(mc.commands))
|
||||
mc.header.sizeofcmds = uint32(offset - start)
|
||||
mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddLoadCmd(lcmd LoadCommand) {
|
||||
var offset uint64
|
||||
payload := lcmd.Serialize(mc)
|
||||
@ -345,7 +318,6 @@ func (mc *MachoContext) RemoveBindSymbols() {
|
||||
value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8))
|
||||
v := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(v, uint64(value))
|
||||
fmt.Printf("change to rebase at %x\n", symbol.file_address)
|
||||
mc.file.WriteAt(v, int64(symbol.file_address))
|
||||
}
|
||||
}
|
||||
@ -511,10 +483,10 @@ func (mc *MachoContext) removeSymtabCommand() {
|
||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||
mc.file.WriteAt(make([]byte, size), start)
|
||||
|
||||
symtab_fix.symoff = 0
|
||||
symtab_fix.nsyms = 0
|
||||
symtab_fix.stroff = 0
|
||||
symtab_fix.strsize = 0
|
||||
// symtab_fix.symoff = 0
|
||||
// symtab_fix.nsyms = 0
|
||||
// symtab_fix.stroff = 0
|
||||
// symtab_fix.strsize = 0
|
||||
mc.file.Seek(ptr, io.SeekStart)
|
||||
mc.file.Write(symtab_fix.Serialize(mc))
|
||||
break
|
||||
@ -562,328 +534,6 @@ func (mc *MachoContext) RemoveExportTrie() {
|
||||
}
|
||||
}
|
||||
|
||||
// this function breaks the file by adding another segment at
|
||||
// the end of the file
|
||||
func (mc *MachoContext) RemoveStrings() {
|
||||
// add a new writable segment with a section
|
||||
// loop over the instructions for adrp,add instructions
|
||||
// if the access points to old cstring section, update
|
||||
// with new values from the new segment and section.
|
||||
|
||||
// data references, e.g., pointer to string, are compiled
|
||||
// into Rebase symbol, so actively check for rebase and
|
||||
// rewrite the rebase value to new segment, section offset.
|
||||
|
||||
// save the strings into a file for recovery. should keep linear
|
||||
// format as before, or customized order, if want complex.
|
||||
|
||||
// __LINKEDIT must be at the end of the section for the binary
|
||||
// to be able to resign, so we have to move __LINKEDIT down
|
||||
// by how much? by the page aligned size of the added segment
|
||||
|
||||
// but __LINKEDIT also contains data for link time data
|
||||
// (fixed ups and bytecode chain) so have to modify
|
||||
// their reference down too,
|
||||
// symtab and dysymtab can be ignored, by removing them lmao
|
||||
|
||||
var cstring *Section64
|
||||
for _, command := range mc.commands {
|
||||
switch command.(type) {
|
||||
case *Segment64:
|
||||
var segment = command.(*Segment64)
|
||||
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
|
||||
for _, section := range segment.Sections() {
|
||||
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__cstring")) == 0 {
|
||||
cstring = section.(*Section64)
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if cstring == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// get last segment, as the start point we extend from
|
||||
// this assumes that the segment are ordered correctly,
|
||||
// first segment offset is lower then second segment offset,
|
||||
// and so on, yielding last segment is the last part of the
|
||||
// binary. Our new segment add another part to the binary
|
||||
// at the end.
|
||||
|
||||
// last segment is always the linkedits
|
||||
last_segment := mc.Segments()[len(mc.Segments())-1]
|
||||
fmt.Printf("last segment %v %s\n", last_segment, string(last_segment.SegName()))
|
||||
|
||||
// all data must be inside the segment (or in header)
|
||||
|
||||
// occupy the segment of linkedit and move linkedit down
|
||||
|
||||
segstart := last_segment.Vmaddr()
|
||||
|
||||
// segment must be page aligned, and the size is based on
|
||||
// the section size
|
||||
secstart := segstart
|
||||
secsize := cstring.Size()
|
||||
|
||||
filestart := last_segment.Fileoff()
|
||||
// align to page address, not sure if neccessary, because the
|
||||
// loader can pick up from anywhere and load in memory (mmap)
|
||||
if filestart%0x4000 != 0 {
|
||||
filestart += 0x4000 - (filestart % 0x4000)
|
||||
}
|
||||
|
||||
fmt.Printf("section size %x\n", secsize)
|
||||
secsize_aligned := uint64(0)
|
||||
if secsize%0x4000 == 0 {
|
||||
// very rare, but possible, it occupies whole pages
|
||||
secsize_aligned = secsize
|
||||
} else {
|
||||
secsize_aligned = secsize + (0x4000 - (secsize % 0x4000))
|
||||
}
|
||||
filesize := secsize_aligned
|
||||
|
||||
segname := make([]byte, 16)
|
||||
copy(segname, []byte("__BSHIELD"))
|
||||
secname := make([]byte, 16)
|
||||
copy(secname, []byte("__secrets"))
|
||||
|
||||
fmt.Printf("segstart %x\n", segstart)
|
||||
fmt.Printf("file_start %x\n", filestart)
|
||||
|
||||
// size of the section and segment is defined by the total
|
||||
// space for strings required
|
||||
|
||||
new_cstring_section := Section64{
|
||||
sectname: secname,
|
||||
segname: segname,
|
||||
addr: secstart,
|
||||
size: secsize,
|
||||
offset: uint32(filestart),
|
||||
align: 3, // idk, see AddSection
|
||||
reloff: 0,
|
||||
nreloc: 0,
|
||||
flags: 0,
|
||||
reserved1: 0,
|
||||
reserved2: 0,
|
||||
reserved3: 0,
|
||||
}
|
||||
string_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: segname,
|
||||
vmaddr: segstart,
|
||||
vmsize: secsize_aligned,
|
||||
fileoff: filestart,
|
||||
filesize: filesize,
|
||||
maxprot: 3, // read/write
|
||||
initprot: 3, // read/write
|
||||
nsects: 1,
|
||||
flags: 0,
|
||||
sections: []*Section64{&new_cstring_section},
|
||||
}
|
||||
|
||||
// rewrite the header to be correct
|
||||
// mc.AddLoadCmd(&string_segment)
|
||||
|
||||
edit_segname := make([]byte, 16)
|
||||
copy(edit_segname, []byte("__LINKEDIT"))
|
||||
edit_segment := Segment64{
|
||||
c: LoadCmd{cmd: LC_SEGMENT_64, cmdsize: 0},
|
||||
segname: edit_segname,
|
||||
vmaddr: segstart + secsize_aligned, // move down
|
||||
vmsize: last_segment.Vmsize(),
|
||||
fileoff: filestart + filesize,
|
||||
filesize: last_segment.Filesize(),
|
||||
maxprot: 1, // read/write
|
||||
initprot: 1, // read/write
|
||||
nsects: 0,
|
||||
flags: 0,
|
||||
sections: []*Section64{},
|
||||
}
|
||||
|
||||
// modify the segment list
|
||||
mc.segments[len(mc.segments)-1] = &string_segment
|
||||
mc.segments = append(mc.segments, &edit_segment)
|
||||
|
||||
// modify the command list
|
||||
for i, cmd := range mc.commands {
|
||||
if cmd.(*Segment64) == last_segment.(*Segment64) {
|
||||
mc.commands = append(mc.commands[:i+1], mc.commands[i:]...)
|
||||
mc.commands[i] = &string_segment
|
||||
mc.commands[i+1] = &edit_segment
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// modify offset in other commands to use new link edit offset
|
||||
|
||||
edit_offset_migrate := func(file_offset uint64) uint64 {
|
||||
// they should keep the old offset,
|
||||
// but the base related to linkedit is modified
|
||||
relative_offset := file_offset - last_segment.Fileoff()
|
||||
return relative_offset + edit_segment.Fileoff()
|
||||
}
|
||||
|
||||
for _, cmd := range mc.commands {
|
||||
if lcmd, ok := cmd.(*LinkEdit); ok {
|
||||
lcmd.dataoff = uint32(edit_offset_migrate(uint64(lcmd.dataoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*Symtab); ok {
|
||||
lcmd.stroff = uint32(edit_offset_migrate(uint64(lcmd.stroff)))
|
||||
lcmd.symoff = uint32(edit_offset_migrate(uint64(lcmd.symoff)))
|
||||
}
|
||||
if lcmd, ok := cmd.(*DySymtab); ok {
|
||||
lcmd.indirectsymoff = uint32(edit_offset_migrate(uint64(lcmd.indirectsymoff)))
|
||||
}
|
||||
}
|
||||
|
||||
mc.RewriteHeader()
|
||||
|
||||
tmp_file := mc.file.Name()
|
||||
|
||||
// has to reopen file as append
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR|os.O_APPEND, 0644)
|
||||
|
||||
// make extra space
|
||||
expected_end := edit_segment.Fileoff() + edit_segment.Filesize()
|
||||
end, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
if end < int64(expected_end) {
|
||||
mc.file.WriteAt(make([]byte, expected_end-uint64(end)), end)
|
||||
}
|
||||
|
||||
// close and reopen as read/write, the buffer at the end is now empty
|
||||
mc.file.Close()
|
||||
mc.file, _ = os.OpenFile(tmp_file, os.O_RDWR, 0644)
|
||||
|
||||
// peek at old link edit and move down
|
||||
old_linkedit := make([]byte, last_segment.Filesize())
|
||||
mc.file.ReadAt(old_linkedit, int64(last_segment.Fileoff()))
|
||||
mc.file.WriteAt(old_linkedit, int64(edit_segment.Fileoff()))
|
||||
|
||||
// prepare dummy bytes into new string segment, 0 for now
|
||||
// this is a way to divert their effort, writing fake strings
|
||||
// will be written again at runtime
|
||||
dummy := make([]byte, edit_segment.Fileoff()-string_segment.Fileoff())
|
||||
mc.file.ReadAt(dummy, int64(cstring.Offset()))
|
||||
// copy(dummy, []byte("We R BShield\n"))
|
||||
for i := 0; i < len(dummy); i++ {
|
||||
dummy[i] = dummy[i] ^ 0x4f
|
||||
}
|
||||
mc.file.WriteAt(dummy, int64(string_segment.Fileoff()))
|
||||
|
||||
// TODO: erase old strings
|
||||
cstring_start := uint64(cstring.Offset())
|
||||
random := make([]byte, cstring.Size())
|
||||
rand.Read(random)
|
||||
mc.file.WriteAt(random, int64(cstring_start))
|
||||
|
||||
// re-read internal buffer
|
||||
last, _ := mc.file.Seek(0, io.SeekEnd)
|
||||
mc.buf = make([]byte, last)
|
||||
mc.file.Seek(0, io.SeekStart)
|
||||
if _, err := io.ReadFull(mc.file, mc.buf); err != nil {
|
||||
// panic?
|
||||
}
|
||||
|
||||
// loop over __TEXT,__text and find all occurances of (adrp, add)
|
||||
// edit the offset to points to new region
|
||||
// because adrp sets the register to the address page at its address
|
||||
// (for page align 0x4000), e.g.,
|
||||
// `adrp x0` instruction at 0x100003f70, yields x0 = 0x100003000
|
||||
// technically, adrp can offset as far as 33-bit, roughly 4GB memory
|
||||
// so we should be very free, because very few program goes this far
|
||||
// but if this happens, god bless you
|
||||
|
||||
// encoding ADRP is actually hard hmmge?
|
||||
|
||||
// this part uses file offsets for calculations
|
||||
|
||||
in_cstring := func(offset uint64) bool {
|
||||
cstring_start := uint64(cstring.Offset())
|
||||
cstring_end := cstring_start + cstring.Size()
|
||||
return (offset >= cstring_start) && (offset < cstring_end)
|
||||
}
|
||||
|
||||
text := mc.segments[1]
|
||||
text_start := text.Fileoff()
|
||||
text_end := text_start + text.Filesize()
|
||||
|
||||
inst := make([]byte, 4)
|
||||
for addr := text_start; addr < text_end; addr = addr + 4 {
|
||||
mc.file.ReadAt(inst, int64(addr))
|
||||
inst_adrp := binary.LittleEndian.Uint32(inst)
|
||||
mc.file.ReadAt(inst, int64(addr+4))
|
||||
inst_add := binary.LittleEndian.Uint32(inst)
|
||||
|
||||
if !(C.is_adrp(C.uint(inst_adrp)) != 0 && C.is_add(C.uint(inst_add)) != 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
base := (addr >> 12) << 12
|
||||
|
||||
// calculate the old string reference
|
||||
ref_base := C.adrp_imm_get(C.uint(inst_adrp))
|
||||
ref_offset := C.add_imm_get(C.uint(inst_add))
|
||||
ref := base + uint64(ref_base+ref_offset)
|
||||
|
||||
if !in_cstring(ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
oldstr := uint64(ref)
|
||||
oldstr_relative := oldstr - uint64(cstring.Offset())
|
||||
|
||||
// find the new string address
|
||||
// using oldstr relative address to cstring section
|
||||
newstr := uint64(new_cstring_section.Offset()) + oldstr_relative
|
||||
newstr_base := (newstr >> 12) << 12 // to calculate new offset in adrp
|
||||
newstr_offset := newstr - newstr_base // to calculate new offset in add
|
||||
|
||||
C.adrp_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_adrp)), C.uint(newstr_base-base))
|
||||
C.add_imm_set((*C.uint32_t)(unsafe.Pointer(&inst_add)), C.uint(newstr_offset))
|
||||
|
||||
binary.LittleEndian.PutUint32(inst, inst_adrp)
|
||||
mc.file.WriteAt(inst, int64(addr))
|
||||
binary.LittleEndian.PutUint32(inst, inst_add)
|
||||
mc.file.WriteAt(inst, int64(addr+4))
|
||||
}
|
||||
|
||||
// modify the rebase table (for both opcode and fixups chain versions)
|
||||
// this is for pointer references
|
||||
|
||||
isModernSymbol := mc.dyldinfo == nil
|
||||
isLegacySymbol := !isModernSymbol
|
||||
for _, symbol := range mc.CollectBindSymbols() {
|
||||
if isLegacySymbol {
|
||||
} else {
|
||||
// (high8 << 56 | target) - mach_header
|
||||
|
||||
ref := uint64(symbol.high8<<56 | symbol.target)
|
||||
if !in_cstring(ref) {
|
||||
continue
|
||||
}
|
||||
|
||||
oldstr := ref
|
||||
oldstr_relative := oldstr - uint64(cstring.Offset())
|
||||
newstr := uint64(new_cstring_section.Offset()) + oldstr_relative
|
||||
|
||||
target := newstr & 0x00FFFFFFFFFFFFFF
|
||||
high8 := newstr >> 56
|
||||
value := C.MakeRebaseFixupOpcode(C.int(symbol.next), C.ulonglong(target), C.ulonglong(high8))
|
||||
v := make([]byte, 8)
|
||||
mc.byteorder.PutUint64(v, uint64(value))
|
||||
fmt.Printf("string rebase change at %x\n", symbol.file_address)
|
||||
mc.file.WriteAt(v, int64(symbol.file_address))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *MachoContext) AddSection(segname string, name string, size int) Section {
|
||||
// mc.file.WriteAt(mc.header.Serialize(mc), 0)
|
||||
var ret Section
|
||||
|
@ -54,7 +54,6 @@ int GetSegmentFixAt(uint8_t* buffer, uint32_t i, struct SegmentFix* fix) {
|
||||
fix->format = chain_header->pointer_format;
|
||||
fix->page_count = chain_header->page_count;
|
||||
fix->pages = chain_header->page_start;
|
||||
fix->page_size = chain_header->page_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,6 @@ struct SegmentFix {
|
||||
uint64_t segment;
|
||||
uint32_t format;
|
||||
uint32_t page_count;
|
||||
uint16_t page_size;
|
||||
uint16_t* pages;
|
||||
};
|
||||
|
||||
|
@ -190,7 +190,7 @@ func (lcmd *Segment64) Vmaddr() uint64 {
|
||||
return lcmd.vmaddr
|
||||
}
|
||||
func (lcmd *Segment64) Vmsize() uint64 {
|
||||
return lcmd.vmsize
|
||||
return lcmd.vmaddr
|
||||
}
|
||||
func (lcmd *Segment64) Fileoff() uint64 {
|
||||
return lcmd.fileoff
|
||||
|
2
research/custom_loader/.gitignore
vendored
2
research/custom_loader/.gitignore
vendored
@ -1 +1,3 @@
|
||||
out/
|
||||
coreutils-9.1/
|
||||
*.tar.xz
|
||||
|
@ -1533,7 +1533,7 @@ uint64_t find_replace_cls_refs(struct libcache cache) {
|
||||
for (int nclass = 0; nclass < size / sizeof(struct _class_t); nclass++, data_ptr++) {
|
||||
if (!data_ptr->ro)
|
||||
continue;
|
||||
if (data_ptr->ro->flags & 0x01) { continue; }
|
||||
if (data_ptr->ro->flags & 0x01/*if meta class*/) { continue; }
|
||||
if (custom_strcmp(data_ptr->ro->name, "Hooker") == 0){
|
||||
printf("Found Hooker @ %p\n", data_ptr);
|
||||
return (uint64_t)data_ptr;
|
||||
|
220
research/custom_loader/benchmark.py
Normal file
220
research/custom_loader/benchmark.py
Normal file
@ -0,0 +1,220 @@
|
||||
# import unittest
|
||||
import subprocess, resource
|
||||
import lief
|
||||
import os
|
||||
# import time
|
||||
import re
|
||||
|
||||
PATH = "./coreutils-9.1/src"
|
||||
|
||||
class Line:
|
||||
file = None
|
||||
@classmethod
|
||||
def init(cls):
|
||||
cls.file = open("out.csv", "w")
|
||||
cls.file.write("Name,File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s),File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s)\n")
|
||||
|
||||
def write(self):
|
||||
out = f"{self.name},{self.norm_size},{self.norm_symbols},{self.norm_imports},0,{self.norm_exe:.3f},{self.obf_size},{self.obf_symbols},{self.obf_imports},{self.restore:.3f},{self.obf_exe:.3f}\n"
|
||||
self.file.write(out)
|
||||
|
||||
def __init__(self, name: str) -> None:
|
||||
self.name = name
|
||||
self.norm_path = f"{PATH}/{name}"
|
||||
self.obf_path = f"{PATH}/{name}-dir/out/{name}-fixed"
|
||||
|
||||
def numOfSymbols(sym: list) -> int:
|
||||
count = 0
|
||||
for i in sym:
|
||||
if i.type != 0:
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def numOfImports(imp: list) -> int:
|
||||
count = 0
|
||||
for i in imp:
|
||||
if i.name != "":
|
||||
count += 1
|
||||
return count
|
||||
|
||||
def setup(binpath: str, libpath: str = None):
|
||||
if os.path.isdir("/tmp/test"):
|
||||
os.system("rm -rf /tmp/test")
|
||||
os.mkdir("/tmp/test")
|
||||
# if libpath:
|
||||
# os.system(f"cp {binpath} {libpath} ./test_file.txt /tmp/test")
|
||||
# else:
|
||||
# os.system(f"cp {binpath} ./test_file.txt /tmp/test")
|
||||
os.system("cp ./test_file.txt /tmp/test")
|
||||
|
||||
|
||||
# class Benchmark(unittest.TestCase):
|
||||
def info(l: Line):
|
||||
l.norm_size = int(os.path.getsize(l.norm_path) / 1024)
|
||||
l.obf_size = int(os.path.getsize(l.obf_path) / 1024)
|
||||
|
||||
norm = lief.parse(l.norm_path)
|
||||
obf = lief.parse(l.obf_path)
|
||||
|
||||
l.norm_symbols = numOfSymbols(norm.symbols)
|
||||
l.obf_symbols = numOfSymbols(obf.symbols)
|
||||
l.norm_imports = numOfImports(norm.imported_functions)
|
||||
l.obf_imports = numOfImports(obf.imported_functions)
|
||||
|
||||
def run(l, cmd):
|
||||
print(f"[+] Running benchmark for {l.name} with command \"{cmd}\"")
|
||||
cmd = cmd.split(" ")
|
||||
setup(l.norm_path)
|
||||
start = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
|
||||
p1 = subprocess.run([l.norm_path] + cmd, capture_output=True)
|
||||
end = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
|
||||
l.norm_exe = end - start
|
||||
|
||||
setup(l.obf_path)
|
||||
start = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
|
||||
p2 = subprocess.run([l.obf_path] + cmd, capture_output=True)
|
||||
end = resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime
|
||||
if p2.returncode == -11:
|
||||
print(f"\033[91m[!] Error in {l.name} (segfault)\033[0m")
|
||||
Line.file.write(f"{l.name},segfault\n")
|
||||
return
|
||||
# print(p2.stdout)
|
||||
match = re.search(b"restoration library time: ([0-9.]+)", p2.stdout)
|
||||
l.restore = float(match.group(1))
|
||||
l.obf_exe = end - start
|
||||
if p2.returncode != p1.returncode:
|
||||
print(f"\033[91m[!] Error in {l.name} (diff exit code)\033[0m")
|
||||
Line.file.write(f"{l.name},exit code diff\n")
|
||||
return
|
||||
l.write()
|
||||
# if p1.stdout in p2.stdout:
|
||||
# l.write()
|
||||
# else:
|
||||
# print(f"\033[91m[!] Error in {l.name} (stdout diff)\033[0m")
|
||||
# print(p1.stdout)
|
||||
# print("-"*20)
|
||||
# print(p2.stdout)
|
||||
|
||||
# Line.file.write(f"{l.name},stdout diff\n")
|
||||
|
||||
|
||||
def test_basic(name, cmd):
|
||||
l = Line(name)
|
||||
info(l)
|
||||
run(l, cmd)
|
||||
|
||||
test_data = [
|
||||
("md5sum", "/tmp/test/test_file.txt"),
|
||||
("split", "/tmp/test/test_file.txt /tmp/test/out"),
|
||||
("cat", "/tmp/test/test_file.txt"),
|
||||
("mkfifo", "/tmp/test/a"),
|
||||
("shuf", "--random-source=/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("pathchk", "/tmp/test/test_file.txt"),
|
||||
("expand", "/tmp/test/test_file.txt"),
|
||||
("tty", ""),
|
||||
("basename", "/tmp/test/test_file.txt"),
|
||||
("nice", ""),
|
||||
("truncate", "-s 0 /tmp/test/test_file.txt"),
|
||||
("echo", "hello"),
|
||||
("du", "-h /tmp"),
|
||||
("ptx", "/tmp/test/test_file.txt"),
|
||||
("join", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("df", "--help"),
|
||||
("pwd", ""),
|
||||
("test", "-f /tmp/test_file.txt"),
|
||||
("csplit", "/tmp/test_file.txt 1"),
|
||||
("sort", "/tmp/test_file.txt"),
|
||||
("whoami", ""),
|
||||
("touch", "/tmp/test/a"),
|
||||
("unlink", "/tmp/test/test_file.txt"),
|
||||
("b2sum", "/tmp/test/test_file.txt"),
|
||||
("sleep", "1"),
|
||||
("fmt", "/tmp/test/test_file.txt"),
|
||||
("stty", ""),
|
||||
("logname", ""),
|
||||
("chgrp", "root /tmp/test/test_file.txt"),
|
||||
("printenv", ""),
|
||||
("seq", "1 10"),
|
||||
("uname", ""),
|
||||
("sha224sum", "/tmp/test/test_file.txt"),
|
||||
("od", "/tmp/test/test_file.txt"),
|
||||
("date", ""),
|
||||
("base64", "/tmp/test/test_file.txt"),
|
||||
("realpath", "/tmp/test/test_file.txt"),
|
||||
("readlink", "/tmp/test/test_file.txt"),
|
||||
("dircolors", ""),
|
||||
("timeout", "1s sleep 2"),
|
||||
("tac", "/tmp/test/test_file.txt"),
|
||||
("numfmt", "1000"),
|
||||
("wc", "/tmp/test/test_file.txt"),
|
||||
("basenc", "/tmp/test/test_file.txt"),
|
||||
("comm", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("nproc", ""),
|
||||
("expr", "1"),
|
||||
("cksum", "/tmp/test/test_file.txt"),
|
||||
("printf", "hello"),
|
||||
("groups", ""),
|
||||
("chcon", "-t s0 /tmp/test/test_file.txt"),
|
||||
("factor", "10"),
|
||||
("tail", "-n 1 /tmp/test/test_file.txt"),
|
||||
("env", ""),
|
||||
("pr", "/tmp/test/test_file.txt"),
|
||||
("head", "-n 1 /tmp/test/test_file.txt"),
|
||||
("kill", "$$"),
|
||||
("uniq", "/tmp/test/test_file.txt"),
|
||||
("stat", "-f /tmp/test/test_file.txt"),
|
||||
("link", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
# ("make-prime-list", "10"), # build fail
|
||||
("sum", "/tmp/test/test_file.txt"),
|
||||
("tsort", "/tmp/test/test_file.txt"),
|
||||
# ("extract-magic", "/tmp/test/test_file.txt"), build fail
|
||||
("mknod", "/tmp/test/test_file.txt"),
|
||||
("users", ""),
|
||||
("dd", "--help"),
|
||||
("who", ""),
|
||||
("sha1sum", "/tmp/test/test_file.txt"),
|
||||
("mktemp", ""),
|
||||
("cut", "-c 1 /tmp/test/test_file.txt"),
|
||||
("sha256sum", "/tmp/test/test_file.txt"),
|
||||
("dir", "/tmp/test/test_file.txt"),
|
||||
("mkdir", "/tmp/test/a"),
|
||||
("nl", "/tmp/test/test_file.txt"),
|
||||
("ginstall", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("shred", "-u /tmp/test/test_file.txt"),
|
||||
("fold", "-w 10 /tmp/test/test_file.txt"),
|
||||
("rmdir", "/tmp/test/a"),
|
||||
("sha384sum", "/tmp/test/test_file.txt"),
|
||||
("mv", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("dirname", "/tmp/test/test_file.txt"),
|
||||
("id", ""),
|
||||
("base32", "/tmp/test/test_file.txt"),
|
||||
("pinky", ""),
|
||||
("ln", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("hostid", ""),
|
||||
("chroot", "/tmp/test /tmp/test/test_file.txt"),
|
||||
("ls", "/tmp/test"),
|
||||
("true", ""),
|
||||
("cp", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("sync", ""),
|
||||
("yes", "--help"),
|
||||
("unexpand", "/tmp/test/test_file.txt"),
|
||||
("chown", "root /tmp/test/test_file.txt"),
|
||||
("getlimits", ""),
|
||||
("chmod", "777 /tmp/test/test_file.txt"),
|
||||
("uptime", ""),
|
||||
("rm", "/tmp/test/test_file.txt"),
|
||||
("vdir", "/tmp/test"),
|
||||
("false", ""),
|
||||
("sha512sum", "/tmp/test/test_file.txt"),
|
||||
("tr", "a b /tmp/test/test_file.txt"),
|
||||
("paste", "/tmp/test/test_file.txt /tmp/test/test_file.txt"),
|
||||
("nohup", "sleep 1")
|
||||
]
|
||||
|
||||
# core="tee md5sum split cat shuf mkfifo pathchk runcon expand tty basename nice truncate echo du ptx join df pwd test csplit sort whoami touch dcgen unlink b2sum sleep fmt stty logname chgrp printenv seq uname sha224sum od date base64 realpath readlink dircolors timeout tac numfmt wc basenc comm nproc expr stdbuf cksum printf groups chcon factor tail env pr head kill uniq stat link make-prime-list sum tsort extract-magic mknod users dd who sha1sum mktemp cut sha256sum dir mkdir nl ginstall shred fold rmdir sha384sum mv dirname id base32 pinky ln hostid chroot ls true cp sync yes unexpand chown getlimits chmod uptime rm vdir false sha512sum tr paste nohup"
|
||||
|
||||
if __name__ == "__main__":
|
||||
Line.init()
|
||||
for name, cmd in test_data:
|
||||
test_basic(name, cmd)
|
||||
# unittest.main()
|
11
research/custom_loader/install.sh
Normal file
11
research/custom_loader/install.sh
Normal file
@ -0,0 +1,11 @@
|
||||
curl -LO https://ftp.gnu.org/gnu/coreutils/coreutils-9.1.tar.xz
|
||||
|
||||
tar -xvf coreutils-9.1.tar.xz
|
||||
|
||||
cd coreutils-9.1
|
||||
|
||||
./configure
|
||||
make
|
||||
|
||||
rm coreutils-9.1.tar.xz
|
||||
|
27
research/custom_loader/obfuscate.sh
Executable file
27
research/custom_loader/obfuscate.sh
Executable file
@ -0,0 +1,27 @@
|
||||
rm -r coreutils-9.1/src/*-dir
|
||||
|
||||
core="tee md5sum split cat shuf mkfifo pathchk runcon expand tty basename nice truncate echo du ptx join df pwd test csplit sort whoami touch dcgen unlink b2sum sleep fmt stty logname chgrp printenv seq uname sha224sum od date base64 realpath readlink dircolors timeout tac numfmt wc basenc comm nproc expr stdbuf cksum printf groups chcon factor tail env pr head kill uniq stat link make-prime-list sum tsort extract-magic mknod users dd who sha1sum mktemp cut sha256sum dir mkdir nl ginstall shred fold rmdir sha384sum mv dirname id base32 pinky ln hostid chroot ls true cp sync yes unexpand chown getlimits chmod uptime rm vdir false sha512sum tr paste nohup"
|
||||
for i in $core; do
|
||||
echo "[+] $i"
|
||||
WD=coreutils-9.1/src/${i}-dir
|
||||
OUT=$WD/out
|
||||
mkdir -p $WD
|
||||
mkdir -p $OUT
|
||||
|
||||
cp b.cc $WD
|
||||
|
||||
{
|
||||
clang++ -mmacosx-version-min=14 -o $OUT/libb.dylib -shared dummy.cc
|
||||
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/${i}-fixed -b $OUT/b.bcell --dylibs=./$OUT/libb.dylib --remove-imports --remove-exports --remove-symbol-table --remove-others coreutils-9.1/src/${i}
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
|
||||
|
||||
clang++ -mmacosx-version-min=14 -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib $WD/b.cc
|
||||
|
||||
codesign --force --deep -s - $OUT/${i}-fixed
|
||||
codesign --force --deep -s - $OUT/libb.dylib
|
||||
chmod +x $OUT/${i}-fixed
|
||||
} > /dev/null 2>&1
|
||||
done
|
||||
|
||||
|
104
research/custom_loader/out_x86_64.csv
Normal file
104
research/custom_loader/out_x86_64.csv
Normal file
@ -0,0 +1,104 @@
|
||||
Name,File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s),File size(KiB),Number of symbols,Number of imports,Restoration time(s),Execution time(s)
|
||||
md5sum,segfault
|
||||
split,150,1321,101,0,0.003,169,0,4,0.002,0.005
|
||||
cat,124,916,84,0,0.003,143,0,4,0.002,0.005
|
||||
mkfifo,121,819,78,0,0.003,140,0,4,0.002,0.004
|
||||
shuf,149,1255,91,0,0.004,168,0,4,0.002,0.005
|
||||
pathchk,121,791,79,0,0.003,139,0,4,0.001,0.004
|
||||
expand,123,904,82,0,0.003,142,0,4,0.002,0.005
|
||||
tty,120,773,77,0,0.003,139,0,4,0.001,0.004
|
||||
basename,121,805,74,0,0.003,140,0,4,0.001,0.004
|
||||
nice,121,802,79,0,0.003,139,0,4,0.002,0.004
|
||||
truncate,123,868,81,0,0.003,141,0,4,0.002,0.005
|
||||
echo,118,710,67,0,0.003,137,0,4,0.001,0.004
|
||||
du,321,2323,130,0,0.003,341,0,4,0.003,0.005
|
||||
ptx,257,1763,111,0,0.031,277,0,4,0.002,0.038
|
||||
join,146,1164,90,0,0.013,165,0,4,0.002,0.018
|
||||
df,197,1797,102,0,0.003,217,0,4,0.002,0.005
|
||||
pwd,122,861,86,0,0.003,141,0,4,0.002,0.004
|
||||
test,121,826,71,0,0.003,140,0,4,0.002,0.004
|
||||
csplit,235,1559,106,0,0.003,255,0,4,0.002,0.005
|
||||
sort,239,2154,146,0,0.004,258,0,5,0.003,0.007
|
||||
whoami,120,790,77,0,0.003,139,0,4,0.002,0.004
|
||||
touch,187,1406,108,0,0.003,206,0,4,0.003,0.006
|
||||
unlink,121,817,77,0,0.003,140,0,4,0.002,0.004
|
||||
b2sum,141,956,88,0,0.004,160,0,5,0.002,0.005
|
||||
sleep,123,848,79,0,0.003,141,0,4,0.002,0.005
|
||||
fmt,139,908,81,0,0.007,158,0,4,0.003,0.010
|
||||
stty,158,1023,89,0,0.003,177,0,4,0.002,0.005
|
||||
logname,120,789,76,0,0.003,139,0,4,0.002,0.004
|
||||
chgrp,171,1433,105,0,0.003,190,0,4,0.003,0.006
|
||||
printenv,120,769,75,0,0.003,138,0,4,0.001,0.004
|
||||
seq,139,887,83,0,0.003,158,0,4,0.002,0.004
|
||||
uname,120,786,76,0,0.003,139,0,4,0.001,0.004
|
||||
sha224sum,segfault
|
||||
od,161,1156,90,0,0.022,181,0,4,0.002,0.023
|
||||
date,195,1178,95,0,0.003,215,0,4,0.002,0.005
|
||||
base64,122,856,81,0,0.003,141,0,4,0.002,0.005
|
||||
realpath,145,1078,81,0,0.003,164,0,4,0.002,0.005
|
||||
readlink,144,1042,81,0,0.003,163,0,4,0.002,0.005
|
||||
dircolors,159,1012,96,0,0.003,178,0,4,0.002,0.004
|
||||
timeout,124,888,95,0,0.006,142,0,4,0.002,0.007
|
||||
tac,216,1387,97,0,0.003,235,0,4,0.002,0.005
|
||||
numfmt,160,1079,92,0,0.003,179,0,4,0.002,0.005
|
||||
wc,146,1104,95,0,0.004,165,0,4,0.002,0.006
|
||||
basenc,145,1163,81,0,0.003,164,0,4,0.003,0.005
|
||||
comm,126,948,84,0,0.004,144,0,4,0.002,0.006
|
||||
nproc,121,817,79,0,0.003,140,0,4,0.002,0.004
|
||||
expr,232,1436,102,0,0.003,252,0,4,0.002,0.004
|
||||
cksum,187,1436,115,0,0.004,206,0,5,0.002,0.006
|
||||
printf,122,853,74,0,0.003,141,0,4,0.001,0.004
|
||||
groups,122,843,82,0,0.003,141,0,4,0.002,0.005
|
||||
chcon,169,1380,96,0,0.003,188,0,4,0.002,0.005
|
||||
factor,183,1284,115,0,0.003,202,0,4,0.003,0.006
|
||||
tail,165,1192,95,0,0.003,184,0,4,0.003,0.006
|
||||
env,142,1007,92,0,0.003,161,0,4,0.002,0.004
|
||||
pr,185,1370,101,0,0.005,204,0,4,0.002,0.007
|
||||
head,142,994,81,0,0.003,161,0,4,0.002,0.004
|
||||
kill,121,802,81,0,0.003,140,0,4,0.002,0.004
|
||||
uniq,142,987,86,0,0.003,161,0,4,0.002,0.006
|
||||
stat,209,1561,108,0,0.003,228,0,4,0.002,0.005
|
||||
link,121,818,78,0,0.003,140,0,4,0.002,0.005
|
||||
sum,140,923,86,0,0.003,159,0,4,0.002,0.005
|
||||
tsort,123,891,83,0,0.008,142,0,4,0.003,0.015
|
||||
mknod,123,875,81,0,0.003,142,0,4,0.002,0.005
|
||||
users,121,818,81,0,0.003,140,0,4,0.002,0.004
|
||||
dd,165,1284,101,0,0.003,184,0,5,0.002,0.005
|
||||
who,142,972,97,0,0.003,161,0,4,0.002,0.004
|
||||
sha1sum,segfault
|
||||
mktemp,126,960,86,0,0.003,145,0,4,0.002,0.004
|
||||
cut,140,903,88,0,0.003,159,0,4,0.002,0.005
|
||||
sha256sum,segfault
|
||||
dir,281,2551,148,0,0.003,301,0,5,0.003,0.005
|
||||
mkdir,144,1019,92,0,0.003,163,0,4,0.002,0.005
|
||||
nl,214,1377,92,0,0.004,233,0,4,0.002,0.006
|
||||
ginstall,239,2490,161,0,0.003,259,0,4,0.004,0.006
|
||||
shred,151,1215,109,0,0.003,170,0,4,0.002,0.005
|
||||
fold,122,843,80,0,0.004,141,0,4,0.002,0.005
|
||||
rmdir,123,863,83,0,0.003,142,0,4,0.002,0.005
|
||||
sha384sum,segfault
|
||||
mv,238,2461,149,0,0.003,258,0,4,0.004,0.007
|
||||
dirname,121,798,74,0,0.003,139,0,4,0.001,0.004
|
||||
id,141,940,88,0,0.003,160,0,4,0.002,0.005
|
||||
base32,122,861,81,0,0.003,141,0,4,0.002,0.005
|
||||
pinky,125,929,98,0,0.003,144,0,4,0.002,0.005
|
||||
ln,179,1589,111,0,0.003,198,0,4,0.003,0.006
|
||||
hostid,120,783,75,0,0.003,139,0,4,0.001,0.004
|
||||
chroot,148,1170,99,0,0.003,167,0,4,0.003,0.006
|
||||
ls,281,2551,148,0,0.003,301,0,5,0.003,0.006
|
||||
true,118,708,65,0,0.003,137,0,4,0.001,0.004
|
||||
cp,233,2273,148,0,0.003,253,0,4,0.003,0.006
|
||||
sync,121,809,80,0,0.003,140,0,4,0.002,0.004
|
||||
yes,121,801,75,0,0.003,140,0,4,0.003,0.008
|
||||
unexpand,123,907,82,0,0.003,142,0,4,0.003,0.009
|
||||
chown,172,1449,107,0,0.003,191,0,4,0.003,0.006
|
||||
getlimits,137,808,78,0,0.004,156,0,4,0.003,0.008
|
||||
chmod,165,1245,92,0,0.003,184,0,4,0.002,0.004
|
||||
uptime,140,924,93,0,0.003,159,0,5,0.003,0.009
|
||||
rm,171,1417,98,0,0.003,190,0,5,0.003,0.008
|
||||
vdir,281,2551,148,0,0.003,301,0,5,0.005,0.011
|
||||
false,118,708,65,0,0.003,137,0,4,0.003,0.008
|
||||
sha512sum,segfault
|
||||
tr,142,1049,83,0,0.003,161,0,4,0.003,0.009
|
||||
paste,122,880,77,0,0.004,141,0,4,0.003,0.009
|
||||
nohup,123,856,82,0,0.005,142,0,4,0.003,0.012
|
|
3384
research/custom_loader/test_file.txt
Normal file
3384
research/custom_loader/test_file.txt
Normal file
File diff suppressed because it is too large
Load Diff
1
research/strings_empty/.gitignore
vendored
1
research/strings_empty/.gitignore
vendored
@ -1 +0,0 @@
|
||||
out/*
|
@ -1,75 +0,0 @@
|
||||
set -e
|
||||
clear
|
||||
VERSION=${1:-14}
|
||||
OUT=./out
|
||||
LOGIC=1
|
||||
make -C ../../macho-go
|
||||
mkdir -p $OUT
|
||||
|
||||
echo "using mach-o version $VERSION"
|
||||
if [[ $VERSION -ge 14 ]]
|
||||
then
|
||||
echo "Resulting binary uses MODERN symbol resolver"
|
||||
else
|
||||
echo "Resulting binary uses LEGACY symbol resolver"
|
||||
fi
|
||||
|
||||
cat <<'fly'
|
||||
______
|
||||
_\ _~-\___
|
||||
= = ==(____AA____D
|
||||
\_____\___________________,-~~~~~~~`-.._
|
||||
/ o O o o o o O O o o o o o o O o |\_
|
||||
`~-.__ ___..----.. )
|
||||
`---~~\___________/------------`````
|
||||
= ===(_________D
|
||||
fly
|
||||
|
||||
# this is a joke for those who knows
|
||||
# https://www.blackhat.com/presentations/bh-dc-09/Iozzo/BlackHat-DC-09-Iozzo-let-your-mach0-fly-whitepaper.pdf
|
||||
echo "make your Mach-O fly"
|
||||
|
||||
if [[ $LOGIC -eq 0 ]]
|
||||
then
|
||||
|
||||
clang-format -i -style=llvm *.cc
|
||||
|
||||
elif [[ $LOGIC -eq 1 ]]
|
||||
then
|
||||
|
||||
# build test binaries
|
||||
clang -mmacosx-version-min=$VERSION -o $OUT/c_code tests/c_code.c
|
||||
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/objc_code tests/objc_code.m
|
||||
swiftc -o $OUT/swift_code tests/swift_code.swift
|
||||
|
||||
# c program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/c_code_fixed -b $OUT/c_code.bcell -l $OUT/librestore_c.dylib --remove-strings $OUT/c_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_c.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# objc program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/objc_code_fixed -b $OUT/objc_code.bcell -l $OUT/librestore_objc.dylib --remove-strings $OUT/objc_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_objc.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# swift program
|
||||
../../macho-go/bin/ios-wrapper pepe -o $OUT/swift_code_fixed -b $OUT/swift_code.bcell -l $OUT/librestore_swift.dylib --remove-strings $OUT/swift_code
|
||||
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/c_code.bcell -o $OUT/restore.h
|
||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/librestore_swift.dylib -shared -Wl,-reexport_library restore.cc
|
||||
|
||||
# executable
|
||||
chmod +x $OUT/c_code_fixed
|
||||
chmod +x $OUT/objc_code_fixed
|
||||
chmod +x $OUT/swift_code_fixed
|
||||
|
||||
# resign
|
||||
codesign --force --deep -s - $OUT/c_code_fixed
|
||||
codesign --force --deep -s - $OUT/objc_code_fixed
|
||||
codesign --force --deep -s - $OUT/swift_code_fixed
|
||||
|
||||
# run
|
||||
$OUT/c_code_fixed
|
||||
$OUT/objc_code_fixed
|
||||
$OUT/swift_code_fixed
|
||||
|
||||
fi
|
@ -1,167 +0,0 @@
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach/mach.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
// #include <Foundation/Foundation.h>
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include "out/restore.h"
|
||||
|
||||
int custom_strcmp(const char *p1, const char *p2) {
|
||||
const unsigned char *s1 = (const unsigned char *)p1;
|
||||
const unsigned char *s2 = (const unsigned char *)p2;
|
||||
unsigned char c1, c2;
|
||||
do {
|
||||
c1 = (unsigned char)*s1++;
|
||||
c2 = (unsigned char)*s2++;
|
||||
if (c1 == '\0')
|
||||
return c1 - c2;
|
||||
} while (c1 == c2);
|
||||
return c1 - c2;
|
||||
}
|
||||
|
||||
int custom_strncmp(const char *s1, const char *s2, register size_t n) {
|
||||
register unsigned char u1, u2;
|
||||
|
||||
while (n-- > 0) {
|
||||
u1 = (unsigned char)*s1++;
|
||||
u2 = (unsigned char)*s2++;
|
||||
if (u1 != u2)
|
||||
return u1 - u2;
|
||||
if (u1 == '\0')
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint32_t magic64 = 0xfeedfacf;
|
||||
const uint32_t magic32 = 0xfeedface;
|
||||
|
||||
struct ProgramVars {
|
||||
void *mh; // mach_header or mach_header64
|
||||
int *NXArgcPtr;
|
||||
const char ***NXArgvPtr;
|
||||
const char ***environPtr;
|
||||
const char **__prognamePtr;
|
||||
};
|
||||
|
||||
void restore_strings(void* main);
|
||||
|
||||
__attribute__((constructor)) static void
|
||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||
const char *const apple[], const struct ProgramVars *vars) {
|
||||
void* main = (void *)(vars->mh);
|
||||
restore_strings(main);
|
||||
}
|
||||
|
||||
/// strings in __TEXT,__cstring has been removed and this
|
||||
/// function tries to recover those strings. Using either
|
||||
/// these methods below.
|
||||
///
|
||||
/// 1. Recover __TEXT,__cstring
|
||||
/// 2. Build a new segment with section for strings
|
||||
///
|
||||
/// (1) might seem reasonable at first, but requires __TEXT
|
||||
/// segment to be writable. Although we can make that, but
|
||||
/// we are not sure if the modification is allowed by Apple.
|
||||
///
|
||||
/// (2) actually require a little bit more work, by defining
|
||||
/// a new segment with a section inside. This segment is
|
||||
/// mounted readable/writable. Not only that, all string
|
||||
/// references must also be updated.
|
||||
/// In code, ARMv8, the sequence `adrp` `add` referencing
|
||||
/// string must now be updated with new parameters as the
|
||||
/// address/offset has now been changed.
|
||||
/// In ARMv8, every instruction is 8 bytes, so looping
|
||||
/// through all the code and change the instruction is easy.
|
||||
///
|
||||
/// It can be seen that opting for method (2) is safer,
|
||||
/// as Apple allows for arbitrary segment. This option
|
||||
/// requires that there is enough space left for a new segment.
|
||||
/// Calculated, it should be around 152 bytes.
|
||||
///
|
||||
/// 4 + 4 + 16 + 8*4 + 4*4 + 16 + 16 + 8*2 + 4*8
|
||||
/// ^~~~^ ^~~~~~~~~~~~~^ ^~~~~~~~~~~~~~~~~~^
|
||||
/// 1 2 3
|
||||
///
|
||||
/// 1: load command header
|
||||
/// 2: segment data
|
||||
/// 3: section
|
||||
///
|
||||
/// However, if we can expand the old section, and remove the
|
||||
/// old section entry, then we only need 72 bytes. Because,
|
||||
/// we only move the section entry.
|
||||
///
|
||||
/// Remember to update the command count in macho header (+1).
|
||||
void restore_strings(void* main) {
|
||||
printf("=== rebuilding the strings ===\n");
|
||||
|
||||
void* header = main;
|
||||
const uint32_t magic = *(uint32_t *)header;
|
||||
char *ptr = (char *)header;
|
||||
if (magic == magic64) {
|
||||
ptr += 0x20;
|
||||
} else {
|
||||
ptr += 0x20 - 0x4;
|
||||
}
|
||||
const uint32_t ncmds = *((uint32_t *)header + 4);
|
||||
|
||||
uint32_t slide = 0;
|
||||
|
||||
char* secrets = 0;
|
||||
uint64_t secrets_size = 0;
|
||||
|
||||
for (int i = 0; i < ncmds; i++) {
|
||||
const uint32_t cmd = *((uint32_t *)ptr + 0);
|
||||
const uint32_t cmdsize = *((uint32_t *)ptr + 1);
|
||||
if (cmd == LC_SEGMENT_64) {
|
||||
char *name = (char *)((uint64_t *)ptr + 1);
|
||||
uint64_t vmaddr = *((uint64_t *)ptr + 3);
|
||||
uint64_t fileoffset = *((uint64_t *)ptr + 5);
|
||||
// this assumes that __TEXT comes before __DATA_CONST
|
||||
if (custom_strcmp(name, "__TEXT") == 0) {
|
||||
slide = (uint64_t)header - vmaddr;
|
||||
|
||||
} else if (custom_strcmp(name, "__BSHIELD") == 0) {
|
||||
printf("found __BSHIELD segment at %p\n", ptr);
|
||||
|
||||
uint64_t nsect = *((uint32_t *)ptr + 8 * 2);
|
||||
char *sections_ptr = (char *)((uint32_t *)ptr + 18);
|
||||
for (int sec = 0; sec < nsect; sec++) {
|
||||
char *secname = sections_ptr;
|
||||
if (custom_strncmp(secname, "__secrets", 16) == 0) {
|
||||
uint64_t addr = *((uint64_t *)sections_ptr + 4);
|
||||
uint64_t size = *((uint64_t *)sections_ptr + 5);
|
||||
printf("secrets offset 0x%lx\n", addr);
|
||||
|
||||
secrets = (char*)(addr + slide);
|
||||
secrets_size = size;
|
||||
}
|
||||
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr += cmdsize;
|
||||
}
|
||||
|
||||
printf("secrets %p\n", secrets);
|
||||
printf("secrets_size = 0x%lx\n", secrets_size);
|
||||
for (size_t i = 0; i < 0x4000; i++) {
|
||||
secrets[i] = secrets[i] ^ 0x4f;
|
||||
}
|
||||
// secrets[0] = 'F';
|
||||
// secrets[1] = 'R';
|
||||
// secrets[2] = 'E';
|
||||
// secrets[3] = 'E';
|
||||
// secrets[4] = ' ';
|
||||
// secrets[5] = 'S';
|
||||
// secrets[6] = 'P';
|
||||
// secrets[7] = 'A';
|
||||
// secrets[8] = 'C';
|
||||
// secrets[9] = 'E';
|
||||
// secrets[10] = '\n';
|
||||
// secrets[11] = 0;
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("Hello, World!\n");
|
||||
return 0;
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
int main() {
|
||||
@autoreleasepool {
|
||||
NSLog(@"Hello, World!");
|
||||
}
|
||||
return 0;
|
||||
}
|
@ -1 +0,0 @@
|
||||
print("Hello, World!")
|
Reference in New Issue
Block a user