From 373bda9f0219ecd3f1069bf5fe0637c61dc87787 Mon Sep 17 00:00:00 2001 From: YuutaW <17158086+Trumeet@users.noreply.github.com> Date: Thu, 28 Feb 2019 20:32:26 -0800 Subject: feat(app): hide and obfuscate license checking codes as much as we can Signed-off-by: YuutaW <17158086+Trumeet@users.noreply.github.com> --- .../java/moe/yuuta/gplicense/LicenseChecker.java | 38 +++++--- .../java/moe/yuuta/gplicense/LicenseValidator.java | 2 +- .../moe/yuuta/gplicense/ServerManagedPolicy.java | 2 +- .../gplicense/ipc/ILicenseResultListener.java | 101 --------------------- .../moe/yuuta/gplicense/ipc/ILicensingService.java | 101 --------------------- 5 files changed, 25 insertions(+), 219 deletions(-) delete mode 100644 app/src/main/java/moe/yuuta/gplicense/ipc/ILicenseResultListener.java delete mode 100644 app/src/main/java/moe/yuuta/gplicense/ipc/ILicensingService.java (limited to 'app/src/main/java/moe/yuuta/gplicense') diff --git a/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java b/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java index 92986b8..7d4cfa8 100644 --- a/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java +++ b/app/src/main/java/moe/yuuta/gplicense/LicenseChecker.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.ServiceConnection; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Handler; import android.os.HandlerThread; @@ -27,8 +26,11 @@ import java.util.LinkedList; import java.util.Queue; import java.util.Set; -import moe.yuuta.gplicense.ipc.ILicenseResultListener; -import moe.yuuta.gplicense.ipc.ILicensingService; +import moe.yuuta.ext.ConnCallback; +import moe.yuuta.ext.IPCResultListener; +import moe.yuuta.ext.IService; +import moe.yuuta.ext.LicServiceConn; +import moe.yuuta.ext.ResultCallback; import moe.yuuta.gplicense.util.Base64; import moe.yuuta.gplicense.util.Base64DecoderException; import moe.yuuta.workmode.BuildConfig; @@ -44,7 +46,7 @@ import moe.yuuta.workmode.BuildConfig; * Must also provide the Base64-encoded RSA public key associated with your developer account. The * public key is obtainable from the publisher site. */ -public class LicenseChecker implements ServiceConnection { +public class LicenseChecker implements ConnCallback { private static final Logger logger = XLog.tag("LCK").build(); private static final String KEY_FACTORY_ALGORITHM = "RSA"; @@ -55,7 +57,7 @@ public class LicenseChecker implements ServiceConnection { private static final SecureRandom RANDOM = new SecureRandom(); private static final boolean DEBUG_LICENSE_ERROR = BuildConfig.DEBUG; - private ILicensingService mService; + private IService mService; private PublicKey mPublicKey; private final Context mContext; @@ -70,6 +72,8 @@ public class LicenseChecker implements ServiceConnection { private final Set mChecksInProgress = new HashSet(); private final Queue mPendingChecks = new LinkedList(); + private LicServiceConn mConn; + /** * @param context a Context * @param policy implementation of Policy @@ -118,7 +122,7 @@ public class LicenseChecker implements ServiceConnection { * recommend obfuscating the string that is passed into bindService using another method of your * own devising. *

- * source string: "com.android.vending.licensing.ILicensingService" + * source string: "com.android.vending.licensing.IService" *

* * @param callback @@ -127,7 +131,7 @@ public class LicenseChecker implements ServiceConnection { // If we have a valid recent LICENSED response, we can skip asking // Market. if (mPolicy.allowAccess()) { - logger.i("Using cached license response"); + logger.i("Using cached response"); callback.allow(Policy.LICENSED); } else { LicenseValidator validator = new LicenseValidator(mPolicy, new NullDeviceLimiter(), @@ -136,12 +140,13 @@ public class LicenseChecker implements ServiceConnection { if (mService == null) { logger.i("Binding to service."); try { + mConn = new LicServiceConn(this); boolean bindResult = mContext .bindService( new Intent( new String( // Base64 encoded - - // com.android.vending.licensing.ILicensingService + // com.android.vending.licensing.IService // Consider encoding this in another way in your // code to improve security Base64.decode( @@ -163,7 +168,7 @@ public class LicenseChecker implements ServiceConnection { // com.android.vending Base64.decode( "Y29tLmFuZHJvaWQudmVuZGluZw=="))), - this, // ServiceConnection. + mConn, // ServiceConnection. Context.BIND_AUTO_CREATE); if (bindResult) { mPendingChecks.offer(validator); @@ -188,12 +193,12 @@ public class LicenseChecker implements ServiceConnection { while ((validator = mPendingChecks.poll()) != null) { try { logger.i("Executing on service for " + validator.getPackageName()); - mService.checkLicense( + mService.exec( validator.getNonce(), validator.getPackageName(), - new ResultListener(validator)); + new IPCResultListener(new ResultListener(validator))); mChecksInProgress.add(validator); } catch (RemoteException e) { - logger.w("RemoteException in checkLicense call.", e); + logger.w("RemoteException in exec call.", e); handleServiceConnectionError(validator); } } @@ -206,7 +211,7 @@ public class LicenseChecker implements ServiceConnection { } } - private class ResultListener extends ILicenseResultListener.Stub { + private class ResultListener implements ResultCallback { private final LicenseValidator mValidator; private Runnable mOnTimeout; @@ -226,6 +231,7 @@ public class LicenseChecker implements ServiceConnection { // Runs in IPC thread pool. Post it to the Handler, so we can guarantee // either this or the timeout runs. + @Override public void verifyLicense(final int responseCode, final String signedData, final String signature) { mHandler.post(() -> { @@ -280,11 +286,13 @@ public class LicenseChecker implements ServiceConnection { } } + @Override public synchronized void onServiceConnected(ComponentName name, IBinder service) { - mService = ILicensingService.Stub.asInterface(service); + mService = IService.Stub.asInterface(service); runChecks(); } + @Override public synchronized void onServiceDisconnected(ComponentName name) { // Called when the connection with the service has been // unexpectedly disconnected. That is, Market crashed. @@ -311,7 +319,7 @@ public class LicenseChecker implements ServiceConnection { private void cleanupService() { if (mService != null) { try { - mContext.unbindService(this); + mContext.unbindService(mConn); } catch (IllegalArgumentException e) { // Somehow we've already been unbound. This is a non-fatal // error. diff --git a/app/src/main/java/moe/yuuta/gplicense/LicenseValidator.java b/app/src/main/java/moe/yuuta/gplicense/LicenseValidator.java index 142f350..d34d614 100644 --- a/app/src/main/java/moe/yuuta/gplicense/LicenseValidator.java +++ b/app/src/main/java/moe/yuuta/gplicense/LicenseValidator.java @@ -184,7 +184,7 @@ class LicenseValidator { handleApplicationError(LicenseCheckerCallback.ERROR_NOT_MARKET_MANAGED); break; default: - logger.e("Unknown response code for license check."); + logger.e("Unknown response code for checking."); handleInvalidResponse(); } } diff --git a/app/src/main/java/moe/yuuta/gplicense/ServerManagedPolicy.java b/app/src/main/java/moe/yuuta/gplicense/ServerManagedPolicy.java index 8355cb8..fcc3451 100644 --- a/app/src/main/java/moe/yuuta/gplicense/ServerManagedPolicy.java +++ b/app/src/main/java/moe/yuuta/gplicense/ServerManagedPolicy.java @@ -159,7 +159,7 @@ public class ServerManagedPolicy implements Policy { lValidityTimestamp = Long.parseLong(validityTimestamp); } catch (NumberFormatException e) { // No response or not parsable, expire in one minute. - logger.w("License validity timestamp (VT) missing, caching for a minute"); + logger.w("Validity timestamp (VT) missing, caching for a minute"); lValidityTimestamp = System.currentTimeMillis() + MILLIS_PER_MINUTE; validityTimestamp = Long.toString(lValidityTimestamp); } diff --git a/app/src/main/java/moe/yuuta/gplicense/ipc/ILicenseResultListener.java b/app/src/main/java/moe/yuuta/gplicense/ipc/ILicenseResultListener.java deleted file mode 100644 index a7683b6..0000000 --- a/app/src/main/java/moe/yuuta/gplicense/ipc/ILicenseResultListener.java +++ /dev/null @@ -1,101 +0,0 @@ -package moe.yuuta.gplicense.ipc; - -import android.os.Binder; -import android.os.IBinder; -import android.os.IInterface; -import android.os.Parcel; -import android.os.RemoteException; - -import androidx.annotation.NonNull; - -import moe.yuuta.gplicense.util.Base64; -import moe.yuuta.gplicense.util.Base64DecoderException; - -public interface ILicenseResultListener extends IInterface { - abstract class Stub extends Binder implements ILicenseResultListener { - private static final String DESCRIPTOR; - - static { - try { - // Base64 encoded - - // com.android.vending.licensing.ILicenseResultListener - DESCRIPTOR = new String(Base64.decode( - "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2VSZXN1bHRMaXN0ZW5lcg==")); - } catch (Base64DecoderException e) { - throw new RuntimeException(e); - } - } - - protected Stub() { - this.attachInterface(this, DESCRIPTOR); - } - - static ILicenseResultListener asInterface(IBinder obj) { - if ((obj == null)) { - return null; - } - IInterface iin = obj.queryLocalInterface(DESCRIPTOR); - if (iin instanceof ILicenseResultListener) { - return (ILicenseResultListener) iin; - } - return new Stub.Proxy(obj); - } - - @Override - public IBinder asBinder() { - return this; - } - - @Override - public boolean onTransact(int code, @NonNull Parcel data, Parcel reply, int flags) throws RemoteException { - switch (code) { - case INTERFACE_TRANSACTION: { - reply.writeString(DESCRIPTOR); - return true; - } - case TRANSACTION_verifyLicense: { - data.enforceInterface(DESCRIPTOR); - int responseCode = data.readInt(); - String signedData = data.readString(); - String signature = data.readString(); - this.verifyLicense(responseCode, signedData, signature); - return true; - } - default: { - return super.onTransact(code, data, reply, flags); - } - } - } - - private static class Proxy implements ILicenseResultListener { - private IBinder mRemote; - - Proxy(IBinder remote) { - mRemote = remote; - } - - @Override - public IBinder asBinder() { - return mRemote; - } - - @Override - public void verifyLicense(int responseCode, String signedData, String signature) throws RemoteException { - Parcel args = Parcel.obtain(); - try { - args.writeInterfaceToken(DESCRIPTOR); - args.writeInt(responseCode); - args.writeString(signedData); - args.writeString(signature); - mRemote.transact(TRANSACTION_verifyLicense, args, null, IBinder.FLAG_ONEWAY); - } finally { - args.recycle(); - } - } - } - - static final int TRANSACTION_verifyLicense = IBinder.FIRST_CALL_TRANSACTION; - } - - void verifyLicense(int responseCode, String signedData, String signature) throws RemoteException; -} \ No newline at end of file diff --git a/app/src/main/java/moe/yuuta/gplicense/ipc/ILicensingService.java b/app/src/main/java/moe/yuuta/gplicense/ipc/ILicensingService.java deleted file mode 100644 index 14e27f4..0000000 --- a/app/src/main/java/moe/yuuta/gplicense/ipc/ILicensingService.java +++ /dev/null @@ -1,101 +0,0 @@ -package moe.yuuta.gplicense.ipc; - -import android.os.Binder; -import android.os.IBinder; -import android.os.IInterface; -import android.os.Parcel; -import android.os.RemoteException; - -import androidx.annotation.NonNull; - -import moe.yuuta.gplicense.util.Base64; -import moe.yuuta.gplicense.util.Base64DecoderException; - -public interface ILicensingService extends IInterface { - abstract class Stub extends Binder implements ILicensingService { - private static final String DESCRIPTOR; - - static { - try { - // Base64 encoded - - // com.android.vending.licensing.ILicensingService - DESCRIPTOR = new String(Base64.decode( - "Y29tLmFuZHJvaWQudmVuZGluZy5saWNlbnNpbmcuSUxpY2Vuc2luZ1NlcnZpY2U=")); - } catch (Base64DecoderException e) { - throw new RuntimeException(e); - } - } - - public Stub() { - this.attachInterface(this, DESCRIPTOR); - } - - public static ILicensingService asInterface(IBinder obj) { - if ((obj == null)) { - return null; - } - IInterface iin = obj.queryLocalInterface(DESCRIPTOR); - if (iin instanceof ILicensingService) { - return (ILicensingService) iin; - } - return new ILicensingService.Stub.Proxy(obj); - } - - @Override - public IBinder asBinder() { - return this; - } - - @Override - public boolean onTransact(int code, @NonNull Parcel data, Parcel reply, int flags) throws RemoteException { - switch (code) { - case INTERFACE_TRANSACTION: { - reply.writeString(DESCRIPTOR); - return true; - } - case TRANSACTION_checkLicense: { - data.enforceInterface(DESCRIPTOR); - long nonce = data.readLong(); - String packageName = data.readString(); - ILicenseResultListener listener = ILicenseResultListener.Stub.asInterface(data.readStrongBinder()); - this.checkLicense(nonce, packageName, listener); - return true; - } - default: { - return super.onTransact(code, data, reply, flags); - } - } - } - - private static class Proxy implements ILicensingService { - private IBinder mRemote; - - Proxy(IBinder remote) { - mRemote = remote; - } - - @Override - public IBinder asBinder() { - return mRemote; - } - - @Override - public void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException { - Parcel args = Parcel.obtain(); - try { - args.writeInterfaceToken(DESCRIPTOR); - args.writeLong(nonce); - args.writeString(packageName); - args.writeStrongBinder(listener != null ? listener.asBinder() : null); - mRemote.transact(TRANSACTION_checkLicense, args, null, IBinder.FLAG_ONEWAY); - } finally { - args.recycle(); - } - } - } - - static final int TRANSACTION_checkLicense = IBinder.FIRST_CALL_TRANSACTION; - } - - void checkLicense(long nonce, String packageName, ILicenseResultListener listener) throws RemoteException; -} \ No newline at end of file -- cgit v1.2.3