Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit 1d6d7ca6 authored by Marvin W.'s avatar Marvin W. 🐿️
Browse files

First bunch of code

parent 2f72cf7f
Loading
Loading
Loading
Loading

build.gradle

0 → 100644
+43 −0
Original line number Diff line number Diff line
/*
 * Copyright 2013-2016 microG Project Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.3'
    }
}

allprojects {
    apply plugin: 'idea'
    ext.androidBuildVersionTools = "23.0.3"
    ext.isReleaseVersion = false
}

def androidCompileSdk() { return 23 }

def androidTargetSdk() { return 23 }

def androidMinSdk() { return 10 }

subprojects {
    group = 'org.microg'
    repositories {
        jcenter()
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -32,6 +32,12 @@ android {
        versionName getMyVersionName()
        versionCode getMyVersionCode()
    }

    sourceSets {
        main {
            java.srcDirs += 'src/main/protos-java'
        }
    }
}

if (file('user.gradle').exists()) {
+6 −2
Original line number Diff line number Diff line
@@ -4,15 +4,19 @@

    <uses-permission android:name="android.permission.INTERNET"/>

    <uses-sdk
        android:minSdkVersion="9"
        android:targetSdkVersion="23"/>

    <application
        android:allowBackup="true"
        android:label="microG DroidGuard Helper">

        <service
            android:name="org.microg.gms.droidguard.RemoteDroidGuardService"
            android:permission="android.permission.INTERNET"
            android:enabled="true"
            android:exported="true">
            android:exported="true"
            android:permission="android.permission.INTERNET">
        </service>
    </application>

+1 −1
Original line number Diff line number Diff line
@@ -3,5 +3,5 @@ package org.microg.gms.droidguard;
import org.microg.gms.droidguard.IRemoteDroidGuardCallback;

interface IRemoteDroidGuard {
    void guard(IRemoteDroidGuardCallback callback, String type, in Bundle data);
    void guard(IRemoteDroidGuardCallback callback, String packageName, String type, in Bundle data);
}
 No newline at end of file
+277 −0
Original line number Diff line number Diff line
package org.microg.gms.droidguard;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Base64;
import android.util.Log;

import com.squareup.wire.Wire;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import dalvik.system.DexClassLoader;
import okio.ByteString;

public class RemoteDroidGuardService extends Service {
    private static final String TAG = "GmsDroidGuardRemote";
    private static final String DG_CLASS_NAME = "com.google.ccc.abuse.droidguard.DroidGuard";
    private static final String DG_URL = "https://www.googleapis.com/androidantiabuse/v1/x/create?alt=PROTO&key=AIzaSyBofcZsgLSS7BOnBjZPEkk4rYwzOIz-lTI";
    private static Map<String, Class<?>> loadedClass = new HashMap<>();

    @Override
    public IBinder onBind(Intent intent) {
        return new IRemoteDroidGuard.Stub() {

            @Override
            public void guard(final IRemoteDroidGuardCallback callback, final String packageName, final String type, Bundle data) throws RemoteException {
                final Map map = new HashMap();
                for (String key : data.keySet()) {
                    String val = data.getString(key);
                    if (val != null) map.put(key, val);
                }
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            callback.onResult(RemoteDroidGuardService.this.guard(packageName, type, map));
                        } catch (Exception e) {
                            Log.w(TAG, e);
                            try {
                                callback.onError();
                            } catch (RemoteException e1) {
                                stopSelf();
                            }
                        }
                    }
                }).start();
            }
        };
    }

    private byte[] guard(String packageName, String type, Map map) throws Exception {
        SignedDGResponse signedResponse = request(new DGRequest.Builder()
                .usage(new DGUsage(type, packageName))
                .isGoogleCn(false)
                .someTrue(true)
                .currentVersion(3)
                .cached(getCached())
                .arch(getArch())
                .build());
        DGResponse response = new Wire().parseFrom(signedResponse.data.toByteArray(), DGResponse.class);
        String checksum = response.vmChecksum.hex();
        File dgCacheDir = getDir("dg_cache", 0);
        File dgDir = new File(dgCacheDir, checksum);
        dgDir.mkdirs();
        File dgFile = new File(dgDir, "the.apk");
        loadAndPrepareDg(response, dgCacheDir, dgDir, dgFile);

        Class<?> clazz;
        if (!loadedClass.containsKey(checksum)) {
            ClassLoader loader = new DexClassLoader(new File(dgDir, "the.apk").getAbsolutePath(),
                    new File(dgDir, "opt").getAbsolutePath(), new File(dgDir, "lib").getAbsolutePath(), getClassLoader());
            clazz = loader.loadClass(DG_CLASS_NAME);
            loadedClass.put(checksum, clazz);
        } else {
            clazz = loadedClass.get(checksum);
        }

        Object instance = clazz
                .getDeclaredConstructor(Context.class, String.class, byte[].class, Object.class)
                .newInstance(this, type, response.byteCode.toByteArray(), new Callback(packageName));
        clazz.getMethod("init").invoke(instance);
        byte[] bytes = (byte[]) clazz.getMethod("run", Map.class).invoke(instance, map);
        try {
            clazz.getMethod("close").invoke(instance);
        } catch (Exception e) {
            Log.w(TAG, e);
        }
        return bytes;
    }

    private List<ByteString> getCached() {
        List<ByteString> res = new ArrayList<>();
        File dgDir = getDir("dg_cache", 0);
        for (String cache : dgDir.list()) {
            if (new File(dgDir, cache).isDirectory()) {
                res.add(ByteString.decodeHex(cache));
            }
        }
        return res;
    }

    private void loadAndPrepareDg(DGResponse response, File dgCacheDir, File dgDir, File dgFile) throws IOException {
        File dgCacheFile = new File(dgCacheDir, response.vmChecksum.hex() + ".apk");
        if (!dgFile.exists()) {
            if (response.content == null) {
                Log.d(TAG, "Downloading DG implementation from " + response.vmUrl + " to " + dgCacheFile);
                InputStream is = new URL(response.vmUrl).openStream();
                streamToFile(is, dgCacheFile);
            } else {
                Log.d(TAG, "Using provided response data for " + dgCacheFile);
                FileOutputStream fos = new FileOutputStream(dgCacheFile);
                fos.write(response.content.toByteArray());
                fos.close();
            }
            new File(dgDir, "opt").mkdirs();
            File libDir = new File(dgDir, "lib");
            libDir.mkdirs();
            ZipFile zipFile = new ZipFile(dgCacheFile);
            Enumeration<? extends ZipEntry> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if (!entry.isDirectory()) {
                    String name = entry.getName();
                    String targetName;
                    if (name.startsWith("lib/" + Build.CPU_ABI + "/")) {
                        targetName = name.substring(Build.CPU_ABI.length() + 5);
                    } else if (name.startsWith("lib/" + Build.CPU_ABI2 + "/")) {
                        targetName = name.substring(Build.CPU_ABI2.length() + 5);
                    } else {
                        continue;
                    }
                    streamToFile(zipFile.getInputStream(entry), new File(libDir, targetName));
                }
            }
            dgCacheFile.renameTo(dgFile);
        } else {
            Log.d(TAG, "Using cached file from " + dgFile);
        }
    }

    private static void streamToFile(InputStream is, File targetFile) throws IOException {
        FileOutputStream fos = new FileOutputStream(targetFile);
        byte[] bytes = new byte[1024];
        while (true) {
            int n = is.read(bytes);
            if (n < 0) {
                break;
            }
            fos.write(bytes, 0, n);
        }
        is.close();
        fos.close();
    }

    private static String getArch() {
        try {
            return System.getProperty("os.arch");
        } catch (Exception ex) {
            return "";
        }
    }

    private static SignedDGResponse request(DGRequest request) throws IOException {
        HttpURLConnection connection = (HttpURLConnection) new URL(DG_URL).openConnection();
        connection.setRequestMethod("POST");
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setRequestProperty("Content-type", "application/x-protobuf");
        connection.setRequestProperty("Content-Encoding", "gzip");
        connection.setRequestProperty("Accept-Encoding", "gzip");
        connection.setRequestProperty("User-Agent", "DroidGuard/" + 9452000); // TODO

        Log.d(TAG, "-- Request --\n" + request);
        OutputStream os = new GZIPOutputStream(connection.getOutputStream());
        os.write(request.toByteArray());
        os.close();

        if (connection.getResponseCode() != 200) {
            byte[] bytes = null;
            String ex = null;
            try {
                bytes = readStreamToEnd(connection.getErrorStream());
                ex = new String(readStreamToEnd(new GZIPInputStream(new ByteArrayInputStream(bytes))));
            } catch (Exception e) {
                if (bytes != null) {
                    throw new IOException(getBytesAsString(bytes), e);
                }
                throw new IOException(connection.getResponseMessage(), e);
            }
            throw new IOException(ex);
        }

        InputStream is = connection.getInputStream();
        SignedDGResponse response = new Wire().parseFrom(new GZIPInputStream(is), SignedDGResponse.class);
        is.close();
        return response;
    }

    private static byte[] readStreamToEnd(final InputStream is) throws IOException {
        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        if (is != null) {
            final byte[] buff = new byte[1024];
            int read;
            do {
                bos.write(buff, 0, (read = is.read(buff)) < 0 ? 0 : read);
            } while (read >= 0);
            is.close();
        }
        return bos.toByteArray();
    }

    private static String getBytesAsString(byte[] bytes) {
        if (bytes == null) return "null";
        try {
            CharsetDecoder d = Charset.forName("US-ASCII").newDecoder();
            CharBuffer r = d.decode(ByteBuffer.wrap(bytes));
            return r.toString();
        } catch (Exception e) {
            return Base64.encodeToString(bytes, Base64.NO_WRAP);
        }
    }

    public final class Callback {

        private String packageName;

        public Callback(String packageName) {
            this.packageName = packageName;
        }

        public final String a(final byte[] array) {
            Log.d(TAG, "a: " + Base64.encodeToString(array, Base64.NO_WRAP));
            return "a";
        }

        public final String b() {
            Log.d(TAG, "b");
            return "b";
        }

        public final String c() {
            Log.d(TAG, "c");
            return packageName;
        }

        public final void d(final Object o, final byte[] array) {
            Log.d(TAG, "d: " + o + ", " + Base64.encodeToString(array, Base64.NO_WRAP));
        }
    }
}
Loading