Hi everybody,
I am working on an app that cannot run on a rooted device due to security reasons. In this purpose, 3 methods are used to check if the device is rooted. One of them is the following :
Code:
public static boolean checkRootMethod3() {
if (new ExecShell().executeCommand(SHELL_CMD.check_su_binary) != null) {
return true;
} else {
return false;
}
}
/**
* @author Kevin Kowalewski
*/
class ExecShell {
public static enum SHELL_CMD {
check_su_binary(new String[] { "/system/xbin/which", "su"
}), ;
String[] command;
SHELL_CMD(final String[] command) {
this.command = command;
}
}
private static String LOG_TAG = ExecShell.class.getName();
public ArrayList<String> executeCommand(final SHELL_CMD shellCmd) {
String line = null;
final ArrayList<String> fullResponse = new ArrayList<String>();
Process localProcess = null;
try {
localProcess = Runtime.getRuntime().exec(shellCmd.command);
} catch (final Exception e) {
return null;
// e.printStackTrace();
}
final BufferedReader in = new BufferedReader(new InputStreamReader(
localProcess.getInputStream()));
try {
while ((line = in.readLine()) != null) {
fullResponse.add(line);
}
} catch (final Exception e) {
if (ThalerMobileConfig.DEBUG) {
e.printStackTrace();
}
}
return fullResponse;
}
The issue is that this method returns true for the OnePlus One mobile phone although it is a non-rooted device. I try to check if the device is running on CyanogenMod in order to bypass the check but I do not manage to do it. Here are 2 methods that I tried :
1)
Code:
private static boolean isCyanogenMod() {
String version = System.getProperty("os.version");
if (version.contains("cyanogenmod")) {
return true;
}
return false;
}
2)
Code:
String version = "";
try {
Process p = Runtime.getRuntime().exec("uname -a");
InputStream in = p.getInputStream() ;
int c;
while ((c = in.read()) != -1) {
version += (char)c;
}
in.close();
if (version.contains("cyanogenmod")) {
return true;
}
return false;
} catch (Exception ex) {
return false ;
}
The issue with the first method is that only the first line of the kernel is returned and the word cyanogenmod is not detected.
The issue with the second method is that the following error message is returned :
"java.io.IOException: Error running exec(). Command: [uname, -a] Working Directory: null Environment:null"
Could you please help me to :
Find how to get the full string of the kernel version with method 1)
OR
Find how to manage to run the command uname -a with method 2)
OR
Any other solution to detect a rooted device without confusion with Cyanogenmod.
Thanks a lot for you help and sorry if it seems trivial for you but I am not an Android Expert.
Best Regards,
Laurent
For detecting CM, simply use Build public API. You don't need to use xposed for that.
http://developer.android.com/reference/android/os/Build.html
There are many other properties you could check to see if the phone is running CM, like here one a Oneplus One running CM12 :
~$ getprop | grep cm
[audio.offload.pcm.16bit.enable]: [true]
[audio.offload.pcm.24bit.enable]: [true]
[ro.cm.device]: [bacon]
[ro.cm.display.version]: [12-20150312-NIGHTLY-bacon]
[ro.cm.releasetype]: [NIGHTLY]
[ro.cm.version]: [12-20150312-NIGHTLY-bacon]
[ro.cmlegal.url]: [https://cyngn.com/legal/privacy-policy]
[use.voice.path.for.pcm.voip]: [true]
~$ getprop | grep cyan
[ro.build.host]: [cyanogenmod]
[ro.error.receiver.system.apps]: [org.cyanogenmod.bugreport]
[ro.rommanager.developerid]: [cyanogenmodnightly]
~$
What I don't really understand is why you focus on detecting CM. Is it just because by chance you happened to try on a OPO with CM? To me it just rings a bell that the first check is just not really fit for your purpose here. There are thousands of roms our there that derives from CM, and won't have the CyanogenMod word wherever you try to find it, on thousands of different device models, with dozens of different Android versions.
If the security of the app relies on checking that the device isn't rooted, I feel it's a bad security by design.
Btw there's an Xposed module called root cloak that is made to fool apps that try to detect root, so don't try to hard, it will probably not be worth it.
What you can do is read about this root cloak module as IIRC it details what root detection methods it can fool, so you can learn about root detection methods.
Hope that can help you, I'm not trying to discourage you.
Sent from my OnePlus One with Tapatalk
Related
(Unfortunately I couldn't post in the relevant thread because of forum permissions)
I'm using Semaphore ICS kernel 1.2 on my Galaxy S (Dark Knight 5.5). I tried to use the Android USB Host API. But UsbManager fails to enumerate all my devices.
I tried to circumvent the "normal" way by constructing and hacking my way by constructing UsbDevice, UsbInterface and UsbInterface manually by scanning the filesystem (/sys/bus/usb/devices)
In the end I still hit a wall -
Code:
public UsbDeviceConnection openDevice(UsbManager manager, UsbDevice device)
{
String devname = device.getDeviceName();
try
{
Field f = UsbManager.class.getDeclaredField("mService");
f.setAccessible(true);
Object service = f.get(manager);
if(service == null)
return null;
Class<?> IUsbManager = Class.forName("android.hardware.usb.IUsbManager");
Method opendevice = IUsbManager.getMethod("openDevice", new Class<?>[] { String.class });
opendevice.setAccessible(true);
ParcelFileDescriptor pfd = (ParcelFileDescriptor)opendevice.invoke(service, new Object[] { devname });
if(pfd == null)
return null;
Class<?> UsbDevConnection = UsbDeviceConnection.class;
Constructor<?> ctor = UsbDevConnection.getConstructor(new Class<?>[] { String.class, ParcelFileDescriptor.class });
ctor.setAccessible(true);
UsbDeviceConnection conn = (UsbDeviceConnection)ctor.newInstance(new Object[] { devname, pfd });
Method open = UsbDevConnection.getMethod("open", new Class<?>[] { UsbDevice.class });
open.setAccessible(true);
Boolean result = (Boolean)open.invoke(new Object[] { conn, device });
pfd.close();
if(result)
return conn;
}
catch(Exception ex)
{
Log.i("USB_MANAGER", "Exception: " + ex.getMessage());
}
return null;
}
Invoking IUsbDevice.open() would throw an exception:
java.lang.IllegalArgumentException: device /dev/bus/usb/001/002 does not exist or is restricted
Putting that aside, chmod -R 777 /dev/bus/usb with root permissions does not work.
I've ran out of hacks I could find... As this is supposed to be a supported feature in ICS installed on USB Host capable device, I resort to (wanting to) ask the kernel author on this issue here.
Edit:
Native USB access works (UHC managed to mount my thumb drives)
Same problem here. Have you fixed this?
Hey.
I am not entirely sure how to log data from a function.
I am trying to log the accessed URL from the first of the 3 functions in the code pasted below.
I have found the method, but I don't know if It is possible to log the output
Code:
Method - name = HttpConnector.java:
package dk.inlogic.app.res;
private static String getAction(Context context, String action, String version) {
try {
String result = getURLaction(context, action, PreferenceConnector.readString(context, PreferenceConnector.USERNAME, ""), PreferenceConnector.readString(context, PreferenceConnector.PASSWORD, ""), version);
if (result != null) {
return result;
}
} catch (Exception e) {
Log.e("HttpConnector getAction Exception", "action = " + action + ", version = " + version + ", " + e.toString());
}
return "";
}
public static String getTimetable(Context context) {
return getAction(context, "timetable", "2");
}
File 2 - Name, TimetableFragment.java:
package dk.inlogic.app.timetable;
protected String doInBackground(String... args) {
try {
Log.i("TimetableFragment ProgressTask doInBackground", "check for new file");
if (AppInfo.instance().isLive() && HttpConnector.getUpdateNowIfOld("rss2.xml")) {
Log.i("TimetableFragment ProgressTask doInBackground", "Get new file");
saveFile(HttpConnector.getTimetable(this.context));
return "";
} else {
if (!AppInfo.instance().isLive()) {
new DemoDownloadThread(TimetableFragment.this.getActivity()).start();
}
return "";
}
} catch (Exception e) {
Log.e("TimetableFragment doInBackground Exception", e.toString());
}
}
If by "output" you mean result, then you can use param.getResult.
If that's not it, I don't understand what you're trying to do.
Hello. I have this code.
Code:
public String getId() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
String id;
try {
data.writeInterfaceToken("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService");
binder.transact(1, data, reply, 0);
reply.readException();
id = reply.readString();
} finally {
reply.recycle();
data.recycle();
}
return id;
}
How i can hook " id = reply.readString();" when a don't know method name like "getId()". Thanks.
PS: Sorry for my bad English.
Now i can hook "transact" method like this:
Code:
XposedHelpers.findAndHookMethod("android.os.BinderProxy", loadPackageParam.classLoader, "transact", int.class, Parcel.class, Parcel.class, int.class, new XC_MethodHook() {
protected void afterHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
IBinder binder = (IBinder) param.thisObject;
String interfaceBinder = binder.getInterfaceDescriptor();
if (interfaceBinder.equals("com.google.android.gms.ads.identifier.internal.IAdvertisingIdService")) {
// change result for readString
}
}
});
But i cant override "readString" result for Parcel (args[2]) object. How can i do this? Thanks.
SU Version 2.82 and using BlueStacks 3
I've made a post on StackExchange but w/o any responses - inside are links I'll reference below but unfortunately can't directly post here due to being a new user of these forums. Googling the following should turn up the result: SU Command to start java class for input automation not working
Since then I've learned a bit more about what I'm trying to do (which is surprising considering I've been attempting this since the 30th of January) and implemented a singleton so that I can toast from within the class I'm attempting to start. Regardless of whether this allows me to inject inputs to other apps, my current problem is simply that I'm unable to use a SU command to open up my Main.Java class as described in the following links:
The code describing what I'm trying to do: OmerJerk Execute Java Class as Root User
The code w/ a full implementation: Remotedroid on GitHub
^ ServerService runs MainStarter which runs Main.Java as SU so that Main.Java can run EventInput to inject motion events
The super basic implementation I've got is below, but I've tried a bunch of things. I can't seem to figure out what's going wrong.
A snippet from ActivityMain wherein I'm running the Main.Java (attempting at least):
Code:
new Main().main(COMMAND3); //COMMAND3 is just a String[] because if it's not provided this won't execute. This isn't what I'm trying to do, though. Just a test to see if my Main.Java was broke.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids){
try {
//BlueStacks is 32 bit so it only has app_process - it doesn't have app_process32 and I believe if you try to target it it simply fails (the symbolic link is inconsistent iirc) vv
String[] COMMAND4 = {"su", "-c", "\"CLASSPATH=" + MainActivity.this.getPackageCodePath(), "/system/bin/app_process", "/system/bin", MainActivity.this.getPackageName() + ".Main"};
java.lang.Process console = Runtime.getRuntime().exec(COMMAND4);
BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(console.getOutputStream()));
String outputStr = new String();
BufferedReader reader = new BufferedReader(new InputStreamReader(console.getInputStream()));
while (reader.ready()) {
outputStr += reader.readLine();
}
PropertyReader.getInstance().setText(outputStr);
} catch (IOException e) {
e.printStackTrace();
PropertyReader.getInstance().showToast("IOException" + e.getMessage());
}
// final List<String> SUOutput = Shell.SU.run(String.format(COMMAND,
// new String[] {
// getApplicationContext()
// }));
// final String joined = TextUtils.join(", ", SUOutput);
//
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// if (SUOutput != null) {
// Toast.makeText(MainActivity.this, "Output isn't null" + joined, Toast.LENGTH_LONG).show();
// Toast.makeText(MainActivity.this, joined, Toast.LENGTH_LONG).show();
// Toast.makeText(MainActivity.this, SUOutput.toString(), Toast.LENGTH_LONG).show();
//
// } else {
// Toast.makeText(MainActivity.this, "Output is null o-o", Toast.LENGTH_LONG).show();
// }
// }
// });
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "It finished.. ?", Toast.LENGTH_LONG).show();
PropertyReader.getInstance().ToastString();
}
});
return null;
}
}.execute();
}
And my Main.Java is reaaaally simple and very stripped down at this point:
Code:
package intsumniac.overbitegames.com.intsumniac;
import android.os.Process;
public class Main {
public static void main(String[] args) {
PropertyReader.getInstance().showToast("Main Is working!!! SUCCESS" + "current process id = " + Process.myPid() + "current process uid = " + Process.myUid()); //Should be 0, preferably
}
}
It is certainly possible to run Java stuff as root, however you are lacking many contexts/instances/etc. There is some trickery to be able to get around some of that. Some of my apps' root parts are mostly Java, in fact.
Rule of thumb is that most Android API calls are not available, just standard Java things. Toasting for example is most certainly not available.
Have you ever tried to extend your favorite app with new features using Xposed, but were shocked halfway that your hooked app doesn't declare a permission in AndroidManifest ? And then you spent infinite hours on the internet trying to solve this frustrating problem, you decided to use services and an external intent, but you found out that it was not convenient, and finally you gave up...
So you are like me, who wasted hours looking for a solution, until I figured out how to do it myself. Here's a snippet to save time for future Xposed enthusiasts. Put this code snippet in handleLoadPackage
Java:
// Hook will only patch System Framework
if (!lpparam.packageName.equals("android")) return;
String targetPkgName = "com.example.app"; // Replace this with the target app package name
String[] newPermissions = new String[] { // Put the new permissions here
"android.permission.INTERNET",
"android.permission.ACCESS_NETWORK_STATE"
};
String grantPermissionsMethod = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
grantPermissionsMethod = "restorePermissionState";
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.S_V2) {
XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS NEWER THAN ANDROID 12");
}
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.P) {
grantPermissionsMethod = "grantPermissions";
}
else {
grantPermissionsMethod = "grantPermissionsLPw";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
XposedBridge.log("[WARNING] THIS HOOK IS NOT GUARANTEED TO WORK ON ANDROID VERSIONS PRIOR TO JELLYBEAN");
}
}
XposedBridge.hookAllMethods(XposedHelpers.findClass("com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader),
grantPermissionsMethod, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// on Android R and above, param.args[0] is an instance of android.content.pm.parsing.ParsingPackageImpl
// on Android Q and older, param.args[0] is an instance of android.content.pm.PackageParser$Package
// However, they both declare the same fields we need, so no need to check for class type
String pkgName = (String) XposedHelpers.getObjectField(param.args[0], "packageName");
XposedBridge.log("Package " + pkgName + " is requesting permissions");
if (pkgName.equals(targetPkgName)) {
List<String> permissions = (List<String>) XposedHelpers.getObjectField(param.args[0], "requestedPermissions");
for (String newPermission: newPermissions) {
if (!permissions.contains(newPermission)) {
permissions.add(newPermission);
XposedBridge.log("Added " + newPermission + " permission to " + pkgName);
}
}
}
}
});
Notes:
You must check System Framework in LSposed Manager
A reboot is required after adding the target permissions
You still need to prompt the user to accept sensitive permissions (ie android.permission.READ_CONTACTS), even if you have added them using this method
Wow, thx. Great for the install permissions!
I wrote a class to grant install and runtime/sensitive permissions (without prompting users).
Android 12 and 13 implementation:
Java:
public class Grant_Package_Permissions {
private static final int sdk = android.os.Build.VERSION.SDK_INT;
public static void hook(LoadPackageParam lpparam) {
try {
Class<?> PermissionManagerService = XposedHelpers.findClass(
sdk >= 33 /* android 13+ */ ?
"com.android.server.pm.permission.PermissionManagerServiceImpl" :
"com.android.server.pm.permission.PermissionManagerService", lpparam.classLoader);
Class<?> AndroidPackage = XposedHelpers.findClass(
"com.android.server.pm.parsing.pkg.AndroidPackage", lpparam.classLoader);
Class<?> PermissionCallback = XposedHelpers.findClass(
sdk >= 33 /* android 13+ */ ?
"com.android.server.pm.permission.PermissionManagerServiceImpl$PermissionCallback" :
"com.android.server.pm.permission.PermissionManagerService$PermissionCallback", lpparam.classLoader);
// PermissionManagerService(Impl) - restorePermissionState
XposedHelpers.findAndHookMethod(PermissionManagerService, "restorePermissionState",
AndroidPackage, boolean.class, String.class, PermissionCallback, int.class, new XC_MethodHook() {
@SuppressWarnings("unchecked")
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// params
Object pkg = param.args[0];
int filterUserId = (int) param.args[4];
// obtém os campos
Object mState = XposedHelpers.getObjectField(param.thisObject, "mState");
Object mRegistry = XposedHelpers.getObjectField(param.thisObject, "mRegistry");
Object mPackageManagerInt = XposedHelpers.getObjectField(param.thisObject, "mPackageManagerInt");
// Continua ?
String packageName = (String) XposedHelpers.callMethod(pkg, "getPackageName");
Object ps = XposedHelpers.callMethod(mPackageManagerInt,
sdk >= 33 /* android 13+ */ ?
"getPackageStateInternal" :
"getPackageSetting", packageName);
if (ps == null)
return;
int[] getAllUserIds = (int[]) XposedHelpers.callMethod(param.thisObject, "getAllUserIds");
int userHandle_USER_ALL = XposedHelpers.getStaticIntField(Class.forName("android.os.UserHandle"), "USER_ALL");
final int[] userIds = filterUserId == userHandle_USER_ALL ? getAllUserIds : new int[]{filterUserId};
for (int userId : userIds) {
List<String> requestedPermissions;
Object userState = XposedHelpers.callMethod(mState, "getOrCreateUserState", userId);
int appId = (int) XposedHelpers.callMethod(ps, "getAppId");
Object uidState = XposedHelpers.callMethod(userState, "getOrCreateUidState", appId);
// package 1
if (packageName.equals("PACKAGE_1")) {
requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.RECORD_AUDIO);
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.MODIFY_AUDIO_SETTINGS);
}
// package 2
if (packageName.equals("PACKAGE_2")) {
requestedPermissions = (List<String>) XposedHelpers.callMethod(pkg, "getRequestedPermissions");
grantInstallOrRuntimePermission(requestedPermissions, uidState, mRegistry,
Manifest.permission.READ_CONTACTS);
}
}
}
});
} catch (Exception e) {
XposedBridge.log(e);
}
}
private static void grantInstallOrRuntimePermission(List<String> requestedPermissions, Object uidState,
Object registry, String permission) {
if (!requestedPermissions.contains(permission))
XposedHelpers.callMethod(uidState, "grantPermission",
XposedHelpers.callMethod(registry, "getPermission", permission));
}
}
Edit: Android 12 and 13 implementation!
this looks promising. how to use this in xposed ? is there a module available?