Quantcast
Channel: Essence Sharing | 干货分享 - iOSRE
Viewing all 301 articles
Browse latest View live

浅谈ldrestart指令如何在不重启设备电源的情况下刷新用户环境

$
0
0

@Lakr233 wrote:

不知道各位有没有注意到一个很有一的情况,那就是越狱软件在注销桌面的时候多了一个选项,名字叫做“Reload System Daemon”。LaunchDaemon作为系统的守护者,有着神圣不可轻犯的权威。你每次kill一个守护者,作为boss代号为launchd的守护着就会负责重启他。当launchd被kill,你的设备就失去了用户环境,内核会立即panic。那怎样在不丢失用户态环境的情况下“重启设备”,并且不产生崩溃日志呢?答案也很简单,我们要让除了launchd以外的所有进程被爸爸干掉。

首先,我们需要导入头文件。能力有限,不一一叙述头文件。(我太菜了

#include <cstdio>
#include <cstdlib>
#include <errno.h>
#include <signal.h>
#include <sysexits.h>
#include <unistd.h>
#include <launch.h>
#include <sys/stat.h>

继续我们在main里面添加代码。我们要做的事情有三件。

  1. 获取进程列表
  2. 让进程自己死去
  3. 清理环境并且自己结束自己

获取进程列表

我们需要先创建一个请求,然后发送请求给launchd。在获取到数据以后,便可以释放我们创建的请求。这里没有auto_release_pool所以要额外的小心。咱们的守护者会死去,所以可能没有人来搭理善后。内核能做的非常有限。

    launch_data_t req = launch_data_new_string(LAUNCH_KEY_GETJOBS);
    launch_data_t res = launch_msg(req);
    launch_data_free(req);

接下来我们需要验证数据的合法性,检查他是否存在,并确认眼神撒

if (res == NULL || launch_data_get_type(res) != LAUNCH_DATA_DICTIONARY)
    return EPERM; // Error Permit

让我们来看看我们获取到了什么 这里的数据很有意思。

<OS_xpc_dictionary: dictionary[0x103089ca0]: { /* GREAT STUFF*/ }>

我们拿到了一份清单。接下来我们写一个自己的方法,并通过迭代器来让每一个进程都作为参数执行我们需要的代码。

void execEach(launch_data_t v1, const char *name, void *baton) 
launch_data_dict_iterate(res, &execEach, NULL); // 接力开始!

让进程自己死去

首先我们从迭代器传入的launch_data_t中提取进程pid,然后给他发送信号。

if (launch_data_get_type(v1) != LAUNCH_DATA_DICTIONARY) // 再次检查
        return;
launch_data_t _Nullable v2(launch_data_dict_lookup(v1, LAUNCH_JOBKEY_PID)); 
if (v2 == NULL || launch_data_get_type(v2) != LAUNCH_DATA_INTEGER) // 检查pid data
        return; // 并不觉得我们需要这个但是做作更健康
long long qaqPID = launch_data_get_integer(v2); // 从data获取PID
if (kill(qaqPID, 0) == -1)
        return; // 我们没有权限给他发送信号,差不多就是一些boos了。

那么问题来了,为什么我们不kill(qaqPID, 9)呢?因为这样进程就会产生一个没有被handle的sig,进而被其他守护者或kernel捕捉产生crash log。我们发送0的意义在于检查这个进程是不是真的需要被kill我们有没有能力让他kill以避免ldrestart在执行过程中崩溃。

接着我们获取进程在注册字典中的名字。

launch_data_t _Nullable v3 = launch_data_dict_lookup(v1, LAUNCH_JOBKEY_LABEL);
if (v3 == NULL || launch_data_get_type(v3) != LAUNCH_DATA_STRING)
        return; // 通用检查

接下来到了让他自己死去的环节了。我们创建一个请求并发送。

launch_data_t dadHere = launch_data_alloc(LAUNCH_DATA_DICTIONARY); // 分配一个data
// 在爸爸指令里面插入停止代码 lol 注意v3是进程标识符 他是launch_data_t
auto hi(launch_data_dict_insert(dadHere, v3, LAUNCH_KEY_STOPJOB));
auto rettt(launch_msg(dadHere));
// 好了 结束了 他已经躺好了 轮到我们善后自己了 launch_data_t 似乎都需要手动释放
launch_data_free(dadHere); launch_data_free(rettt);

这种方式同样适用于检测到越狱以后无crash无追踪退出,但是具体的实现这里就不详细叙述了。

另外,现在还有更加优秀的sbreload来加速桌面环境的刷新。一般来说越狱开发者注销桌面有四个选择,按照推荐顺序分别是【sbreload】【ldrestart】【killall backboardd】【killall SpringBoard】。sbreload用户体验应该是最好的,ldrestart更加彻底。而干掉backboardd一定程度上解决的SpringBoard死得不够彻底的问题,也能够很神奇的解决一些循环等待SpringBoard加载而锁死的问题。虽然SpringBoard有超时自动kill的机制,但是这个时间经测试大概在5分钟左右。>> iOS 11.3.1

有错误还请各位指出咯。

Posts: 1

Participants: 1

Read full topic


iOS inlinehook绕过反调试

$
0
0

@4chendy wrote:

开始

之前写过一篇iOS LLDB中基于内存单指令patch实现反反调试介绍了在LLDB中如何通过单指令patch的方式去绕过ptrace来反调试。但后面还有一些情况没有解决:如果不是调用ptrace函数,而是直接编写内联汇编的方式,调用对应的系统调用来间接实现反调试,那么这种方式之前的那种办法就不再可行。本文就准备解决那种用内联汇编的反调试方式。

两种绕过方案

这里有两种方案去绕过这种反调试:

  • 静态内存匹配特征patch
  • 实现一个简单的inlinehook动态hook绕过

静态内存匹配特征patch

在介绍思路之前,先看下一遍内联汇编去实现反调试的代码

    asm volatile(
                 "mov x0,#31\n"
                 "mov x1,#0\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#26\n"
                 "svc #128\n"
                 );

这里可以看出原理就是调用了26号系统调用,那么我们是不是可以去代码段里面去搜索,找到满足该特征的代码位置,然后直接将svc置为 nop不就可以了?下面引出两个问题,如何去遍历代码段以及如何去修改?

获取代码段位置以及大小

void getTextSegmentAddr(struct segmentRange *textSegRange){
    
    int offset = 0;
    struct mach_header_64* header = (struct mach_header_64*)_dyld_get_image_header(0);
    
    if(header->magic != MH_MAGIC_64) {
        return ;
    }
    
    offset = sizeof(struct mach_header_64);
    int ncmds = header->ncmds;
    
    while(ncmds--) {
        /* go through all load command to find __TEXT segment*/
        struct load_command * lcp = (struct load_command *)((uint8_t*)header + offset);
        offset += lcp->cmdsize;
        
        if(lcp->cmd == LC_SEGMENT_64) {
            struct segment_command_64 * curSegment = (struct segment_command_64 *)lcp;
            struct section_64* curSection = (struct section_64*)((uint8_t*)curSegment + sizeof(struct segment_command_64));
            
            // check current section of segment is __TEXT?
            if(!strcmp(curSection->segname, "__TEXT") && !strcmp(curSection->sectname, "__text")){
                uint64_t memAddr = curSection->addr;
                textSegRange->start = memAddr + _dyld_get_image_vmaddr_slide(0);
                textSegRange->end = textSegRange->start + curSection->size;
                break;
            }
            
        }
    }
    return ;
}

代码不复杂,就是动态解析了自身内存里面的macho文件,根据macho文件格式找到代码段LC_SEGMENT_64(_TEXT)然后就能得到__text的开始位置以及大小。

内存搜索匹配ptrace内联汇编代码

void* lookup_ptrace_svc(void* target_addr, uint64_t size){
    uint8_t * p = (uint8_t*)target_addr;
    
    for(int i = 0; i < size ;i++ ){
        /*
         mov       x16, #0x1a -> 0xd2800350
         svc        #0x80 -> 0xd4001001
         */
        if (*((uint32_t*)p) == 0xd2800350 && *((uint32_t*)p+1) == 0xd4001001) {
            return p;
        }
        p++;
    }
    return NULL;
}

传入的就是代码段的地址以及大小,然后遍历整个代码段,找到满足以下ptrace特征汇编代码

 mov       x16, #0x1a -> 0xd2800350
 svc        #0x80 -> 0xd4001001

然后就返回该地址。

patch代码(将svc改为nop)

iOS LLDB中基于内存单指令patch实现反反调试这篇文章介绍了如何去patch代码的原理,但当时遇到一个bug:在iOS11/12上面patch会失败,后面我花了一段时间去分析了失败的原因,后来也写了一篇文章去记录了分析的过程,感兴趣的可以访问iOS12内存patch remap bug分析

这里我就直接给出patch的代码

uint8_t patch_ins_data[4] = {0x1f, 0x20, 0x03, 0xd5}; // nop
patchCode(ptrace_svc_p+4, patch_ins_data , 4);

完整流程代码如下

- (void)kill_anti_debug{
   
    struct segmentRange textSegRange;
    getTextSegmentAddr(&textSegRange);
    void* ptrace_svc_p = lookup_ptrace_svc((void*)textSegRange.start, textSegRange.end-textSegRange.start);
    if (!ptrace_svc_p) {
        ADDLOG(@"[-] not found ptrace svc");
        return;
    }
    
    ADDXLOG(@"[+] found ptrace svc # address=%p", ptrace_svc_p);
    
    char* ptrace_bytes = hex_dump((void*)ptrace_svc_p, 8);
    
    ADDXLOG(@"[+] read ptrace svc ins address:%p size:0x%x inst_bytes:%s", ptrace_svc_p, 8, ptrace_bytes);
    free(ptrace_bytes);
  
    ADDLOG(@"[*] start to ptach ptrace svc to ret");
    
   
    uint8_t patch_ins_data[4] = {0x1f, 0x20, 0x03, 0xd5};
    
    patchCode(ptrace_svc_p+4, patch_ins_data , 4);
    ADDLOG(@"[*] ptach ptrace svc to nop done, read new value");
    
    ptrace_bytes = hex_dump((void*)ptrace_svc_p, 8);
    ADDXLOG(@"[+] read ptrace svc ins address:%p size:0x%x inst_bytes:%s", ptrace_svc_p, 8, ptrace_bytes);
    free(ptrace_bytes);
    
}

通过比对前后的代码就发现svc出地址的代码已经变成了nop从而绕过了反调试

inlinehook动态hook绕过

这种方式主要针对那些混淆了系统调用号或者其他编译版本,其绕过原理是直接hook svc指令,然后判断是否为26号系统调用(让其他系统调用正常执行),若满足就直接跳过svc指令。

整体流程代码如下

  struct segmentRange textSegRange;
  getTextSegmentAddr(&textSegRange);
  void* svc_p = lookup_svc_ins((void*)textSegRange.start, textSegRange.end-textSegRange.start);
  if (!svc_p) {
      ADDLOG(@"[-] not found svc");
      return;
  }

  ADDXLOG(@"[+] found svc # address=%p", svc_p);

  char* svc_bytes = hex_dump((void*)svc_p, 4);

  ADDXLOG(@"[+] read ptrace svc ins address:%p size:0x%x inst_bytes:%s", svc_p, 4, svc_bytes);
  free(svc_bytes);

  xia0Hook(svc_p);

同样遍历代码段找到所有的svc指令,然后进行hook,下面看hook的具体实现

bool xia0Hook(void* target_addr){
     int len = (int)sysconf(_SC_PAGESIZE);
    
    // 1. get target address page and patch offset
    unsigned long page_start = (unsigned long) (target_addr) & ~PAGE_MASK;
    unsigned long patch_offset = (unsigned long)target_addr - page_start;
    printf("[*] Target address:%p Page start:%p Patch offset:%p", target_addr, (void*)page_start, (void*)patch_offset);
    
    // 2. map new page for patch
    void *new = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0);
    if (!new ){
        printf("[-] mmap failed!");
        return false;
    }
    
    // 3.copy target 4 ins to new page
    int copy_size = 4*4;
    void* copy_from_addr = target_addr - copy_size;
    memcpy((void *)(new), copy_from_addr, copy_size);
    
    /*
     cmp x16, #0x1a
     b.ne loc_not_ptrace_svc_jmp
     ldr x17, #0x8
     br x17
     orig_svc_next_addr_1
     orig_svc_next_addr_2
     ldr x17, #0x8
     br x17
     orig_svc_addr_1
     orig_svc_addr_2
     */
    uint64_t orig_svc_addr = (uint64_t)target_addr;
    uint64_t orig_svc_next_addr = (uint64_t)(target_addr+1*4);
    uint8_t check_jmp_data[] = {0x1f, 0x6a, 0x00, 0xf1, 0x51, 0x00, 0x00, 0x58, 0x20, 0x02, 0x1f, 0xd6, orig_svc_next_addr&0xff, (orig_svc_next_addr>>8*1)&0xff,  (orig_svc_next_addr>>8*2)&0xff,  (orig_svc_next_addr>>8*3)&0xff,  (orig_svc_next_addr>>8*4)&0xff,  (orig_svc_next_addr>>8*5)&0xff,  (orig_svc_next_addr>>8*6)&0xff,  (orig_svc_next_addr>>8*7)&0xff, 0x51, 0x00, 0x00, 0x58, 0x20, 0x02, 0x1f, 0xd6, orig_svc_addr&0xff, (orig_svc_addr>>8*1)&0xff,  (orig_svc_addr>>8*2)&0xff,  (orig_svc_addr>>8*3)&0xff,  (orig_svc_addr>>8*4)&0xff,  (orig_svc_addr>>8*5)&0xff,  (orig_svc_addr>>8*6)&0xff,  (orig_svc_addr>>8*7)&0xff};
    
    int check_jmp_data_size = 10*4;
    memcpy((void *)(new+4*4), check_jmp_data, check_jmp_data_size);
    
    // 4.patch target address to jmp hook code
    void* patch_addr = copy_from_addr;
    uint64_t new_p = (uint64_t)new;
    
    /*
     ldr x16, #0x8
     br x16
     hook_code_addr_1
     hook_code_addr_2
     */
    uint8_t patch_data[] = {0x50, 0x00, 0x00, 0x58, 0x00, 0x02, 0x1f, 0xd6,new_p&0xff, (new_p>>8*1)&0xff,  (new_p>>8*2)&0xff,  (new_p>>8*3)&0xff,  (new_p>>8*4)&0xff,  (new_p>>8*5)&0xff,  (new_p>>8*6)&0xff,  (new_p>>8*7)&0xff};
    int patch_data_size = 4*4;
    patchCode(patch_addr, patch_data, patch_data_size);
    
    // 5. set new page to r-x
    mprotect(new, len, PROT_READ | PROT_EXEC);
    
    return true;
}

