// Unconstrained domain the daemon and root processes run in #define SEPOL_PROC_DOMAIN "magisk" // Highly constrained domain, sole purpose is to connect to daemon #define SEPOL_CLIENT_DOMAIN "magisk_client" // Unconstrained file type that anyone can access #define SEPOL_FILE_TYPE "magisk_file" // Special file type to allow clients to transit to client domain automatically #define SEPOL_EXEC_TYPE "magisk_exec"
static bool should_drop_privileges() { + return false; // "adb root" not allowed, always drop privileges. if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
@@ -128,46 +129,47 @@ static void drop_privileges(int server_port) { // Don't listen on a port (default 5037) if running in secure mode. // Don't run as root if running in secure mode. if (should_drop_privileges()) { - const bool should_drop_caps = should_drop_capabilities_bounding_set(); + //const bool should_drop_caps = should_drop_capabilities_bounding_set();
$ mkdir system $ sudo mount system.img.raw system $ vim ./system/init.usb.rc $ sudo umount system
例如:
1 2 3 4 5 6
# adbd is controlled via property triggers in init.<platform>.usb.rc service adbd /system/bin/adbd --root_seclabel=u:r:magisk:s0 class core socket adbd seqpacket 660 system system disabled seclabel u:r:magisk:s0
$ sudo mount system.img.raw system_raw mount: /home/ccint3/Desktop/sailfish/sailfish-qp1a.191005.007.a3-factory-d4552659/sailfish-qp1a.191005.007.a3/image-sailfish-qp1a.191005.007.a3/system_raw: wrong fs type, bad option, bad superblock on /dev/loop6, missing codepage or helper program, or other error. $ sudo mount -r system.img.raw system_raw $ sudo mount -o rw,remount system_raw mount: /home/ccint3/Desktop/sailfish/sailfish-qp1a.191005.007.a3-factory-d4552659/sailfish-qp1a.191005.007.a3/image-sailfish-qp1a.191005.007.a3/system_raw: cannot remount /dev/loop6 read-write, is write-protected.
intadbd_main(int server_port){ // ... #if defined(ALLOW_ADBD_NO_AUTH) // If ro.adb.secure is unset, default to no authentication required. auth_required = android::base::GetBoolProperty("ro.adb.secure", false); #elif defined(__ANDROID__) if (is_device_unlocked()) { // allows no authentication when the device is unlocked. auth_required = android::base::GetBoolProperty("ro.adb.secure", false); } #endif adbd_auth_init(); // ... return0; }
staticvoiddrop_privileges(int server_port){ // ... if (should_drop_privileges()) { // ... } else { // minijail_enter() will abort if any priv-dropping step fails. minijail_enter(jail.get()); if (root_seclabel != nullptr) { if (selinux_android_setcon(root_seclabel) < 0) { LOG(FATAL) << "Could not set SELinux context"; } } std::string error; std::string local_name = android::base::StringPrintf("tcp:%d", server_port); if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) { LOG(FATAL) << "Could not install *smartsocket* listener: " << error; } } }
staticboolshould_drop_privileges(){ // "adb root" not allowed, always drop privileges. if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) returntrue; // The properties that affect `adb root` and `adb unroot` are ro.secure and // ro.debuggable. In this context the names don't make the expected behavior // particularly obvious. // // ro.debuggable: // Allowed to become root, but not necessarily the default. Set to 1 on // eng and userdebug builds. // // ro.secure: // Drop privileges by default. Set to 1 on userdebug and user builds. bool ro_secure = android::base::GetBoolProperty("ro.secure", true); bool ro_debuggable = __android_log_is_debuggable(); // Drop privileges if ro.secure is set... bool drop = ro_secure; // ... except "adb root" lets you keep privileges in a debuggable build. std::string prop = android::base::GetProperty("service.adb.root", ""); bool adb_root = (prop == "1"); bool adb_unroot = (prop == "0"); if (ro_debuggable && adb_root) { drop = false; } // ... and "adb unroot" lets you explicitly drop privileges. if (adb_unroot) { drop = true; } return drop; }
读取ro.secure和service.adb.root属性并判断是否需要降级。
我们需要将should_drop_privileges的返回值改为false;通过字符串ro.secure使用IDA Pro x64定位到目标代码位置。并反汇编后代码如下:
if(this.httpMethod != null) { if(!this.hasBody) { // 这个request不包含body if(this.isMultipart) { throwthis.methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).", new Object[0]); }
if(this.isFormEncoded) { throwthis.methodError("FormUrlEncoded can only be specified on HTTP methods with request body (e.g., @POST).", new Object[0]); throwthis.methodError("Multipart can only be specified on HTTP methods with request body (e.g., @POST).", new Object[0]); } }
v4 = this.parameterTypes[v2]; if(Utils.hasUnresolvableType(v4)) { throwthis.parameterError(v2, "Parameter type must not include a type variable or wildcard: %s", new Object[]{v4}); }
Annotation[] v3_1 = this.parameterAnnotationsArray[v2]; if(v3_1 == null) { throwthis.parameterError(v2, "No Retrofit annotation found.", new Object[0]); }
throwthis.parameterError(v2, "No Retrofit annotation found.", new Object[0]); throwthis.parameterError(v2, "Parameter type must not include a type variable or wildcard: %s", new Object[]{v4}); label_81: if(this.relativeUrl == null && !this.gotUrl) { throwthis.methodError("Missing either @%s URL or @Url parameter.", new Object[]{this.httpMethod}); }
if((this.isFormEncoded) && !this.gotField) { throwthis.methodError("Form-encoded method must contain at least one @Field.", new Object[0]); }
if((this.isMultipart) && !this.gotPart) { throwthis.methodError("Multipart method must contain at least one @Part.", new Object[0]); }
returnnew ServiceMethod(this); }
StringBuilder v1_1 = new StringBuilder("\'"); v1_1.append(Utils.getRawType(this.responseType).getName()); v1_1.append("\' is not a valid response body type. Did you mean ResponseBody?"); throwthis.methodError(v1_1.toString(), new Object[0]); }
let rObject = Java.use('java.lang.Object'); let rThread = Java.use('java.lang.Thread'); let rString = Java.use('java.lang.String'); let rInteger = Java.use('java.lang.Integer'); let rBoolean = Java.use('java.lang.Boolean'); let rLong = Java.use('java.lang.Long'); let rArray = Java.use('java.lang.reflect.Array');
let rRetrofit = Java.use('com.sankuai.meituan.retrofit2.Retrofit'); let rClientCall = Java.use('com.sankuai.meituan.retrofit2.ClientCall'); let rRetrofit$Builder = Java.use('com.sankuai.meituan.retrofit2.Retrofit$Builder');
// 我们将retrofit.c类视为Retrofit的创建器,用来生成Retrofit对象 let rRetrofit$Creator = Java.use('com.sankuai.waimai.platform.capacity.network.retrofit.c');
// 我们需要注册HomePageApi这个interface let rHomePageApi = Java.use('com.sankuai.waimai.business.page.home.net.request.HomePageApi');
reverse --list list all reverse socket connections from device reverse [--no-rebind] REMOTE LOCAL reverse socket connection using: tcp:<port> (<remote> may be "tcp:0" to pick any open port) localabstract:<unix domain socket name> localreserved:<unix domain socket name> localfilesystem:<unix domain socket name> reverse --remove REMOTE remove specific reverse socket connection reverse --remove-all remove all reverse socket connections from device
get(name, obj) { let notAccessible; let ret; let field = this.find(name); notAccessible = !field.isAccessible(); if (notAccessible) field.setAccessible(true); ret = field.get.overload('java.lang.Object').call(field, obj); if (!notAccessible) field.setAccessible(false); return ret; };
set(name, obj, val) { let isAccessible; let field = this.find(name); isAccessible = field.isAccessible(); if (!isAccessible) field.setAccessible(true); field.set.overload('java.lang.Object', 'java.lang.Object').call(field, obj, val); if (!isAccessible) field.setAccessible(false); }; }(ret.class); ret.$methods = newclassMethods{ wrapper;
constructor(wrapper) { this.wrapper = wrapper; }
find(name, ...args) { let method; try { method = this.wrapper.getDeclaredMethod(name, args); } catch (e) { method = this.wrapper.getMethod(name, args); } returnfunction (obj, ...args) { let isAccessible; let ret; isAccessible = method.isAccessible(); if (!isAccessible) method.setAccessible(true); ret = method.invoke(obj, args); if (!isAccessible) method.setAccessible(false); return ret; }; }; }(ret.class); classCache[className] = ret; } return ret; });
打印堆栈
根据指定的tab数量转为空格
1 2 3 4 5 6 7 8 9
functiongenTableString(tableSize) { let tableString = ''; tableSize = Math.max(Math.min(tableSize, 10), 0); for (let i = 0; i < tableSize; i++) { // One table character maps to two space characters. tableString += ' '; } return tableString; }
functionjavaThreadTraceToString(thread, tableSize) { let table = genTableString(tableSize); let tag = `${table}Thread Trace:\n`; return`${tag}${table}${thread.getStackTrace().join(`\n${table} `)}`; }
// 调用代码 let rThread = Java.use('java.lang.Thread'); let thread = rThread.currentThread(); console.log(javaThreadTraceToString(thread, 1));