コードを保護するAdMob SDKリバースまたは別の方法

この話は、夏にミンスクにベントレーサロンがオープンするというニュースから始まりました。 だから、私はセカンドゲームに広告を埋め込む時が来たことに気づきました。そうしないと、ラインの最後にいる危険があります。 最新バージョンのSDK(現時点では6.4.1)をダウンロードし、ゲームに統合して起動し、すぐにlogcatに疑わしい行を見つけました



05-14 15:06:06.312: D/dalvikvm(1379): DexOpt: --- BEGIN 'ads2133480362.jar' (bootstrap=0) --- 05-14 15:06:06.632: D/dalvikvm(1413): creating instr width table 05-14 15:06:06.671: D/dalvikvm(1413): DexOpt: load 2ms, verify+opt 18ms 05-14 15:06:06.703: D/dalvikvm(1379): DexOpt: --- END 'ads2133480362.jar' (success) --- 05-14 15:06:06.703: D/dalvikvm(1379): DEX prep '/data/data/by.squareroot.kingsquare/cache/ads2133480362.jar': unzip in 0ms, rewrite 391ms
      
      





dexoptは、DEXファイルをチェックおよび最適化するためのプログラムです。 特にアプリケーションを起動した後、奇妙なads2133480362.jarファイルを使用して、彼女が働くべき理由は明らかではありません。 私はこのファイルとは何の関係もありませんでしたし、これはかつてなかったので、疑いはすべてAdMobに落ちました。 どうやら、AdMob SDKはjarファイルをアプリケーションのキャッシュディレクトリに保存し、そこからクラスをロードし、バナーをロードおよび表示するときにそれらを使用します AdMob SDKの開発者が私たちから熱心に隠していることは、まだわからないままです。



リバーシムAdMob SDK



もちろん、SDKのクラスは難読化されていますが、これはタスクをそれほど複雑にしません。 出発点を見つけるために、どのクラスがContext.getCacheDir()メソッドを呼び出しているかを見てみましょう。 それらはわずかで、2つだけでした。 そのうちの1 つでは 、このメソッドを使用してWebSettings.setAppCachePath()を設定し、すでに空の名前ak.classの疑わしいクラスが1つだけ残るようにします。

個人的には、 JDを使用して逆コンパイルします。 Context.getCacheDir()の呼び出しがあるこのクラスのメソッドの一部を見てみましょう。



 byte[] arrayOfByte1 = an.a(ao.a()); byte[] arrayOfByte2 = an.a(arrayOfByte1, ao.b()); File localFile2 = File.createTempFile("ads", ".jar", paramContext.getCacheDir()); FileOutputStream localFileOutputStream = new FileOutputStream(localFile2); localFileOutputStream.write(arrayOfByte2, 0, arrayOfByte2.length); localFileOutputStream.close();
      
      





邪悪なプロガードによって課された呪いを取り除き、クラスと変数の名前をより理解しやすいものに変更すると、次のコードが得られます。



 String keyBase64 = Base64Consts.getKeyBase64(); byte[] keyBytes = Decrypter.decodeKey(keyBase64); String classBase64 = Base64Consts.getClassBase64(); byte[] classBytes = Decrypter.decodeClassBytes(keyBytes, classBase64); File classFile = File.createTempFile("ads", ".jar", context.getCacheDir()); FileOutputStream out = new FileOutputStream(classFile); out.write(classBytes, 0, classBytes.length); out.close();
      
      