这里代码比较复杂,大致分为以下步骤

  • map一页内存new,后面会将hook的代码写到里面

  • copy原svc前的四条指令保存到new页(目前没有进行相对寻址修复)

  • 将hook判断的代码写到紧接着前面四条指令的后面,汇编代码大致如下

     cmp x16, #0x1a
     b.ne loc_not_ptrace_svc_jmp
     ldr x17, #0x8
     br x17
     orig_svc_next_addr_1
     orig_svc_next_addr_2
     ldr x17, #0x8
     br x17
     orig_svc_addr_1
     orig_svc_addr_2
    

    就是简单的判断了系统调用号是否为26,若满足就跳到svc的下一条指令,若不是则跳回原svc指令以保证其他系统调用正常执行。

  • patch目标地址进行hook跳转,由于进行任意地址跳转需要4条指令大小,所以这里覆盖了svc前的四条指令

     ldr x16, #0x8
     br x16
     hook_code_addr_1
     hook_code_addr_2
    

    这里就是在执行svc指令前使其跳转到我们的hook代码

  • 最后将new这页设置为可读不可写可执行的页属性

总结/Todo

其实对于这种inlinehook去绕过调试,后面发现已经有人已经实现了,因为只要实现了inlinehook,肯定能hook代码绕过。不过我这里主要是想去自己分析以及实现这里面的很多细节。因为hook框架由于要考虑到稳定,兼容等等因素,所以往往代码不是很直接。而这里通过仅仅实现绕过反调试的需求,所以代码都比较通俗易懂,原理来说都是一样的。只有自己去动手写了代码才发现里面的乐趣所在,比如如何去实现系统调用的判断?如何解决寄存器污染?如何去实现代码段patch?当然还有很多汇编级别的坑存在,踩坑解决坑同样有意思,这里就不一一介绍。

后面主要还有两个事需要做:

  • 相对寻址指令的修复问题,以及hook代码的稳定兼容扩展问题。
  • 抽离相关代码,集成到xia0LLDB之中,真正实现调试器中一键绕过反调试。

参考

Posts: 5

Participants: 5

Read full topic

如何在rootless环境下的launchdaemon执行bash脚本

$
0
0

@Lakr233 wrote:

之前被坑到了 忘记了bash目录不在根目录下 。。。 发个帖提醒一下

extern char **environ;
void run_cmd(char *incmd) {
pid_t pid;

NSString *run = [[NSString alloc] initWithUTF8String:incmd];
run = [[NSString alloc] initWithFormat:@"'%@'", run];
char *cmd = (char *)[run UTF8String];
// aviod echo went wrong on rootless jb

char *argv[] = {"bash", "-c", cmd, NULL, NULL};
int status;

NSString *cmdStr = [[NSString alloc] initWithUTF8String: cmd];
NSLog(@"[Execute] bash -c %@", cmdStr);

if (isRootless) {
    NSString *rtcmd = [[NSString alloc] initWithFormat: @"PATH=/var/containers/Bundle/tweaksupport/usr/local/bin:/var/containers/Bundle/tweaksupport/usr/bin:/var/containers/Bundle/tweaksupport/bin:/var/containers/Bundle/iosbinpack64/usr/sbin/"];
    rtcmd = [rtcmd stringByAppendingString:@" "];
    rtcmd = [rtcmd stringByAppendingString: [[NSString alloc] initWithUTF8String:incmd]];
    char *rootlessARGS[] = {"bash", "-c", (char *)[rtcmd UTF8String], NULL, NULL};
    status = posix_spawn(&pid, "/var/containers/Bundle/tweaksupport/bin/bash", NULL, NULL, rootlessARGS, environ);
} else {
    status = posix_spawn(&pid, "/bin/bash", NULL, NULL, argv, environ);
}

if (status == 0) {
    if (waitpid(pid, &status, 0) == -1) {
        perror("waitpid");
    }
}

}

