初探安卓隐藏Bootloader锁
2023-9-16 13:20:40 Author: mp.weixin.qq.com(查看原文) 阅读量:8 收藏

做完硬件断点记录器后就开始研究如何隐藏Android的Bootloader锁了,把目前为止的思路发出来。

首先想到的一个方向是要先spoof安卓Setting里的oemlock status。

收集所需信息

在aoxpxref网站上搜索

bootloader unlocked

看到一个可疑的文件

/frameworks/base/core/java/android/service/oemlock/OemLockManager.java

xref 找到

/frameworks/base/services/core/java/com/android/server/oemlock/OemLockService.java

看到函数

private static final String FLASH_LOCK_PROP = "ro.boot.flash.locked";
private static final String FLASH_LOCK_UNLOCKED = "0"

public boolean isDeviceOemUnlocked() {
enforceOemUnlockReadPermission();
String locked = SystemProperties.get(FLASH_LOCK_PROP);
switch (locked) {
case FLASH_LOCK_UNLOCKED:
return true;
default:
return false;
}
}

可以看到Setting检测的是系统属性ro.boot.flash.locked来判断Bootloader是否解锁。

AOSP搜索ro.boot.flash.locked,发现/system/core/init/init.cpp有初始化该属性值的代码。

static void export_oem_lock_status() {
if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
return;
}
SetProperty(
"ro.boot.flash.locked",
android::base::GetProperty("ro.boot.verifiedbootstate", "") == "orange" ? "0" : "1");
}

ro.boot.flash.locked的值是受到ro.boot.verifiedbootstate的值影响

google搜了一下后面的这个属性,发现这个属性可能的值有red,orange,yellow,green。

看到一个隐藏bl锁的github项目

https://github.com/topjohnwu/Magisk/blob/3c04dab47274e9bbbfb3ddd1fcf71c929c8ed0c0/native/jni/magiskhide/hide_policy.cpp#L12

看到替换表

static const char *prop_key[] =
{ "ro.boot.vbmeta.device_state", "ro.boot.verifiedbootstate", "ro.boot.flash.locked",
"ro.boot.veritymode", "ro.boot.warranty_bit", "ro.warranty_bit",
"ro.debuggable", "ro.secure", "ro.build.type", "ro.build.tags",
"ro.vendor.boot.warranty_bit", "ro.vendor.warranty_bit",
"vendor.boot.vbmeta.device_state", nullptr };

static const char *prop_val[] =
{ "locked", "green", "1",
"enforcing", "0", "0",
"0", "1", "user", "release-keys",
"0", "0",
"locked", nullptr };

可以确定green是bl未解锁,还有一个ro.boot.vbmeta.device_state要改成locked状态,实机看这个属性解锁bl状态下是unlocked,除了前三个之外其他属性解bl之后没变。

还有 googleplay service 的验证

https://developer.android.com/training/safetynet/attestation

https://developer.android.com/google/play/integrity/overview

对应的解决方案是

https://github.com/kdrag0n/safetynet-fix

这个问题不大,国内又不用googleplay service。

总结需要修改的信息

按照上述总结一下,隐藏bl锁需要修改以下属性:

寻找修改Property的方法

Java层最终会调用native_get方法获取Property的值。

顺着native_get找到函数__system_property_find这个是用户层标准的获取property值的函数,来自libc.so,往下翻就是android property系统的实现了,可以看这篇文章

https://bbs.kanxue.com/thread-274100.htm

进用户层初始化property service的地方

/system/core/init/property_service.cpp

找到函数PropertyInit从名字猜测这是init进程初始化系统基本property值的地方,ProcessBootconfigProcessKernelCmdline最后是通过读取/proc/bootconfig/proc/cmdline调用_system_property_add来设置ro.boot.verifiedbootstatero.boot.vbmeta.device_state的值。

cat一下这两个设备文件,确实有相关信息。

adb shell su -c cat /proc/bootconfig
androidboot.hardware = "qcom"
androidboot.memcg = "1"
androidboot.usbcontroller = "a600000.dwc3"
androidboot.bootdevice = "1d84000.ufshc"
androidboot.boot_devices = "soc/1d84000.ufshc"
androidboot.prjname = "22803"
androidboot.startupmode = "pwrkey"
androidboot.mode = "normal"
androidboot.product.hardware.sku = "0"
androidboot.hw_region_id = "1"
androidboot.serialno = "xxxxxxx"
androidboot.baseband = "msm"
androidboot.dtbo_idx = "12"
androidboot.dtb_idx = "1"
androidboot.force_normal_boot = "0"
androidboot.fstab_suffix = "default"
androidboot.verifiedbootstate = "orange"
androidboot.keymaster = "1"
androidboot.vbmeta.device = "PARTUUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
androidboot.vbmeta.avb_version = "1.0"
androidboot.vbmeta.device_state = "unlocked"
androidboot.vbmeta.hash_alg = "sha256"
androidboot.vbmeta.size = "23232"
androidboot.vbmeta.digest = "xxxxx"
androidboot.vbmeta.invalidate_on_error = "yes"
androidboot.veritymode = "enforcing"
androidboot.slot_suffix = "_a"

我想到了三个思路:

1.在内核层修改/proc/bootconfig和/proc/cmdline的内容。
2.对_system_property_add下硬件断点,回调里修改value的参数。
3.修改/dev/properties/u:object_r:bootloader_prop:s0里的信息。

第一个思路

kernel版本 5.10.168 路径/common/fs/proc/bootconfig.c中的proc_boot_config_init函数初始化了/proc/bootconfig的输出,顺着全局变量xbc_data找到系统获取bootconfig的地方/common/init/main.c -> setup_bootconfig, 可以看到bootconfig是从initrd里拿的。

