Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
465aed7ba1 | |||
998d5844e7 | |||
78a8ca45d5 | |||
3e99eff22d | |||
8be97742c9 | |||
06525b8a5e | |||
57b0ae26a7 | |||
f795e9b99d | |||
792316f4ea | |||
62fa58f039 | |||
62daeb1c52 | |||
37c2f93383 |
@ -46,6 +46,8 @@ func (printer *InfoPrinter) Print() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mc.CollectObjectiveCClasses()
|
||||||
|
|
||||||
fmt.Println("======")
|
fmt.Println("======")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,10 +483,10 @@ func (mc *MachoContext) removeSymtabCommand() {
|
|||||||
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
fmt.Printf("// Erase at=0x%x size=0x%x\n", start, size)
|
||||||
mc.file.WriteAt(make([]byte, size), start)
|
mc.file.WriteAt(make([]byte, size), start)
|
||||||
|
|
||||||
symtab_fix.symoff = 0
|
// symtab_fix.symoff = 0
|
||||||
symtab_fix.nsyms = 0
|
// symtab_fix.nsyms = 0
|
||||||
symtab_fix.stroff = 0
|
// symtab_fix.stroff = 0
|
||||||
symtab_fix.strsize = 0
|
// symtab_fix.strsize = 0
|
||||||
mc.file.Seek(ptr, io.SeekStart)
|
mc.file.Seek(ptr, io.SeekStart)
|
||||||
mc.file.Write(symtab_fix.Serialize(mc))
|
mc.file.Write(symtab_fix.Serialize(mc))
|
||||||
break
|
break
|
||||||
|
@ -6,10 +6,163 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
. "ios-wrapper/pkg/ios"
|
. "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 {
|
type SpecialSelector struct {
|
||||||
idx uint
|
idx uint
|
||||||
name string
|
name string
|
||||||
@ -30,8 +183,12 @@ func (sel *SpecialSelector) Name() string {
|
|||||||
// we currently have the following symbols guaranteed to be in this list:
|
// we currently have the following symbols guaranteed to be in this list:
|
||||||
// - load
|
// - load
|
||||||
// - retain
|
// - 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 {
|
func (mc *MachoContext) CollectSpecialSelectors() []*SpecialSelector {
|
||||||
|
|
||||||
var special_selectors []*SpecialSelector
|
var special_selectors []*SpecialSelector
|
||||||
var methods []byte
|
var methods []byte
|
||||||
var methname_offset uint32
|
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"
|
// 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 {
|
if bytes.Compare(bytes.Trim(section.SectName(), "\x00"), []byte("__objc_methname")) == 0 {
|
||||||
// mc.file.WriteAt([]byte("__objc_methbruh"), section_ptr)
|
// 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
|
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_offset = text_start - shellcode_size
|
||||||
shellcode_bytes := append(shellcode_start, offset...)
|
shellcode_bytes := append(shellcode_start, offset...)
|
||||||
@ -360,4 +517,8 @@ func (mc *MachoContext) ReworkForObjc() {
|
|||||||
mc.file.WriteAt(bs, offset)
|
mc.file.WriteAt(bs, offset)
|
||||||
offset += 4
|
offset += 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// make __TEXT writable lol
|
||||||
|
mc.file.Seek(0, 0)
|
||||||
|
mc.file.WriteAt([]byte{0x7}, 0xa0)
|
||||||
}
|
}
|
||||||
|
2
research/custom_loader/.gitignore
vendored
2
research/custom_loader/.gitignore
vendored
@ -1 +1,3 @@
|
|||||||
out/
|
out/
|
||||||
|
coreutils-9.1/
|
||||||
|
*.tar.xz
|
||||||
|
@ -2,12 +2,19 @@
|
|||||||
#include <objc/message.h>
|
#include <objc/message.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void* sel_lookUpByName(const char* name);
|
||||||
|
|
||||||
|
|
||||||
@interface Foo : NSObject
|
@interface Foo : NSObject
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation Foo
|
@implementation Foo
|
||||||
- (void)bar {
|
- (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
|
@end
|
||||||
|
|
||||||
@ -18,48 +25,99 @@
|
|||||||
static int x;
|
static int x;
|
||||||
|
|
||||||
+ (void)load {
|
+ (void)load {
|
||||||
NSLog(@"%@", self);
|
|
||||||
// NSLog(@"x=%d", x)
|
|
||||||
printf("printf in [Bar load]\n");
|
|
||||||
x = 1;
|
x = 1;
|
||||||
|
printf("Invoke +load method\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dummy {
|
- (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
|
@end
|
||||||
|
|
||||||
|
|
||||||
__attribute__((constructor)) static void
|
__attribute__((constructor)) static void
|
||||||
hmmge(int argc, char** argv) {
|
hmmge(int argc, char** argv) {
|
||||||
// create a dummy blank function to be replaced to call OBJC load
|
printf("Invoke C constructor\n");
|
||||||
printf("hmmge=%p\n", hmmge);
|
printf("Checking for arguments to be passed correctly\n");
|
||||||
printf("hmmge argc=%d\n", argc);
|
printf(" argc=%d\n", argc);
|
||||||
for (int i = 0; i < argc; i++) {
|
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 *bar = [[Bar alloc] init];
|
||||||
[bar dummy];
|
[bar dummy];
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char * argv[]) {
|
int main(int argc, const char * argv[], char* envp[]) {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
NSLog(@"main()");
|
NSLog(@"Invoke main()");
|
||||||
NSLog(@"selector for \"bar:\" %p", @selector(bar:));
|
|
||||||
|
|
||||||
|
// Foo bar using Objective-C syntax
|
||||||
Foo *foo = [[Foo alloc] init];
|
Foo *foo = [[Foo alloc] init];
|
||||||
[foo bar];
|
[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);
|
typedef void (*barfunc)(id, SEL);
|
||||||
barfunc bar_ = (barfunc)&objc_msgSend;
|
barfunc bar_ = (barfunc)&objc_msgSend;
|
||||||
bar_(foo, @selector(bar));
|
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++) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,17 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
// #include <Foundation/Foundation.h>
|
||||||
|
#include <objc/objc.h>
|
||||||
|
|
||||||
#include "out/b.h"
|
#include "out/b.h"
|
||||||
|
|
||||||
char *pwd;
|
char *pwd;
|
||||||
uint32_t pwd_len;
|
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) {
|
int custom_strcmp(const char *p1, const char *p2) {
|
||||||
const unsigned char *s1 = (const unsigned char *)p1;
|
const unsigned char *s1 = (const unsigned char *)p1;
|
||||||
@ -813,6 +819,7 @@ void test(struct libcache &cache);
|
|||||||
__attribute__((constructor)) static void
|
__attribute__((constructor)) static void
|
||||||
bruh(int argc, const char *const argv[], const char *const envp[],
|
bruh(int argc, const char *const argv[], const char *const envp[],
|
||||||
const char *const apple[], const struct ProgramVars *vars) {
|
const char *const apple[], const struct ProgramVars *vars) {
|
||||||
|
start = clock();
|
||||||
printf("=== manual symbol bind starts ===\n");
|
printf("=== manual symbol bind starts ===\n");
|
||||||
// set_cwd(envp);
|
// set_cwd(envp);
|
||||||
|
|
||||||
@ -906,16 +913,16 @@ void build_cache(struct libcache &cache, void *main) {
|
|||||||
typedef char *(*dyld_get_image_name_t)(int);
|
typedef char *(*dyld_get_image_name_t)(int);
|
||||||
typedef void *(*dyld_get_image_header_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(
|
int (*dyld_image_count_func)(void) = (dyld_image_count_t)find_in_export_trie(
|
||||||
libdyld, libdyld_export_trie, dyld_image_count_s);
|
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) =
|
void *(*dyld_get_image_header_func)(int) =
|
||||||
(dyld_get_image_header_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
(dyld_get_image_header_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
||||||
dyld_get_image_header_s);
|
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) =
|
char *(*dyld_get_image_name_func)(int) =
|
||||||
(dyld_get_image_name_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
(dyld_get_image_name_t)find_in_export_trie(libdyld, libdyld_export_trie,
|
||||||
dyld_get_image_name_s);
|
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);
|
char *name = dyld_get_image_name_func(i);
|
||||||
bootstrap_libcache_item(&cache.libs[i], header, name);
|
bootstrap_libcache_item(&cache.libs[i], header, name);
|
||||||
cache.libs[i].hash = calculate_libname_hash(&cache, 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++) {
|
// for (int i = 0; i < 0x2ac; i++) {
|
||||||
// text_start[0xb8c + i] = text_start[0xb8c + i] ^ 0xcc;
|
// 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_objc(libfixing, cache);
|
||||||
fix_initializer(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[],
|
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");
|
printf("[+] initializers completed\n");
|
||||||
free(custom_initializer_i);
|
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) {
|
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
|
// 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);
|
uint64_t *data_ptr = (uint64_t *)(addr + slide);
|
||||||
|
|
||||||
uint32_t trie_size;
|
uint32_t trie_size;
|
||||||
char* symbol = (char*)"__dyld_get_objc_selector";
|
char *symbol = (char *)"__dyld_get_objc_selector";
|
||||||
void *libdyld = cache.libdyld;
|
void *libdyld = cache.libdyld;
|
||||||
void *libdyld_export_trie = get_export_trie(libdyld, trie_size);
|
void *libdyld_export_trie = get_export_trie(libdyld, trie_size);
|
||||||
typedef void *(*dyld_get_objc_selector_t)(const char *);
|
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];
|
const char *name = bshield_data::special_selectors_name[i];
|
||||||
data_ptr[idx] = (uint64_t)dyld_get_objc_selector_func(name);
|
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;
|
sections_ptr += 16 * 2 + 8 * 2 + 4 * 8;
|
||||||
}
|
}
|
||||||
|
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()
|
@ -1,9 +1,10 @@
|
|||||||
# set -ex
|
set -e
|
||||||
|
clear
|
||||||
VERSION=${1:-14}
|
VERSION=${1:-14}
|
||||||
|
METH=${2}
|
||||||
OUT=./out
|
OUT=./out
|
||||||
LOGIC=${2}
|
LOGIC=3
|
||||||
|
make -C ../../macho-go
|
||||||
mkdir -p $OUT
|
mkdir -p $OUT
|
||||||
|
|
||||||
echo "using mach-o version $VERSION"
|
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
|
# create our dummy lib first
|
||||||
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
clang++ -mmacosx-version-min=$VERSION -o $OUT/libb.dylib -shared -Wl,-reexport_library out/libc.dylib dummy.cc
|
||||||
# build a references libb
|
# 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
|
# 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 --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
|
../../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
|
# 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++ -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
|
# ../../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
|
# resign
|
||||||
codesign --force --deep -s - $OUT/a-fixed
|
codesign --force --deep -s - $OUT/a-fixed
|
||||||
|
31
research/custom_loader/hooking.mm
Normal file
31
research/custom_loader/hooking.mm
Normal 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
|
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
Loading…
Reference in New Issue
Block a user