Posts: 1

Participants: 1

Read full topic

聊一个与socket有关的内核漏洞

$
0
0

@Lakr233 wrote:

本来我是不想发的,应为这个漏洞的具体实现有很多地方我自己都搞不懂。但是后来想想好像发了也不能怎样啊你有本事你笑我狂笑别停笑的大声点!(对不起又中二了)然后就是希望能有大佬指正这里面可能存在的错误也顺便吸取一点经验。海涵撒

以下,拷贝自俺的内网博客。

浅谈一个iOS内核执行的利用原理

这个漏洞来自盘古的ISC2019的演讲。我在这里写下他作为今后的注记。希望能有一天回过头来,看看这篇文章,回味一下那位少年第一次明白一个漏洞时的欣喜还有一种说不出的激动。

首先我们来聊一聊socket的生命。Socket是一种很有意思的通信介质,他可以绑定在对外的端口上,可以绑定在文件上,也可以是内存中的一个指针。两个socket能相互连接,相互作用,传输数据,相亲相爱的过完简简单单的一辈子。两个正常的socket,会有两把雨伞,凭借着已经确认的眼神,漫步走在阿里园区,彼此交换心声,这是他们与生俱来的本领。

55

上面的代码需要一些头文件,类型和函数主要来自types.h/socket.h/un.h。这里我们创建两个socket来玩玩。接下来,我们要把socket绑定到文件上。

这里我们创建了sockaddr_un这个来自sys/un的结构体。我们清除他,并把他初始化。这里我们把sock的文件定在了桌面上。这么一来,我们就可以在bind函数中,把socket绑定到文件上。按照socket的惯例,当你绑定了一个socket到一个指定文件文件上的时候,这个文件的vnode就会被锁上。你没办方继续绑定其他的socket到这个文件上。

但是这里就有一个问题,创建文件并返回vnode需要大量的时间。这里大量的时间指的是储存设备跟不上处理的速度。于是,XNU就很“聪明”的临时解锁了这个socket。当请求的vnode被返回时,XNU会继续上锁并进行接下来的操作。我在XNU的源代码内找到了这样一条有意思的代码。(我其实自己也不确定是不是这里但是好像这个也能这个么玩

这里,当socket提交到bind的时候,bind函数会执行一些简单的检查比如socket是不是已经被shutdown了,是不是已经bind过了之类的。在检查完毕以后,在提交vnode请求之前,socket会被unlock。这么一来,我们依然有机会去race两个socket,去绑到同一个文件上面去。

于是乎,我们就可以循环尝试去bind这两个socket。(不知道为啥我两个小时持续bind一直没成功过)据盘古的大大说这个bind成功率很高,而且也没有打对应的patch。所以我很疑惑咯。

接下来,如果我们bind成功了,我们就能尝试去free一个socket。当你close这个socket的时候,会有一个结构体被释放。这个结构体被释放以后,我们就会拥有一个悬垂指针。(Dangling Pointer?)(Dancing Point!)接下来我们的目标就是把我们需要的数据写会这个结构体对应的内存里面。怎么做呢?

首先,我们在bind我们需要的socket之前申请4k个socket,并在我们需要的socket之后申请4k个socket。这样一来,我们在内存中就拥有了一大段连续的地址。接下来我们用double bind+free的方式创造一个悬垂指针。接下来,我们需要释放全部的socket并触发一次内核的Garbage Collect以便重新申请需要的地址。

这里我们申请去分配一大堆没用的mach port。如果我们在发送kalloc的时候,内核的响应时间比较长,那么应该就是出发了内核内存回收。在触发了内存回收以后,我们重新申请非常大的一段内核地址,这样一来就有很大的概率hit到之前的悬垂指针所指向的内存。

boom

接下来我们计算出悬垂指针所在的位置,按照offset写入我们需要的数据。接下来我们就可以利用socket.connect这个方法去掉用其中的一个被我们修改过的内核函数指针所在的函数。我们在调用之前,别忘了往寄存器内写入参数。这样一来,内核的任意执行就完成了。当然A12不允许你这么做我也没办法咯~

2019年秋

Posts: 1

Participants: 1

Read full topic

解决 iOS 12.4 Killed: 9 的问题

$
0
0

@exchen wrote:

解决 iOS 12.4 Killed: 9 的问题

随着 iOS 12.4 的越狱出来之后,不少人的手机都升级到 12.4,最近我也在使用 12.4 做调试机,一开始 debugserver 也遇到一些坑,不过好在都解决了。最后剩下一个很头疼的问题,就是自己写的 App 上传到手机 /Applications 目录下,发现既然不能运行,提示 Killed: 9。

按照老套路,使用 codesign 或者 ldid 签名:

codesign -s - --entitlements ent.plist -f test12

由于 iOS 12 需要添加 platform-application,和签名 debugserver 一样,完整的 ent.plist 如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.backboardd.debugapplications</key>
	<true/>
	<key>com.apple.backboardd.launchapplications</key>
	<true/>
	<key>com.apple.diagnosticd.diagnostic</key>
	<true/>
	<key>com.apple.frontboard.debugapplications</key>
	<true/>
	<key>com.apple.frontboard.launchapplications</key>
	<true/>
	<key>com.apple.security.network.client</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
	<key>com.apple.springboard.debugapplications</key>
	<true/>
	<key>com.apple.system-task-ports</key>
	<true/>
	<key>get-task-allow</key>
	<true/>
	<key>platform-application</key>
	<true/>
	<key>run-unsigned-code</key>
	<true/>
	<key>task_for_pid-allow</key>
	<true/>
</dict>
</plist>

签名之后再次上传到 /Applications,运行还是提示 Killed 9,尝试 setuid(0); setgid(0);,还有设置文件权限也都不管用,都是提示 Killed 9

chown root:wheel test12
chmod u+s test12

不过发现之前写的插件打包成 deb 可以在 iOS 12.4 运行,文件也是会释放到 /Applications 目录,于是尝试把 test12.app 制作成 deb 包,上传到 /var/mobile/test12.deb,使用 dpkg 命令安装,安装成功后发现果然是可以执行。

 dpkg -i /var/mobile/test12.deb
 uicache

使用 ldid -e /Applications/test12.app/test12 >> /var/mobile/test12.plist,将 entitlement 导出查看比之前的多了两条,不过添加上这两条重新签名依然是不行。

<key>com.apple.private.skip-library-validation</key>
<true/>
<key>com.apple.private.security.no-container</key>
<true/>

思考一个问题,为什么手动上传的文件不能运行,而制作成 deb 包安装就可以呢?这两者有什么区别吗?尝试对 deb 安装后的文件静态修改,插入 LoadCommand 加载动态库,发现只要静态改过可执行文件,运行就提示 Killed 9,但是改过的文件打包成 deb,发现又可以运行了。

看样子是因为 dpkg 做了什么特殊的操作。在手机上找到 /usr/bin/dpkg,载入 IDA 静态分析,在 dir_sign_file 函数中找到了一条和签名相关的命令,猜测可能是和这个命令有关系,在释放完文件后,执行了签名命令。如下图所示:

最终得到的签名命令是这个,使用下面的命令对文件进行签名即可解决 Killed 9 的问题。

/usr/bin/ldid -P -K/usr/share/jailbreak/signcert.p12 -S/usr/share/entitlements/global.xml -M /Applications/test12.app/test12

更多干货分享,请关注微信公众号,会第一时间推送的。

//公众号图片暂时移除

Posts: 9

Participants: 4

Read full topic

IDA遇到浮点数计算时F5解析失败,如何复现计算过程

$
0
0

@LQMIKU wrote:

996.icu LICENSE

  • 问题背景
  • 解决方法
  • 总结

问题背景

问题背景也是写这篇文章的原因,起因于我之前在论坛发的贴子:求助:大量浮点数运算IDA的F5操作无法解析

总结下贴子中内容,也就是说,IDA在遇到大量浮点数运算时,按Fn+F5解析为伪代码时,是不会把这些运算给解析出来的。而Hopper则会解析为:在伪代码中保留汇编运算。

对于需要完整复现一套算法的逆向工程师来说,这种无法解析的情况是必须迈过去的一道坎,也是一个追求卓越的逆向工程师的必经之路。

参考贴子中大佬给的建议,以下是我个人的解决方法,也算是抛砖引玉,还望各位不吝赐教,欢迎提出更好的解决方法或者改进建议(或许可以编写一套自己的汇编解析方法,来自动生成伪代码)。

解决方法

我们知道在arm64架构中,存在着x0-x28的通用寄存器,以及v0-v31的浮点型寄存器(如下图,图片来自Xcode的调试信息窗口/iPhone真机调试)。


而大量的浮点数运算,就是靠这些浮点型寄存器完成的。也就是上图中的Floating Point Registers

正如函数传递通用参数(非浮点数)的调用约定是按x0-x7的顺序来的,传递浮点数时,则是按照v0-v7的顺序来的。因此,首先我们可以根据这个参数传递规则来确认输入的参数。

接着,就是本文的核心了,如何复现函数中的浮点数计算过程。

浮点型寄存器的结构其实很简单,从v0到v31,每一个浮点型寄存器都是16个字节组成的,形象的理解下,就是4个float数,即一个浮点型寄存器=4个float数。

因此,在复现的代码中,我们可以用float数组来模拟浮点型寄存器,比如v0浮点型寄存器,就可以用如下数组定义来表示

float v0[4] = {0};//模拟v0寄存器并初始化为0
float v1[4] = {0};//模拟v1寄存器并初始化为0
...

其余以此类推。
我们以一条非常简单的指令为例,来看看浮点数运算是怎么被复现的:

FSUB		S1, S3, S4

即S1 = S3 - S4,这里的S代表什么呢?我们可以从《ios应用逆向与安全》这本书中找到答案,第230页有如下说明:

128位寄存器,Q0-Q31
64位寄存器,D0-D31
32位寄存器,S0-S31

或者更直接的,通过动态调试,在调试信息窗口查看浮点型寄存器来进行确认。这样我们就知道了:上面那个S,就是代表1个float,并且是浮点型寄存器的第1个float。因此可复现为以下代码:

v1[0] = v3[0] - v4[0];

如法炮制,对于以下指令:

FMUL		D6, D6, D7

则可复现为:

*(double *)v6 = (*(double *)v6) * (*(double *)v7);

当然,除了以上这些非常简单的运算指令,我们还会遇到其他的arm64汇编指令,这种情况我们就可以直接在armDeveloper这个网站搜索指令,了解意思后再进行复现即可。

虽然上面的网站可以直接搜索指令,但国内网络体验并不好,因此也可以参考《ios应用逆向与安全》书中第229页的方法,在arm InfoCenter网站下载官方手册。不过目前官网也给出了以下说明(技术内容被移动到了上面那个体验并不好的armDeveloper网站= =),好在手册还能下载:

总结

一句话总结:浮点数计算由浮点型寄存器完成,因此理解浮点型寄存器的组成结构,用float数组复现对应计算就行!
最后一点建议,arm64汇编并不难,就是对一堆寄存器的简单操作:把内存中的数据载入寄存器->计算->存入内存。正是因为其操作过于简单,也导致了汇编的单调乏味和容易出错的特性,也是后来C、C++等高等语言出现的契机。对于我们搞逆向的来说,耐心点、细心点就问题不大哈哈。

Posts: 1

Participants: 1

Read full topic

又又又又一个脱壳工具来了

$
0
0

@4chendy wrote:

是的,又又又又一个脱壳工具来了:LLDB调试器版本的脱壳工具

场景

我能想到的场景就是最近分析一个app的时候会在mod_init_func的时候crash,这样导致现有的脱壳工具无法正常脱壳,或者之前Frida没兼容iOS12以及其他特殊场景。所以就做了LLDB中的脱壳工具。

使用

由于在LLDB中脱壳的特殊性,所以这样我介绍下如何去使用这个脱壳工具

以下命令已集成到xia0LLDBissh

  • 以后台模式启动目标app
xia0 ~ $ issh debug -x backboard /var/containers/Bundle/Application/86E712C8-84CA-49AF-B2EA-01C37395A746/WeChat.app/WeChat
[*]:iproxy process for 2222 port alive, pid=1830
[*]:++++++++++++++++++ Nice to Work :) +++++++++++++++++++++
[*]:iOSRE dir exist
[*]:iproxy process for 1234 port alive, pid=14885
[*]:Run ps -e | grep debugserver | grep -v grep; [[ 0 == 0 ]] && (killall -9 debugserver 2> /dev/null)
[*]:/iOSRE/tools/debugserver file exist, Start debug...
[*]:Run /iOSRE/tools/debugserver 127.0.0.1:1234 -x backboard /var/containers/Bundle/Application/86E712C8-84CA-49AF-B2EA-01C37395A746/WeChat.app/WeChat
  • 连接到远端debugserver
