How do I detect if SELinux is enabled in an android application? - android

I'm trying to use the SecureRandom workaround that Google posted in my android application:
http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html
This work around involve writing to (and reading from) /dev/urandom. However, it looks like Samsung has enabled SELinux in such a way that prevents applications from accessing /dev/urandom.
I don't have one of these devices, so it is a little hard for me to test solutions, other than to push out attempts at workarounds on the Android market, but it seems like this is not an error that I can trap with a try catch block. It also appears that File.canRead and canWrite return true. You can see my attempts at workaround in the supportedOnThisDevice method in the following class:
PRNGFixes.java
I'm looking for a reliable way to detect if I am an such a device, and if so, not apply the Google SecureRandom workaround.

This is my way to check if SELinux is in enforce-mode - can be done via any Shell-script, not depending on RootTools:
private static boolean isSELinuxEnforcing() {
try {
CommandCapture command = new CommandCapture(1, "getenforce");
RootTools.getShell(false).add(command).waitForFinish();
boolean isSELinuxEnforcing = command.toString().trim().equalsIgnoreCase("enforcing");
return isSELinuxEnforcing;
} catch (Exception e) {
// handle exception
}
return false;
}

I've heard Samsung is starting to ship devices with the SELinux policy set to Enforce, but I don't know if it's true or not. As far as I know most devices on 4.3 still have it set to permissive.
According to Google, "SELinux reinforcement is invisible to users and developers, and adds robustness to the existing Android security model while maintaining compatibility with existing applications." So you may need to check the system properties or test it through a shell to find out for sure.
If you can get someone to send you their build.prop you may be able to catch it by comparing their ro.build.selinux property via System.getProperty("ro.build.selinux"),
but you'll also want to verify you're able to access it more directly in case it is unreliable or getProperty() for that is broken in future updates.
Root (System user on SELinux) is another option when available, but either way a shell based solution is probably your best bet.

System.getProperty("ro.build.selinux")
Did not work for me on Samsung S4 Android 4.3. So I wrote this
private static final int JELLY_BEAN_MR2 = 18;
public static boolean isSELinuxSupported() {
// Didnt' work
//String selinuxStatus = System.getProperty(PROPERTY_SELINUX_STATUS);
//return selinuxStatus.equals("1") ? true : false;
String selinuxFlag = getSelinuxFlag();
if (!StringUtils.isEmpty(selinuxFlag)) {
return selinuxFlag.equals("1") ? true : false;
} else {
// 4.3 or later ?
if(Build.VERSION.SDK_INT >= JELLY_BEAN_MR2) {
return true;
}
else {
return false;
}
}
}
public static String getSelinuxFlag() {
String selinux = null;
try {
Class<?> c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
selinux = (String) get.invoke(c, "ro.build.selinux");
} catch (Exception ignored) {
}
return selinux;
}

if you have access to the frameworks
import android.os.SELinux;
SELinux.isSELinuxEnforced();

Most devices as as of Jellybean MR2 and onwards will have SELinux enabled on their devices, but if you are working with OEMs or doing platform work this might not necessarily be true.
The method I am using to verify is with the getenforce shell command:
public boolean isSeLinuxEnforcing() {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec("getenforce");
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine())!= null) {
output.append(line);
}
} catch (Exception e) {
Log.e(TAG, "OS does not support getenforce");
// If getenforce is not available to the device, assume the device is not enforcing
e.printStackTrace();
return false;
}
String response = output.toString();
if ("Enforcing".equals(response)) {
return true;
} else if ("Permissive".equals(response)) {
return false;
} else {
Log.e(TAG, "getenforce returned unexpected value, unable to determine selinux!");
// If getenforce is modified on this device, assume the device is not enforcing
return false;
}
}
It appears that most devices are only writing a system property for selinux if they aren't running in enforcing status. You can additionally check the property: ro.boot.selinux to see if the Kernel passed in the permissive parameter on your current build.

Related

How to detect installed Chrome version from my Android App?

