2018-07-16

Samsung KNOX Protect and Bypass

1. Samsung KNOX Protect and Bypass

1.1. KASLR

debug 接口泄漏内核地址

1.2. Real-time kernel protection (RKP)

Linux kernel and secure world 的接口,通过 rpk_call() 调用

  1. 阻止未经授权的特权代码运行
  2. 阻止用户进程直接访问内核数据
  3. 监控一些危险内核数据结构并检测他们是不是被利用或攻击了

    敏感操作全部由 rpk_call 调用实现,

    1. cred 操作: (copy_creds, commit_creds, rkp_override_creds…)
    2. 内存操作: (exec_mmap, allocate_slab, …)
    3. 执行操作: (execve…)
    4. 页表操作: (rkp_assign_pgd, set_pte, set_pmd, set_pud, …)
    5. mount 操作: (do_new_mount, …)

1.2.1. Kernel code protection

内核在编译的时候都开启了"KERNEL_TEXT_RDONLY", ".text"节区就不可写了, ".data"节区不可执行. 用户代码不能在内核模式下运行(PXN).

1.2.2. Kernel page and page table protection

RKP 为敏感的内核数据和对象提供只读内核页面,只能通过 secure world 来申请,释放,来操作他们的内核页面。这些页的表项应该不会受到页属性篡改的影响。

当内核必须访问受到保护的 PGD/PTE/PMD/PUD,相关函数时,将使用 rpk_call()函数来进入 secure world 来完成

static inline void set_pte(pte_t *ptep, pte_t pte)
{
#ifdef CONFIG_TIMA_RKP
  if (pte && rkp_is_pg_dbl_mapped((u64)(pte)) ) {
    panic("TIMA RKP : Double mapping Detected pte = 0x%llx ptep = %p",(u64)pte, ptep);
    return;
  }
  if (rkp_is_pg_protected((u64)ptep)) {
    rkp_call(RKP_PTE_SET, (unsigned long)ptep, pte_val(pte), 0, 0, 0);
  } else {
    asm volatile("mov x1, %0\n"
                 "mov x2, %1\n"
                 "str x2, [x1]\n"
                 :
                 : "r" (ptep), "r" (pte)
                 : "x1", "x2", "memory" );
    if (pte_valid_not_user(pte)) {
      dsb(ishst);
      isb();
    }
  }
#else
  *ptep = pte;

#endif /* CONFIG_TIMA_RKP */
}

1.2.3. Kernel data protection

内核数据保护基于页保护, 许多危险的全局变量都会放在".rkp.prot.page"节里, 这个节的页面在内核初始化之后是不能被覆盖的.

#define RKP_RO_AREA __attribute__((section (".rkp.prot.page")))


android_kernel_samsung_g9550\fs\namespace.c:
  73: RKP_RO_AREA struct super_block *sys_sb;
  74: RKP_RO_AREA struct super_block *rootfs_sb;

android_kernel_samsung_g9550\include\linux\security.h:
  85: #define RKP_RO_AREA __attribute__((section (".rkp.prot.page")))

android_kernel_samsung_g9550\init\main.c:
  253: RKP_RO_AREA unsigned int is_boot_recovery = 0;

