SAMSUNG CROM Service解锁分析

解锁BUG

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

APK分析

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

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

logcat 分析

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

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

SO库分析

这里使用了Ghidra进行分析,libkwb.so文件加载还需要几个其他的so库文件依赖。所依赖的so文件有:libc.so、libcrypto.so、libdl.so、liblog.so、libm.so、libQSEEComAPI.so、libstdc++.so

除了libQSEEComAPI.so在/system/vendor/lib/中,其他的so库文件都可在/system/lib/中找到

这几个so文件都pull到本地,添加到项目中进行分析

在Functions中可以找到对应的函数Java_com_sec_android_lib_kwb_LibKwb_getCustomBinStatusFlag

进行Decompile可以发现只有getRPMBFlag()函数

进入getRPMBFlag()函数

里面又引用了FUN_0011940()函数

在FUN_00011940()函数中先对FUN_00011cc4()函数的返回值进行判断,如果为0就执行FUN_00011dec()函数重赋值给iVar1,然后执行FUN_00011d28()函数

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

进FUN_00011ebc()函数,发现主要是设置uid等权限的

在38行有打开/data/kwb文件的操作

用adb进行查看,发现该文件为0大小

并没什么作用。

退出,跟进到FUN_00011c30()函数中

启动的什么app,对此作用不大。

继续退出,在FUN_00011cc4()函数的17行中发现了点希望。

对FUN_000120fc()返回值进行判断,如果返回值不为0就说明解锁失败,那就继续跟进FUN_000120fc()吧

对DAT_00019890进行比较,如果大于-1就flock(DAT_00019890,8)对于flock搜了下

flock相当于文件锁。

解锁总结

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

另类绕过(不可行)

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

这时打开CROM Serviceapp应用就会看到解锁提示

点击确认前需要执行busybox chattr -i kwb解锁文件写入操作。

高通接口解锁

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

mclf loader 提取脚本:

import sys, os, struct

def main():

#Reading the arguments

if len(sys.argv) != 4:

print("USAGE: ")

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("

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("

p_offset= struct.unpack("

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文件

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

到此为止吧😂😂😂

2026-08-05 12:30:27