I am working on a feature where I need to transition user from Android Native APP to Chrome custom tab only if chrome version installed on the device is greater than version 65. So my question is, is there a way we can detect chrome version from Android Native App ?
private boolean isChromeInstalledAndVersionGreaterThan65() {
PackageInfo pInfo;
try {
pInfo = getPackageManager().getPackageInfo("com.android.chrome", 0);
} catch (PackageManager.NameNotFoundException e) {
//chrome is not installed on the device
return false;
}
if (pInfo != null) {
//Chrome has versions like 68.0.3440.91, we need to find the major version
//using the first dot we find in the string
int firstDotIndex = pInfo.versionName.indexOf(".");
//take only the number before the first dot excluding the dot itself
String majorVersion = pInfo.versionName.substring(0, firstDotIndex);
return Integer.parseInt(majorVersion) > 65;
}
return false;
}
Obviously this will work until Chrome will be versioned how they did until now, if they will decide to change the versioning the logic should be updated as well. (I don't think this will happen but for max safety put everything in a try-catch)
private boolean checkVersion(String uri) {
String versionName;
int version = 0;
PackageManager pm = getPackageManager();
try {
versionName = pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES).versionName;
String[] split = versionName.split(Pattern.quote("."));
String major = split[0];
version = Integer.parseInt(major);
return version > 65;
} catch (PackageManager.NameNotFoundException e) {
//Catch the Exception
}
return false;
}
Then call above method using
checkVersion("com.android.chrome"); //This will return a boolean value

Is There any way to Protect my App from Rooted phone

I know this question asked multiple time, i have googled it too but not found any working answer.
I am developing a android App with sqlite database and want to secure my database.db file from rooted phone, I have applied the check for rooted Device using below code but its not working on some Samsung and Redmi.
public class CheckRooted {
public static boolean isRooted() {
// get from build info
String buildTags = android.os.Build.TAGS;
if (buildTags != null && buildTags.contains("test-keys")) {
return true;
}
// check if /system/app/Superuser.apk is present
try {
File file = new File("/system/app/Superuser.apk");
if (file.exists()) {
return true;
}
} catch (Exception e1) {
// ignore
}
// try executing commands
return canExecuteCommand("su");
}
// executes a command on the system
private static boolean canExecuteCommand(String command) {
boolean executedSuccesfully;
try {
Runtime.getRuntime().exec(command);
executedSuccesfully = true;
} catch (Exception e) {
executedSuccesfully = false;
}
return executedSuccesfully;
}
}
Also suggest me how to protect my database.db file from rooted phones, I can't use any paid services like GreenDao or others.
I'm not very familiar with Android, but this can help you with your database encryption problem.
It is called "SQLCipher".
Check it out. It also has a community edition, which is also allowed to be used with commercial apps.
Further more, if the root checks aren't working on some Samsung and Redmi Devices, make sure they do not use slightly different commands.
Try testing for multiple root commands instead of just one single one.

Is possible turn on/off data connection in Android Lollipop rooted?

I'm creating an app that needs to change the data connection.
I found a solution: using su commands, but the problem is that Toast Warning shows every time when I execute the command....
Is possible using these commands without toast warning ?
Or
Is there a way to toggle the data connection enabled with TelephonyManager using reflections? I tried it, but it didn't works.
My code is below:
public static void setMobileDataState(boolean mMobileDataEnabled){
try{
if(mMobileDataEnabled)
Shell.runAsRoot(new String[]{"svc data enable"});
else
Shell.runAsRoot(new String[]{"svc data disable"});
}
catch (Exception ex){
Utilities.log(ex.toString());
}
}
public class Shell {
public static void runAsRoot(String[] mCommands){
try {
Process mProcess = Runtime.getRuntime().exec("su");
DataOutputStream mOS = new DataOutputStream(mProcess.getOutputStream());
for (String mCommand : mCommands) {
mOS.writeBytes(mCommand + "\n");
}
mOS.writeBytes("exit\n");
mOS.flush();
}catch (Exception o){
Utilities.log(o.toString());
}
}
}
I found the solution.....
I did the following:
I rooted my device
I installed my app as system app, it's simple, just copy your apk into /system/priv-app/myApk.apk and set chmod 644 permissions. If you have a doubt, check this post (If i set my android app to a system app, after a factory reset, will it be removed from the phone?).
I just removed the /system/app/SuperSU folder
I did a factory reset on device, and that is it ..... =D

Get screenshot of current foreground app on Android having root priviliges

