本文は Android WeChat 8.0.3 に基づいています。
最近、いくつかのソフトウェアの逆向き学習を始めたので、記録しておきます。
この記事は WeChat シリーズの第一篇で、サイコロを振る機能とじゃんけん機能の分析です。
サイコロを振ることとじゃんけんはランダムな結果が出るため、推測するに、ランダム数を使って実現されているはずで、後に実際にそうであることがわかりました。
1、 フックポイントを探す#
Java の Random クラスに対応するクラス名は java.util.Random で、objection を使って watching します:
サイコロの絵文字を送信すると、Random の以下のメソッドが呼び出されることがわかりました:
java.util.Random.nextInt を watch し、再度サイコロを振って、呼び出しスタック、パラメータ、戻り値をダンプします:
nextInt (n) : メソッド呼び出しが 0(含む)と n(含まない)の間の擬似ランダムで均等分布の int 値を返します。
nextInt のパラメータは 6 で、戻り値は 1、サイコロの結果は 2 です。
実際、ここもフックポイントとして使えますが、詳細が不足しています。フックすべきポイントはサイコロを振ることとじゃんけんだけで、Java のツールクラスのランダム数メソッドを直接フックするのは少し粗雑です。
呼び出しスタックを遡ると、getIntRandom は tencent SDK のメソッドで、明らかにランダム数を生成するメソッドです。
さらに上に進むと、om.tencent.mm.plugin.emoji.e.f.o があり、これらの 2 つのメソッドを watch して、何か追加情報が得られるか見てみます。
android hooking watch class_method com.tencent.mm.sdk.platformtools.Util.getIntRandom --dump-args --dump-return
android hooking watch class_method com.tencent.mm.plugin.emoji.e.f.o --dump-args --dump-return
再度サイコロを振ると、今度の結果は 5 点です。
e.f.o メソッドのパラメータは:
戻り値は:
このメソッドのパラメータと戻り値から推測すると、dice.png はサイコロの画像で、dice_5.png は 5 点のサイコロです。
この呼び出しプロセスでは、画像から getIntRandom が得たランダム数「0-5」が、サイコロの結果の点数「1-6」に対応していることがわかります。
次に、じゃんけんのロジックを見てみると、サイコロを振るのと一致しています。
1.field_name は「jsb.png」で、これはじゃんけんの「剪刀石头布」のピンインの頭文字です:
2、getIntRandom のパラメータは 2 で、[0,2] の int ランダム数を取得します:
3、戻り値の field_name は jsb_b.png で、じゃんけんの結果「布」と一致します。
ここまで分析した結果、利用中に e.f.o のパラメータに基づいて現在取得するランダム数がどの絵文字(じゃんけんまたはサイコロ)であるかを判断し、その後 getIntRandom の戻り値をフックして変更することで、望む結果を得ることができます。
フックできるポイントをまとめると:
com.tencent.mm.plugin.emoji.e.f.o(com.tencent.mm.storage.emotion.EmojiInfo)
com.tencent.mm.sdk.platformtools.Util.getIntRandom(int,int)
2、Frida スクリプトの作成#
設定:じゃんけんは常に剪刀を出し、サイコロは常に 6 点を出す。
Frida スクリプトを作成します:
function main() {
Java.perform(function () {
Java.openClassFile("/data/local/tmp/r0gson.dex").load();
const gson = Java.use('com.r0ysue.gson.Gson')
var jsb = 0 // じゃんけんは常に剪刀
var dice = 5 // サイコロは常に6点
var efo = Java.use('com.tencent.mm.plugin.emoji.e.f')
efo.o.implementation = function (x) {
var xstr = gson.$new().toJson(x)
if (xstr.search('dice.png') != -1) {
console.log('サイコロを振っています')
hookIntRandom(dice)
} else if (xstr.search('jsb.png') != -1) {
console.log("じゃんけんをしています")
hookIntRandom(jsb)
}
var res = this.o(x)
return res
}
})
}
function hookIntRandom(a) {
Java.perform(function () {
var util = Java.use('com.tencent.mm.sdk.platformtools.Util')
util.getIntRandom.implementation = function (int1, int2) {
var res = this.getIntRandom(int1, int2)
return a
}
})
}
setImmediate(main)
注:スクリプトでは肉丝の Gson を使用してオブジェクトを印刷していますが、非常に便利です。
3、参考資料#
参考資料
Frida で [object] を印刷する際の Gson パッケージの重複名問題 - https://bbs.pediy.com/thread-259186.htm