これで、jarファイルがどこから来たかを順番に解析できます。 Base64Constsクラス(以前のao )には、Base64でエンコードされた文字列が含まれています。



 public class Base64Consts { public static String getKeyBase64() { return "ARuhFl7nBw/97YxsDjOCIqF0d9D2SpkzcWN42U/KR6Q="; } public static String getClassBase64() { return "SuhNEgGjhJl/XS1FVuhqPkUehkYsZY0198PVH9C0C..."; //    ,      } }
      
      





keyBase64文字列Decrypter.decodeKey()メソッドを使用してキーに変換されます。



 public static byte[] decodeKey(String keyBase64) { byte[] keyBytes = Base64Util.decode(keyBase64); ByteBuffer byteBuffer = ByteBuffer.wrap(keyBytes, 4, 16); byte[] key128 = new byte[16]; byteBuffer.get(key128); for (int i = 0; i < key128.length; i++) { key128[i] = ((byte)(key128[i] ^ 0x44)); } return key128; }
      
      





メソッドは文字列をバイト配列にデコードします(android.util.Base64はAPIレベル8でのみ登場したため、AdMob SDKはこれらの目的でクラスを使用します)。 。 各バイトは、xorマジックナンバー0x44です。 これらの操作の結果として、128ビットのAESキーが取得されます。



classBase64文字列Decrypter.decodeClassBytes()メソッドを使用して、jarファイルであるバイト配列に変換されます。



 public static byte[] decodeClassBytes(byte[] keyBytes, String cryptedBytesBase64) { byte[] cryptedBytes = Base64Util.decode(cryptedBytesBase64); ByteBuffer buffer = ByteBuffer.allocate(cryptedBytes.length); buffer.put(cryptedBytes); buffer.flip(); byte[] initializationVector = new byte[16]; byte[] input = new byte[cryptedBytes.length - 16]; buffer.get(initializationVector); buffer.get(input); SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(initializationVector)); return cipher.doFinal(input); }
      
      





このメソッドは、文字列をバイト配列にデコードします。この配列の最初の16バイトは初期化ベクトルであり、その他はすべて暗号化されたデータです。 出力はバイトの配列であり、jarファイルに格納され、そこからクラスが動的にロードされます。 最後に、内部の内容を確認できます。



神秘的なad.jar


AdMob SDKは、 DexClassLoaderを使用してこのjarファイルからクラスをロードします。 このように内部的に使用されます:



 DexClassLoader classLoader = new DexClassLoader(classFile, context.getCacheDir()), null, context.getClassLoader()); Class clazz = classLoader.loadClass(b(keyBytes, Base64Consts.getClassNameBase64())); Method m = clazz.getMethod(b(keyBytes, Base64Consts.getMethodNameBase64()), new Class[0]);
      
      





その後、jarファイルは削除されます。 クラスとメソッドの名前は、jarファイル自体(Base64 + AES)と同じ方法で暗号化されるため、jarファイルの中をすぐに見ることがより速く、簡単になります。



classes.dexファイルは内部にあることが判明しました。 dex2jarを介して実行すると、今度はクラスを含む別のjarファイルを取得しました。



マリオありがとう! しかし、私たちの王女は別の城にいます!


ここで失望が待っていました。 内部には5つの難読化されたクラスがあり、興味深いものは何も表していませんでした。 たとえば、次のようなクラス:



 public class a { public static Long a() { return Long.valueOf(Calendar.getInstance().getTime().getTime() / 1000L); } }
      
      





そしてこのように:



 public class d { public static String a() { new Build.VERSION(); return Build.VERSION.RELEASE; } }
      
      





クラスの1つはSettings.Secure.ANDROID_IDの値を取り、md5ハッシュと見なします。 もう1つは、apkファイル全体のSHA-2ハッシュを考慮しています。 どうやら、これらのパラメーターはサーバーに送信される要求で使用されます。

一般的に、秘密のアルゴリズムも、隠されたメッセージも、何もありません。 なぜそのような些細なコードを隠すのが謎なのか。



卵の中の針、アヒルの中の卵...



内部に興味深いものは何もありませんでしたが、AdMobはコードを保護するために興味深い方法を使用しています。 コードはコンパイルされ、jarファイルにアセンブルされ、jarファイルはdex形式に変換され、dexファイルは再びjarにパックされ、jarファイルはAESで暗号化され、最後にBase64エンコードされます。 原則として、特にサーバーからキーを取得する場合は良い方法です。



このようなトリッキーな方法は、 Google Playデベロッパープログラムポリシーの危険な製品の定義に該当する可能性がありますが、

Google Playからダウンロードしたアプリは、Google Playの更新メカニズム以外の方法を使用して、独自のAPKバイナリコードを変更、置換、または更新することはできません。


原則として、コードが変更されます-ライブラリはクラスがロードされる空から形成されます。 しかし、AdMobは間違いなくそれを実行できます。



All Articles