解锁BUG

手机使用的是三星A7000,之前在Android 6.0.1时安装了CROM Service应用并成功解锁,后来回退版本至Android 5.0.2并重新安装了CROM Serice,发现提示已解锁,但是进入BL中显示的是未解锁状态。

APK分析

使用jadx-gui进行分析,在KwbLaunchActivity中存在程序运行的主要逻辑。

59a42752d1431b20576e391bc1f6d483.png
通过加载kwbso库中的getCustomBinStatusFlag()函数进行一个判断是否已解锁,如果该函数返回值为0进行后续的解锁,否则会判断为已解锁状态。

logcat 分析

由上面的代码可以看出使用了Log.d输出流log信息,可以使用logcat进行过滤判断getCustomBinStatusFlag()函数返回的状态信息。使用adb shell连接,然后执行logcat | grep "LibKwb.getCustomBinStatusFlag()"过滤特定的内容,然后打开应用等待输出内容

bb87c5cbb4ebc4b760a81a8145324fcc.png
这里可以看得到直接返回了状态信息为1,所以应用只能提示已解锁了,不会进行到解锁的步骤中。

SO库分析

这里使用了Ghidra进行分析,libkwb.so文件加载还需要几个其他的so库文件依赖。所依赖的so文件有:libc.solibcrypto.solibdl.soliblog.solibm.solibQSEEComAPI.solibstdc++.so 除了libQSEEComAPI.so/system/vendor/lib/中,其他的so库文件都可在/system/lib/中找到

8576c85282992fba80ab7c010e918e36.png
这几个so文件都pull到本地,添加到项目中进行分析
7d9db0e3a41c81290f08ce56d7e63c0f.png
Functions中可以找到对应的函数Java_com_sec_android_lib_kwb_LibKwb_getCustomBinStatusFlag
68af507f78d70829fcf0c956e81ca38d.png
进行Decompile可以发现只有getRPMBFlag()函数
0a6546b14a9a5e83511632bc4f780911.png
进入getRPMBFlag()函数
00b84ebc3ef33469fbbc872b9cefe93b.png
里面又引用了FUN_0011940()函数
d5280e0a7665671bc438363b3bcbaeea.png
FUN_00011940()函数中先对FUN_00011cc4()函数的返回值进行判断,如果为0就执行FUN_00011dec()函数重赋值给iVar1,然后执行FUN_00011d28()函数
a90a7ac63620793c506f395b467383d4.png

先进入FUN_00011cc4()函数内进行查看

3457e5cf53be3e2cba7e73f74695d888.png
FUN_00011ebc()函数,发现主要是设置uid等权限的
5f49b15c4bb797ed13e9e831248b88cf.png
在38行有打开/data/kwb文件的操作
4fd5ecd694f8627db0984f4b00f0022d.png
用adb进行查看,发现该文件为0大小
61a410499badf938f733035e325f9b28.png
并没什么作用。
退出,跟进到FUN_00011c30()函数中
ce9ae8214bdfbba2c74e5570664e4b83.png
启动的什么app,对此作用不大。 继续退出,在FUN_00011cc4()函数的17行中发现了点希望。
5435316a324773e40399558c4802b7f0.png
FUN_000120fc()返回值进行判断,如果返回值不为0就说明解锁失败,那就继续跟进FUN_000120fc()
254780dc2f71d721103e935e7d54538c.png
DAT_00019890进行比较,如果大于-1flock(DAT_00019890,8)对于flock搜了下
293be03dca6927c613c7e224c8ac8e11.png
flock相当于文件锁。

解锁总结

原理是通过加载so库判断是否已解锁,未解锁就会发送解锁请求给服务器,服务器返回解锁token,so库对token进行验证,如果通过就会启动高通的QSEECOM接口进行解锁操作。故此操作不可逆。

另类绕过(不可行)

因为我是Android 5.0.2通过漏洞或者kingoroot等软件可以进行root,所以进入adb shell中使用echo 1 > /data/kwb并使用busybox chattr +i /data/kwb强制锁定该文件,不让他为空。

26dd32ef20faf2c5ee6d8607ca9bae79.png
这时打开CROM Serviceapp应用就会看到解锁提示
61c35e33305234ca187a77ccca8d7424.png
点击确认前需要执行busybox chattr -i kwb解锁文件写入操作。
400db6082229abbf068ff1e170f39264.png

高通接口解锁

网上有篇文章分析了高通的接口以及如何提取的方法。参考链接 mclf loader 提取脚本:

import sys, os, struct

def main():

    #Reading the arguments
    if len(sys.argv) != 4:
        print("USAGE: <TRUSTLET_DIR> <TRUSTLET_NAME> <OUTPUT_FILE_PATH>")
        return


    trustlet_dir = sys.argv[1]
    trustlet_name = sys.argv[2]
    output_file_path = sys.argv[3]


    bitness = 32
    ELF_HEADER_SIZE = 0x34
    E_PHNUM_OFFSET = 0x2C
    PHDR_SIZE = 0x20
    P_FILESZ_OFFSET = 0x10
    P_OFFSET_OFFSET = 0x4

    #Reading the ELF header from the ".mdt" file
    mdt = open(os.path.join(trustlet_dir, "%s.mdt" % trustlet_name), "rb")
    elf_header = mdt.read(ELF_HEADER_SIZE)

    phnum = struct.unpack("<H", elf_header[E_PHNUM_OFFSET:E_PHNUM_OFFSET+2])[0]
    print("[+] Found %d program headers" % phnum)
    
    #Reading each of the program headers and copying the relevant chunk
    output_file = open(output_file_path, 'wb')
    for i in range(0, phnum):

        #Reading the PHDR
        print("[+] Reading PHDR %d" % i)
        phdr = mdt.read(PHDR_SIZE)  
        p_filesz = struct.unpack("<I", phdr[P_FILESZ_OFFSET:P_FILESZ_OFFSET+4])[0] 
        p_offset= struct.unpack("<I", phdr[P_OFFSET_OFFSET:P_OFFSET_OFFSET+4])[0] 
        print("[+] Size: 0x%08X, Offset: 0x%08X" % (p_filesz, p_offset))

        if p_filesz == 0:
            print("[+] Empty block, skipping")
            continue #There's no backing block

        #Copying out the data in the block
        block = open(os.path.join(trustlet_dir, "%s.b%02d" % (trustlet_name, i)), 'rb').read()
        output_file.seek(p_offset, 0)
        output_file.write(block)

    mdt.close()
    output_file.close()

def parse_DER():
    trustlet_dir = sys.argv[1]
    trustlet_name = sys.argv[2] 
    mdt = open(os.path.join(trustlet_dir, "%s.mdt" % trustlet_name), "rb")
    certs = mdt.read()
    certs_index = []

    for i in range(0x34,len(certs)-5):
        if certs[i] == '\x30' and certs[i+1] == '\x82' and certs[i+4] == '\x30' and certs[i+5] == '\x82':            
            certs_index.append(i)
    certs_index.append(len(certs))        
        
    print(certs_index)
    for i in range(len(certs_index)-1):
        start = certs_index[i]
        end = certs_index[i+1]
        certname = "cert_"+str(start)+".der"
        print(certname)
        open(certname,'wb').write(bytes(certs[start : end]))


if __name__ == "__main__":
    main()
    parse_DER()

提取出的文件为去符号的elf文件

990b6dccec1cc4d8527ea9fbb2af702e.png
所以要添加符号表。网上有恢复符号表的脚本,但是不太会用。。。

到此为止吧😂😂😂