提取flag 给了一个.rar压缩包,经过解压缩后得到了encrypted_payload.bin、firmware.bin和一个xin.txt文件,在xin.txt文件里面的内容如下:
flag存在于firmware.bin中,并且通过异或加密(密钥0x9A),请提取flag{}。 payload和flag形影不离。
通过这个知道flag是和payload紧密相邻的,一个在前,一个在后。这道题的话,flag是在payload之后的,给了encrypted_payload.bin,思路就是找到payload在firmware.bin文件中的偏移以及payload的字符数量,跳过payload,获取到加密后的flag,最后使用脚本解密flag,得到最后的flag。
首先使用下述指令找到偏移
└─$ grep -obUaFf encrypted_payload.bin firmware.bin 1000:�������������������������颭���������� grep:文本搜索工具 -F:固定字符串匹配,关闭正则表达式,按原始字节逐个对比 -f xxx.bin:从.bin文件中读取一整块二进制数据作为搜索模式 -a:把二进制文件当作文本文件处理 -U:不处理Windows风格的回车换行,保持原始二进制字节流 -b:输出匹配位置相对于文件开头的字节偏移量(十进制) -o:只输出实际匹配到的部分,而不是整行
接着使用下述指令找到encrypted_payload.bin的字符数量
└─$ ll encrypted_payload.bin -rwxrw-rw- 1 kali kali 38 Dec 22 07:56 encrypted_payload.bin ll:相当于执行了ls -l,列出文件的详细信息
然后使用下述指令从firmware.bin文件中提取出加密后的flag
dd if=firmware.bin of=encrypted_flag.bin bs=1 skip=1038 count=43 dd:Linux下底层数据拷贝/转换工具,直接操作字节流 if:input file of:output file bs:block size,操作字节大小 skip:跳过源文件前多少字节 count:复制block的多少
得到了encrypted_flag.bin这个二进制文件后,再利用解密脚本对其进行解密即可获得最后的flag
key = 0x9A with open("encrypted_flag.bin","rb") as f: flag = f.read() flag = bytes(b ^ key for b in flag) print("flag is",flag.decode())
私钥解密 通过解压得到firmware.bin文件和一个xin.txt文件,xin.txt文件内容如下:
有个被加密的bin文件,其中含有flag{},你能解出来吗?
接着将firmware.bin文件放入kali中,使用binwalk命令去分离一下,看一下里面具体有什么信息:
┌──(kali㉿kali)-[~/Desktop] └─$ tree firmware_extracted firmware_extracted ├── 0.squashfs └── squashfs-root ├── bin │ ├── bad_enc_10.sh │ ├── bad_enc_1.sh │ ├── bad_enc_2.sh │ ├── bad_enc_3.sh │ ├── bad_enc_4.sh │ ├── bad_enc_5.sh │ ├── bad_enc_6.sh │ ├── bad_enc_7.sh │ ├── bad_enc_8.sh │ └── bad_enc_9.sh ├── etc │ └── config ├── flag.enc └── usr ├── clue │ └── clue.txt └── secret └── private_key_encrypted.pem 7 directories, 15 files
里面有10个.sh文件、一个config文件、一个flag.enc文件(被加密后的flag)、一个clue.txt文件以及private_key_encrypted.pem。.sh文件里面都是一些错误的解密/加密脚本,没有什么用,config文件里面的内容如下:
clue.txt文件的内容如下:
知道是弱口令密码,确实前面那段话不知道有什么作用,难道也是告诉我们密码是6位?因为是弱口令密码,就猜测是123456吧。关键点在于.enc和.pem这两个文件,接下来解释一下这两个文件是什么东西
.pem文件是一种常见的文本格式文件,主要用于存储加密、证书、密钥这类与安全相关的信息。简单来说就是一个用Base64编码的容器装一些东西。 .enc文件是一个经过加密处理的文件,通常为不可读的二进制密文。
对于上述两种文件的处理,可以使用openssl指令对其进行操作。
首先得先得到.pem文件里面的东西,想要得到.pem里面的东西得输入密码,猜测弱口令密码为123456,使用下面这个指令进行操作:
openssl rsa -in private_key_encrypted.pem -out private_key_decrypted.pem openssl rsa:调用openssl的rsa密钥管理功能,专门处理rsa私钥 -in xxx.pem:输入文件,这是有密码保护的私钥 -out xxx.pem:输出文件,解密后生成的无密码明文私钥 Enter pass phrase for private_key_encrypted.pem:123456 writing RSA key 至于为什么是rsa呢?因为在点开private_key_encrypted.pem输入密码后出现: private_key_encrypted.pem Private RSA Key Strength: 2048 bits 因此知道这是rsa
得到这个私钥文件后就可以拿这个去解密那个flag.enc文件了,指令如下:
openssl pkeyutl -decrypt -in flag.enc -out flag.txt -inkey ~/Desktop/firmware_extracted/squashfs-root/usr/secret/private_key_decrypted.pem openssl pkeyutl:调用openssl的通用非对称密钥工具 -decrypt:指定操作为解密 -in xxx.enc:输入文件,加密后的数据 -out xxx.txt:输出文件,解密成功后的明文结果 -inkey xxx.pem:指定私钥文件
最后打开flag.txt文件得到flag。
easy壳 给了一个.apk文件,放入jadx里面进行查看发现是加了壳的,再根据题目名称,就可以猜出这道题就是让我们进行去壳操作,从而获得flag。这道题是用雷电模拟器作为安卓模拟器,这里需要下载frida工具,具体安装流程就自行查找。
准备工作做好后,首先先将这个文件拖进模拟器中进行查看
让我们输入密码,怎么输入都不对,因此就先放入jadx里面查看,发现是加了壳的
但是作为新手,需要知道,怎么看出加了壳的,下面进行简要的讲解一下:
首先是如果这个.apk文件没有加壳的话,我们应该能看到里面真实的业务逻辑出现在反编译后的MainActivity等类中,而现在却只能看到几行代码,所以这一定是不对的,其次是在正常的安卓app里面,setContentView()传入的应该是一个布局文件ID或者View对象,而这里传入的是一个布尔值资源ID(R.bool开头的),因此可以判断出这道题是加了壳的
既然已经知道是加了壳的,那么接下来就是要进行脱壳操作,这里使用的是frida工具。首先查找设备
PS D:\国内赛\PolarIoTs_2026> adb devices List of devices attached emulator-5554 device
接着使用命令控制模拟器,并进入root模式
PS D:\国内赛\PolarIoTs_2026> adb -s emulator-5554 shell star2qltechn:/ $ su :/ #
将frida-server传入到雷电模拟器中并找到相应位置,并运行frida-server
:/ # cd mnt :/mnt # cd shared/ :/mnt/shared # cd Pictures/ :/mnt/shared/Pictures # ls cache data frida-server frida-server-17.9.1-android-x86_64 :/mnt/shared/Pictures # ./frida-server-17.9.1-android-x86_64 & [1] 2899
再打开一个终端进行转发端口(转发端口(Port Forwarding)的作用是将一个网络端口的流量重定向到另一个端口或地址。)
PS D:\国内赛\PolarIoTs_2026> adb forward tcp:27042 tcp:27042 27042
最后在这个新打开的终端中进行脱壳
PS D:\国内赛\PolarIoTs_2026> frida-dexdump -D emulator-5554 -f com.example.iot2 -o D:\国内赛\PolarIoTs_2026 --deep-search --------------------------------------------------------------------------------------------------------------------- __ _ _ _ _ / _|_ __(_) __| | __ _ __| | _____ ____| |_ _ _ __ ___ _ __ | |_| '__| |/ _` |/ _` |_____ / _` |/ _ \ \/ / _` | | | | '_ ` _ \| '_ \ | _| | | | (_| | (_| |_____| (_| | __/> < (_| | |_| | | | | | | |_) | |_| |_| |_|\__,_|\__,_| \__,_|\___/_/\_\__,_|\__,_|_| |_| |_| .__/ |_| https://github.com/hluwa/frida-dexdump --------------------------------------------------------------------------------------------------------------------- Spawning `com.example.iot2`... INFO:Agent:DexDumpAgent<Connection(pid=Session(pid=2848), connected:True), attached=True>: Attach. INFO:frida-dexdump:Waiting 5s... INFO:frida-dexdump:[+] Searching... INFO:frida-dexdump:[*] Successful found 12 dex, used 11 time. INFO:frida-dexdump:[+] Starting dump to 'D:\国内赛\PolarIoTs_2026'... INFO:frida-dexdump:[+] DexMd5=c47cc0bd5bd01ef9dd17cb21969146e0, SavePath=D:\国内赛\PolarIoTs_2026\classes.dex, DexSize=0x2dc0 INFO:frida-dexdump:[+] DexMd5=a71dd37b90cc4ebe302a3d7c82e7ec4d, SavePath=D:\国内赛\PolarIoTs_2026\classes02.dex, DexSize=0x847ff4 INFO:frida-dexdump:[+] DexMd5=b31d485cbb6dd5021f3625290e643608, SavePath=D:\国内赛\PolarIoTs_2026\classes03.dex, DexSize=0x844e34 INFO:frida-dexdump:[+] DexMd5=02554f973f74b16caee3b9365bf57483, SavePath=D:\国内赛\PolarIoTs_2026\classes04.dex, DexSize=0x844ff4 INFO:frida-dexdump:[+] DexMd5=92b29855e4ae034064cdc59ace3c11a9, SavePath=D:\国内赛\PolarIoTs_2026\classes05.dex, DexSize=0x1f4 INFO:frida-dexdump:[+] DexMd5=927fd995fa493447a7d33907c377a9e3, SavePath=D:\国内赛\PolarIoTs_2026\classes06.dex, DexSize=0x2a1c49 INFO:frida-dexdump:[+] DexMd5=21d6e5ff1a7fad396814818d416660aa, SavePath=D:\国内赛\PolarIoTs_2026\classes07.dex, DexSize=0x1f4 INFO:frida-dexdump:[+] DexMd5=59f2a8e66f1ccdb2ffc58746318cc23e, SavePath=D:\国内赛\PolarIoTs_2026\classes08.dex, DexSize=0x22aa71 INFO:frida-dexdump:[+] DexMd5=2a2a89317e000a76ae646c206407d998, SavePath=D:\国内赛\PolarIoTs_2026\classes09.dex, DexSize=0x848000 INFO:frida-dexdump:[+] DexMd5=9fa495ae8325e6f7a672fa53f2afa09c, SavePath=D:\国内赛\PolarIoTs_2026\classes10.dex, DexSize=0x847fd8 INFO:frida-dexdump:[*] All done...
再将得到的文件拖入到jadx中找到MainActivity
package com.example.iot2; import android.os.Bundle; import android.view.View; import android.widget.EditText; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; /* loaded from: D:\国内赛\PolarIoTs_2026\classes03.dex */ public class MainActivity extends AppCompatActivity { private final String CORRECT_PWD = "adsbu32cais"; private final String FLAG = "flag{sadbuy12368gdsabhu}"; private EditText et_pwd; /* JADX INFO: Access modifiers changed from: protected */ @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity public void onCreate(Bundle bundle) { super.onCreate(bundle); setContentView(R.layout.activity_main); this.et_pwd = (EditText) findViewById(R.id.et_password); } public void checkPassword(View view) { if (this.et_pwd.getText().toString().trim().equals("adsbu32cais")) { Toast.makeText(this, "flag{sadbuy12368gdsabhu}", 1).show(); } else { Toast.makeText(this, "密码错误,请重试!", 0).show(); } } }
这就对了,找到flag
至于com.example.iot2这一坨是怎么来的,将对应的app打开,在powershell里面输入下述代码就行了
adb shell dumpsys window | findstr mCurrentFocus
appreverse 同样的道理,和上面easy壳的脱壳操作是一样的,先将其进行脱壳后放入到jadx中进行观察
package com.example.appreverse; import android.os.Bundle; import android.view.View; import android.widget.Toast; import androidx.appcompat.app.AppCompatActivity; import com.example.appreverse.databinding.ActivityMainBinding; /* loaded from: D:\国内赛\PolarIoTs_2026\appreverse\classes03.dex */ public class MainActivity extends AppCompatActivity { private ActivityMainBinding binding; public native boolean nativeCheckFlag(String str); static { System.loadLibrary("nativeflag"); } /* JADX INFO: Access modifiers changed from: protected */ @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity public void onCreate(Bundle bundle) { super.onCreate(bundle); ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater()); this.binding = inflate; setContentView(inflate.mo318getRoot()); } public void checkFlag(View view) { if (nativeCheckFlag(this.binding.etFlag.getText().toString().trim())) { Toast.makeText(this, "✅ Flag正确!", 0).show(); } else { Toast.makeText(this, "❌ Flag错误!", 0).show(); } } } 首先System.loadLibrary("nativeflag");这句代码加载了名为nativeflag的Native库,也就是(libnativeflag.so),声明了一种方法,具体实现是在libnativeflag.so中,接着就是与输入进行对比。因此主要就是获得libnativeflag.so文件,通过这个文件来具体分析一些东西
将这个.apk文件进行解压就可以得到libnativeflag.so文件,再将这个文件放入ida中进行逆向分析
bool __fastcall Java_com_example_appreverse_MainActivity_nativeCheckFlag(__int64 a1, __int64 a2, __int64 a3) { const char *s; // x0 const char *src; // x20 int n8_1; // w0 _BOOL4 n8; // w22 size_t size; // x23 unsigned __int8 *dest; // x0 unsigned __int8 *ptr; // x21 unsigned __int64 v12; // x9 int8x16_t v13; // q0 int8x16_t *v14; // x11 unsigned __int64 v15; // x12 int8x16_t v16; // q2 int8x8_t *v17; // x11 unsigned __int64 v18; // x12 unsigned __int64 v19; // x12 unsigned __int64 v20; // x8 unsigned __int8 *v21; // x9 char *s1_1; // x0 const char *s1; // x23 if ( a3 ) { s = (const char *)(*(__int64 (__fastcall **)(__int64, __int64, _QWORD))(*(_QWORD *)a1 + 1352LL))(a1, a3, 0); if ( s ) { src = s; n8_1 = strlen(s); n8 = n8_1; if ( n8_1 ) { size = n8_1 + 1; dest = (unsigned __int8 *)malloc(size); if ( dest ) { ptr = dest; memcpy(dest, src, size); if ( n8 < 1 ) goto LABEL_21; if ( (unsigned int)n8 < 8uLL ) { v12 = 0; goto LABEL_19; } if ( (unsigned int)n8 >= 0x20uLL ) { v13.n128_u64[0] = 0x1F1F1F1F1F1F1F1FLL; v13.n128_u64[1] = 0x1F1F1F1F1F1F1F1FLL; v14 = (int8x16_t *)(ptr + 16); v12 = n8 - (unsigned __int64)(n8 & 0x1F); v15 = v12; do { v15 -= 32LL; v16 = veorq_s8(*v14, v13); v14[-1] = veorq_s8(v14[-1], v13); *v14 = v16; v14 += 2; } while ( v15 ); if ( (n8 & 0x1F) == 0 ) goto LABEL_21; if ( (n8 & 0x1Fu) < 8uLL ) { LABEL_19: v20 = n8 - v12; v21 = &ptr[v12]; do { --v20; *v21++ ^= 0x1Fu; } while ( v20 ); goto LABEL_21; } } else { v12 = 0; } v17 = (int8x8_t *)&ptr[v12]; v18 = v12 + (n8 & 7); v12 = n8 - (unsigned __int64)(n8 & 7); v19 = v18 - n8; do { v19 += 8LL; v17->n64_u64[0] = veor_s8((int8x8_t)v17->n64_u64[0], (int8x8_t)0x1F1F1F1F1F1F1F1FLL).n64_u64[0]; ++v17; } while ( v19 ); if ( (n8 & 7) != 0 ) goto LABEL_19; LABEL_21: s1_1 = (char *)malloc((4 * ((n8 + 2) / 3)) | 2); if ( s1_1 ) { s1 = s1_1; base64_encode(ptr, n8, s1_1); n8 = strcmp(s1, "eXN+eGRoe3ZqLSwvJyh5bHp9aissJyh5e2xi") == 0; free(ptr); ptr = (unsigned __int8 *)s1; } else { n8 = 0; } free(ptr); goto LABEL_25; } n8 = 0; } LABEL_25: (*(void (__fastcall **)(__int64, __int64, const char *))(*(_QWORD *)a1 + 1360LL))(a1, a3, src); return n8; } } return 0; }
这段代码的大致流程就是先将我们输入的字符串中每个字符与0x1f进行异或运算,接着再将异或得到的字符串进行base64编码,最后与eXN+eGRoe3ZqLSwvJyh5bHp9aissJyh5e2xi这段字符串进行对比。那么思路就清晰了,将eXN+eGRoe3ZqLSwvJyh5bHp9aissJyh5e2xi进行base64解码,再将解码后的字符串与0x1f进行异或运算就可以得到最后的flag了,脚本如下:
import base64 encoded = "eXN+eGRoe3ZqLSwvJyh5bHp9aissJyh5e2xi" decoded = base64.b64decode(encoded) flag = bytes([b ^ 0x1F for b in decoded]) print(flag.decode())
AES密钥 这道题目给了一个实例与一个压缩包,