compress the extracted information

This commit is contained in:
nganhkhoa 2023-07-10 14:14:03 +07:00
parent 2eede8f9b2
commit ed2f09348e
5 changed files with 220 additions and 94 deletions

View File

@ -1,7 +1,8 @@
package action package action
import ( import (
"fmt" // "fmt"
"sort"
// log "github.com/sirupsen/logrus" // log "github.com/sirupsen/logrus"
. "ios-wrapper/internal/wrapper/ofile" . "ios-wrapper/internal/wrapper/ofile"
@ -11,25 +12,46 @@ import (
type saveImports struct{} type saveImports struct{}
func (action *saveImports) withMacho(mf *MachoFile) error { func (action *saveImports) withMacho(mf *MachoFile) error {
calculateHash := func(name string) uint32 { // calculateHash := func(name string) uint32 {
var h uint32 = 0x811c9dc5 // var h uint32 = 0x811c9dc5
for _, s := range name { // for _, s := range name {
h ^= uint32(s) // h ^= uint32(s)
h *= 0x01000193 // h *= 0x01000193
} // }
return h // return h
} // }
mc := mf.Context() mc := mf.Context()
symbols := []*protomodel.MachoInfo_BindSymbol{} // symbols_storage := []*protomodel.MachoInfo_AllImportedSymbols{}
fmt.Println("struct imported_symbol {const char* name; const char* lib; uint32_t hash; int segment_i; uint64_t offset;};") symbols_raw := mc.CollectBindSymbols()
fmt.Println("const char* lib_to_resolve = \"main\";") sort.Slice(symbols_raw, func(i, j int) bool {
fmt.Println("struct imported_symbol imported_table[] = {") orderedByLibrary := symbols_raw[i].Dylib() < symbols_raw[j].Dylib()
for _, symbol := range mc.CollectBindSymbols() { if symbols_raw[i].Dylib() == symbols_raw[j].Dylib() {
orderedBySymbol := symbols_raw[i].Name() < symbols_raw[j].Name()
return orderedBySymbol
}
return orderedByLibrary
})
libs := []string{}
symbols := []string{}
tables := []*protomodel.MachoInfo_LibraryImportedSymbols{}
var current_table *protomodel.MachoInfo_LibraryImportedSymbols
current_lib := ""
current_symbol := ""
current_lib_idx := -1
current_symbol_idx := -1
// now we expect everything is sorted and easier to build strings tables
// this is not fully optimized, there can be repeated symbol name in different libraries
for _, symbol := range symbols_raw {
if symbol.Type() != "lazy" { if symbol.Type() != "lazy" {
continue continue
} }
dylib_hash := calculateHash(symbol.Dylib()) // dylib_hash := calculateHash(symbol.Dylib())
seg := mc.Segments()[symbol.Segment()] seg := mc.Segments()[symbol.Segment()]
var offset uint64 var offset uint64
@ -42,22 +64,48 @@ func (action *saveImports) withMacho(mf *MachoFile) error {
offset = symbol.Address() - seg.Fileoff() offset = symbol.Address() - seg.Fileoff()
} }
fmt.Printf("{\"%s\", \"%s\", 0x%x, 0x%x, 0x%x},\n", if current_lib != symbol.Dylib() {
symbol.Name(), symbol.Dylib(), dylib_hash, symbol.Segment(), offset) current_lib_idx += len(current_lib) + 1
current_lib = symbol.Dylib()
symbols = append(symbols, libs = append(libs, symbol.Dylib())
&protomodel.MachoInfo_BindSymbol{ tables = append(tables, &protomodel.MachoInfo_LibraryImportedSymbols{
Name: symbol.Name(), LibIndex: uint32(current_lib_idx),
Libname: symbol.Dylib(), Nsymbols: 0,
Libhash: dylib_hash, Symbols: []*protomodel.MachoInfo_SymbolTable{},
Segment: symbol.Segment(),
Offset: offset,
}) })
current_table = tables[len(tables)-1]
}
if current_symbol != symbol.Name() {
current_symbol_idx += len(current_symbol) + 1
current_symbol = symbol.Name()
symbols = append(symbols, symbol.Name())
}
current_table.Nsymbols += 1
current_table.Symbols = append(current_table.Symbols, &protomodel.MachoInfo_SymbolTable{
SymbolIndex: uint32(current_symbol_idx),
SegmentIndex: symbol.Segment(),
Offset: uint32(offset),
})
// fmt.Printf("{\"%s\", \"%s\", 0x%x, 0x%x, 0x%x},\n",
// symbol.Name(), symbol.Dylib(), symbol.Segment(), offset)
// symbols = append(symbols,
// &protomodel.MachoInfo_BindSymbol{
// Name: symbol.Name(),
// Libname: symbol.Dylib(),
// Libhash: dylib_hash,
// Segment: symbol.Segment(),
// Offset: offset,
// })
} }
fmt.Println("};") mf.Info().Symbols = &protomodel.MachoInfo_AllImportedSymbols{
fmt.Printf("uint32_t nimports = %d;\n", len(symbols)) Libs: libs,
mf.Info().Symbols = symbols Symbols: symbols,
Tables: tables,
}
mf.Info().Main = mc.Main()
return nil return nil
} }

View File

@ -1,10 +1,11 @@
package wrapper package wrapper
import ( import (
"bufio"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" // "strings"
"github.com/alecthomas/kong" "github.com/alecthomas/kong"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -194,11 +195,11 @@ func displayBcell(bfile string) {
) )
} }
fmt.Printf(" | Bind Symbols:\n") fmt.Printf(" | Bind Symbols:\n")
for _, symbol := range info.Symbols { // for _, symbol := range info.Symbols {
lib := strings.Replace(symbol.Libname, "/System/Library/Frameworks/", "", 1) // lib := strings.Replace(symbol.Libname, "/System/Library/Frameworks/", "", 1)
fmt.Printf(" | %s offset=0x%x segmentID=0x%x\n", symbol.Name, symbol.Offset, symbol.Segment) // fmt.Printf(" | %s offset=0x%x segmentID=0x%x\n", symbol.Name, symbol.Offset, symbol.Segment)
fmt.Printf(" | from=%s\n", lib) // fmt.Printf(" | from=%s\n", lib)
} // }
} }
} }
@ -231,33 +232,73 @@ func resolveAddresses(dwarf string, load string, addresses []string) {
func bcell2header(bfile string, header string) { func bcell2header(bfile string, header string) {
raw_data, err := ioutil.ReadFile(bfile) raw_data, err := ioutil.ReadFile(bfile)
if err != nil {
log.Panic("Invalid Protobuf<BcellFile> bcell.dat (1)")
}
data := &protomodel.BcellFile{} data := &protomodel.BcellFile{}
err = proto.Unmarshal(raw_data, data) err = proto.Unmarshal(raw_data, data)
if err != nil { if err != nil {
log.Panic("Invalid Protobuf<BcellFile> bcell.dat (2)") log.Panic("Invalid Protobuf<BcellFile> bcell.dat")
} }
fmt.Printf("[+] User Config: %+v\n", data.BcellConfig) f, err := os.Create(header)
for arch, info := range data.MachoInfos { if err != nil {
fmt.Printf("[+] Arch %s:\n", arch) log.Panic("Cannot open header file for writing")
fmt.Printf(" | PointerSize : %+v\n", info.PointerSize)
fmt.Printf(" | Image Base : 0x%x\n", info.ImageBase)
fmt.Printf(" | Init Pointers:\n")
for _, init_ptr := range info.InitPointers {
fmt.Printf(
" | offset 0x%x => addr 0x%x\n",
init_ptr.Offset,
init_ptr.Value,
)
}
fmt.Printf(" | Bind Symbols:\n")
for _, symbol := range info.Symbols {
lib := strings.Replace(symbol.Libname, "/System/Library/Frameworks/", "", 1)
fmt.Printf(" | %s offset=0x%x segmentID=0x%x\n", symbol.Name, symbol.Offset, symbol.Segment)
fmt.Printf(" | from=%s\n", lib)
}
} }
defer f.Close()
w := bufio.NewWriter(f)
// fmt.Printf("[+] User Config: %+v\n", data.BcellConfig)
fmt.Fprintf(w, "#include<stdint.h>\n")
fmt.Fprintf(w, "namespace bshield_data{\n")
for arch, info := range data.MachoInfos {
fmt.Fprintf(w, "const char* arch = \"%s\";\n", arch)
fmt.Fprintf(w, "unsigned int pointer_size = %d;\n", info.PointerSize)
fmt.Fprintf(w, "uint64_t image_base = 0x%x;\n", info.ImageBase)
fmt.Fprintf(w, "uint64_t main = 0x%x;\n", info.Main)
fmt.Fprintf(w, "struct init_pointer {uint64_t offset; uint64_t value;};\n")
fmt.Fprintf(w, "int num_init_pointers = %d;\n", len(info.InitPointers))
fmt.Fprintf(w, "struct init_pointer init_pointers_offsets[] = {\n")
for _, init_ptr := range info.InitPointers {
fmt.Fprintf(w, " {0x%x, 0x%x},\n", init_ptr.Offset, init_ptr.Value)
}
fmt.Fprintf(w, "};\n")
fmt.Fprintf(w, "__attribute__((section(\"__DATA,.bshield_lib\")))\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_sym\")))\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, "// repeate 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_code\")))\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, "}// namespace bshield_data\n")
w.Flush()
} }

View File

@ -27,8 +27,28 @@ message MachoInfo {
uint64 offset = 5; // offset in segment uint64 offset = 5; // offset in segment
} }
message SymbolTable {
uint32 symbolIndex = 1;
uint32 segmentIndex = 2;
uint32 offset = 3;
}
message LibraryImportedSymbols {
uint32 libIndex = 1;
uint32 nsymbols = 2;
repeated SymbolTable symbols = 3;
}
message AllImportedSymbols {
repeated string libs = 1;
repeated string symbols = 2;
repeated LibraryImportedSymbols tables = 3;
}
PointerSize pointer_size = 1; PointerSize pointer_size = 1;
uint64 image_base = 2; uint64 image_base = 2;
repeated InitPointer init_pointers = 3; uint64 main = 3;
repeated BindSymbol symbols = 4; repeated InitPointer init_pointers = 4;
// repeated BindSymbol symbols = 5;
AllImportedSymbols symbols = 5;
} }

