记一次对flyme自带root的功能的逆向
本帖最后由 wmsuper 于 2016-9-26 16:47 编辑1.前言
flyme一般都自带开放系统权限的功能,输入用户密码后就可以执行root功能root后的手机会把/system/xbin/lu 改为su 并赋予S位(讨论的是flyme5.1.8.0A 下同)
2.应用层分析
找到设置的apk,Settings.apk没混淆,很容易定位关键函数
private void handleRootPermissionPreferenceClick() {
if(this.isRootPermissionOpened()) {
this.startFragment(this, RootPermissionSettingsFragment.class.getName(), 2131430221, -1, null);
}
else if(this.isFlymeAccountLogined()) {
this.startActivity(new Intent("com.meizu.action.ROOT"));//执行一个intent
}
}
private boolean needShowRootPreference() {
boolean v0 = true;
if(MzUtils.isGuestUser(this.getActivity())) {
v0 = false;
}
else if(!this.isRootPermissionOpened()) {
if((this.isFlymeAccountLogined()) && (MzUtils.isPackageExistAndHasAction(this.getActivity(), "com.meizu.account", "com.meizu.action.ROOT"))) { //这里可以知道com.meizu.action.ROOT在MzAccount.apk有实现
return v0;
}
v0 = false;
}
return v0;
}
MzAccount.apk 的内容:
//查看MzAccount.apk的AndroidManifest.xml搜索com.meizu.action.ROOT定位到一个activity
<activity android:label="@string/app_name" android:name="com.meizu.root.OpenSystemRightActivity" android:screenOrientation="portrait" android:theme="@style/AccountTheme.Gray">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="com.meizu.action.ROOT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
com.meizu.root.OpenSystemRightActivity的内容
protected void onCreate(Bundle arg5) {
super.onCreate(arg5);
ap.a(((Activity)this), null, this.getResources().getString(2131296648), 0);
this.setContentView(2130903077);
this.c = this.findViewById(2131755221);
this.c.setOnCheckedChangeListener(((CompoundButton$OnCheckedChangeListener)this));
this.a = new SystemRootHelper(((Context)this));//看名称很特别 到这个类里面看看
this.e = this.findViewById(2131755244);
this.e.setEnabled(false);
this.e.setText(this.getString(2131296645));
this.e.setVisibility(0);
this.e.setOnClickListener(new a(this));
}
SystemRootHelper的内容
public boolean a(String arg5) {//root操作
boolean v0 = false;
if(this.b.b(DeviceStateManagerEx.a("Root"), Base64.decode(arg5, 0)) == 0) {//DeviceStateManagerEx.a("Root")映射一个数字 arg5是输入密码后服务器返回的值
v0 = true;
}
return v0;
}
public boolean b(String arg5) { //uroot操作这个调用没有公开
boolean v0 = false;
if(this.b.b(DeviceStateManagerEx.a("Unroot"), Base64.decode(arg5, 0)) == 0) {
v0 = true;
}
return v0;
}
public int b(int arg8, byte[] arg9) {
int v0 = -1;
try {
Object v1_1 = a.a(this.b, "doCommand", new Class[]{Integer.TYPE, byte[].class}, new Object[]{Integer.valueOf(arg8), arg9});//执行服务命令doCommand
if(v1_1 == null) {
return v0;
}
v0 = Integer.parseInt(v1_1.toString());
}
catch(Exception v1) {
v1.printStackTrace();
}
return v0;
}
public DeviceStateManagerEx(Context arg2) {
super();
this.b = arg2.getSystemService("device_states"); //使用了device_states的服务
}
找到系统服务下面是services.jar的内容
static {
System.loadLibrary("android_servers");
}
private native int nativeDoCheckState(int arg1) {
}
private native int nativeDoCommand(int arg1, byte[] arg2) { //这就是我们找的 实现在libandroid_servers.so 里面
}
private native int nativeDoGetInitNo(int arg1, byte[] arg2) {
}
private native int nativeLockRecoveryUnchecked() {
}
private native int nativeLockRecoveryUncheckedByHandle(int arg1) {
}
为了使用ida6.8的反编译 这里选用了lib里面的32bit的libandroid_servers.so
signed int __fastcall android::nativeDoCommandRoot(int *a1, int a2, int a3, int a4)
{
int v4; // r8@1
int v5; // r6@1
int *v6; // r4@1
int v7; // ST00_4@1
int v8; // r10@1
int v9; // r8@1
signed int result; // r0@3
int v11; // r0@6
__int32 v12; // r0@8
int v13; // ST00_4@8
int v14; // r7@8
char v15; // @1
char v16; // @1
int v17; // @1
v4 = a4;
v5 = a3;
v6 = a1;
v17 = _stack_chk_guard;
j_j_memset(&v15, 0, 0x400u);
j_j_memset(&v16, 0, 0x400u);
j_j_memset(v15, 0, 0x64u);
v7 = *v6;
j_j___android_log_print(4, "DeviceStateService", "Native: nativeDoCommand env=%x,*env=%x, handle=%d.", v6);
v8 = sub_1A984((int)v6);
v9 = sub_10768(v6, v4);
if ( v5 == 1 || v5 == 2 )
{
v11 = 0;
do
{
*(_DWORD *)&v15 = 1515870810;
v11 += 4;
}
while ( v11 != 64 );
*(_DWORD *)&v15 = j_j_j__ZN7android20get_platform_versionEv((android *)0x40);
*(_DWORD *)&v15 = v5;
j_j___memcpy_chk(&v15, v8, v9, 1024);
j_j_memcpy(&v15, v15, 0x64u);
v12 = j_j_syscall(0xF0067, 16, 2, &v15, &v16, v5);//关键系统函数调用 sysno=0xF0067
v13 = *v6;
v14 = v12 | (v12 >> 31);
j_j___android_log_print(
4,
"DeviceStateService",
"Native: nativeDoCommandRoot env=%x,*env=%x, handle=%d, rtx.enable:%d,result:%d.",
v6);
result = v14;
}
else
{
result = -2;
}
if ( v17 != _stack_chk_guard )
j_j___stack_chk_fail(result);
return result;
}
可以看到这个sysno很大 这里面肯定有猫腻
3.内核浅析
本人参考了以下链接提取了内核 不同的是这是x64的内核
http://www.blogbus.com/riusksk-logs/272240986.html
http://bbs.pediy.com/showthread.php?t=194803
ida6.8对64位的arm代码支持的不是很好(6.9可以完美支持),这里需要注意的是, 内核载入ida后由于不是完美支持,导致分析不出来。
可载入时先处理器改成ARM ,再将基址改为ffffffc000080000,调节段的大小为64位(如下图),全选后再次分析即可反汇编成功
按照链接中的方法提取符号,生成idc执行来给函数命名
定位到el0_svc
:FFFFFFC000084480 el0_svc ; CODE XREF: ROM:FFFFFFC000083F68j
ROM:FFFFFFC000084480 ADRP X27, #0xFFFFFFC000DEB000
ROM:FFFFFFC000084484 MOV W26, W8
ROM:FFFFFFC000084488 MOV X25, #0x116 ;最大的sysno
ROM:FFFFFFC00008448C
ROM:FFFFFFC00008448C el0_svc_naked ; CODE XREF: ROM:FFFFFFC000084080j
ROM:FFFFFFC00008448C STP X0, X26,
ROM:FFFFFFC000084490 MRS X16, #0, c0, c2, #2
ROM:FFFFFFC000084494 AND X16, X16, #0xFFFFFFFFFFFFFFFE
ROM:FFFFFFC000084498 MSR #0, c0, c2, #2, X16
ROM:FFFFFFC00008449C ISB
ROM:FFFFFFC0000844A0 MSR #7, #8
ROM:FFFFFFC0000844A4 MSR #7, #2
ROM:FFFFFFC0000844A8 MOV X28, SP
ROM:FFFFFFC0000844AC AND X28, X28, #0xFFFFFFFFFFFFC000
ROM:FFFFFFC0000844B0 LDR X16,
ROM:FFFFFFC0000844B4 TST X16, #0xF00
ROM:FFFFFFC0000844B8 B.NE __sys_trace
ROM:FFFFFFC0000844BC ADR X30, ret_fast_syscall
ROM:FFFFFFC0000844C0 CMP X26, X25 ; switch 0 cases;比较大小
ROM:FFFFFFC0000844C4 B.CS ni_sys; jumptable 000044CC default case;超出范围调用ni_sys()
ROM:FFFFFFC0000844C8 LDR X16,
ROM:FFFFFFC0000844CC BR X16 ; switch jump
继续往下跟踪
:FFFFFFC00008FF14 compat_arm_syscall ; CODE XREF: ROM:FFFFFFC000089238p
ROM:FFFFFFC00008FF14
ROM:FFFFFFC00008FF14 var_20 = -0x20
ROM:FFFFFFC00008FF14 var_10 = -0x10
ROM:FFFFFFC00008FF14 var_s0 =0
ROM:FFFFFFC00008FF14
ROM:FFFFFFC00008FF14 STP X29, X30, !
ROM:FFFFFFC00008FF18 MOV X3, X0
ROM:FFFFFFC00008FF1C MOV W0, #0x67
ROM:FFFFFFC00008FF20 MOV X29, SP
ROM:FFFFFFC00008FF24 STP X19, X20,
ROM:FFFFFFC00008FF28 STP X21, X22,
ROM:FFFFFFC00008FF2C MOVK W0, #0xF,LSL#16
ROM:FFFFFFC00008FF30 LDR X1,
ROM:FFFFFFC00008FF34 CMP W1, W0 ;sysno是否为0xf0067 是的话就调用do_private_entry()
ROM:FFFFFFC00008FF38 B.NE loc_FFFFFFC00008FF64
ROM:FFFFFFC00008FF3C LDR W0,
ROM:FFFFFFC00008FF40 LDR W1,
ROM:FFFFFFC00008FF44 LDR X2,
ROM:FFFFFFC00008FF48 LDR X3,
ROM:FFFFFFC00008FF4C BL do_private_entry
ROM:FFFFFFC00008FF50 SBFM X0, X0, #0, #0x1F
ROM:FFFFFFC00008FF54
ROM:FFFFFFC00008FF54 loc_FFFFFFC00008FF54 ; CODE XREF: compat_arm_syscall+E0j
ROM:FFFFFFC00008FF54 ; compat_arm_syscall+E8j ...
ROM:FFFFFFC00008FF54 LDP X19, X20,
ROM:FFFFFFC00008FF58 LDP X21, X22,
ROM:FFFFFFC00008FF5C LDP X29, X30, ,#0x30
ROM:FFFFFFC00008FF60 RET
接下来是验证数据内容,并对mmc进行写操作
4.结语
由于本人能力有限和内核动态调试十分困难,只能找到大概的流程,但是内核是如何更改lu为su 并赋予s位的过程还不是十分清楚,欢迎大牛一起分析。
支持下楼主,因为本人用的就是魅族pro5,去手机店的时候随便问问过老板,几个都说无法刷机的,好像貌似这个魅族的root权限与Flyme锁做的好像相比同类稍微牛一点…… 对这些不懂,@Hmily 你评个分吧 支持一波!!!!!!1 无论结果如何都要支持LZ 我也有点好奇,楼主在哪里弄的这个东东~不明觉厉 毕设就是要做安卓内核的动态动态调试分析的。一起探讨 文章思路很不错,值得进一步挖掘su怎么利用{:17_1062:} 学习了,不过好多不懂 支持 还在学习中。
页:
[1]
2