android_kernel_samsung_g9550\kernel\cred.c:
  43: RKP_RO_AREA int rkp_cred_enable = 0;
  44: RKP_RO_AREA int is_recovery = 0;
  ...
  85   * The initial credentials for the initial task
  86   */
  87: RKP_RO_AREA struct cred init_cred = {
  88   .usage			= ATOMIC_INIT(4),
  89  #ifdef CONFIG_DEBUG_CREDENTIALS

android_kernel_samsung_g9550\security\selinux\hooks.c:
  110  #include<linux/rkp_entry.h>
  111
  112: RKP_RO_AREA struct task_security_struct init_sec;
  ...
  181
  182  #ifdef CONFIG_SECURITY_SELINUX_DEVELOP
  183: RKP_RO_AREA int selinux_enforcing;
  184
  185  static int __init enforcing_setup(char *str)
  ...
  200
  201  #ifdef CONFIG_SECURITY_SELINUX_BOOTPARAM
  202: RKP_RO_AREA int selinux_enabled = CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE;
  203
  204  static int __init selinux_enabled_setup(char *str)
  ...
  217  __setup("selinux=", selinux_enabled_setup);
  218  #else
  219: RKP_RO_AREA int selinux_enabled = 1;
  220  #endif
  221
  ...
6950  #endif
6951
6952: RKP_RO_AREA static struct security_hook_list selinux_hooks[] = {
6953   LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
6954   LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),



1.2.4. Kernel object protection

内核堆上的对象受到 RKP 保护

#define CRED_JAR_RO "cred_jar_ro"    //  credential of processes
#define TSEC_JAR  "tsec_jar"    // security context
#define VFSMNT_JAR  "vfsmnt_cache"    // struct vfsmount – mount namespace

static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
{
  // ...

#ifdef CONFIG_RKP_KDP
  if (s->name &&
      (!strcmp(s->name, CRED_JAR_RO) ||
       !strcmp(s->name, TSEC_JAR)||
       !strcmp(s->name, VFSMNT_JAR))) {
  }
  virt_page = rkp_ro_alloc();

  // ...
#endif
  // ...
}

这些内核对象在内核或用户模式下都是只读的, 申请, 释放和覆盖都是在 secure world 内完成的.

/**
 * override_creds - Override the current process's subjective credentials
 * @new: The credentials to be assigned
 *
 * Install a set of temporary override subjective credentials on the current
 * process, returning the old set for later reversion.
 */
const struct cred *rkp_override_creds(struct cred **cnew)
{
  const struct cred *old = current->cred;
  struct cred *new = *cnew;
  struct cred *new_ro;
  volatile unsigned int rkp_use_count = rkp_get_usecount(new);
  void *use_cnt_ptr = NULL;
  void *tsec = NULL;

  kdebug("override_creds(%p{%d,%d})", new,
         atomic_read(&new->usage),
         read_cred_subscribers(new));

  validate_creds(old);
  validate_creds(new);

  if(rkp_cred_enable) {
    cred_param_t cred_param;
    new_ro = kmem_cache_alloc(cred_jar_ro, GFP_KERNEL);
    if (!new_ro)
      panic("override_creds(): kmem_cache_alloc() failed");

    use_cnt_ptr = kmem_cache_alloc(usecnt_jar,GFP_KERNEL);
    if(!use_cnt_ptr)
      panic("override_creds() : Unable to allocate usage pointer\n");

    tsec = kmem_cache_alloc(tsec_jar, GFP_KERNEL);
    if(!tsec)
      panic("override_creds() : Unable to allocate security pointer\n");

    rkp_cred_fill_params(new,new_ro,use_cnt_ptr,tsec,RKP_CMD_OVRD_CREDS,rkp_use_count);
    rkp_call(RKP_CMDID(0x46),(unsigned long long)&cred_param,0,0,0,0);

    rocred_uc_set(new_ro,2);
    rcu_assign_pointer(current->cred, new_ro);

    if(!rkp_ro_page((unsigned long)new)){
      if(atomic_read(&new->usage) == 1) {
        rkp_free_security((unsigned long)new->security);
        kmem_cache_free(cred_jar, (void *)(*cnew));
        *cnew = new_ro;
      }
    }
  }
  else {
    get_cred(new);
    alter_cred_subscribers(new, 1);
    rcu_assign_pointer(current->cred, new);
  }

  alter_cred_subscribers(old, -1);

  kdebug("override_creds() = %p{%d,%d}", old,
         atomic_read(&old->usage),
         read_cred_subscribers(old));
  return old;
}

Credential verifying in secure world:

在 Galaxy S6 时,攻击者可以简单的调用 rkp_override_creds()来绕过内核对象保护并提升权限,但在 S7 或以后版本不再有效了。

RKP 添加了另一种检测方式,来验证提交的新证书(credential)是否合法。


rkp_call(RKP_CMDID(0x46),(unsigned long long)&cred_param,0,0,0,0);

rkp 实现位于 hyp.mbn


1.2.5. Uid_checking()

在 adb 和 zygote 启动前,uid_checking 总是返回 ALLOW;

如果大于 1000 则不能将新创建的 credential 修改为小于 1000 的值

在这之后无特权的进程(uid>1000)不能覆盖高权限的证书(credential). 也就是说如果 uid 大于 1000 则不能将新创建的 credential 修改为小于 1000 的值。

这也是 S6 无法直接使用 override_creds 的原因。但是在 S7 上是可以修改内核的 capabilities,但是不能修改 uid.

__int64 __fastcall uid_checking(__int64 new_cred, __int64 old_cred)
{
  __int64 _new; // x20@1
  __int64 old; // x19@1
  int v4; // w0@1
  BOOL v5; // w2@1
  int v6; // w0@2
  int v7; // w0@3

  _new = new_cred;
  old = old_cred;
  v4 = check_cred_id_priv(*(_DWORD *)(new_cred + 4LL * uid_offset), *(_DWORD *)(old_cred + 4LL * uid_offset));
  v5 = 1;
  if ( !v4 )
    {
      v6 = check_cred_id_priv(*(_DWORD *)(_new + 4LL * gid_offset), *(_DWORD *)(old + 4LL * gid_offset));
      v5 = 1;
      if ( !v6 )
        {
          v7 = check_cred_id_priv(*(_DWORD *)(_new + 4LL * suid_offset), *(_DWORD *)(old + 4LL * suid_offset));
          v5 = 1;
          if ( !v7 )
            v5 = (unsigned __int64)check_cred_id_priv(
                                                      *(_DWORD *)(_new + 4LL * sgid_offset),
                                                      *(_DWORD *)(old + 4LL * sgid_offset)) != 0;
        }
    }
  return (unsigned int)v5;
}

__int64 __fastcall override_priv_uid(unsigned int new_uid, unsigned int old_uid)
{
  return (old_uid > 1000) & (unsigned __int8)(new_uid <= 1000);
}



1.2.6. sid_checking()

如果原来 sid > 20 && new_sid <= 19 操作会被禁止

__int64 __fastcall sid_checking(__int64 new_cred, __int64 new_cred_copy, __int64 old_cred, __int64 new_cred_copy_buf)
{
  // ...
  copy_security_buf = *(_QWORD *)(new_cred_copy_buf + 0x18);
  if ( copy_security_buf )
    {
      copy_security_pa = virt_to_phys(copy_security_buf);
      new_cred_pa = virt_to_phys(*(_QWORD *)(v4 + 8 * security_offset));
      old_sid = virt_to_phys(*(_QWORD *)(v6 + 8 * security_offset));
      if ( !security_pointer_valid(copy_security_pa) && new_cred_pa && old_sid )
        {
          LODWORD(old_sid) = *(_DWORD *)(old_sid + 4);
          new_sid = *(_DWORD *)(new_cred_pa + 4);
          if ( byte_8580A2A1[0]
               && (old_sid = (unsigned int)old_sid, v14 = new_sid, (unsigned int)old_sid > 0x14uLL)
               && new_sid <= 0x13uLL )
            {
              rkp_debug_log((unsigned __int16 *)"Selinux Priv Escalation !! [assign_creds]", new_sid, old_sid, 0LL);
              result = err_func(0x14u, v14, old_sid, 0LL);
            }
          // ...
        }
      // ...
    }
  // ...
  return result;
}




  1. Integrity_checking()

    检查当前的 credential 是否属于当前进程,当前的 security context 是否属于当前 credential

    int security_integrity_current(void)
    {
      if ( rkp_cred_enable &&
           (rkp_is_valid_cred_sp((u64)current_cred(),(u64)current_cred()->security)||
            cmp_sec_integrity(current_cred(),current->mm)||
            cmp_ns_integrity())) {
        rkp_print_debug();
        panic("RKP CRED PROTECTION VIOLATION\n");
      }
      return 0;
    }
    
    
    signed __int64 intergrity_check()
    {
      __int64 caller_thread_info; // x0@1
      __int64 caller_ti; // x23@1
      signed __int64 caller_thread_info_pa; // x0@1
      __int64 caller_ti_pa; // x22@1
      __int64 caller_cred; // x20@1
      signed __int64 caller_cred_pa; // x0@1
      __int64 _cred; // x19@1
      signed __int64 result; // x0@2
      __int64 security_pa; // x21@3
      __int64 bp_cred; // x1@6
    
      caller_thread_info = get_caller_thread_info();
      caller_ti = caller_thread_info;
      caller_thread_info_pa = get_physical_addr(caller_thread_info);
      caller_ti_pa = caller_thread_info_pa;
      caller_cred = *(_QWORD *)(caller_thread_info_pa + 8LL * cred_offset);
      caller_cred_pa = get_physical_addr(*(_QWORD *)(caller_thread_info_pa + 8LL * cred_offset));
      _cred = caller_cred_pa;
      if ( caller_cred_pa )
        {
          security_pa = get_physical_addr(*(_QWORD *)(caller_cred_pa + 8LL * security_offset));
          if ( security_pa || !byte_8580A2A1[0] )
            {
              bp_cred = *(_QWORD *)(security_pa + 8LL * *(_QWORD *)&bp_cred_offset);
              if ( bp_cred == caller_cred && *(_QWORD *)(_cred + 8LL * bp_task_offset) == caller_ti )
                {
                  result = 1LL;
                }
              // ...
            }
          // ...
        }
      // ...
    }
    
    
    
    
  2. Data Flow Integrity (DFI)

    该方法防止了 init_cred 重用的技巧,current->cred = init_cred;

    struct cred {
      // ...
    #ifdef CONFIG_RKP_KDP
      atomic_t *use_cnt;
      struct task_struct *bp_task;    // cred's owner
      void *bp_pgd;    // process's pgd
      unsigned long long type;
    #endif /*CONFIG_RKP_KDP*/
    };
    

    每次 committing/overriding 新的 credential, secure world RKP 会记录 bp_task

    同时也会在 task_security_struct 记录 credential

    struct task_security_struct {
      u32 osid;   /* SID prior to last execve */
      u32 sid;    /* current SID */
      u32 exec_sid;   /* exec SID */
      u32 create_sid;   /* fscreate SID */
      u32 keycreate_sid;  /* keycreate SID */
      u32 sockcreate_sid; /* fscreate SID */
    #ifdef CONFIG_RKP_KDP
      void *bp_cred;
    #endif
    };
    
    static inline unsigned int rkp_is_valid_cred_sp(u64 cred,u64 sp)
    {
      struct task_security_struct *tsec = (struct task_security_struct *)sp;
    
      if((cred == (u64)&init_cred) &&
         ( sp == (u64)&init_sec)){
        return 0;
      }
      if(!rkp_ro_page(cred)|| !rkp_ro_page(cred+sizeof(struct cred))||
         (!rkp_ro_page(sp)|| !rkp_ro_page(sp+sizeof(struct task_security_struct)))) {
        return 1;
      }
      if((u64)tsec->bp_cred != cred) {
        return 1;
      }
      return 0;
    }
    
  3. Summary of RKP and DFI

    在 RKP 开启后,即使我们可以实现任意内核内存的覆盖,但是我们也不能做到如下三点:

    1. 在内核模式下操作证书(credentials)和安全上下文(security context)
    2. 当前证书指向 init_cred
    3. 调用 rkp_override_creds()通知 secure world 帮助我们覆盖 uid 0~1000 进程的证书。

    但是我们仍然可以做到:

    1. 从用户模式下调用内核函数进行劫持 ptmx_fops->check_flags(intflag).
    2. 覆盖所有内核的证书(cred->cap_**)
    3. 覆盖其他未被保护的内核数据。

1.2.7. SELinux enhancement

1.2.8. Removed selinux_enforcing

selinux_enforcing = 0 即可关闭 selinux,但是 samsung 通过禁用编译选项"CONFIG_SECURITY_SELINUX_DEVELOP"将其删除

#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
extern int selinux_enforcing;
#else
#define selinux_enforcing 1
#endif

  1. Disability of policy reloading

    系统初始化后, init 进程不再 reload SELinux policy, 这就意味着系统启动后,将当前进程的 domain 改为 init 重新加载 selinux policy 的方法将失效。

  2. Removed support of permissive domain

    如果可以重新 reload selinux policy,并将当前进程改为 permissive domain,即可绕过 selinux

    删除了 permissive domain, 如果进程的 domain 是 permissive, 直接返回-EACCES

    static noinline int avc_denied(u32 ssid, u32 tsid,
                                   u16 tclass, u32 requested,
                                   u8 driver, u8 xperm, unsigned flags,
                                   struct av_decision *avd)
    {
      if (flags & AVC_STRICT)
        return -EACCES;
    
      // ...
    #ifdef CONFIG_ALWAYS_ENFORCE
      if (!(avd->flags & AVD_FLAGS_PERMISSIVE))
    #else
        if (selinux_enforcing && !(avd->flags & AVD_FLAGS_PERMISSIVE))
    #endif
          // ] SEC_SELINUX_PORTING_COMMON
          return -EACCES;
      // ...
    }
    

1.3. Bypassing techniques

KASLR -> kernel rwcap -> overwrite ptmx_fops -> overwrite addr_limit -> DFI -> SELINUX -> root

1.3.1. KASLR bypassing

/proc fs, info leak, …


find /sys/kernel/debug | xargs cat

利用一个竞态漏洞root三星s8的方法

1.3.2. DFI bypassing

让内核创建一个线程,符合 RKP 和 DFI 的所有规则

ptmx_fops->check_flags(x0):

char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
static const char reboot_cmd[] = "/sbin/reboot";

static int run_cmd(const char *cmd)
{
  char **argv;
  static char *envp[] = {
    "HOME=/",
    "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
    NULL
  };
  int ret;
  argv = argv_split(GFP_KERNEL, cmd, NULL);
  if (argv) {
    ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
    argv_free(argv);
  } else {
    ret = -ENOMEM;
  }

  return ret;
}

static int __orderly_poweroff(bool force)
{
  int ret;

  ret = run_cmd(poweroff_cmd);

  // ...
  return ret;
}

static bool poweroff_force;

static void poweroff_work_func(struct work_struct *work)
{
  __orderly_poweroff(poweroff_force);
}

static DECLARE_WORK(poweroff_work, poweroff_work_func);

/**
 * orderly_poweroff - Trigger an orderly system poweroff
 * @force: force poweroff if command execution fails
 *
 * This may be called from any context to trigger a system shutdown.
 * If the orderly shutdown fails, it will force an immediate shutdown.
 */
void orderly_poweroff(bool force)
{
  if (force) /* do not override the pending "true" */
    poweroff_force = true;
  schedule_work(&poweroff_work);
}
  1. Call rpk_override_creds() via ptmx_fops->check_flags() to override own cred to gain full kernel capabilities
  2. Overwrite poweroff_cmd with "/data/data/***/expl"
  3. Call orderly_poweroff() via ptmx_fops->check_flags()
  4. Modify expl thread_info->address_limit
  5. expl call rpk_override_creds() to change its context from u:r:kernel:s0 to u:r:init:s0

    but….

    poweroff 调用流程:

    /*
      orderly_poweroff(force) ->
      poweroff_work_func(...) ->
      __orderly_poweroff(force) ->
      run_cmd(cmd) ->
      call_usermodehelper(...) ->
      call_usermodehelper_setup(...) ->
      call_usermodehelper_exec_work(...) ->
      call_usermodehelper_exec_async(...) ->
      do_execve(...) ->
      do_execveat_common(...) ->
      exec_binprm(bprm) ->
      search_binary_handler(...) ->
      load_elf_binary(fmt->load_binary(bprm)) ->
      flush_old_exec(bprm)->
    */
    
    int flush_old_exec(struct linux_binprm * bprm)
    {
      // ...
    #ifdef CONFIG_RKP_NS_PROT
      if(rkp_cred_enable &&
         is_rkp_priv_task() &&
         invalid_drive(bprm)) {
        panic("\n Illegal Execution file_name #%s#\n",bprm->filename);
      }
    #endif /*CONFIG_RKP_NS_PROT*/
      // ...
    }
    
    #define RKP_CRED_SYS_ID 1000
    static int is_rkp_priv_task(void)
    {
      struct cred *cred = (struct cred *)current_cred();
    
      if(cred->uid.val <= (uid_t)RKP_CRED_SYS_ID || cred->euid.val <= (uid_t)RKP_CRED_SYS_ID ||
         cred->gid.val <= (gid_t)RKP_CRED_SYS_ID || cred->egid.val <= (gid_t)RKP_CRED_SYS_ID ){
        return 1;
      }
      return 0;
    }
    
    static int invalid_drive(struct linux_binprm * bprm)
    {
      struct super_block *sb =  NULL;
      struct vfsmount *vfsmnt = NULL;
    
      vfsmnt = bprm->file->f_path.mnt;
      if(!vfsmnt ||
         !rkp_ro_page((unsigned long)vfsmnt)) {
        printk("\nInvalid Drive #%s# #%p#\n",bprm->filename,vfsmnt);
        return 1;
      }
      sb = vfsmnt->mnt_sb;
    
      if((!is_boot_recovery) &&
         sb != rootfs_sb
         && sb != sys_sb) {
        printk("\n Superblock Mismatch #%s# vfsmnt #%p#sb #%p:%p:%p#\n",
               bprm->filename,vfsmnt,sb,rootfs_sb,sys_sb);
        return 1;
      }
    
      return 0;
    }
    
    

    必须是 rootfs(/) 和 system(/system) 目录下文件才可以

  1. SELinux bypassing
  2. patch security_hook_heads

    security_hook_heads 可读写

    
    ROM:0000000000251E60 security_transfer_creds                 ; CODE XREF: key_change_session_keyring+134p
    ROM:0000000000251E60 var_s0          =  0
    ROM:0000000000251E60 var_s10         =  0x10
    ROM:0000000000251E60 var_s20         =  0x20
    ROM:0000000000251E60
    ROM:0000000000251E60                 STP             X29, X30, [SP,#-0x30+var_s0]!
    ROM:0000000000251E64                 MOV             X29, SP
    ROM:0000000000251E68                 STP             X21, X22, [SP,#var_s20]
    ROM:0000000000251E6C                 MOV             X21, X0
    ROM:0000000000251E70                 ADRP            X0, #0x17C9000
    ROM:0000000000251E74                 ADD             X0, X0, #0x428
    ROM:0000000000251E78                 MOV             X22, X1
    ROM:0000000000251E7C                 STP             X19, X20, [SP,#var_s10]
    ROM:0000000000251E80                 ADD             X19, X0, #0x4E0
    ROM:0000000000251E84                 LDR             X20, [X0,#0x4E0]
    ROM:0000000000251E88
    ROM:0000000000251E88 loc_251E88                              ; CODE XREF: security_transfer_creds+44j
    ROM:0000000000251E88                 CMP             X20, X19
    ROM:0000000000251E8C                 B.EQ            loc_251EA8
    ROM:0000000000251E90                 LDR             X2, [X20,#0x18]
    ROM:0000000000251E94                 MOV             X0, X21
    ROM:0000000000251E98                 MOV             X1, X22
    ROM:0000000000251E9C                 BL              jopp_springboard_blr_x2
    ROM:0000000000251EA0                 LDR             X20, [X20]
    ROM:0000000000251EA4                 B               loc_251E88
    ROM:0000000000251EA8 ; ---------------------------------------------------------------------------
    ROM:0000000000251EA8
    ROM:0000000000251EA8 loc_251EA8                              ; CODE XREF: security_transfer_creds+2Cj
    ROM:0000000000251EA8                 LDP             X19, X20, [SP,#var_s10]
    ROM:0000000000251EAC                 LDP             X21, X22, [SP,#var_s20]
    ROM:0000000000251EB0                 LDP             X29, X30, [SP+var_s0],#0x30
    ROM:0000000000251EB4                 RET
    ROM:0000000000251EB4 ; End of function security_transfer_creds
    
    
    if (*(&security_hook_heads[x]) == &security_hook_heads[x] ) {
      bypass
    }
    

    重定向 crrrent_cred()到可读写内存中,然后修改

    void key_change_session_keyring(struct callback_head *twork)
    {
      const struct cred *old = current_cred();
      struct cred *new = container_of(twork, struct cred, rcu);
    
      if (unlikely(current->flags & PF_EXITING)) {
        put_cred(new);
        return;
      }
    
      new->uid  = old->uid;
      new->euid = old->euid;
      new->suid = old->suid;
      new->fsuid  = old->fsuid;
      new->gid  = old->gid;
      new->egid = old->egid;
      new->sgid = old->sgid;
      new->fsgid  = old->fsgid;
      new->user = get_uid(old->user);
      new->user_ns  = get_user_ns(old->user_ns);
      new->group_info = get_group_info(old->group_info);
    
      new->securebits = old->securebits;
      new->cap_inheritable  = old->cap_inheritable;
      new->cap_permitted  = old->cap_permitted;
      new->cap_effective  = old->cap_effective;
      new->cap_ambient  = old->cap_ambient;
      new->cap_bset   = old->cap_bset;
    
      new->jit_keyring  = old->jit_keyring;
      new->thread_keyring = key_get(old->thread_keyring);
      new->process_keyring  = key_get(old->process_keyring);
    
      security_transfer_creds(new, old);    // will bypass check
    
      commit_creds(new);
    }
    
    
    
    

1.3.3. rkp_override_creds

rkp_is_protected 检测 cred 是否在 rkp 保护内存中,否则返回 0

const struct cred *rkp_override_creds(struct cred **cnew)
#else
  const struct cred *override_creds(const struct cred *new)
#endif  /* CONFIG_RKP_KDP */
{
  const struct cred *old = current->cred;
#ifdef CONFIG_RKP_KDP
  struct cred *new = *cnew;
  struct cred *new_ro;
  volatile unsigned int rkp_use_count = rkp_get_usecount(new);
  void *use_cnt_ptr = NULL;
  void *tsec = NULL;
#endif  /* CONFIG_RKP_KDP */
  // ...
}

unsigned  int rkp_get_usecount(struct cred *cred)
{
  if (rkp_ro_page((unsigned long )cred))
    return (unsigned int)rocred_uc_read(cred);
  else
    return atomic_read(&cred->usage);
}

/*Check whether the address belong to Cred Area*/
static inline u8 rkp_ro_page(unsigned long addr)
{
  if(!rkp_cred_enable)
    return (u8)0;
  if((addr == ((unsigned long)&init_cred)) ||
     (addr == ((unsigned long)&init_sec)))
    return (u8)1;
  else
    return rkp_is_pg_protected(addr);
}

#define rkp_is_pg_protected(va) rkp_is_protected(va,__pa(va),(u64 *)rkp_pgt_bitmap,1)

static inline u8 rkp_is_protected(u64 va, u64 pa, u64 *base_addr,u64 type)
{
  u64 phys_addr = pa & (RKP_PHYS_ADDR_MASK);
  u64 index = rkp_get_sys_index((phys_addr>>PAGE_SHIFT));
  u64 *p = base_addr;
  u64 tmp = (index>>6);
  u64 rindex;
  u8 val;
  /* We are going to ignore if input address NOT belong to DRAM area */
  if((phys_addr < PHYS_OFFSET) || (index ==(~0ULL)) ||
     (phys_addr  > RKP_PHYS_OFFSET_MAX)) {
    return 0;
  }

  p += (tmp);
  rindex = index % 64;
  val = (((*p) & (1ULL<<rindex))?1:0);
  return val;
}

Author: idhyt

Created: 2018-07-17 Tue 15:51

Validate

Android Root Zap Framework

‎ 1. Warning 请遵守GPL开源协议, 请遵守法律法规, 本项目仅供学习和交流, 请勿用于非法用途! 道路千万条, 安全第一条, 行车不规范, 亲人两行泪. 2. Android Root Zap Frame...