View File

@ -4,6 +4,8 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "out/b.h"
char *pwd; char *pwd;
uint32_t pwd_len; uint32_t pwd_len;
@ -927,46 +929,59 @@ void fix(struct libcache& cache) {
int npage_rw_fixed = 0; int npage_rw_fixed = 0;
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
#include "out/b.h"
// think of a way to get what binary to fix // think of a way to get what binary to fix
// so we can iterate through them // so we can iterate through them
if (nimports == 0) { char* lib_to_resolve = "main";
printf("there is no imports to fix\n");
}
struct libcache_item* libfixing = get_libcache_with_name(&cache, lib_to_resolve); struct libcache_item* libfixing = get_libcache_with_name(&cache, lib_to_resolve);
for (int i = 0; i < nimports; i++) {
struct imported_symbol symbol = imported_table[i];
uint64_t fix_at = symbol.offset + libfixing->segment[symbol.segment_i];
int need_rw_fix = true; int pc = 0;
for (int j = 0; j < npage_rw_fixed; j++) { for (;pc != bshield_data::n_instructions;) {
if (page_rw_fixed[j] <= fix_at && uint32_t libidx = bshield_data::encoded_table[pc];
page_rw_fixed[j] + 0x1000 > fix_at) { uint32_t nsym = bshield_data::encoded_table[pc + 1];
need_rw_fix = false; pc += 2;
char* lib = bshield_data::libs + libidx;
for (int i = 0; i < nsym; i++) {
uint32_t op = bshield_data::encoded_table[pc];
uint32_t offset = bshield_data::encoded_table[pc + 1];
pc += 2;
uint32_t symidx = op >> 8;
uint32_t segment = op & 0xff;
char* sym = bshield_data::symbols + symidx;
uint64_t fix_at = offset + libfixing->segment[segment];
// enable WRITE protection for this data segment
int need_rw_fix = true;
for (int j = 0; j < npage_rw_fixed; j++) {
if (page_rw_fixed[j] <= fix_at &&
page_rw_fixed[j] + 0x1000 > fix_at) {
need_rw_fix = false;
}
}
if (need_rw_fix) {
uint64_t start_page = fix_at - (fix_at % 0x1000);
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0,
VM_PROT_READ | VM_PROT_WRITE);
page_rw_fixed[npage_rw_fixed++] = start_page;
printf("modify page starts at 0x%llx to RW\n", start_page);
} }
}
if (need_rw_fix) {
uint64_t start_page = fix_at - (fix_at % 0x1000);
vm_protect_func(mach_task_self_func(), start_page, 0x1000, 0,
VM_PROT_READ | VM_PROT_WRITE);
page_rw_fixed[npage_rw_fixed++] = start_page;
printf("modify page starts at 0x%llx to RW\n", start_page);
}
void *resolved; void *resolved = 0;
// search with hash is faster // search with hash is faster
resolved = custom_dlsym(&cache, symbol.hash, symbol.name); // resolved = custom_dlsym(&cache, symbol.hash, symbol.name);
if (resolved == 0) { if (resolved == 0) {
// but fuck apple they have relative path and rpath // but fuck apple they have relative path and rpath
resolved = custom_dlsym(&cache, symbol.lib, symbol.name); resolved = custom_dlsym(&cache, lib, sym);
} }
*(uint64_t *)fix_at = (uint64_t)resolved; *(uint64_t *)fix_at = (uint64_t)resolved;
printf("imports need to fix: (0x%x)%s at 0x%llx\n", symbol.hash, printf("imports need to fix: %s at 0x%llx\n", sym, fix_at);
symbol.name, fix_at); printf(" from=%s\n", lib);
printf(" from=%s\n", symbol.lib); printf(" segment id=%d; offset=0x%llx;", segment, offset);
printf(" segment id=%d; offset=0x%llx;", symbol.segment_i, symbol.offset); printf(" resolved=%llx(%p)\n", *(uint64_t*)fix_at, resolved);
printf(" resolved=%llx(%p)\n", *(uint64_t*)fix_at, resolved); }
} }
// TODO: Reformat the region as per before, or leave as it // TODO: Reformat the region as per before, or leave as it

View File

@ -42,7 +42,8 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_l
clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.cc clang++ -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.cc
# extract symbols from a # extract symbols from a
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table $OUT/a > $OUT/b.h ../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table $OUT/a
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
# build libb with symbols extracted from a # build libb with symbols extracted from a
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
@ -62,7 +63,8 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_l
clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.mm clang -fobjc-arc -ObjC -mmacosx-version-min=$VERSION -o $OUT/a -L"./out" -lb a.mm
# extract symbols from a # extract symbols from a
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table $OUT/a > $OUT/b.h ../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table $OUT/a
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
# build libb with symbols extracted from a # build libb with symbols extracted from a
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc