FridaHook框架學習(2)
阿新 • • 發佈:2020-07-27
FridaHook框架學習(2)
前言
學習過程參考https://bbs.pediy.com/thread-227233.htm。
逆向分析
安裝並執行例子程式,可以看到這個例子是一個驗證註冊碼的程式。
使用jadx解析這個APK。
通過類名以及AndroidManifest可以猜測以及知道LauncherActivity是這個程式的啟動類。先看看LauncherActivity的程式碼。
public class LauncherActivity extends AppCompatActivity { /* access modifiers changed from: protected */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView((int) R.layout.activity_launcher); } public void verifyClick(View v) { try { InputStream in = new URL("http://broken.license.server.com/query?license=" + ((EditText) findViewById(R.id.text_license)).getText().toString()).openConnection().getInputStream(); StringBuilder responseBuilder = new StringBuilder(); byte[] b = new byte[0]; while (in.read(b) > 0) { responseBuilder.append(b); } String response = responseBuilder.toString(); if (response.equals("LICENSEKEYOK")) { String activatedKey = new String(MainActivity.xor(getMac().getBytes(), response.getBytes())); SharedPreferences.Editor editor = getApplicationContext().getSharedPreferences("preferences", 0).edit(); editor.putString("KEY", activatedKey); editor.commit(); new AlertDialog.Builder(this).setTitle((CharSequence) "Activation successful").setMessage((CharSequence) "Activation successful").setIcon(17301543).show(); return; } new AlertDialog.Builder(this).setTitle((CharSequence) "Invalid license!").setMessage((CharSequence) "Invalid license!").setIcon(17301543).show(); } catch (Exception e) { new AlertDialog.Builder(this).setTitle((CharSequence) "Error occured").setMessage((CharSequence) "Server unreachable").setNeutralButton((CharSequence) "OK", (DialogInterface.OnClickListener) null).setIcon(17301543).show(); } } private String getKey() { return getApplicationContext().getSharedPreferences("preferences", 0).getString("KEY", ""); } private String getMac() { try { return ((WifiManager) getApplicationContext().getSystemService("wifi")).getConnectionInfo().getMacAddress(); } catch (Exception e) { return ""; } } public void showPremium(View view) { Intent i = new Intent(this, MainActivity.class); i.putExtra("MAC", getMac()); i.putExtra("KEY", getKey()); startActivity(i); } }
可以通過方法名猜測verifyClick方法應該就是VERIFY按鈕的點選事件,showPremium應該就是另一個按鈕的點選事件。可以看到程式的邏輯大概為通過網路驗證license的正確性,若正確就呼叫MainActivity的xor方法通過MAC和response生成金鑰並儲存在本地的preference中。
其中InputStream是Java一個用於讀取流的類。SharedPreferences是一個類似Python中字典的一種資料結構。
再來看看MainActivity的程式碼
public class MainActivity extends AppCompatActivity { public native String stringFromJNI(String str, String str2); public static byte[] xor(byte[] val, byte[] key) { byte[] o = new byte[val.length]; for (int i = 0; i < val.length; i++) { o[i] = (byte) (val[i] ^ key[i % key.length]); } return o; } public void onCreate(Bundle savedInstanceState) { String key = getIntent().getStringExtra("KEY"); String mac = getIntent().getStringExtra("MAC"); if (key == "" || mac == "") { key = ""; mac = ""; } super.onCreate(savedInstanceState); setContentView((int) R.layout.activity_main); ((TextView) findViewById(R.id.sample_text)).setText(stringFromJNI(key, mac)); } static { System.loadLibrary("native-lib"); } }
可以看到顯示flag的函式寫在了So中,通過金鑰KEY和MAC生成。
可以得到Hook思路:Hook getKey方法直接使用MAC和LICENSEOK構造金鑰並返回,之後點選PREMIUMCONTENT按鈕即可
Hook程式碼編寫
import frida import sys jscode = """ Java.perform(function(){ function stringToBytes(str){ var javaString = Java.use('java.lang.String'); var bytes = []; bytes = javaString.$new(str).getBytes(); return bytes; } function bytesToString(bytes){ var javaString = Java.use('java.lang.String'); return javaString.$new(bytes); } var launcheractivity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity'); var main = Java.use('de.fraunhofer.sit.premiumapp.MainActivity'); var license = "LICENSEKEYOK"; launcheractivity.getKey.implementation = function() { var mac = this.getMac(); var macbyte = stringToBytes(mac); var licensebyte = stringToBytes(license); var xor = main.xor(macbyte,licensebyte); send(xor) var key = bytesToString(xor); send(key.toString()); return key; } launcheractivity.verifyClick.implementation = function(v) { this.showPremium(v); } }); """ def on_message(message, data): if message['type'] == 'send': print("[*] {0}".format(message['payload'])) else: print(message) process = frida.get_usb_device().attach('de.fraunhofer.sit.premiumapp') script = process.create_script(jscode) script.on('message', on_message) script.load() sys.stdin.read()
執行結果
總結
編寫js程式碼的時候是真的累,因為要寫在字串裡,沒法自動補全,有些變數名就打錯了,還全部都是綠綠的很難看出來,終於也看懂了一點js的報錯才找出來了。Js中可以通過Java.use使用Java中的一些基類用於靈活轉換型別。