将返回的data copy进全局变量,然后由驱动打印出来看到数据。

www1@www1s-Mac-mini ~ % adb shell su -c dmesg | grep "\[+"
[ 0.278576] [+] androidboot.hardware=qcom\x0aandroidboot.memcg=1\x0aandroidboot.usbcontroller=a600000.dwc3\x0a\x0aandroidboot.bootdevice=1d84000.ufshc\x0aandroidboot.boot_devices=soc/1d84000.ufshc\x0aandroidboot.prjname=22803\x0aandroidboot.startupmode=hard_reset\x0aandroidboot.mode=normal\x0aandroidboot.product.hardware.sku=0\x0aandroidboot.hw_region_id=1\x0aandroidboot.serialno=xxxxxxxx\x0aandroidboot.baseband=msm\x0aandroidboot.dtbo_idx=12\x0aandroidboot.dtb_idx=1\x0aandroidboot.force_normal_boot=0\x0aandroidboot.fstab_suffix=default\x0a\x0aandroidboot.verifiedbootstate=orange\x0aandroidboot.keymaster=1\x0aandroidboot.vbmeta.device=PARTUUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\x0aandroidboot.vbmeta.avb_version=1.0\x0aandroidboot.vbmeta.device_state=unlocked\x0aandroidboot.vbmeta.hash_alg=sha256\x0aandroidboot.vbmeta.size=23232\x0aandroidboot.vbmeta.digest=xxxxxxxxxxxxx\x0aandroidboot.vbmeta.invalidate_on_error=yes\x0aandroidboot.veritymode=enforcing\x0aandroidboot.slot_suffix=_a\x0a\x0d

尝试文本替换

void regen_bootcfg(char* buffer)
{
char * pos;
pos = strstr(buffer, "verifiedbootstate=orange");
if(pos)
{
memcpy(pos,"verifiedbootstate=green\n",24);
}
pos = strstr(buffer, "vbmeta.device_state=unlocked");
if(pos)
{
memcpy(pos,"vbmeta.device_state=locked\n\n",28);
}
pos = strstr(buffer, "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
if(pos)
{
memcpy(pos,"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx1",36);
}
}

实验发现替换 uuid 和 vbmeta.device_state 系统正常启动,一旦修改 verifiedbootstate 系统就无法启动了,第一个思路就结束在这里了。

第二个思路

先说结果:成功了!!!!!!

安卓的linker使用的是mmap映射的elf文件,hook do_mmap的返回处,判断pgoff和filepath就能拦截到init进程加载libc.so,在这个时机里对_system_property_add函数下execute hook。

void rwhandler_hidebl_mmap_callback(unsigned long addr,struct file *file)
{
char fullpath[1024];
char *path = d_path(&file->f_path, fullpath, 1024);
if(
path &&
!strcmp(path,"/system/lib64/bootstrap/libc.so") &&
current->pid == 1)
{
rwhandler_start_debug();
rwhandler_set_hbp(1,addr+0x90430,4,HW_BREAKPOINT_X);
printk("task = %s file = %s addr = %llX\r\n",current->comm,path,addr);
}
}

hook回调里对参数进行简单处理。

static const char *hide_props[]={"ro.boot.verifiedbootstate","ro.boot.vbmeta.device_state",NULL};
static char *hide_value[]={"green","locked",NULL};

static bool hidebl_hbpcallback(struct perf_event *bp,
struct perf_sample_data *data,
struct pt_regs *regs)
{
if(current->pid == 1)
{
char key[100];
unsigned long keyaddr = regs->regs[0];
unsigned long varaddr = regs->regs[2];
memset(key, 0, 100);
printk("[+] init hit pc = %llX x0 = %llX x1 = %llX\r\n",
regs->pc,regs->regs[0],regs->regs[1]);

keyaddr &= 0xFFFFFFFFFFFFFF;
varaddr &= 0xFFFFFFFFFFFFFF;
if(rwhandler_read_memory(
1,
(unsigned long)keyaddr,
key, 99)>0)
{
char value[100];
memset(value, 0, 100);
printk("[+] value = %llX\r\n",*(uint64_t*)key);
printk("[+] init key = %s\r\n",key);

if(rwhandler_read_memory(
1,
(unsigned long)varaddr,
value, 99)>0)
{
int idx = 0;
printk("[+] init value = %s\r\n",value);
while(hide_props[idx])
{
if(!strcmp(key,hide_props[idx]))
{

if(rwhandler_do_ptrace_write(1, varaddr, hide_value[idx], strlen(hide_value[idx])+1))
{
regs->regs[3]=strlen(hide_value[idx]);
}
break;
}
idx++;
}
}
}
return true;
}
return false;
};

修改成功

成功 Spoof Setting

结尾

第三个方法暂时没试过,感兴趣的可以试试看。

有空就把第二个思路用kprobe来实现hook,包装成驱动连带硬件断点记录器分别开源到github上。

刚入坑安卓,如有不对的地方或者有其他方法检测bl锁请大佬们在评论区指出!!!

看雪ID:cslime

https://bbs.kanxue.com/user-home-844784.htm

*本文为看雪论坛优秀文章,由 cslime 原创,转载请注明来自看雪社区

# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复

球分享

球点赞

球在看


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458517841&idx=1&sn=c50b81f783fbb7a266bc1b36d315914d&chksm=b18d31db86fab8cde61c53f54804cec1f641e77ebc9d43bab443bb13cdf69fa81bae87af6488&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh