Compare commits

...

12 Commits

13 changed files with 4395 additions and 38 deletions

View File

@ -46,6 +46,8 @@ func (printer *InfoPrinter) Print() {
)
}
mc.CollectObjectiveCClasses()
fmt.Println("======")
}
}

View File

@ -483,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

View File

@ -6,10 +6,163 @@ import (
"fmt"
"io"
"strings"
"unsafe"
. "ios-wrapper/pkg/ios"
)
// #include "fixups.h"
import "C"
func (mc *MachoContext) CollectObjectiveCClasses() {
var objc_const *bytes.Reader
var objc_const_start uint64
var objc_const_end uint64
// var objc_methname []byte
for _, cmd := range mc.commands {
if cmd.Cmd() == LC_MAIN {
continue
}
if cmd.Cmd() != LC_SEGMENT_64 {
continue
}
var segment = cmd.(*Segment64)
// we assume the binary comes in perfect ordering, that is as laid out below
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__TEXT")) == 0 {
for _, section := range segment.Sections() {
buffer := make([]byte, section.Size())
mc.file.ReadAt(buffer, int64(section.Offset()))
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_stubs")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methlist")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
// objc_methname := buffer
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classname")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methtype")) == 0 {
}
}
}
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA_CONST")) == 0 {
for _, section := range segment.Sections() {
buffer := make([]byte, section.Size())
mc.file.ReadAt(buffer, int64(section.Offset()))
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classlist")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_nlclslist")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_imageinfo")) == 0 {
}
}
}
if bytes.Compare(bytes.Trim(segment.SegName(), "\x00"), []byte("__DATA")) == 0 {
for _, section := range segment.Sections() {
buffer := make([]byte, section.Size())
mc.file.ReadAt(buffer, int64(section.Offset()))
reader := bytes.NewReader(buffer)
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_const")) == 0 {
objc_const = reader
objc_const_start = uint64(section.Offset())
objc_const_end = objc_const_start + section.Size()
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_selrefs")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_classrefs")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_superrefs")) == 0 {
}
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_data")) == 0 {
// this section contains a series of class_t
// struct _class_t {
// struct _class_t *isa;
// struct _class_t * const superclass;
// void *cache;
// IMP *vtable;
// struct class_ro_t *ro;
// };
for i := uint64(0); i < (section.Size() / uint64(mc.pointersize*5)); i++ {
var isa uint64
var superclass uint64
var cache uint64
var vtable uint64
var ro uint64
binary.Read(reader, mc.byteorder, &isa)
binary.Read(reader, mc.byteorder, &superclass)
binary.Read(reader, mc.byteorder, &cache)
binary.Read(reader, mc.byteorder, &vtable)
binary.Read(reader, mc.byteorder, &ro)
fmt.Printf("at=0x%x\n", section.Offset()+uint32(i)*mc.pointersize*5)
fmt.Printf("isa=0x%x superclass=0x%x\n", isa, superclass)
fmt.Printf("cache=0x%x vtable=0x%x\n", cache, vtable)
fmt.Printf("ro=0x%x\n", ro)
var bind int
var ret1 uint64
var ret2 uint64
C.ParseFixValue(C.int(2), C.uint64_t(ro),
(*C.int)(unsafe.Pointer(&bind)),
(*C.uint64_t)(unsafe.Pointer(&ret1)),
(*C.uint64_t)(unsafe.Pointer(&ret2)),
)
// is rebase, because ro points to objc_const
// and address is in range
if bind != 1 && ret1 >= objc_const_start && ret1 < objc_const_end {
offset := ret1 - objc_const_start
objc_const.Seek(int64(offset), 0)
// struct _class_ro_t {
// uint32_t const flags;
// uint32_t const instanceStart;
// uint32_t const instanceSize;
// uint32_t const reserved; // only when building for 64bit targets
// const uint8_t * const ivarLayout;
// const char *const name;
// const struct _method_list_t * const baseMethods;
// const struct _protocol_list_t *const baseProtocols;
// const struct _ivar_list_t *const ivars;
// const uint8_t * const weakIvarLayout;
// const struct _prop_list_t * const properties;
// };
var tmp uint32
var ivarLayout uint64 // ptr
var name uint64 // ptr
var baseMethods uint64 // ptr
var baseProtocols uint64 // ptr
var ivars uint64 // ptr
var weakIvarLayout uint64 // ptr
var properties uint64 // ptr
binary.Read(objc_const, mc.byteorder, &tmp)
binary.Read(objc_const, mc.byteorder, &tmp)
binary.Read(objc_const, mc.byteorder, &tmp)
binary.Read(objc_const, mc.byteorder, &tmp)
binary.Read(objc_const, mc.byteorder, &ivarLayout)
binary.Read(objc_const, mc.byteorder, &name)
binary.Read(objc_const, mc.byteorder, &baseMethods)
binary.Read(objc_const, mc.byteorder, &baseProtocols)
binary.Read(objc_const, mc.byteorder, &ivars)
binary.Read(objc_const, mc.byteorder, &weakIvarLayout)
binary.Read(objc_const, mc.byteorder, &properties)
fmt.Printf("method list: %x\n", baseMethods)
}
fmt.Printf("========\n")
}
}
}
}
}
}
type SpecialSelector struct {
idx uint
name string
@ -30,8 +183,12 @@ func (sel *SpecialSelector) Name() string {
// we currently have the following symbols guaranteed to be in this list:
// - load
// - retain
//
// besides special selectors, selectors of outside classes must also be
// registered through the cache
// selectors of outside classes are defined as not being referenced by
// internal classes in __objc_data
func (mc *MachoContext) CollectSpecialSelectors() []*SpecialSelector {
var special_selectors []*SpecialSelector
var methods []byte
var methname_offset uint32
@ -137,7 +294,7 @@ func (mc *MachoContext) ReworkForObjc() {
// selector should points to this load selector to make objc thinks that it's "load"
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
// mc.file.WriteAt([]byte("__objc_methbruh"), section_ptr)
mc.file.WriteAt(make([]byte, section.Size()), int64(section.Offset()))
// mc.file.WriteAt(make([]byte, section.Size()), int64(section.Offset()))
}
section_ptr += 16*2 + 8*2 + 4*8
}
@ -324,7 +481,7 @@ func (mc *MachoContext) ReworkForObjc() {
}
}
encode_movz((data_end - text_start) + (shellcode_size - len(shellcode_start)))
encode_movz((data_end - text_start) + (shellcode_size - len(shellcode_start)) + 3)
shellcode_offset = text_start - shellcode_size
shellcode_bytes := append(shellcode_start, offset...)
@ -360,4 +517,8 @@ func (mc *MachoContext) ReworkForObjc() {
mc.file.WriteAt(bs, offset)
offset += 4
}
// make __TEXT writable lol
mc.file.Seek(0, 0)
mc.file.WriteAt([]byte{0x7}, 0xa0)
}

View File

@ -1 +1,3 @@
out/
coreutils-9.1/
*.tar.xz

View File

@ -2,12 +2,19 @@
#include <objc/message.h>
#include <stdio.h>
void* sel_lookUpByName(const char* name);
@interface Foo : NSObject
@end
@implementation Foo
- (void)bar {
NSLog(@"[Foo bar]: %@", self);
NSLog(@"Invoke instance method original bar in Foo");
}
- (void)tobehijacked:(NSString*)input {
NSLog(@"Invoke tobehijacked method %@ from Foo", input);
}
@end
@ -18,48 +25,99 @@
static int x;
+ (void)load {
NSLog(@"%@", self);
// NSLog(@"x=%d", x)
printf("printf in [Bar load]\n");
x = 1;
printf("Invoke +load method\n");
}
- (void)dummy {
NSLog(@"dummy bar x=%d", x);
NSLog(@"Static value check after +load should be 1: x=%d", x);
}
@end
@interface FakeNSDateFormatter : NSDateFormatter {
}
@end
@implementation FakeNSDateFormatter
- (NSDate*)dateFromString:(NSString*)dateString {
NSLog(@"Hijacked the NSDateFormatter");
return [super dateFromString:dateString];
}
@end
__attribute__((constructor)) static void
hmmge(int argc, char** argv) {
// create a dummy blank function to be replaced to call OBJC load
printf("hmmge=%p\n", hmmge);
printf("hmmge argc=%d\n", argc);
printf("Invoke C constructor\n");
printf("Checking for arguments to be passed correctly\n");
printf(" argc=%d\n", argc);
for (int i = 0; i < argc; i++) {
printf(" hmmge argv[%d]=%s\n", i, argv[i]);
printf(" argv[%d]=%s\n", i, argv[i]);
}
NSLog(@"hmmge in objc-c");
NSLog(@"Using Objective-C in C constructor");
NSLog(@"Test static Objective-C class is initialized and +load completed");
Bar *bar = [[Bar alloc] init];
[bar dummy];
}
int main(int argc, const char * argv[]) {
int main(int argc, const char * argv[], char* envp[]) {
@autoreleasepool {
NSLog(@"main()");
NSLog(@"selector for \"bar:\" %p", @selector(bar:));
NSLog(@"Invoke main()");
// Foo bar using Objective-C syntax
Foo *foo = [[Foo alloc] init];
[foo bar];
NSLog(@"directly call \"bar\" %p through objc_msgSend %p with object foo %p\n", @selector(bar), objc_msgSend, foo);
// Foo bar with selector and msgSend
NSLog(@"Directly call \"bar\" %p through objc_msgSend %p with object foo %p", @selector(bar), objc_msgSend, foo);
typedef void (*barfunc)(id, SEL);
barfunc bar_ = (barfunc)&objc_msgSend;
bar_(foo, @selector(bar));
NSString *dummyinput = @"dummy input";
[foo tobehijacked:dummyinput];
NSLog(@"The above invocation should be hijacked with input at %p", dummyinput);
NSString *dateString = @"2024-01-01T00:00:00.000Z";
// NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
// NSDate *date = [dateFormatter dateFromString:dateString];
// this is to test the idea for hooking,
// basically, we create a middle-class inherits the class to be used
//
// example using NSDateFormatter:
// - Create a FakeNSDateFormatter inherits NSDateFormatter
// - Have an overloaded function that calls [super inherited]
// - The internal struct class_t has superclass points to NSDateFormatter
// FakeNSDateFormatter *dateFormatter = [[FakeNSDateFormatter alloc] init];
// [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZ"];
// NSDate *date = [dateFormatter dateFromString:dateString];
// NSLog(@"Test hijacked/hooked Objective-C result %@", date);
NSLog(@"Selector \"dateFromString:\" using @selector %p", @selector(dateFromString:));
NSLog(@"Selector \"bar:\" using @selector %p", @selector(bar:));
NSLog(@"Selector \"dummy\" using @selector %p", @selector(dummy));
NSLog(@"[Bar dummy] implementation is at %p\n", [foo methodForSelector:@selector(bar:)]);
}
printf("argc=%d\n", argc);
printf("Selector lookup 'dateFromString:' addr: %p\n", sel_lookUpByName("dateFromString:"));
printf("Selector lookup 'bar:' addr: %p\n", sel_lookUpByName("bar:"));
printf("Selector lookup 'dummy' addr: %p\n", sel_lookUpByName("dummy"));
printf("Test if arguments are passed correctly to main(argc, argv, env)\n");
printf(" argc=%d\n", argc);
for (int i = 0; i < argc; i++) {
printf(" argv[%d]=%s\n", i, argv[i]);
printf(" argv[%d]=%s\n", i, argv[i]);
}
while (*envp) {
printf(" env[]=%s\n", *envp);
envp++;
}
return 0;
}

View File

@ -3,11 +3,17 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// #include <Foundation/Foundation.h>
#include <objc/objc.h>
#include "out/b.h"
char *pwd;
uint32_t pwd_len;
clock_t start, end;
#define ISARM(header) ((*((uint32_t *)(header)+1) & 0xff) == 0xc)
int custom_strcmp(const char *p1, const char *p2) {
const unsigned char *s1 = (const unsigned char *)p1;
@ -813,6 +819,7 @@ void test(struct libcache &cache);
__attribute__((constructor)) static void
bruh(int argc, const char *const argv[], const char *const envp[],
const char *const apple[], const struct ProgramVars *vars) {
start = clock();
printf("=== manual symbol bind starts ===\n");
// set_cwd(envp);
@ -906,16 +913,16 @@ void build_cache(struct libcache &cache, void *main) {
typedef char *(*dyld_get_image_name_t)(int);
typedef void *(*dyld_get_image_header_t)(int);
char *dyld_image_count_s = (char*)"__dyld_image_count";
char *dyld_image_count_s = (char *)"__dyld_image_count";
int (*dyld_image_count_func)(void) = (dyld_image_count_t)find_in_export_trie(
libdyld, libdyld_export_trie, dyld_image_count_s);
char *dyld_get_image_header_s = (char*)"__dyld_get_image_header";
char *dyld_get_image_header_s = (char *)"__dyld_get_image_header";
void *(*dyld_get_image_header_func)(int) =
(dyld_get_image_header_t)find_in_export_trie(libdyld, libdyld_export_trie,
dyld_get_image_header_s);
char *dyld_get_image_name_s = (char*)"__dyld_get_image_name";
char *dyld_get_image_name_s = (char *)"__dyld_get_image_name";
char *(*dyld_get_image_name_func)(int) =
(dyld_get_image_name_t)find_in_export_trie(libdyld, libdyld_export_trie,
dyld_get_image_name_s);
@ -928,7 +935,7 @@ void build_cache(struct libcache &cache, void *main) {
char *name = dyld_get_image_name_func(i);
bootstrap_libcache_item(&cache.libs[i], header, name);
cache.libs[i].hash = calculate_libname_hash(&cache, name);
printf("%p %s\n", header, name);
// printf("%p %s\n", header, name);
}
}
@ -1181,11 +1188,13 @@ void fix(struct libcache &cache) {
// for (int i = 0; i < 0x2ac; i++) {
// text_start[0xb8c + i] = text_start[0xb8c + i] ^ 0xcc;
// }
// vm_protect_func(mach_task_self_func(), (uint64_t)text_start, 0x1000, 0,
// VM_PROT_READ | VM_PROT_EXECUTE);
fix_objc(libfixing, cache);
fix_initializer(libfixing, cache);
// _TEXT must be RX or RW no RWX
// vm_protect_func(mach_task_self_func(), (uint64_t)text_start, 0x1000, 0,
// VM_PROT_READ | VM_PROT_EXECUTE);
}
void volatile custom_initializer(int argc, const char *const argv[],
@ -1262,9 +1271,341 @@ void volatile custom_initializer(int argc, const char *const argv[],
printf("[+] initializers completed\n");
free(custom_initializer_i);
end = clock();
double cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;
printf("restoration library time: %lf\n", cpu_time_used);
}
void fix_objc_classdata(struct libcache_item *libfixing, struct libcache &cache);
void fix_class_refs(struct libcache_item *libfixing, struct libcache &cache);
void run_objc_readclass(struct libcache_item *libfixing, struct libcache &cache);
// method are splited into 3 kinds, but for simplicity, we think of it as
// 2 kinds: big and small
// our example are small method list, which all pointers are relative and 32-bit
// the size should be 0xc == 12 but we have padding 4-byte 0x0 for some reason?
union _objc_method{
struct {
const char* name;
const char* types;
void* imp;
};
struct {
int32_t sel_offset;
int32_t typ_offset;
int32_t imp_offset;
};
};
struct method_t {
const char* name; /* Pointer to name (or selector reference?) */
const char* types; /* Pointer to type info */
void* imp; /* Pointer to implementation (code) */
};
// entsize & 0x80000000 is small method kind
// entsize = kind | sizeof(_objc_method)
struct _method_list_t {
uint32_t entsize; // sizeof(struct _objc_method)
uint32_t method_count;
union _objc_method method_list[];
};
struct _class_ro_t {
uint32_t flags;
uint32_t const instanceStart;
uint32_t const instanceSize;
uint32_t const reserved; // only when building for 64bit targets
const uint8_t * const ivarLayout;
const char *const name;
struct _method_list_t * baseMethods;
const /*struct _protocol_list_t*/void *const baseProtocols;
const /*struct _ivar_list_t*/void *const ivars;
const uint8_t * const weakIvarLayout;
const /*struct _prop_list_t*/void *const properties;
};
struct _class_t {
struct _class_t *isa;
struct _class_t * superclass;
void *cache;
void *vtable;
struct _class_ro_t *ro;
};
void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
printf("[+] dealing with Objective-C\n");
#ifdef METH1
fix_objc_classdata(libfixing, cache);
#endif
#ifdef METH3
printf("METH3\n");
fix_class_refs(libfixing, cache);
#endif
run_objc_readclass(libfixing, cache);
}
void test_objc_hijack(void* self, void* selector, void* input) {
printf("[Foo tobehijacked] function is HIJACKED\n");
printf("arg1=%p arg2=%p arg3=%p\n", self, selector, input);
}
// a subroutine to perform hooking of fixed-binary classes
// by iterating in the __objc_classref which internally points to
// __objc_data for a list of _class_t structs
// each _classt_t has a _class_ro_t containing pointers to
// the components of an instance, including methods, properties, ivars, ...
//
// in this function, we only work on hooking/hijacking of class methods
// by fixing the method list which to be read by Objective-C runtime during readClass
// the method list is a list of {selector, type, implementation} (all pointers)
// by fixing the implementation (should point to a function) the readClass
// thinks that it is the function associated with the method name/selector
//
// by now, all rebases have been rebased and pointers should be pointing correctly
// however, selectors are to be constructed, unless erased
void fix_objc_classdata(struct libcache_item *libfixing, struct libcache &cache) {
void *header = libfixing->header;
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);
char *command_ptr = ptr;
uint64_t linkedit_vmaddr;
uint64_t linkedit_fileoffset;
uint64_t slide;
uint64_t methlist_start;
uint64_t methlist_size;
uint32_t libsystem_hash =
calculate_libname_hash(&cache, "/usr/lib/libSystem.B.dylib");
typedef void *(*vm_protect_t)(void *, uint64_t, uint64_t, int, int);
typedef void *(*mach_task_self_t)();
mach_task_self_t mach_task_self_func =
(mach_task_self_t)custom_dlsym(&cache, libsystem_hash, "_mach_task_self");
vm_protect_t vm_protect_func =
(vm_protect_t)custom_dlsym(&cache, libsystem_hash, "_vm_protect");
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;
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;
// to be able to fix method list for hooking, we need this section
// to be writable
if (custom_strncmp(secname, "__objc_methlist", 16) == 0) {
uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t *)sections_ptr + 5);
methlist_start = addr + slide;
methlist_size = size;
printf("setting __objc_methlist to RW: addr=%p size=%x\n", addr + slide, size);
vm_protect_func(mach_task_self_func(), methlist_start, methlist_size, 0, VM_PROT_READ | VM_PROT_WRITE);
}
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
}
} else if (custom_strcmp(name, "__DATA") == 0) {
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;
// we can iterate in the __objc_data rather than __objc_classref
// classref can also point to outside classes that are imported
if (custom_strncmp(secname, "__objc_data", 16) == 0) {
uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t *)sections_ptr + 5);
struct _class_t *data_ptr = (struct _class_t *)(addr + slide);
for (int nclass = 0; nclass < size / sizeof(struct _class_t); nclass++, data_ptr++) {
// ro can be null for some reasons
// baseMethods is null if the class is a metaclass
if (!(data_ptr->ro && data_ptr->ro->baseMethods)) {
continue;
}
const char* class_name = data_ptr->ro->name;
struct _method_list_t* methods = data_ptr->ro->baseMethods;
for (int i_method = 0; i_method < methods->method_count; i_method++) {
// have to use reference because the relative offset is calculated with the variable address
// if not using reference, then the variable will be a COPY value and the address is localized
union _objc_method* method = &methods->method_list[i_method];
if (methods->entsize & 0x80000000) {
const char* imp = *(char**)((char*)(&method->sel_offset) + method->sel_offset);
if (custom_strcmp(class_name, "Foo") == 0 && custom_strcmp(imp, "tobehijacked:") == 0) {
// char* current_imp = (char*)(&method->imp_offset) + method->imp_offset;
// encode the relative pointer
uint64_t replace = (uint64_t)test_objc_hijack;
uint64_t original = (uint64_t)&method->imp_offset;
printf("modify the Objective-C method at %p\n", &method->imp_offset);
if (replace > original) {
method->imp_offset = (int32_t)(replace - original);
} else {
method->imp_offset = -(int32_t)(original - replace);
}
}
printf(" method=%p\n", method);
printf(" sel=%x --> %p\n", method->sel_offset, (char*)(&method->sel_offset) + method->sel_offset);
printf(" %s\n", name);
printf(" typ=%x --> %s\n", method->typ_offset, (char*)&method->typ_offset + method->typ_offset);
printf(" fun=%x --> %p\n", method->imp_offset, (char*)(&method->imp_offset) + method->imp_offset);
}
else {
const char* imp = method->name;
if (custom_strcmp(class_name, "Foo") == 0 && custom_strcmp(imp, "tobehijacked:") == 0) {
void* replace = (void*)test_objc_hijack;
printf("modify the Objective-C method at %p with legacy format.\n", &method->imp);
method->imp = replace;
}
printf(" method=%p\n", method);
printf(" sel=%s\n", method->name);
printf(" typ=%p\n", method->types);
printf(" fun=%p\n", method->imp);
}
}
}
}
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
}
} else if (custom_strcmp(name, "__LINKEDIT") == 0) {
linkedit_vmaddr = vmaddr;
linkedit_fileoffset = fileoffset;
}
}
ptr += cmdsize;
}
// _TEXT must be RX or RW no RWX
vm_protect_func(mach_task_self_func(), methlist_start, methlist_size, 0,
VM_PROT_READ | VM_PROT_EXECUTE);
}
uint64_t find_replace_cls_refs(struct libcache cache) {
void *header = cache.thislib;
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);
char *command_ptr = ptr;
uint64_t slide;
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);
if (custom_strcmp(name, "__TEXT") == 0)
slide = (uint64_t)header - vmaddr;
if (custom_strcmp(name, "__DATA") == 0){
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, "__objc_data", 11) == 0){
uint64_t addr = *((uint64_t *)sections_ptr + 4);
uint64_t size = *((uint64_t *)sections_ptr + 5);
struct _class_t *data_ptr = (struct _class_t *)(addr + slide);
for (int nclass = 0; nclass < size / sizeof(struct _class_t); nclass++, data_ptr++) {
if (!data_ptr->ro)
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;
}
}
}
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
}
}
}
ptr += cmdsize;
}
}
void fix_class_refs(struct libcache_item *libfixing, struct libcache &cache) {
uint64_t replace = find_replace_cls_refs(cache);
void *header = libfixing->header;
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);
char *command_ptr = ptr;
uint64_t slide;
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);
if (custom_strcmp(name, "__TEXT") == 0)
slide = (uint64_t)header - vmaddr;
if (custom_strcmp(name, "__DATA") == 0){
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, "__objc_classrefs", 16) == 0){
uint64_t addr = *((uint64_t*)sections_ptr + 4) + slide;
uint64_t size = *((uint64_t*)sections_ptr + 5);
struct _class_t* target_clsref = NULL;
for (int nclass = 0; nclass < size / sizeof(uint64_t*); nclass++){
target_clsref = (_class_t*)(*((uint64_t *)addr + nclass));
// printf("Target clasref @ %p: %p\n", (uint64_t *)addr + nclass, target_clsref);
if (custom_strcmp(target_clsref->ro->name, "Foo") == 0){
// TODO
printf("Target clasref @ %p: %p\n", (uint64_t *)addr + nclass, target_clsref);
*((uint64_t *)addr + nclass) = replace;
printf("New clasref @ %p: %p\n", (uint64_t *)addr + nclass, *((uint64_t *)addr + nclass));
struct _class_t* hooker = (struct _class_t*)replace;
printf("superclass hooker: %p\n", target_clsref->superclass);
hooker->superclass = target_clsref;
printf("New superclass hooker: %p\n", hooker->superclass);
break;
}
}
}
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
}
}
}
ptr += cmdsize;
}
}
void run_objc_readclass(struct libcache_item *libfixing, struct libcache &cache) {
// Manually run the Objective-C runtime for each class
//
@ -1332,7 +1673,7 @@ void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
uint64_t *data_ptr = (uint64_t *)(addr + slide);
uint32_t trie_size;
char* symbol = (char*)"__dyld_get_objc_selector";
char *symbol = (char *)"__dyld_get_objc_selector";
void *libdyld = cache.libdyld;
void *libdyld_export_trie = get_export_trie(libdyld, trie_size);
typedef void *(*dyld_get_objc_selector_t)(const char *);
@ -1346,6 +1687,13 @@ void fix_objc(struct libcache_item *libfixing, struct libcache &cache) {
const char *name = bshield_data::special_selectors_name[i];
data_ptr[idx] = (uint64_t)dyld_get_objc_selector_func(name);
}
typedef void *(*sel_lookUpByName_t)(const char *);
sel_lookUpByName_t sel_lookUpByName =
(sel_lookUpByName_t)custom_dlsym(
&cache, "/usr/lib/libobjc.A.dylib", "_sel_lookUpByName");
printf("selector gogogo: %p\n",
sel_lookUpByName("dateFromString:"));
}
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
}

View 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()

View File

@ -1,9 +1,10 @@
# set -ex
set -e
clear
VERSION=${1:-14}
METH=${2}
OUT=./out
LOGIC=${2}
LOGIC=3
make -C ../../macho-go
mkdir -p $OUT
echo "using mach-o version $VERSION"
@ -75,15 +76,23 @@ clang++ -mmacosx-version-min=$VERSION -o $OUT/libc.dylib -shared c.cc
# create our dummy lib first
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
# build a references libb
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 a.mm
# 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 --keep-imports _printf $OUT/a
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --remove-imports --remove-exports --remove-symbol-table --remove-others $OUT/a
../../macho-go/bin/ios-wrapper pepe -o $OUT/a-fixed -b $OUT/b.bcell --dylibs=./out/libb.dylib --remove-imports --remove-exports --remove-symbol-table --remove-others $OUT/a
../../macho-go/bin/ios-wrapper bcell2header -b $OUT/b.bcell -o $OUT/b.h
if [ "$METH" = "METH1" ]; then
# 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
../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --remove-symbol-table --remove-others --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports ___stack_chk_guard --keep-imports _printf $OUT/libb.dylib
clang++ -D $METH -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib b.cc
# ../../macho-go/bin/ios-wrapper pepe -o $OUT/libb.dylib -b $OUT/libb.bcell --remove-imports --remove-exports --remove-symbol-table --remove-others --keep-imports _dyld_get_sdk_version --keep-imports _malloc --keep-imports ___stack_chk_guard --keep-imports _printf $OUT/libb.dylib
elif [ "$METH" = "METH3" ]; then
clang -mmacosx-version-min=$VERSION -fobjc-arc -ObjC -c -o $OUT/hooking.o hooking.mm
clang++ -mmacosx-version-min=$VERSION -D $METH -c -o $OUT/b.o b.cc
clang++ -fobjc-arc -ObjC -shared -Wl,-reexport_library -o $OUT/libb.dylib $OUT/b.o $OUT/hooking.o
fi
# resign
codesign --force --deep -s - $OUT/a-fixed