I'm developing an application that is installed on the system partition and I would like to know if it's possible to get a screenshot of the current foreground application from a service. Of course, the application being any third party app.
I'm not interested in security issues or anything related to that matter. I only want to get a snapshot of the current foreground third party app.
Note: I'm aware of the /system/bin/screencap solution but I'm looking for a more elegant alternative that does everything programmatically.
The method that I'm going to describe below will let you to programmatically take screen shots of whatever app it's in the foreground from a background process.
I am assuming that you have a rooted device.
I this case you can use the uiautomator framework to get the job done.
This framework has a been created to automate black box testing of apps on android, but it will suite this purpose as well.
We are going to use the method
takeScreenshot(File storePath, float scale, int quality)
This goes in the service class:
File f = new File(context.getApplicationInfo().dataDir, "test.jar");
//this command will start uiautomator
String cmd = String.format("uiautomator runtest %s -c com.mypacket.Test", f.getAbsoluteFile());
Process p = doCmds(cmd);
if(null != p)
{
p.waitFor();
}
else
{
Log.e(TAG, "starting the test FAILED");
}
private Process doCmds(String cmds)
{
try
{
Process su = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(su.getOutputStream());
os.writeBytes(cmds + "\n");
os.writeBytes("exit\n");
os.flush();
os.close();
return su;
}
catch(Exception e)
{
e.printStackTrace();
Log.e(TAG, "doCmds FAILED");
return null;
}
}
This is the class for uiautomator:
public class Test extends UiAutomatorTestCase
{
public void testDemo()
{
UiDevice dev = UiDevice.getInstance();
File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
dev.takeScreenshot(f, 1.0, 100);
}
}
It's best if you create a background thread in which uiautomator will run, that way it will not run onto the ui thread. (the Service runs on the ui thread).
uiatuomator doesn't know about or have a android context.
Once uiautomator gets the control you will be able to call inside it android methods that do not take a context parameter or belong to the context class.
If you need to communicate between uiautomator and the service (or other android components) you can use LocalSocket.
This will allow communication in both ways.
Months have passed since I asked this question but just now had the time to add this feature. The way to do this is simply by calling screencap -p <file_name_absolute_path> and then grabbing the file. Next is the code I used:
private class WorkerTask extends AsyncTask<String, String, File> {
#Override
protected File doInBackground(String... params) {
File screenshotFile = new File(Environment.getExternalStorageDirectory().getPath(), SCREENSHOT_FILE_NAME);
try {
Process screencap = Runtime.getRuntime().exec("screencap -p " + screenshotFile.getAbsolutePath());
screencap.waitFor();
return screenshotFile;
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(File screenshot_file) {
// Do something with the file.
}
}
Remember to add the <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> permission to the manifest. Otherwise screenshot.png will be blank.
This is much simpler than what Goran stated and is what I finally used.
Note: It only worked for me when the app is installed on the system partition.

DexClassLoader, reload Code fails with Signal 7

I'm trying to build a plugin-System, where DexClassLoader is fetching code from other installed apks containing fragments(my plugins), and showing them in my host. This is working quite nice.
I also like to make the plugins hotswappable, this means I can change the code from a plugin, install it new and the host will notice and will load the new code. This also works, if I'm changing the code for the first time. (Although I thought it shouldn't, it seems I've got a wrong understanding of this code:
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
)
If i'm trying it a second time with the same plugin, the host shuts down at requiredClass = classLoader.loadClass(fullName); with something like
libc Fatal signal 7 (SIGBUS) at 0x596ed4d6 (code=2), thread 28814
(ctivityapp.host)
Does anybody has a deeper insight in the functionality of DexClassLoader and may tell me, what is happening here? I'm quite stuck at this.
Heres the full code of the method loading the foreign code:
/**
* takes the name of a package as String, and tries to load the code from the corresponding akp using DexclassLaoder.
* Checking if a package is a valid plugin must be done before calling this.
* The Plugin must contain a public class UI that extends Fragment and implements plugin as a starting point for loading
* #param packageName The full name of the package, as String
* #return the plugins object if loaded, null otherwise
*/
private Plugin attachPluginToHost(String packageName) {
try {
Class<?> requiredClass = null;
final ApplicationInfo info = context.getPackageManager().getApplicationInfo(packageName,0);
final String apkPath = info.sourceDir;
final File dexTemp = context.getDir("temp_folder", 0);
final String fullName = packageName + ".UI";
boolean isLoaded = true;
// Check if class loaded
try {
requiredClass = Class.forName(fullName);
} catch(ClassNotFoundException e) {
isLoaded = false;
}
if (!isLoaded) {
final DexClassLoader classLoader = new DexClassLoader(apkPath, dexTemp.getAbsolutePath(), null, context.getApplicationContext().getClassLoader());
requiredClass = classLoader.loadClass(fullName);
}
if (null != requiredClass) {
// Try to cast to required interface to ensure that it's can be cast
final Plugin plugin = Plugin.class.cast(requiredClass.newInstance());
installedPlugins.put(plugin.getName(), plugin);
return plugin;
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
Many thanks in advance!
Not that it really matters (As nobody is actually viewing this), or that I even understand what's going on, but deleting the corresponding file of the plugin in dexTemp.getAbsolutePath() before reloading it solves the problem.
PS: Tumbleweed-Badge, YAY!

Categories

Resources