初識Frida--Android逆向之Java層hook (二)
今天繼續一個新的示例,同樣採用CTF作為例子,難度稍微加大了一點,如果對Frida基本的使用還不是很瞭解,建議先看看之前的文章初識Frida--Android逆向之Java層hook (一)
文章涉及到的知識點:
怎麼使用javascript例項化類並呼叫類方法
怎麼在"jscode"中增加自定義javascript方法
怎麼較為靈活的hook類方法
apk的安裝與分析
示例下載:whyshouldIpay
下載apk後安裝,一樣還是先來看看是什麼功能,這是一個比較簡單的驗證程式,簡單的使用後,瞭解到PREMIUM CONETNT內容需要輸入License驗證後才能檢視。那估計PREMIUM CONETNT按鈕中的內容應該就是答案了吧。
流程分析
使用jadx將apk反編譯出來,分析,在AndroidManifest.xml中找到了啟動的Activity是LauncherActivity。
找到其中驗證的主要程式碼verifyClick,分析如下:
public void verifyClick(View v) {
//第一個驗證,將輸入的Licese通過網路驗證,但這個肯定是通不過的,這是一個可能需要繞過的點。
try{
InputStreamin=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();
//網路驗證需要伺服器返回"LICENSEKEYOK",才能進行下一步
if(response.equals("LICENSEKEYOK")) {
//當網路驗證成功後,生成啟用祕鑰,並寫入到preferences檔案中
String activatedKey=new String(MainActivity.xor(getMac().getBytes(), response.getBytes()));
Editor editor=getApplicationContext().getSharedPreferences("preferences",0).edit();
editor.putString("KEY", activatedKey);
editor.commit();
//這樣便成功啟用
new Builder(this).setTitle((CharSequence)"Activation successful").setMessage((CharSequence)"Activation successful").setIcon(17301543).show();
return;
}
new Builder(this).setTitle((CharSequence)"Invalid license!").setMessage((CharSequence)"Invalid license!").setIcon(17301543).show();
} catch (Exception e) {
new Builder(this).setTitle((CharSequence)"Error occured").setMessage((CharSequence)"Server unreachable").setNeutralButton((CharSequence)"OK", null).setIcon(17301543).show();
}
}
在verifyClick中可以知道生成啟用祕鑰的演算法是MainActivity.xor。
String activatedKey=new String(MainActivity.xor(getMac().getBytes(), response.getBytes()));
來到MainActivity中,檢視該方法,看上去筆算起來還是比較麻煩。
public static byte[] xor(byte[] val, byte[] key) {
byte[] o=new byte[val.length];
for(inti=0; i < val.length; i++) {
o[i]=(byte) (val[i] ^ key[i%key.length]);
}
returno;
}
接下來當程式被啟用成功後,點選PREMIUM CONETNT按鈕,會呼叫MainActivity中的方法,可以看到它將MAC,以及生成的Key傳送到了MainActivity中。
public void showPremium(View view) {
Intent i=new Intent(this, MainActivity.class);
i.putExtra("MAC", getMac());
i.putExtra("KEY", getKey());
startActivity(i);
}
在MainActivity的onCreate方法中,看到了最終答案生成的native方法stringFromJNI(key, mac)。
protected void onCreate(Bundle savedInstanceState) {
//獲取Intent傳遞過來的值
String key=getIntent().getStringExtra("KEY");
String mac=getIntent().getStringExtra("MAC");
if(key=="" || mac == "") {
key="";
mac="";
}
super.onCreate(savedInstanceState);
setContentView((int) R.layout.activity_main);
//呼叫native函式,算出答案
((TextView) findViewById(R.id.sample_text)).setText(stringFromJNI(key, mac));
}
好,現在原始碼分析基本上能夠理清楚了,大概的過程就是這樣。
輸入License,進行驗證
通過網路驗證獲取返回值“LICENSEKEYOK”後,然後呼叫MainActivity.xor在本地preferences檔案中生成祕鑰,啟用成功。
本地獲取MAC地址及祕鑰Key傳入MainActivity得出答案。
hook點分析
接下來重點就是要尋找hook點,經過剛才解題流程的分析,得出hook思路如下:
獲取getMac()函式的返回值,與“LICENSEKEYOK"字串進行xor運算得出祕鑰Key.
hook getKey方法,讓它不從preferences檔案讀取Key,而是我們自己構造。
hookverifyClick,讓它呼叫showPremium方法
JavaScript程式碼構造與執行
0x00 hook getMac()
先來一個簡單的示例,看看getMac()方法返回的的是什麼,採用的方法是hookshowPremium,這樣就能通過點選PREMIUM CONETNT按鈕直接得到getMac()的返回值。
JavaScript程式碼如下:
js_code='''
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
//hook showPremium從而方便直接點選按鈕得出Mac值
hook_Activity.showPremium.implementation = function(v){
//因為showPremium,getMac()均在LauncherActivity類中,所有直接通過this就能直接呼叫getMac()方法
var Key = this.getKey();
var Mac = this.getMac();
send(Key);
send(Mac);
}
});
'''
完整python程式碼如下:
importfrida,sys
defon_message(message, data):
ifmessage['type']=='send':
print("[*] {0}".format(message['payload']))
else:
print(message)
js_code='''
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
hook_Activity.showPremium.implementation = function(v){
var Key = this.getKey();
var Mac = this.getMac();
send(Key);
send(Mac);
}
});
'''
session=frida.get_usb_device().attach("de.fraunhofer.sit.premiumapp")
script=session.create_script(js_code)
script.on('message',on_message)
script.load()
sys.stdin.read()
執行看看結果:
0x01 計算祕鑰Key
接下來開始真正第一步的hook,將mac值與“LICENSEKEYOK"通過MainActivity.xor獲取祕鑰Key。那就直接hookgetKey方法吧,這樣可以自己來構造祕鑰Key。
仔細分析,會發現在這一步中可能會遇到下面的問題:
怎麼呼叫xor方法。
java是強型別語言,javascript是弱型別語言,怎麼將javascript引數進行型別轉換並傳遞到java語言中。
怎麼將javascript引數進行型別轉換並傳遞到java語言中?其實方法很簡單,既然java是強型別語言,那就根據它要求的型別傳遞對應引數即可,看看它引數的型別。
public static byte[] xor(byte[] val, byte[] key) {
byte[] o=new byte[val.length];
for(inti=0; i < val.length; i++) {
o[i]=(byte) (val[i] ^ key[i%key.length]);
}
returno;
}
那麼,在javascript程式碼中,先準備一個將字串型別轉換為byte[]型別的方法stringToBytes,再通過例項化MainActivity類的方式呼叫xor(),然後還需要一個將byte[]迴轉為String的方法,因為祕鑰key是Sting型別的。
js_code='''
//字串轉換byte[]的方法
stringToBytes = function(str) {
var ch, st, re = [];
for (var i = 0; i < str.length; i++ ) {
ch = str.charCodeAt(i);
st = [];
do {
st.push( ch & 0xFF );
ch = ch >> 8;
}
while ( ch );
re = re.concat( st.reverse() );
}
return re;
}
//將byte[]轉成String的方法
function byteToString(arr) {
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
//hook 程式碼
Java.perform(function(){
var hook_Activity = Java.use('de.fraunhofer.sit.premiumapp.LauncherActivity');
var MainActivity = Java.use('de.fraunhofer.sit.premiumapp.MainActivity')
var LicenseStr = "LICENSEKEYOK";
//hook getKey()方法,直接構造密碼,而不從preferences讀取
hook_Activity.getKey.implementation = function(){
//獲取Mac
var Mac = this.getMac();
//例項化MainActivity
var instance = MainActivity.$new();
//型別轉換
var MacByte =stringToBytes(Mac);
var LicenseByte = stringToBytes(LicenseStr);
send("MacByte:"+MacByte)
send("LicenseByte:"+LicenseByte)
//呼叫例項化物件的xor方法
xorResult = instance.xor(MacByte,LicenseByte);
send(xorResult);
//型別迴轉
var Key = byteToString(xorResult)
send(Key);
return Key;
}
hook_Activity.verifyClick.implementation = function(view){
this.showPremium(view);
}
});
'''
接下來,執行看看,能不能獲取祕鑰Key。
不知道怎麼啟動模擬器中的frida-server,以及埠轉發,可以先看看初識Frida--Android逆向之Java層hook (一)
啟動python指令碼,在模擬器中直接點選PREMIUM CONTENT,即可看到執行結果。
0x02 呼叫showPremium獲取答案
前面2個步驟,可以說是已經完成90%了,接下來只需要在hook一個能夠觸發showPremium方法的即可。方法就隨意了,這裡採用hook verifyClick的方式,這樣點選app上的VERIFY按鈕,觸發verifyClick方法去呼叫showPremium,進而獲得最終答案。
hook_Activity.verifyClick.implementation=function(view){
this.showPremium(view);
}
啟動指令碼,點選app上的VERIFY按鈕看看執行結果:
完整python程式碼:下載
總結
通過上面的例子,可以學習在java層怎麼使用frida實現:
任意類方法呼叫。
任意類方法重實現。
以及學會怎麼構造和使用自定義javascript方法。
當然這還僅僅只是一個開始.....
來源:https://bbs.pediy.com/thread-227233.htm
本文由看雪論壇 ghostmazeW 原創
轉載請註明來自看雪社群
相關文章
- 初識Frida--Android逆向之Java層hook (一)2018-06-11AndroidJavaHook
- hook初識之inline hook2024-04-19Hookinline
- 羽夏逆向指引—— Hook2022-03-28Hook
- 初識NIO之Java小Demo2018-08-21Java
- iOS逆向 程式碼注入+Hook2019-12-04iOSHook
- 初識Java2024-07-06Java
- Java初識2024-11-21Java
- 初識 webpack (二)2020-01-01Web
- spark初識二2021-09-09Spark
- mysql初識(二)2013-06-30MySql
- 初識LinkedList底層原理2018-03-22
- Java 之 不要問我從哪裡來 《初識Java》2014-09-19Java
- 初識 HTTP/2(二)2017-01-13HTTP
- Oracle VM初識(二)2015-05-22Oracle
- 一.初識Java2020-12-27Java
- 初識Java反射2016-09-17Java反射
- 初識Java Java基礎知識2020-06-23Java
- C++逆向 可變引數Hook2022-04-21C++Hook
- iOS逆向之旅(進階篇) — HOOK(FishHook)2018-10-26iOSHook
- 惡意使用者識別?——Java 層反模擬器、反Hook、反多開技巧2018-01-30JavaHook
- 初識vue系列之二2017-04-16Vue
- 初識 Java 註解2018-11-16Java
- iOS 逆向 - Hook / fishHook 原理與符號表2019-11-12iOSHook符號
- 【Java基礎】01初識Java2024-08-03Java
- (二)Java高併發秒殺API之Service層2018-12-12JavaAPI
- Hook技術之Hook Activity2019-02-17Hook
- python爬蟲之js逆向(二)2019-11-05Python爬蟲JS
- 二. 重識Java之夯實註解2018-12-21Java
- JS 逆向之 Hook,吃著火鍋唱著歌,突然就被麻匪劫了!2021-10-04JSHook
- iOS逆向學習筆記 - 彙編(一) - 初識彙編2018-05-16iOS筆記
- 【Kotlin】初識Kotlin(二)2022-02-23Kotlin
- 第二課初識PHP程式2017-11-16PHP
- iOS逆向之旅(進階篇) — HOOK(Logos)2018-10-26iOSHookGo
- 初識Java與RabbitMQ(三)2020-03-20JavaMQ
- 初識Java類和物件2020-10-20Java物件
- 初識Java內部類2004-10-16Java
- 初識Kotlin之集合2019-05-18Kotlin
- RabbitMQ系列之---初識RabbitMQ2019-05-09MQ