(lldb) pcc
Process 19633 stopped
* thread #1, stop reason = signal SIGSTOP
    frame #0: 0x00000001200f5000 dyld`_dyld_start
dyld`_dyld_start:
->  0x1200f5000 <+0>:  mov    x28, sp
    0x1200f5004 <+4>:  and    sp, x28, #0xfffffffffffffff0
    0x1200f5008 <+8>:  mov    x0, #0x0
    0x1200f500c <+12>: mov    x1, #0x0
    0x1200f5010 <+16>: stp    x1, x0, [sp, #-0x10]!
    0x1200f5014 <+20>: mov    x29, sp
    0x1200f5018 <+24>: sub    sp, sp, #0x10             ; =0x10
    0x1200f501c <+28>: ldr    x0, [x28]
Target 0: (dyld) stopped.
  • 一些断点设置情况
(lldb) b getpid
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) c
Process 19633 resuming
1 location added to breakpoint 1
Process 19633 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000181ceb570 libsystem_kernel.dylib`__getpid
libsystem_kernel.dylib`__getpid:
->  0x181ceb570 <+0>:  adrp   x9, 124120
    0x181ceb574 <+4>:  add    x9, x9, #0x90             ; =0x90
    0x181ceb578 <+8>:  ldr    w0, [x9]
    0x181ceb57c <+12>: cmp    w0, #0x0                  ; =0x0
    0x181ceb580 <+16>: b.ls   0x181ceb588               ; <+24>
    0x181ceb584 <+20>: ret
    0x181ceb588 <+24>: mov    x16, #0x14
    0x181ceb58c <+28>: svc    #0x80
Target 0: (WeChat) stopped.
(lldb) xbr -E init
[*] breakpoint at mod int first function:0x1034c7db8
Breakpoint 2: where = WeChat`___lldb_unnamed_symbol143521$$WeChat, address = 0x00000001034c7db8
(lldb) br disable 1
1 breakpoints disabled.
(lldb) c
Process 19633 resuming

这里解释一下为什么需要这么设置断点:b getpid这个断点主要是保证xbr -E init这个命令在断点触发的时候能够顺利执行,xbr -E init这个命令能够解析内存中的MachO格式找到mod_init_func然后对第一个init函数下断点,这样保证是目前app的最早执行时机,另外还能指定下断点到main函数xbr -E main,由于main函数在init之后,所以一般就对init下断点(有种情况在于app可能没有init函数,这时候就需要对main下断点,这里按实际情况处理)。
这样下好断点以后,禁用或者删除第一个断点(第一个断点调用很频繁且后面不再需要),这样让程序继续执行,等待断点触发。

  • 执行dumpdecrypted命令进行脱壳
(lldb) dumpdecrypted
[+] Dumping WeChat
[+] detected 64bit ARM binary in memory.
[+] offset to cryptid found: @0x100018d48(from 0x100018000) = d48
[+] Found encrypted data at address 00004000 of length 101662720 bytes - type 1.
[+] Opening /private/var/containers/Bundle/Application/86E712C8-84CA-49AF-B2EA-01C37395A746/WeChat.app/WeChat for reading.
[+] Reading header
[+] Detecting header type
[+] Executable is a plain MACH-O image
[+] Opening /var/mobile/Containers/Data/Application/9649276C-C413-4916-B5AB-AE13C8D7B652/Documents/WeChat.decrypted for writing.
[+] Copying the not encrypted start of the file
[+] Dumping the decrypted data into the file
[+] Copying the not encrypted remainder of the file
[+] Setting the LC_ENCRYPTION_INFO->cryptid to 0 at offset d48
[+] Closing original file
[+] Closing dump file
[*] This mach-o file decrypted done.

Developed By xia0@2019

如果一切顺利,就能顺利完成脱壳,这里目标文件在/var/mobile/Containers/Data/Application/9649276C-C413-4916-B5AB-AE13C8D7B652/Documents/WeChat.decrypted

  • 取回脱壳后的文件到本地,这里用到了issh一条命令取回
xia0 ~ $ issh scp /var/mobile/Containers/Data/Application/9649276C-C413-4916-B5AB-AE13C8D7B652/Documents/WeChat.decrypted /tmp
[*]:iproxy process for 2222 port alive, pid=1830
[*]:++++++++++++++++++ Nice to Work :) +++++++++++++++++++++
[*]:/var/mobile/Containers/Data/Application/9649276C-C413-4916-B5AB-AE13C8D7B652/Documents/WeChat.decrypted is remote file, so cp it from device
WeChat.decrypted                                                                                             100%  122MB  11.7MB/s   00:10

其他注意事项

目标App文件过大,可能会出现加密段会有内存读取错误,解决办法就是手动在LLDB里面读取一下这个段内容,然后再执行就没问题了。

用到两个工具:

参考

Posts: 1

Participants: 1

Read full topic

AntiHook

$
0
0

@Tanner wrote:

AntiFishHook