View File

@ -0,0 +1,31 @@
#import <Foundation/Foundation.h>
#include <objc/message.h>
#include <stdio.h>
@interface Hehe : NSObject
- (void)bar;
- (void)tobehijacked:(NSString*)input;
@end
@interface Hooker : Hehe
@end
@implementation Hehe
- (void)bar {
NSLog(@"Invoke instance method %@", self);
}
- (void)tobehijacked:(NSString*)input {
NSLog(@"Invoke tobehijacked method %@", input);
}
@end
@implementation Hooker
- (void)tobehijacked:(NSString*)input {
NSLog(@"Hijacked tobehijacked method %@ from Hooker", input);
}
- (void)bar {
[super bar];
}
@end

View 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

View 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

View 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
1 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)
2 md5sum,segfault
3 split,150,1321,101,0,0.003,169,0,4,0.002,0.005
4 cat,124,916,84,0,0.003,143,0,4,0.002,0.005
5 mkfifo,121,819,78,0,0.003,140,0,4,0.002,0.004
6 shuf,149,1255,91,0,0.004,168,0,4,0.002,0.005
7 pathchk,121,791,79,0,0.003,139,0,4,0.001,0.004
8 expand,123,904,82,0,0.003,142,0,4,0.002,0.005
9 tty,120,773,77,0,0.003,139,0,4,0.001,0.004
10 basename,121,805,74,0,0.003,140,0,4,0.001,0.004
11 nice,121,802,79,0,0.003,139,0,4,0.002,0.004
12 truncate,123,868,81,0,0.003,141,0,4,0.002,0.005
13 echo,118,710,67,0,0.003,137,0,4,0.001,0.004
14 du,321,2323,130,0,0.003,341,0,4,0.003,0.005
15 ptx,257,1763,111,0,0.031,277,0,4,0.002,0.038
16 join,146,1164,90,0,0.013,165,0,4,0.002,0.018
17 df,197,1797,102,0,0.003,217,0,4,0.002,0.005
18 pwd,122,861,86,0,0.003,141,0,4,0.002,0.004
19 test,121,826,71,0,0.003,140,0,4,0.002,0.004
20 csplit,235,1559,106,0,0.003,255,0,4,0.002,0.005
21 sort,239,2154,146,0,0.004,258,0,5,0.003,0.007
22 whoami,120,790,77,0,0.003,139,0,4,0.002,0.004
23 touch,187,1406,108,0,0.003,206,0,4,0.003,0.006
24 unlink,121,817,77,0,0.003,140,0,4,0.002,0.004
25 b2sum,141,956,88,0,0.004,160,0,5,0.002,0.005
26 sleep,123,848,79,0,0.003,141,0,4,0.002,0.005
27 fmt,139,908,81,0,0.007,158,0,4,0.003,0.010
28 stty,158,1023,89,0,0.003,177,0,4,0.002,0.005
29 logname,120,789,76,0,0.003,139,0,4,0.002,0.004
30 chgrp,171,1433,105,0,0.003,190,0,4,0.003,0.006
31 printenv,120,769,75,0,0.003,138,0,4,0.001,0.004
32 seq,139,887,83,0,0.003,158,0,4,0.002,0.004
33 uname,120,786,76,0,0.003,139,0,4,0.001,0.004
34 sha224sum,segfault
35 od,161,1156,90,0,0.022,181,0,4,0.002,0.023
36 date,195,1178,95,0,0.003,215,0,4,0.002,0.005
37 base64,122,856,81,0,0.003,141,0,4,0.002,0.005
38 realpath,145,1078,81,0,0.003,164,0,4,0.002,0.005
39 readlink,144,1042,81,0,0.003,163,0,4,0.002,0.005
40 dircolors,159,1012,96,0,0.003,178,0,4,0.002,0.004
41 timeout,124,888,95,0,0.006,142,0,4,0.002,0.007
42 tac,216,1387,97,0,0.003,235,0,4,0.002,0.005
43 numfmt,160,1079,92,0,0.003,179,0,4,0.002,0.005
44 wc,146,1104,95,0,0.004,165,0,4,0.002,0.006
45 basenc,145,1163,81,0,0.003,164,0,4,0.003,0.005
46 comm,126,948,84,0,0.004,144,0,4,0.002,0.006
47 nproc,121,817,79,0,0.003,140,0,4,0.002,0.004
48 expr,232,1436,102,0,0.003,252,0,4,0.002,0.004
49 cksum,187,1436,115,0,0.004,206,0,5,0.002,0.006
50 printf,122,853,74,0,0.003,141,0,4,0.001,0.004
51 groups,122,843,82,0,0.003,141,0,4,0.002,0.005
52 chcon,169,1380,96,0,0.003,188,0,4,0.002,0.005
53 factor,183,1284,115,0,0.003,202,0,4,0.003,0.006
54 tail,165,1192,95,0,0.003,184,0,4,0.003,0.006
55 env,142,1007,92,0,0.003,161,0,4,0.002,0.004
56 pr,185,1370,101,0,0.005,204,0,4,0.002,0.007
57 head,142,994,81,0,0.003,161,0,4,0.002,0.004
58 kill,121,802,81,0,0.003,140,0,4,0.002,0.004
59 uniq,142,987,86,0,0.003,161,0,4,0.002,0.006
60 stat,209,1561,108,0,0.003,228,0,4,0.002,0.005
61 link,121,818,78,0,0.003,140,0,4,0.002,0.005
62 sum,140,923,86,0,0.003,159,0,4,0.002,0.005
63 tsort,123,891,83,0,0.008,142,0,4,0.003,0.015
64 mknod,123,875,81,0,0.003,142,0,4,0.002,0.005
65 users,121,818,81,0,0.003,140,0,4,0.002,0.004
66 dd,165,1284,101,0,0.003,184,0,5,0.002,0.005
67 who,142,972,97,0,0.003,161,0,4,0.002,0.004
68 sha1sum,segfault
69 mktemp,126,960,86,0,0.003,145,0,4,0.002,0.004
70 cut,140,903,88,0,0.003,159,0,4,0.002,0.005
71 sha256sum,segfault
72 dir,281,2551,148,0,0.003,301,0,5,0.003,0.005
73 mkdir,144,1019,92,0,0.003,163,0,4,0.002,0.005
74 nl,214,1377,92,0,0.004,233,0,4,0.002,0.006
75 ginstall,239,2490,161,0,0.003,259,0,4,0.004,0.006
76 shred,151,1215,109,0,0.003,170,0,4,0.002,0.005
77 fold,122,843,80,0,0.004,141,0,4,0.002,0.005
78 rmdir,123,863,83,0,0.003,142,0,4,0.002,0.005
79 sha384sum,segfault
80 mv,238,2461,149,0,0.003,258,0,4,0.004,0.007
81 dirname,121,798,74,0,0.003,139,0,4,0.001,0.004
82 id,141,940,88,0,0.003,160,0,4,0.002,0.005
83 base32,122,861,81,0,0.003,141,0,4,0.002,0.005
84 pinky,125,929,98,0,0.003,144,0,4,0.002,0.005
85 ln,179,1589,111,0,0.003,198,0,4,0.003,0.006
86 hostid,120,783,75,0,0.003,139,0,4,0.001,0.004
87 chroot,148,1170,99,0,0.003,167,0,4,0.003,0.006
88 ls,281,2551,148,0,0.003,301,0,5,0.003,0.006
89 true,118,708,65,0,0.003,137,0,4,0.001,0.004
90 cp,233,2273,148,0,0.003,253,0,4,0.003,0.006
91 sync,121,809,80,0,0.003,140,0,4,0.002,0.004
92 yes,121,801,75,0,0.003,140,0,4,0.003,0.008
93 unexpand,123,907,82,0,0.003,142,0,4,0.003,0.009
94 chown,172,1449,107,0,0.003,191,0,4,0.003,0.006
95 getlimits,137,808,78,0,0.004,156,0,4,0.003,0.008
96 chmod,165,1245,92,0,0.003,184,0,4,0.002,0.004
97 uptime,140,924,93,0,0.003,159,0,5,0.003,0.009
98 rm,171,1417,98,0,0.003,190,0,5,0.003,0.008
99 vdir,281,2551,148,0,0.003,301,0,5,0.005,0.011
100 false,118,708,65,0,0.003,137,0,4,0.003,0.008
101 sha512sum,segfault
102 tr,142,1049,83,0,0.003,161,0,4,0.003,0.009
103 paste,122,880,77,0,0.004,141,0,4,0.003,0.009
104 nohup,123,856,82,0,0.005,142,0,4,0.003,0.012

File diff suppressed because it is too large Load Diff