菜逼!!啥都记录一点
ISCC-2025 整好借着打ISCC,顺便写两道WP
ISCC2025-whereisflag APK直接打开看一眼,直接看activity里面的MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.example.whereisflag;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;import com.example.whereisflag.databinding.ActivityMainBinding;public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; private EditText flagEditText; private Button submitButton; static { System.loadLibrary("whereisflag" ); } @Override protected void onCreate (Bundle bundle) { super .onCreate(bundle); ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater()); this .binding = inflate; setContentView(inflate.getRoot()); this .flagEditText = this .binding.editTextFlag; Button button = this .binding.button; this .submitButton = button; button.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View view) { String obj = MainActivity.this .flagEditText.getText().toString(); if (obj.length() != 16 ) { Toast.makeText(MainActivity.this , "flag长度错误,请继续寻找" , 0 ).show(); } else if (new a ().b(obj)) { Toast.makeText(MainActivity.this , "恭喜你找到了正确的flag" , 1 ).show(); } else { Toast.makeText(MainActivity.this , "flag错误,请继续寻找" , 0 ).show(); } } }); } }
很明显,最简单的,看一下调用a的方法b
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.example.whereisflag; public class a { public native String compute (String str) ; public boolean b (String str) { if (str.startsWith("ISCC{" ) && str.endsWith("}" )) { return compute(str.substring(5 , 15 )).equals("iB3A7kSISR" ); } return false ; } }
显而易见,这就是我们需要的flag了
flag的包装头ISCC{xxxxxx}
肯定是有加密的,让我们简单分析一下
分析完毕,我们拆包去lib中找我们需要的加密逻辑。
简单逆向分析一下咯,直接看encrypt函数
喂给ai
没有ai我怎么活呢?,很明显了首先有个字符串反转,~~~然后有个换表,这里说错了,只是进行了凯撒加密,只不过对应的表换了,并不是我们常见的A-C这样~~,然后进行了凯撒加密,偏移是3
先找一下表吧
找到了捏,WHEReISFLAGBCDJKMNOPQTUVXYZabcdfghijklmnopqrstuvwxyz0123456789
逆向的话,仔细看你就会发现它其实是对着表进行偏移的,就是上面的表
也就是我们只需要偏移回去在倒转即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ABC = "WHEReISFLAGBCDJKMNOPQTUVXYZabcdefghijklmnopqrstuvwxyz0123456789" def decode (encrypted: str ) -> str : return '' .join([ABC[(ABC.index(ch) - 2 ) % len (ABC)] for ch in encrypted])[::-1 ] if __name__ == "__main__" : secret = "iB3A7kSISR" content = decode(secret) result = f"ISCC{{{content} }}" print ("解密结果:" , result)
之后得到了flag。
然后就完事了,唉,怎么说呢!
还是比较简单的
怎么说呢?现在准备还是一天一道CTF吧。(感觉做不到)
攻防世界 Android2.0 2025.5.25
怎么说,JADX打开,简单来说是直接看Activity
基本来说,加密逻辑就在MainActivity里了
直接看Activity的生命周期
onCreate()–>onStart()–>onResume()–>onPause()–>onStop()–>onDestory()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.example.test.ctf03;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { Button button; EditText pwd; TextView textView; @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); this .button.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { String str = MainActivity.this .pwd.getText().toString(); int result = JNI.getResult(str); MainActivity.this .Show(result); } }); } public void init () { this .pwd = (EditText) findViewById(R.id.pwd); this .button = (Button) findViewById(R.id.button); this .textView = (TextView) findViewById(R.id.result); } public void Show (int type) { switch (type) { case 0 : this .textView.setText("Wrong" ); break ; case 1 : this .textView.setText("Great" ); break ; } } }
所以找到核心的验证加密逻辑JNI.getResult(str)
,点进去看看
加密逻辑一看就在Native层了,拆包看一下。正好lib中也只有一个.so文件(动态链接库)
点进去看见是ARM-32位的。
我点进去是先看的函数栏,找加密逻辑喽
看着像分成了三段
其实感觉就是一个简单的拼接
这个第二段和第三段在一块
感觉不太对,转不成ASCLL码
先拼接一下试试,不太对
第一部分:LN^dl
第二部分: 5-
第三部分:AFBo}
想太多
我们直接看Java_com_example_test_ctf03_JNI_getResult
这个函数,直接喂给ai看反汇编代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 int Java_com_example_test_ctf03_JNI_getResult (JNIEnv *env, jobject thiz, jstring input) { const char *user_input = (*env)->GetStringUTFChars(env, input, 0 ); int result = 0 ; if (strlen (user_input) != 15 ) { return 0 ; } void Init (char *out1, char *out2, char *out3, const char *in, int len) { if (len < 1 ) { out1[0 ] = 0 ; out2[0 ] = 0 ; out3[0 ] = 0 ; return ; } uint32_t div3_magic = 0x55555556 ; uint32_t div3_magic_2 = 0xAAAAAAAB ; for (int i = 0 ; i < len; i++) { int mod = i % 3 ; switch (mod) { case 0 : { uint32_t idx = (i * div3_magic_2) >> 1 ; out1[idx] = in[i]; break ; } case 1 : { uint32_t idx = (i * div3_magic_2) >> 1 ; out2[idx] = in[i]; break ; } case 2 : { uint32_t idx = (i * div3_magic_2) >> 1 ; out3[idx] = in[i]; break ; } } } out1[len / 3 ] = 0 ; out2[len / 3 ] = 0 ; out3[len / 3 ] = 0 ; } int First (char *buf) { int i = 0 ; unsigned char xor_key = 0x80 ; while (i < 4 ) { buf[i] = (xor_key ^ (buf[i] << 1 )) & 0xFF ; i++; } if (strcmp (buf, "LN^dl" ) == 0 ) return 1 ; return 0 ; } char *buf1 = malloc (1 ); char *buf2 = malloc (1 ); char *buf3 = malloc (1 ); int len = 15 ; Init(buf1, buf2, buf3, user_input, len); if (First(buf1)) { for (int i = 0 ; i < 4 ; i++) { buf2[i] ^= buf1[i]; } if (strcmp (buf2, " 5-" ) == 0 ) { for (int i = 0 ; i < 4 ; i++) { buf3[i] ^= buf2[i]; } if (strcmp (buf3, "AFBo}" ) == 0 ) { result = 1 ; } } } return result; }
怎么说,ai还是强大的
这里直接建议用ida9打开就好了
首先知道我们要的flag长度是15,init函数把我们的字符串分成三段。first函数的加密逻辑(xor_key ^ (buf[i] << 1)) & 0xFF;
,异或0x80并且左移一位,然后和LN^dl
比较。buf2是异或buf1,buf3是异或buf2,分别与 5-
和AFBo}
比较。
所以直接进行逆向解密
part1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 src=list ("LN^dl" ) for i in range (5 ): print (chr (0x80 ^(ord (src[i])>>1 ),end='' ) dst = list ("LN^dl" ) for i in range (4 ): after = ord (dst[i]) for c in range (256 ): if (0x80 ^ (c << 1 ) & 0xFF ) == after: print (chr (c), end='' ) break
这里笔者进行逆向是发现不对,只能暴力破解为好
part2
1 2 3 4 5 6 7 buf1="LN^dl" buf2=[0x20 , 0x35 , 0x2D , 0x16 , 0x61 ] for i in range (4 ): print (chr (buf2[i]^ord (buf1[i])),end='' )
看反编译代码只对前四个进行了操作,则第五个不动吗????
所以第五个还是a
注意buf2肯定也还是五字节的
只不过有些无法转成ASCLL而已
part3
1 2 3 4 5 buf3="AFBo}" buf2=[0x20 , 0x35 , 0x2D , 0x16 , 0x61 ] for i in range (4 ): print (chr (buf2[i]^ord (buf3[i])),end='' )
加上第五个字符
最终
1 2 3 4 5 6 7 8 buf1="fgorl" buf2="l{sra" buf3="asoy}" for i in range (5 ): print (buf1[i]+buf2[i]+buf3[i],end='' )
总结:
我去我去,注意笔者在part1的时候没有注意只对前4个字节进行了操作,所以踩坑了。part2的时候没注意是对加密后的buf1进行操作的又踩坑了,最终的拼接是要flag形状的。
怎么说呢!题出的还是非常好的
APK逆向 —-2025.5.27
老样子直接拖进JADX里面看看,直接看activity
里的MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.example.crackme; import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class MainActivity extends Activity { private Button btn_register; private EditText edit_sn; String edit_userName; @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle(R.string.unregister); this .edit_userName = "Tenshine" ; this .edit_sn = (EditText) findViewById(R.id.edit_sn); this .btn_register = (Button) findViewById(R.id.button_register); this .btn_register.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { if (!MainActivity.this .checkSN(MainActivity.this .edit_userName.trim(), MainActivity.this .edit_sn.getText().toString().trim())) { Toast.makeText(MainActivity.this , R.string.unsuccessed, 0 ).show(); return ; } Toast.makeText(MainActivity.this , R.string.successed, 0 ).show(); MainActivity.this .btn_register.setEnabled(false ); MainActivity.this .setTitle(R.string.registered); } }); } @Override public boolean onCreateOptionsMenu (Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true ; }
然后我们可以雷电模拟运行一下这个APP
看主要的校验逻辑吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public boolean checkSN (String userName, String sn) { if (userName == null ) { return false ; } try { if (userName.length() == 0 || sn == null || sn.length() != 22 ) { return false ; } MessageDigest digest = MessageDigest.getInstance("MD5" ); digest.reset(); digest.update(userName.getBytes()); byte [] bytes = digest.digest(); String hexstr = toHexString(bytes, "" ); StringBuilder sb = new StringBuilder (); for (int i = 0 ; i < hexstr.length(); i += 2 ) { sb.append(hexstr.charAt(i)); } String userSN = sb.toString(); return new StringBuilder ().append("flag{" ).append(userSN).append("}" ).toString().equalsIgnoreCase(sn); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); return false ; } } private static String toHexString (byte [] bytes, String separator) { StringBuilder hexString = new StringBuilder (); for (byte b : bytes) { String hex = Integer.toHexString(b & 255 ); if (hex.length() == 1 ) { hexString.append('0' ); } hexString.append(hex).append(separator); } return hexString.toString(); } }
现在我们知道的用户名就是Tenshine
,我们简单的MD5加密一下B9C77224FF234F27AC6BADF83B855C76
1 2 3 4 5 6 7 src="B9C77224FF234F27AC6BADF83B855C76" for i in range (0 , len (src), 2 ): print (src[i],end="" ) flag{BC72F242A6AF3857}
然后试一下
额,行吧,交的时候注意换成32小写即可
像这道题就是直接在Java层中找加密逻辑
APK中的结构
层级
说明
示例
💡 应用层(App Layer)
你写的代码,比如 MainActivity.java
com.example.crackme.MainActivity
🔧 Framework 层(Java Framework)
Android 提供的类库,比如 Activity
, Button
, Toast
android.app.Activity
⚙️ Native 层(Native Libraries)
用 C/C++ 编写的库,NDK、libc、libcrypto 等
可能用来做更隐蔽的加密
🧩 HAL / Kernel 层
驱动层,硬件访问层
与 CrackMe 无关
人民的名义-抓捕赵德汉1-200 2025.5.28
这次下载完是jar后缀
不到是啥意思,直接使用JADX打开
感觉主要的加密逻辑就在这了
喂给ai,这是一个动态加载类并验证密码的实列过程,它把真正验证密码的类加密后嵌入资源中,运行时再动态加载并调用。
怎么说?frida hook一下吗?
我先看看idea能进行打个断点调试吗?
没源码吗?这是打不开反正
这里也是直接看WP了
第一次发现jar包还能直接运行呢
然后就能运行了
java版本太高运行不起来是正常的
换个低版本即可
没啥说的
1 2 3 fa3733c647dca53a66cf8df953c2d539 md5解密得到flag monkey99
这题出的不好
XCTF_MOBILE15_人民的名义-抓捕赵德汉1-200-CSDN博客
ill-intentions 2025.5.31
JADX打开,老样子直接进行看MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.example.application;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.IntentFilter;import android.os.Bundle;import android.widget.TextView;import com.example.hellojni.Manifest;public class MainActivity extends Activity { @Override public void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); TextView tv = new TextView (getApplicationContext()); tv.setText("Select the activity you wish to interact with.To-Do: Add buttons to select activity, for now use Send_to_Activity" ); setContentView(tv); IntentFilter filter = new IntentFilter (); filter.addAction("com.ctf.INCOMING_INTENT" ); BroadcastReceiver receiver = new Send_to_Activity (); registerReceiver(receiver, filter, Manifest.permission._MSG, null ); } }
简单翻译一下这句英文
简单模拟一下这个APK看一下
简单ai了一下MainActivity主要是显示一段引导文字,提醒用户暂时通过广播与指定的Activity交互,注册了一个广播接收器Send_to_Activity
,监听 com.ctf.INCOMING_INTENT
。
然后我们主要看Send_to_Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.example.application;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.widget.Toast;public class Send_to_Activity extends BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { String msgText = intent.getStringExtra("msg" ); if (msgText.equalsIgnoreCase("ThisIsTheRealOne" )) { Intent outIntent = new Intent (context, (Class<?>) ThisIsTheRealOne.class); context.startActivity(outIntent); } else if (msgText.equalsIgnoreCase("IsThisTheRealOne" )) { Intent outIntent2 = new Intent (context, (Class<?>) IsThisTheRealOne.class); context.startActivity(outIntent2); } else if (msgText.equalsIgnoreCase("DefinitelyNotThisOne" )) { Intent outIntent3 = new Intent (context, (Class<?>) DefinitelyNotThisOne.class); context.startActivity(outIntent3); } else { Toast.makeText(context, "Which Activity do you wish to interact with?" , 1 ).show(); } } }
监听”msg”字段的广播,根据其值启动不同的Activity
"ThisIsTheRealOne"
→ 启动 ThisIsTheRealOne
活动
"IsThisTheRealOne"
→ 启动 IsThisTheRealOne
活动
"DefinitelyNotThisOne"
→ 启动 DefinitelyNotThisOne
活动
看一下这三个Activity会发现它的每个类都加载了一个名为hello-jni的本地库。每个类都定义了若干native方法,每个类在界面中提供了一个按钮,点击后会发送一个广播com.ctf.OUTGOING_INTENT
,其中附带了一段加密/处理过的msg
这下简单了,直接进行拆包,看动态加载库即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 __int64 __fastcall Java_com_example_application_IsThisTheRealOne_perhapsThis ( __int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5) { __int64 v5; const char *v8; const char *v9; const char *v10; char *v11; char v12; char v14[80 ]; char v15[80 ]; char dest[80 ]; char v17[88 ]; v5 = 0LL ; v8 = (*(*a1 + 1352LL ))(a1, a3, 0LL ); v9 = (*(*a1 + 1352LL ))(a1, a4, 0LL ); v10 = (*(*a1 + 1352LL ))(a1, a5, 0LL ); v11 = &v8[strlen (v8)]; *v11 = '{barGwnw' ; strcpy (v11 + 16 , "tfqm}" ); *(v11 + 1 ) = 'Cr\x7FhbtuO' ; strncpy (dest, v8, 'L' ); strncpy (v14, v9, 'L' ); strncpy (v15, v10, 'L' ); dest[76 ] = 0 ; v15[76 ] = 0 ; v14[76 ] = 0 ; do { v12 = dest[v5] ^ v14[v5] ^ v15[v5]; v17[v5++] = v12; printf ("%c\n" , v12); } while ( v5 != 76 ); v17[76 ] = 0 ; printf ("Here is your Reply: %s" , v17); return (*(*a1 + 1336LL ))(a1, v17); } __int64 __fastcall Java_com_example_application_ThisIsTheRealOne_orThat ( __int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5) { __int64 v5; const char *v8; const char *v9; const char *v10; char *v11; char v12; char v14[80 ]; char v15[80 ]; char dest[80 ]; char v17[88 ]; v5 = 0LL ; v8 = (*(*a1 + 1352LL ))(a1, a3, 0LL ); v9 = (*(*a1 + 1352LL ))(a1, a4, 0LL ); v10 = (*(*a1 + 1352LL ))(a1, a5, 0LL ); v11 = &v8[strlen (v8)]; *v11 = '[\x7FLElYIJ' ; *(v11 + 1 ) = 'n`xuz`jx' ; *(v11 + 2 ) = 'AOfz\x7FvO\x7F' ; strcpy (v11 + 24 , "RkiPkYmbFftKqusfn}flhQfsqxr\\TeaSgnmdc!" ); strncpy (dest, v8, 'L' ); strncpy (v14, v9, 'L' ); strncpy (v15, v10, 'L' ); dest[76 ] = 0 ; v15[76 ] = 0 ; v14[76 ] = 0 ; do { v12 = dest[v5] ^ v14[v5] ^ v15[v5]; v17[v5++] = v12; printf ("%c\n" , v12); } while ( v5 != 76 ); v17[76 ] = 0 ; printf ("Here is your Reply: %s" , v17); return (*(*a1 + 1336LL ))(a1, v17); } __int64 __fastcall Java_com_example_application_DefinitelyNotThisOne_definitelyNotThis ( __int64 a1, __int64 a2, __int64 a3, __int64 a4) { (*(*a1 + 1352LL ))(a1, a3, 0LL ); (*(*a1 + 1352LL ))(a1, a4, 0LL ); return (*(*a1 + 1336LL ))(a1, "Told you so!" ); }
这三段代码里ThisIsTheRealOne
是我们真正想要的,本来是想要动态调试一下这个.so文件的,有报错没调试起来。上网找了找
照着文章 整了整,但是把你会发现报错,我们调试android_server64
时他会报错Segmentation fault ,然后呢我们换成android x64 server
进行调试的时候,你又会发现我们ida又会报错
SO,笔者的建议还是使用frida进行hook好了,但是笔者这里不会搓脚本,然后也是使用ai编写的了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Interceptor .attach (Module .getExportByName (null , "dlopen" ), { onEnter : function (args ) { this .path = Memory .readCString (args[0 ]); }, onLeave : function (retval ) { if (this .path .indexOf ("libhello-jni.so" ) !== -1 ) { console .log ("[*] libhello-jni.so loaded" ); var base = Module .findBaseAddress ("libhello-jni.so" ); console .log ("[*] Base address:" , base); var target = base.add (0x6c0 ); console .log ("[*] Hook target function at" , target); Interceptor .attach (target, { onEnter : function (args ) { console .log ("[*] Function called" ); console .log ("Arg0: " + args[0 ]); console .log ("Arg1: " + args[1 ]); }, onLeave : function (retval ) { console .log ("[*] Return value:" , retval); } }); } } });
不咋会用frida
hook之后应该做什么哇?这里直接也是看WP了
下面直接贴hook脚本了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function main ( ) {Java .perform (function ( ) { var DefinitelyNotThisOneHandler = Java .use ('com.example.application.DefinitelyNotThisOne' ) DefinitelyNotThisOneHandler .definitelyNotThis .implementation = function (arg0, arg1 ) { console .log ('DefinitelyNotThisOneHandler called: ' + arg0 + " \n" + arg1) var ret = this .definitelyNotThis (arg0, arg1) console .log ('DefinitelyNotThisOneHandler ret: ' + ret ) return ret } var ThisIsTheRealOneHandler = Java .use ('com.example.application.ThisIsTheRealOne' ) ThisIsTheRealOneHandler .orThat .overload ('java.lang.String' , 'java.lang.String' , 'java.lang.String' ).implementation = function (arg0, arg1, arg2 ) { console .log ('ThisIsTheRealOneHandler called: ' + arg0 + " \n" + arg1 + " \n" + arg2) var ret = this .orThat (arg0, arg1, arg2) console .log ('ThisIsTheRealOneHandler ret: ' + ret ) return ret } var IsThisTheRealOneHandler = Java .use ('com.example.application.IsThisTheRealOne' ) IsThisTheRealOneHandler .perhapsThis .overload ('java.lang.String' , 'java.lang.String' , 'java.lang.String' ).implementation = function (arg0, arg1, arg2 ) { console .log ('IsThisTheRealOneHandler called: ' + arg0 + " \n" + arg1 + " \n" + arg2) var ret = this .perhapsThis (arg0, arg1, arg2) console .log ('IsThisTheRealOneHandler ret: ' + ret ) return ret } }) } setImmediate (main)
步骤就不说了,不会hook的,简单去学一下吧,或者看一下笔者的另一篇文章。
hook上之后,要在模拟器那边简单触发一下。
这里讲一下objection
它是基于frida的命令工具,主要用于对 Android 和 iOS 应用的动态分析 ,尤其适合逆向工程、安全测试和 CTF 挖洞。
常见的功能如下:
功能
说明
explore
进入应用交互控制台
android hooking
Hook Java 方法、类和 native 函数
android intent
启动 Activity,查看 intent 内容
android memory
查看内存中的字符串、对象等
android keystore
与系统 Keystore 交互
android file download
下载应用内部的文件
android sslpinning disable
绕过 SSL Pinning(HTTPS 拦截)
android misc
常用的辅助操作,如截图、剪贴板查看等
命令
1 2 3 4 5 6 >objection -g com.example.hellojni explore >>>CLI中输入 >android intent launch_activity com.example.application.IsThisTheRealOne >android hooking watch class_method android.content.Intent.putExtra --dump-return --dump-args --dump-backtrace
然后我们hook的时候就可以看见把拦截的信息打印下来了
bananaship师傅说还是很好用的,简单了解一下吧
总结:
学的东西还是挺多的,至少ida的对安卓设备的调试会了,虽说没有用上。hook也是学上了,对frida的了解又加深了。对hook脚本的编写希望能在进一些。