antiFishHook 是我今年早些时候学习庆总的书,写的一个反fishhook的一个工具
根据mach-o的符号动态链接原理,让non-lazy/lazy symbol 指针重新指向对应的symbol stub代码位置,起到在runtime的反hook

那么如何找到符号指针对应的stub代码,其实可以先用MachOView静态的观察数据段的lazy symbol pointers指向的指令的特点。如下图,

每个指针的值减去0x100000000可以得到一个文件偏移的值,0x7E74,0x7E38 … 而文件偏移值指向的是代码段 stub helper的位置, 并且每个函数的指令格式都一样, 如下

ldr w16 #xxxx0
b xxxx1
xxxx0

xxxx0表示ldr w16 #xxxx0指令位置 + 8字节内存地址的数据
xxxx1表示symbol stub代码的位置

每个不同的符号,xxxx0是不同的,但是xxxx1的位置是一样的
因此可以知道符号的信息在xxxx0中,那么xxxx0代表什么呢?

首先先来看看xxxx0的值都是些什么吧,xxxx0占用4个字节,某个符号的xxxx0数据如下

0x20,0x01,0x00,0x00, 机器读取的数据为0x0120, 那么这代表什么数据呢?

如果对mach-o比较熟悉的可能知道mach-o有一个Dynamic Loader Info段, 这里一共有四个区,分别是

Rebase Info: pointer rebase的信息
Binding Info: non-lazy symbol pointer绑定需要的信息
Lazy Binding Info: lazy symbol pointer绑定需要的信息
Export Info: 暴露给外部的符号的信息

在拜读庆总的《iOS应用逆向与安全》过程中, 书中有说到0x0120是Binding Info或者Lazy Binding Info区起始开始到符号信息的偏移,而符号信息如下图


可以看到偏移+6 bytes是该符号名,根据这个信息,就可以筛选出stub helper模板指令对应的符号了,从而将non/lazy symbol pointer从新指向对应的stub helper区代码地址,起到anti-fishhook的功能

AntiMSHook

antiMSHook 是我最近学习MSHookFunction原理的时候想到一个anti-msHook方案,实现效果和anti-fishhook一样,让msHook在runtime失效。

anti-msHook包括两个hook check和anti-hook

hook check 比较简单,主要是检测函数入口的前面4个指令是否是MSHook的模板指令

anti-hook 则利用了MSHook将被hook的指令分配到新的内存区域(mmap),通过遍历进程的虚拟内存区域,查看可读可执行的区域开头几个指令是否存在下面指令,如果存在,则找到了被msHook的指令。直接jump到该内存区域,执行原来的函数逻辑

ldr x16 #8
br x16
bytes  // bytes(pointer) => orig_func_address + 16

(最近写的,目前在我的越狱iOS 8.1.3 5s上实验了一个demo是OK的)

单指令Hook检测

在学习iOS inlinehook绕过反调试时产生的想法。
目前还只是想法, 也不知道可行性怎么样

