[Xposed General] _amadan_:
Hello,

I'm fairly new to using Xposed. I've used it a couple of times to solve some Android CTF challenges, where its ability to hook into an app is a real benefit.

But now I'm trying to understand it a little bit better. As a simple toy project, I'm considering using Xposed to implement a sort of periodic update for Xposed modules. It's not exactly the most useful mod, but it helps me get a better understanding of the framework.

Based on the development tutorial, I thought I could just implement a naive cron.d over the updateClock() method and fire off an AsyncTask to handle

communication to an external server (a package repo if you will). So far, my code goes like this:

package com.example.red;

import android.app.AndroidAppHelper;
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.AsyncTask;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Calendar;

class UpdateModulesTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... voids) {
XposedBridge.log("Updating modules");
try {
Application app = AndroidAppHelper.currentApplication();
Context ctx = app.getApplicationContext();
int res = ctx.checkCallingOrSelfPermission(android.Manifest.permission.INTERNET);
if (res == PackageManager.PERMISSION_DENIED)
XposedBridge.log("No internet");
else
XposedBridge.log("Internet granted");
URL url = new URL("<MY URL>");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
DataOutputStream out = new DataOutputStream(conn.getOutputStream());
out.writeBytes("key=value");
out.flush();
out.close();
int rcode = conn.getResponseCode();
XposedBridge.log("Return code " + String.valueOf(rcode));
if (rcode == 200) {
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line = "";
StringBuilder response = new StringBuilder();
while ((line = in.readLine()) != null) {
response.append(line);
}
in.close();
XposedBridge.log(response.toString());
}
} catch (Exception e) {
XposedBridge.log(e.getMessage());
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
XposedBridge.log(sw.toString());
}
return null;
}
}

public class Main implements IXposedHookLoadPackage {
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedBridge.log(lpparam.packageName);

if (lpparam.packageName.equals("com.android.systemui")) {
findAndHookMethod("com.android.systemui.statusbar.policy.Clock", lpparam.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Calendar now = Calendar.getInstance();
int hour = now.get(Calendar.HOUR_OF_DAY);
XposedBridge.log("Hour is: " + String.valueOf(hour));
if (hour == 11) {
int minute = now.get(Calendar.MINUTE);
XposedBridge.log("Minute is: " + String.valueOf(minute));
if (minute % 2 == 0) { // update modules
new UpdateModulesTask().execute();
}
}
}
});
}
}
}

What Xposed logs, instead, goes like this:

I/Xposed ( 3574): Hour is: 11
I/Xposed ( 3574): Minute is: 50
I/Xposed ( 3574): Updating modules
I/Xposed ( 3574): Internet granted
I/Xposed ( 3574): Permission denied (missing INTERNET permission?)
I/Xposed ( 3574): java.lang.SecurityException: Permission denied (missing INTERNET permission?)
I/Xposed ( 3574):	at java.net.InetAddress.lookupHostByName(InetAddress.java:451)
I/Xposed ( 3574):	at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
I/Xposed ( 3574):	at java.net.InetAddress.getAllByName(InetAddress.java:215)
I/Xposed ( 3574):	at com.android.okhttp.HostResolver$1.getAllByName(HostResolver.java:29)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:232)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:124)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:272)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:373)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:106)
I/Xposed ( 3574):	at com.android.okhttp.internal.http.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:208)
I/Xposed ( 3574):	at com.example.red.UpdateModulesTask.doInBackground(Main.java:41)
I/Xposed ( 3574):	at com.example.red.UpdateModulesTask.doInBackground(Main.java:26)
I/Xposed ( 3574):	at android.os.AsyncTask$2.call(AsyncTask.java:288)
I/Xposed ( 3574):	at java.util.concurrent.FutureTask.run(FutureTask.java:237)
I/Xposed ( 3574):	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
I/Xposed ( 3574):	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
I/Xposed ( 3574):	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
I/Xposed ( 3574):	at java.lang.Thread.run(Thread.java:818)
I/Xposed ( 3574): Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
I/Xposed ( 3574):	at libcore.io.Posix.android_getaddrinfo(Native Method)
I/Xposed ( 3574):	at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:55)
I/Xposed ( 3574):	at java.net.InetAddress.lookupHostByName(InetAddress.java:438)
I/Xposed ( 3574):	... 18 more
I/Xposed ( 3574): Caused by: android.system.ErrnoException: android_getaddrinfo failed: EACCES (Permission denied)
I/Xposed ( 3574):	... 21 more

So, from what I understand, com.android.systemui should have the "android.permission.INTERNET" permission, which is also what the "Internet granted" message in the logcat output indicates. I also checked for SELinux violations and it doesn't seem to be the case.

What I'd like to understand is why my hook, called from the context of com.android.systemui (which hold the Internet permission), checks to see if it has the Internet permission (and it does) and then receives a permission denied error when actually attempting make the connection, despite the fact that there is no intervention from SELinux. Am I missing something obvious with regards to the way Xposed works?