还是反调试那个例子

  1. 检查目的函数起始地址到10个(视情况定)指令内是否存在mov x16, #26(0xd2800350); svc #128(0xd4001001) 二进制 (检测svc 没被patch)
  2. 参考金丝雀的思路,在写代码时,在目的函数执行前初始化1个变量如int a=0; 在svc指令后面对a++, 目的函数执行完判断a == 1 (防止svc #128之前被patch ret)

(当然完整性校验或许更为简单)

刚学逆向没多久,初级UI仔说的不对地方或者可行性有问题的,还请大佬们多多指教😀

Posts: 3

Participants: 3

Read full topic


Xcode 11 Beta 无法安装开发环境的临时解决方案

$
0
0

@Lakr233 wrote:

预期:打开Xcode并关闭

实际:34

原因:Xcode自带组件证书过期

解决方案:

sudo installer -pkg "/Volumes/Applications/Xcode.app/Contents/Resources/Packages/【假装我是通配符】.pkg" -target /

Posts: 1

Participants: 1

Read full topic

动态修改代码MemoryPatcher开源

$
0
0

@JerryXu wrote:

看到github上一个名叫KittyMemory的内存修改修补代码的源码,突然灵光一闪,决定写一个比它简单的修改器。
说干就干,用时一天,写出了一个非常简短的内存修改,可以用于ios的代码修补(注意是汇编代码),也可以用于wg的开发(下面源码就是用MemoryPatcher框架写的cjzc的ios的wg(午后,无扩散,自瞄)。
优点:简短,快速。缺点:功能不齐全,建议拓展。最重要的是必须越狱,否则应用崩溃(此处我在编写的时候有疑惑,我并没有依赖Cydiasubstrate为什么不越狱会闪退?)
源码地址:MemoryPatcher

Posts: 6

Participants: 5

Read full topic

沙盒文件,UserDefaults,视图层级用Woodpecker轻松搞定

$
0
0

@woodpecker wrote:

给大家推荐下最近开发的Mac应用 Woodpecker,是一款iOS开发辅助工具,可以在Mac上访问和修改App的信息,目前的主要功能如下:

  1. 沙盒文件快速查看、编辑,支持sqlite
  2. 查看修改UserDefaults内容
  3. 查看视图层级,修改属性
  4. 监控网络请求http(s),不用设置代理
  5. 提供插件支持等

个人感觉对逆向工作也有很大帮助,使用方式和Reveal类似,只需导入一个framework即可,感兴趣的朋友可以试一试,绝大部分功能是免费的。
App Store:点击下载

Posts: 19

Participants: 9

Read full topic

Frida-iOS-dump无法安装在Catalina的解决方案

$
0
0

@Lakr233 wrote:

原理很简单:
Python.framework -> /System/Volumes/Data/System/Library/FrameworksStash/Python.framework

Catalina启动的时候挂载顺序如下

/dev/disk2s6 on / (apfs, local, read-only, journaled)
/dev/disk2s2 on /System/Volumes/Data (apfs, local, journaled, nobrowse)

Catalina恢复模式挂载如下

/dev/disk2s6 on /Volumes/macOS
/dev/disk2s2 on /Volumes/macOS\ -\ Data

Solution: 进入恢复模式或者从U盘启动,开个终端进uid0

#创建临时文件夹
mkdir -p /System/Volumes/Data/System/Library/FrameworksStash/Python.framework
#移动这个framework到数据分区
mv /System/Library/Frameworks/Python.framework\
⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ /Volumes/macOS\ - \ Data/System/Library/FrameworksStash/Python.framework
#链接framework到临时文件夹也就是启动以后会挂载的目标文件夹
ln -s /System/Volumes/Data/System/Library/FrameworksStash/Python.framework
⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠ ⁠/System/Library/Frameworks/Python.framework
#删除临时文件夹防止挂载冲突
rm -rf /System/Volumes/Data/System/Library/FrameworksStash/Python.framework

然后再按照标准过程安装就不会有问题了,安装完成记得修改一下访问权限 如果后续再出现No module named的话可以考虑指定python版本


11

Posts: 2

Participants: 1

Read full topic

Run a daemon (as root) on iOS11 and iOS12

$
0
0

@shenlongfuhuo wrote:

daemon包括三个部分:一个可执行的二进制文件、一个plist配置文件、一个二进制文件授权文件

注意事项

可执行二进制文件配置

  • 下载最新版theos,利用theos来创建一个可执行二进制文件
    $ nic.pl
    NIC 2.0 - New Instance Creator
    ------------------------------
      [1.] iphone/activator_event
      [2.] iphone/application_modern
      [3.] iphone/application_swift
      [4.] iphone/cydget
      [5.] iphone/flipswitch_switch
      [6.] iphone/framework
      [7.] iphone/ios7_notification_center_widget
      [8.] iphone/library
      [9.] iphone/notification_center_widget
      [10.] iphone/preference_bundle_modern
      [11.] iphone/tool
      [12.] iphone/tool_swift
      [13.] iphone/tweak
      [14.] iphone/xpc_service
    Choose a Template (required): 11
    Project Name (required): k9sd
    Package Name [com.yourcompany.k9sd]: com.slfh.k9sd
    Author/Maintainer Name [XX]: slfh
    Instantiating iphone/tool in k9sd/...
    Done
    
  • sublime打开main.mm填入以下内容
    #include <spawn.h>
    
    int spawn(const char* executable, ...) {
        int     ret;
        pid_t   pid;
        va_list args;
        va_start(args, executable);
        setuid(0);
        ret = posix_spawn(&pid, executable, NULL, NULL, (char* const *)args, NULL);
        if (ret == 0) waitpid(pid, NULL, 0);
        return ret;
    }
    
    static void logout(CFNotificationCenterRef center, void   *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo) {
        spawn("/usr/bin/killall", "/usr/bin/killall", "-9", "SpringBoard", NULL);
    }
    
    int main(int argc, char **argv, char **envp) {
        NSLog(@"k9sd: k9sd is launched!");
        CFNotificationCenterAddObserver(  CFNotificationCenterGetDarwinNotifyCenter(), NULL, logout,   CFSTR("com.slfh.k9sd.logout"), NULL,   CFNotificationSuspensionBehaviorCoalesce);
        CFRunLoopRun(); // keep it running in background
        return 0;
    }
    

plist文件配置

  • 创建plist文件并配置权限

    $ cd k9sd/
    $ touch com.slfh.k9sd.plist
    $ chmod 644 com.slfh.k9sd.plist
    
  • 安装后把com.slfh.k9sd.plist放到iPhone上的/Library/LaunchDaemons/目录

     $ mkdir -p ./Layout/Library/LaunchDaemons/
     $ mv com.slfh.k9sd.plist ./Layout/Library/LaunchDaemons/
    
  • sublime打开com.slfh.k9sd.plist,并填入内容

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs  /PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
            <key>KeepAlive</key>
            <true/>
            <key>Label</key>
            <string>com.slfh.k9sd</string>
            <key>Program</key>
            <string>/usr/bin/k9sd</string>
            <key>RunAtLoad</key>
            <true/>
    </dict>
    </plist>
    

测试

  • 利用make install进行安装
  • 安装后ssh进入iPhone执行ps -e | grep k9sd,发现并没有启动
  • 使用控制台可以查看如下错误,错误是由于没有给二进制文件授权
    Sandbox: hook..execve() killing k9sd[pid=14153, uid=0]: outside of container && !i_can_has_debugger
    

二进制文件授权

  • 新建授权文件

    touch ./Layout/Library/LaunchDaemons/com.slfh.k9sd.entitlements
    
  • 打开com.slfh.k9sd.entitlements,填入以下内容

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs  /PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>platform-application</key>
        <true/>
    </dict>
    </plist>
    
  • 新建postinst文件,作用是deb安装完成后执行相应的命令

    $ mkdir ./bin
    $ touch ./bin/postinst
    $ chmod 755 ./bin/postinst
    
  • 打开postinst文件,填入以下内容

    #!/bin/sh
    /usr/bin/ldid -S/Library/LaunchDaemons/com.slfh.k9sd.entitlements /usr/bin/k9sd;
    /bin/launchctl load /Library/LaunchDaemons/com.slfh.k9sd.plist;
    exit 0;
    
  • 打开Makefile文件填入以下内容(如果make错误,需要把这些命令缩进对齐然后再次tab)

    before-package::  
      cp ./bin/postinst ./.theos/_/DEBIAN/
      rm -rf ./packages/*.deb
    
    after-install::
      install.exec "killall -9 SpringBoard"
    

再次测试

  • 利用make clean && make && make package && make install进行安装(记得要先打包然后安装!!!)

  • 安装后ssh进入iPhone执行ps -e | grep k9sd,发现已经启动了

    slfh:~ root# ps -e | grep k9sd
    13572 ??         0:00.00 (k9sd)
    14184 ??         0:00.02 /usr/bin/k9sd
    14318 ttys000    0:00.04 grep k9sd
    slfh:~ root#
    
  • 使用Cycript发送通知,进行重启SpringBoard操作

iOS11使用Cycript参考
iOS12使用Cycript参考

  • iOS11如下
    slfh:~ root# cycript -p SpringBoard
    cy# np = @encode(unsigned int(*)(char const*))(dlsym(RTLD_DEFAULT,     "notify_post"))
    &(extern "C" unsigned int notify_post(char const*))
    cy# np("com.slfh.k9sd.logout")
    [14621] DarwinInjector.cpp[246]: _krncall(mach_vm_read_overwrite) =10000003
    *** _assert(status == 0):../Inject.cpp(143):InjectLibrary
    slfh:~ root#
    
  • iOS12如下
    iPhone:~ root# cyrun -n SpringBoard -e -d -f
    applicationName: SpringBoard is running (9707)
        executableName: SpringBoard
        bundleIdentifier: com.apple.springboard
        Cycript is inactive:
        Device is not passcode locked
        Tweak Mode
    Waiting for Process to close...
    Waiting for SpringBoard to launch...
    Waiting for Cycript to become active...
    Success, you may now run
        cycript -r 127.0.0.1:8556
    cy# np = @encode(unsigned int(*)(char const*))(dlsym(RTLD_DEFAULT,         "notify_post"))
    &(extern "C" unsigned int notify_post(char const*))
    cy# &(extern "C" unsigned int notify_post(char const*))
    &(extern "C" unsigned int notify_post(char const*))
    cy# np("com.slfh.k9sd.logout")
    0
    cy#
    
  • 成功,到此结束

Posts: 1

Participants: 1

Read full topic

Supercharge.app

$
0
0

@Lakr233 wrote:

超级牛逼的插件制作工具

Supercharge Preview is now available at supercharge.app.

Posts: 2

Participants: 2

Read full topic

闲着没事拿 知了im 练练手

$
0
0

@yh8577 wrote:

春节快到了是不是又有红包抢了.

知了im 自动抢红包.

直接上源码

%hook WFCCNetworkService

  • (void)onReceiveMessage:(id)arg1 hasMore:(_Bool)arg2 {

    NSArray *datas = [NSArray arrayWithArray:arg1];

    WFCCMessage *msg = datas.lastObject;

    NSObject *obj = msg.content;

    NSString *obj_cls = NSStringFromClass(obj.class);

    if ([obj_cls isEqualToString:@“WFCCRedpacketMessageContent”]) {

      WFCCRedpacketMessageContent *msgContent = obj;
    
      [%c(RPRedpacketReceiver) grabRedpacket:msgContent andGrabBlock:nil];
    

    }

    %orig;

}
%end

Posts: 1

Participants: 1

Read full topic


符号绑定的另一种打开方式

$
0
0

@LQM_pig wrote:

懒加载和非懒加载

iOS对于引用的外部符号,分为 Lazy SymbolNon-Lazy Symbol ,分别存储在 __DATA,__got 节和 __DATA,__la_symbol_ptr 节。


Non-Lazy Symbol 符号在dyld加载模块的时候,就会将真实的函数地址写入到对应的地址中,实现绑定。而 Non-Lazy Symbol 则会在第一次调用该函数的时候,为其动态寻找真实函数地址并进行绑定。

facebook基于符号绑定机制,写出了hook神器 fishhook ,通过查找符号指针并替换,从而达到hook效果!!!

然而,基于模块检测反hook,却甚是烦人。你可能会有反反hook来应付,但是它也有可能会有反反反hook来对付你~~~

那么,怎么才能终结这场hook与反hook的心理战呢?

Mach-O View 分析动态符号绑定过程

简单分析一下 Lazy Symbol 的绑定过程:



这里以 NSLog 为例:

可以看出,符号 NSLog 所指向的地址为:0x0000000100006474。

转化为文件偏移为:0x0000000100006474 - 0x100008078 + 32888 = 0x6474;

到文件偏移为 0x6474 的位置查看:


这是一段可执行代码,地址0x6474处的意思是:读取0x647c位置处的四个字节的数据(0x1d),保存到w16寄存器。然后无条件跳转到0x645c(这里的地址,全部都是指文件偏移)。

这段代码,光这么看其实看不出什么,但是如果去调试的话,就会发现,这段代码实际上是在调用 dyld_stub_binder 为懒加载符号绑定真实地址。而刚刚在0x6474处的代码获取到的四字节的数据,实际上是符号绑定信息的偏移:

0xc428+0x1d = 0xc445

也就是说,动态绑定NSLog所需要的数据,就存储在0xc445处。

那么,理论上来说,如果我们尝试着修改这里的数据,是不是就会改变符号的查找的过程呢?

实践

想的再多,都不如动手操作!!!

新建一个工程,书写如下代码(main.m):

__attribute__((constructor)) static void entry(int argc,char *argv[],char **apple,char **executablepath,struct mach_header_64 **mh_ptr){
    
    if (!strncmp(argv[0], "aaa", 3)) {
        printf("the same!!");
    }
}

并按照如上方式,查找到函数 strncmp 的Lazy Binding Info,做如下修改:


修改后

编写动态库并注入到可执行文件:
__attribute__((visibility("default"))) int strncmq(const char *__s1, const char *__s2, size_t __n);

int strncmq(const char *__s1, const char *__s2, size_t __n){
    printf("hook:%s\nhook:%s",__s1,__s2);
    return strncmp(__s1, __s2, __n);
}

重签名运行!!


发现已经替换成功了!!!

但是,用ida或者hopper分析一下二进制文件,会发现调用的还是原来的strncmp符号:


说明如果进行模块检测的话,还是可以检测出来的~因为虽然符号查找替换了,但是实际上"外套"还是strncmp。所以,继续把外套也修改了!!!

修改这两个处:


修改后:

总结

对于动态绑定的外部引用符号,能动手脚的地方确实很多!!!

Posts: 9

Participants: 5

Read full topic

Run an App as root on iOS11 and iOS12

$
0
0

@shenlongfuhuo wrote:

注意事项

  • 测试环境
    • macOS: 10.14.6
    • iPhoneOS: iOS11.0、iOS12.0、
    • iPhone机型:两个iPhone6
    • 越狱工具:unc0ver3.6.2
    • 没有测试:iOS13和iOS10,也许可以。
  • 关于链接
  • 实现目标:点击按钮执行killall -9 SpringBoard,注销SpringBoard

理论部分

1. App所有权

  • /Applications目录存放系统自带的应用,这个目录下存放的应用一般属于root:admin
  • 想要Run an App as root,要把自己的.app文件放到这个目录下

2. 用户标识

  • 文件的所有者和程序的所有者是不一样的,程序的所有者通常被用作用户标识
  • 为了Run an App as root,我们需要更改用户的uid和euid为0

新建RootApp

  • 使用nic.pl进行新建

    $ nic.pl
    NIC 2.0 - New Instance Creator
    ------------------------------
      [1.] iphone/activator_event
      [2.] iphone/application_modern
      [3.] iphone/application_swift
      [4.] iphone/cydget
      [5.] iphone/flipswitch_switch
      [6.] iphone/framework
      [7.] iphone/ios7_notification_center_widget
      [8.] iphone/library
      [9.] iphone/notification_center_widget
      [10.] iphone/preference_bundle_modern
      [11.] iphone/tool
      [12.] iphone/tool_swift
      [13.] iphone/tweak
      [14.] iphone/xpc_service
    Choose a Template (required): 2
    Project Name (required): RootApp
    Package Name [com.yourcompany.rootapp]: me.rootapp.app
    Author/Maintainer Name [XX]: drag
    [iphone/application_modern] Class name prefix (two or more characters) [XX]: RA
    Instantiating iphone/application_modern in rootapp/...
    Done.
    
  • RARootViewController.m中填写以下内容

    #import <spawn.h>
    
    int spawn(const char* executable, ...) {
        int     ret;
        pid_t   pid;
        va_list args;
        va_start(args, executable);
        ret = posix_spawn(&pid, executable, NULL, NULL, (char* const *)args, NULL);
        if (ret == 0) waitpid(pid, NULL, 0);
        return ret;
    }
    
    - (void)addButtonTapped:(id)sender {
      [_objects insertObject:[NSDate date] atIndex:0];
      [self.tableView insertRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:0   inSection:0] ] withRowAnimation:UITableViewRowAnimationAutomatic];
       NSLog(@"RootAppTest: %d, %d, %d", getuid(), geteuid(), spawn("/usr/bin/killall","/usr/bin/killall", "-9", "SpringBoard", NULL));
    }
    
  • 执行make clean && make && make package && make install进行编译安装

    • 看不到App图标,执行uicache刷新就可以了
    • RootApp会安装到/Applications目录下
  • 打开App,点击加号按钮打印uid和euid都为501,注销操作也失败,所以我们需要设置uid和euid。

    RootAppTest: 501, 501, 1
    

设置uid和euid

  • main.m中设置uid
    #import "RAAppDelegate.h"
    int main(int argc, char *argv[]) {
      @autoreleasepool {
        setuid(0);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass(RAAppDelegate.  class));
      }
    }
    
  • Makefile中添加如下内容设置euid
      after-stage::
        $(ECHO_NOTHING)chmod +s $(THEOS_STAGING_DIR)/Applications/RootApp.app/RootApp$(ECHO_END)
    
  • 执行make clean && make && make package && make install后,打开App,点击加号按钮,打印uid和euid都为0,注销操作执行失败,还需要为可执行文件签权
    RootAppTest: 0, 0, 1
    

可执行文件签权

  • 新建RootApp.entitlements文件,填写内容为:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/  DTDs  /PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>com.apple.private.security.no-container</key>
        <true/>
        <key>com.apple.private.skip-library-validation</key>
        <true/>
        <key>platform-application</key>
        <true/>
    </dict>
    </plist>
    
  • 把RootApp.entitlements放到和Makefile同级目录,Makefile填入下面内容对可执行文件签权
    RootApp_CODESIGN_FLAGS = -SRootApp.entitlements
    

最后

  • 最终Makefile内容为
    ARCHS = arm64
    TARGET = iPhone:latest:8.0
    
    include $(THEOS)/makefiles/common.mk
    
    APPLICATION_NAME = RootApp
    RootApp_FILES = main.m RAAppDelegate.m RARootViewController.m
    RootApp_FRAMEWORKS = UIKit CoreGraphics
    RootApp_CODESIGN_FLAGS = -SRootApp.entitlements
    
    include $(THEOS_MAKE_PATH)/application.mk
    
    after-stage::
        $(ECHO_NOTHING)chmod +s $(THEOS_STAGING_DIR)/Applications/RootApp.app/  RootApp$(ECHO_END)
    
    after-install::
      install.exec "su mobile -c uicache"
      install.exec "killall \"RootApp\"" || true
    
  • 执行make clean && make && make package && make install后,点击按钮,注销SpringBoard成功。

Posts: 1

Participants: 1

Read full topic

iOS 越狱史及技术简析 - iPhone OS (iOS 1)

$
0
0

@geniusumi wrote:

本文是对自己这么多年以来对 iPhone 越狱学习的一个总结。

总是谈越狱越狱,结果自己连基本核心都没有搞太懂,于是就想从技术角度回顾一下越狱这么多年都做了些什么,也希望能帮到有相同兴趣的人。

因为我从 iPhone 3G 才开始使用 iPhone,故之前的资料只能从网上摘取,如果有相关错误,敬请指出。

iPhone OS (iOS 1.0) - 2007 年 10 月

当时, iOS 还被叫做 iPhone OS ,预装在 iPhone 第一代里面。没有 App Store,只有一些基础应用,当然,也没有办法安装新的应用。

苹果一开始的想法是用户只需要使用 Safari 访问 HTML5 应用就够了,所以他们并没有提供 iPhone SDK。这个阶段越狱团队的工作一方面是逆向 iPhone 自带的应用程序,搞清楚 iPhone 原生应用程序是如何工作的,然后提供一套工具链用于开发自制的第三方应用[^1](事实证明,后来苹果发布的 SDK 就是这套东西),一方面是取得系统 root 权限,然后安装自制铃声和应用程序。

一个远古第三方自制程序是这样写的

#import <CoreFoundation/CoreFoundation.h>
#import <Foundation/Foundation.h>
#import <UIKit/UITableColumn.h>
#import <UIKit/UISwitchControl.h>
#import <UIKit/UIAlertSheet.h>
...
void progressCallback(unsigned int progress, unsigned int total, char* formatString, void* application) {
    UpgradeApplication* myApp = (UpgradeApplication*) application;
    [myApp doProgress:progress withTotal: total withFormat: formatString];
}
...
@implementation UpgradeApplication

- (void) applicationDidFinishLaunching: (id) unused
{
    UIWindow *window;
    UIView *mainView;
    struct CGRect rect;
    ...
}

[^1]: iPhone Open Application Development, 2nd Edition by Jonathan Zdziarski

这个时候的 iPhone 的安全机制如下:

  • 安全引导链(每个引导环节,都会由上一个引导环节检查签名,但bootrom此时不会检查LLB的签名)
  • fsboot 默认只读
  • AFC 默认只能访问并读写 /Media(即/var/root/Media)[^2]
  • lockdownd 激活锁,防止第三方运营商 SIM 卡使用 iPhone

所有应用程序都放在/Application下,并默认以root权限执行,没有代码签名,甚至刷机不会校验 SHSH(iPhone 一代),这意味着你可以随便降级,所以在这个阶段,越狱实际上是非常简单的。

iBoot cp-command

iOS 版本: iOS 1.0 - iOS 1.0.2

利用软件: iBrickr (Windows) / AppTapp Installer (Mac OS X)

严格意义上来说,这并不算一个漏洞,而更像是一个 feature。起因是 Apple 并没有删除恢复模式中的很多命令,比如cp

流程大致如下:

  • 让设备进入恢复模式,从固件中找出 Restore Ramdisk 和 kernelcache,然后上传到设备上。
  • 创建两个文件 /var/root/Media/fstab/var/root/Media/Services.plist,后者会创建一个新的服务com.apple.afc2,允许通过AFC访问所有文件目录。
  • /dev/disk0s1 挂载到 /mnt1,用户数据/dev/disk0s2挂载到/mnt2,然后把我们上一步创建的两个文件用cp分别替换掉/mnt1/etc/fstab/mnt1/System/Library/Lockdown/Services.plist
  • iBrickr 还会安装 PXLDaemon 守护进程,这个守护进程可以与电脑端 iBrickr 通信,安装第三方软件包,替换铃声等等。而 AppTapp Install 则会安装 Installer.app,其功能与前者大致相同。
  • 重启完成越狱。

[^2]: 这里的/Media指的是/root/Media而并非我们熟知的/var/mobile/Media,因为mobile用户在iOS 1.0-1.1.2中暂时还不存在,下同。

修复:

  • 在 iOS 1.1.2 后更新 iBoot,删除了cp以及其他一些命令。

libtiff-exploit (CVE-2006-3459)

iOS 版本: iOS 1.0 - iOS 1.1.1

利用软件: AppSnapp / JailbreakMe 1.0

核心原理是经典的 buffer overflow — libtiff 在处理 tiff 文件的时候会发生 buffer overflow,允许任意代码执行,只需要让用户访问一个含有tiff图片的网站就可以完成越狱。

碰巧的是,当年 PSP 也饱受 tiff 漏洞的摧残,这个漏洞应该是从 PSP 的■■中收到了启发。

shellcode 做了一些微小的工作:

  • /var/root/Media重命名为/var/root/oldMedia
  • 创建符号链接/var/root/Media/ -> /
  • 重新挂载/dev/disk0s1为读写
    stack.Add(Node(0, Node::PTR));           // r0 = "/var/root/Media"
    stack.Add(Node(1, Node::PTR));           // r1 = "/var/root/Oldmedia"
    stack.Add(Node(20, Node::BYTES));        // r2,r3,r5,r6,r12
    stack.Add(Node(12, Node::STACK));        // sp    -> offset 12
    stack.Add(ldmia_sp_r4);                  // lr = load r4,r7,pc from sp
    stack.Add(rename);                       // pc = rename(r0, r1)

    ...

    stack.Add(Node(2, Node::PTR));           // r0 = "/"
    stack.Add(Node(0, Node::PTR));           // r1 = "/var/root/Media"
    stack.Add(Node(20, Node::BYTES));        // r2,r3,r5,r6,r12
    stack.Add(Node(12, Node::STACK));        // sp -> offset 12
    stack.Add(ldmia_sp_r0);                  // lr = load from r0..pc from sp
    stack.Add(symlink);                      // pc = symlink(r0, r1)

    stack.Add(Node(3, Node::PTR));           // r0 = "hfs"
    stack.Add(Node(2, Node::PTR));           // r1 = "/"
    stack.Add(Node(0x00050000, Node::VAL));  // r2 = MNT_RELOAD | MNT_UPDATE
    stack.Add(Node(8, Node::STACK));         // r3 = **data
    stack.Add(mount);                        // pc = mount(r0, r1, r2, r3)
    stack.Add(Node(4, Node::PTR));           // data = "/dev/disk0s1"

完整代码可以在这里看到。

修复:

  • 更新 libtiff 库。

mknod-vulnerability

iOS 版本: iOS 1.1.2

利用软件: OktoPrep + touchFree

iOS 1.1.2 版本之后,Apple 修复了 libtiffiBoot cp-command 的漏洞,然而,因为前文所提到的,iPhone 第一代可以随便刷机,所以新版的方法也很简单粗暴:先在 iOS 1.1.1 动手脚,然后升级到 iOS 1.1.2 继续搞事情。

具体来说:

  • 在 iPhone 升级之前,OktoPrep 使用mknod /var/root/Media/disk c 14 1直接在用户盘符创建一个字符设备,这句命令的意思是,为主设备号是14,次设备号是1的设备创建一个在/var/root/Media/disk的字符设备,这等同于/dev/rdisk0s1。(可以使用ls -lR /dev查看主次设备号)
  • 升级系统到 iOS 1.1.2,由于升级系统只更改fsroot,而我们创建的文件在用户数据分区中,所以不受影响。
  • touchFree 检查 /var/root/Media/disk 是否存在,然后创建/var/root/Media/touchFree文件夹,复制必要文件到此文件夹中。
  • /var/root/Media/diskdump为rdisk0s1.dmg,挂载这个dmg文件,修改/etc/fstab
  • com.apple.syslogd.plist里面添加DYLD_INSERT_LIBRARIES:/var/root/Media/touchFree/planetbeing.dylib环境变量键值对(熟悉逆向的朋友们可能已经猜到了,没错,syslogd在下一次执行的时候会首先被注入这个动态库。)
  • 动态库会将touchFree文件夹里的东西复制到/bin,创建 AFC2 服务,运行/var/root/Media/touchFree/run.sh脚本,然后把自己的注入环境变量删掉。
  • 脚本会继续复制Installer.app和ssh服务,然后给Springboard和lockdown打补丁。

其中,Springboard 补丁是因为当时 Springboard 显示的程序是写死在allowedDisplayIcons里的,所以需要给[SBIconModel addItemsToIconList: fromPath: withTags:][SBIconController relayoutIcons]里面打补丁,让Installer.app能显示在主屏幕,而lockdown的补丁主要是绕过iPhone激活锁。

修复:
/dev/rdisk0s1被加上了nodev,所以不能再用mknod创建它的设备文件了。

Ramdisk Hack

iOS 版本: iOS 1.1.3 - 1.1.5

利用软件: ZiPhone & iLiberty

从 iOS 1.1 开始,iPhone 新增了以下安全机制:

  • 新增mobile用户,大部分进程都开始以mobile权限运行。(iOS 1.1.3)
  • 所有固件都被Key 0x837加密了

这意味着即使应用出现了 libtiff 的 userland 漏洞,也不再能一招吃遍天了。

这次出现的漏洞还是在恢复模式中。之前 Apple 把 cp 之类的调试命令删掉了,但是,他们还留了一个boot-args环境变量。我们知道,恢复模式需要挂载一个 Ramdisk,这个 Ramdisk 在一般情况下是需要验证签名的。但如果 Ramdisk 的内存地址超过了0x09C00000,则无论什么情况下都会启动。

这回我们轻车熟路了,以ZiPhone为例:

  • AFC 复制必要文件到/var/mobile/Media
  • 重启进入恢复模式,设置引导环境变量setenv boot-args rd=md0 -s -x pmd0=0x09CC2000.0x0133D000(以单用户模式、安全模式启动)
  • 发送bootx,让 iPhone 从 Ramdisk 启动
  • Ramdisk 首先挂载/dev/disk0s1/dev/disk0s2,然后复制一些必要的文件,同时修改/etc/fstab,开机启动prof.sh
if [ "`/usr/bin/nvram jailbreak 2>/dev/null|/bin/cut -f 2`" == "1" ] ; then
/bin/echo "Starting jailbreak..."
/bin/cp /bin/sh /mnt1/bin/sh
/bin/cp /bin/sync /mnt1/bin/sync
/bin/cp /bin/rm /mnt1/bin/rm
/bin/cp /zib/prof.sh /mnt1/private/etc/profile
/bin/cp /zib/fstab /mnt1/private/etc/fstab
…
fi
  • 重启,prof.sh会完成之后的一系列操作

修复:
boot-args从iOS 2.0开始,在生产环境中不生效。(这一点可以从 iBoot 泄露的源码中看出)

// iBoot/apps/iBoot/main.c
bool
env_blacklist_nvram(const char *name)
{
    static const char * const whitelist[] = {
        ...
        "boot-args", // factory needs to set boot-args
        ...
        NULL
    }
    ...
}

总结

本文回顾了 iPhone OS 中大部分已经公开并使用的越狱工具与他们的利用方法。

虽然 iPhone OS 离我们已经有一轮年头了,但从上面的做的事情中,我们可以看出,实际上,越狱所做的东西还是没有变化太多。

就以本文所有越狱工具为例,基本上就做这么几件事:

  • 修改 /etc/fstab,挂载系统盘为rw
  • 安装一个软件包管理器(Installer.app),然后安装第三方软件
  • 给系统打补丁(解锁,换壁纸,铃声)

为了做这些事情,我们需要:

  • 修改系统文件
  • 破坏 iOS 引导链

在之后长达 12 年中,围绕着这两件事情,苹果跟安全研究员们展开了斗智斗勇。

参考资料

[1] https://www.peerlyst.com/posts/ios-jailbreaks-history-part-1-ivan-ponurovskiy?trk=profile_page_overview_panel_posts

[2] https://www.theiphonewiki.com/

[3] http://blog.iphone-dev.org/

Posts: 1

Participants: 1

Read full topic

来聊一个launchd新的奇怪机制

$
0
0

@Halo-Michael wrote:

首先我们来看一段panic log:
“panicString” : "panic(cpu 1 caller 0xfffffff00dc589a4): “userspace panic: File Radar to: [ launchd | missing executable ]. Set boot-arg launchd_missing_exec_no_panic=1 to work around. Job: ‘aqua.igg.daemon.plist’ Executable: ‘/Applications/iGameGuardian.app/no_gg_daemon’”\nDebugger message: panic\nMemory ID: 0x1\nOS version: 17D5026c\nKernel version: Darwin Kernel Version 19.3.0: Sun Dec 8 21:03:13 PST 2019; root:xnu-6153.80.8.0.1~13/RELEASE_ARM64_T8010\nKernelCache
可以看到由于插件igg创建了一个空守护进程文件守护一个不存在的进程
当checkra1n试图操控launchd加载这个守护进程文件的时候,由于找不到对应的文件,导致panic
解决办法是删掉这个空守护进程文件,或者按提示把launchd_missing_exec_no_panic=1加到boot-args中(未测试)
同时可以看到系统版本号为13.3.1(17D5026c),内核版本号19.3.0(6153.80.8.0.1~13)
这个机制我确定在之前的版本中是不存在的。那么问题来了,苹果为什么要设置这样一个奇怪的机制?恶心len?
开发者只能以后注意一下 不要学iGG开发者搞这种守护空气的守护进程

Posts: 3

Participants: 2

Read full topic

iOS11完美越狱不请自来

$
0
0

@Halo-Michael wrote:

直接上地址


应该是在36c3里面演示了的
还没看原理 不过应该之后会有介绍原理的文章

Posts: 1

Participants: 1

Read full topic

Viewing all 301 articles
Browse latest View live