吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 723|回复: 19
收起左侧

[Android CTF] 2025解题领红包 Android中级详细WP

[复制链接]
Command 发表于 2025-2-13 07:09
本帖最后由 Command 于 2025-2-13 07:44 编辑

APK

Jadx打开APK, 搜索"错误"可以找到这里

public final class BattleActivityKt$BattleScreen$1$3$1$1$2 extends AbstractC1360j implements InterfaceC1335a {
        // ......
        public final void invoke() {
                String BattleScreen$lambda$21;
                BattleScreen$lambda$21 = BattleActivityKt.BattleScreen$lambda$21(this.$activationCode$delegate);
                if (!BattleActivityKt.Check(BattleScreen$lambda$21)) {
                    Toast.makeText(this.$context, "秘钥错误,请重试!", 0).show();
                    return;
                }
                // ......
        }
}

能够发现这里调用了BattleActivityKt.Check对输入的Flag进行校验

public static final native boolean Check(String str);

发现是Native函数, 那就直接看libwuaipojie2025_game.so

SO

IDA, 启动! (本文结合Unidbg分析ARM64的so)

找函数

发现Check在so里并未被导出, 找JNI_OnLoad

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
  __int64 v3; // x0
  _QWORD v4[2]; // [xsp+0h] [xbp-10h] BYREF
  v4[1] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  if ( (*vm)->GetEnv(vm, (void **)v4, 65542LL) )
    return -1;
  v3 = (*(__int64 (__fastcall **)(_QWORD, const char *))(*(_QWORD *)v4[0] + 48LL))(
         v4[0],
         "com/zj/wuaipojie2025_game/ui/BattleActivityKt");
  if ( !v3 )
    return -1;
  if ( (*(int (__fastcall **)(_QWORD, __int64, char **, __int64))(*(_QWORD *)v4[0] + 1720LL))(
         v4[0],
         v3,
         off_163510,
         1LL) < 0 )
    return -1;
  return 65542;
}

(这啥玩意? 算了懒得分析了, 直接Unidbg模拟一下看看)

AndroidEmulator emulator = AndroidEmulatorBuilder
                .for64Bit()
        .addBackendFactory(new Unicorn2Factory(true))
        .build();
Memory memory = emulator.getMemory();
LibraryResolver resolver = new AndroidResolver(23);
memory.setLibraryResolver(resolver);
VM vm = emulator.createDalvikVM(new File("52pojie.apk"));
vm.setVerbose(true);
DalvikModule dm = vm.loadLibrary(new File("libwuaipojie2025_game.so"), true);
dm.callJNI_OnLoad(emulator);

由此可知Check的地址为0xe8c54

RegisterNative(com/zj/wuaipojie2025_game/ui/BattleActivityKt, Check(Ljava/lang/String;)Z, RX@0x400e8c54[libwuaipojie2025_game.so]0xe8c54)
// Check (a3为传入str)
bool __fastcall sub_E8C54(JNIEnv *a1, jobject a2, jstring *a3)
{
  const char *InputUTF; // x0
  const char *Input; // x21
  char v11; // w0
  char v14; // w8
  void (__fastcall *v15)(_QWORD *, const char *, __int64, _QWORD *); // x22
  _QWORD *v16; // x23
  _BOOL4 v19; // w22
  _QWORD v21[2]; // [xsp+20h] [xbp-40h] BYREF
  _OWORD v22[2]; // [xsp+30h] [xbp-30h] BYREF

  InputUTF = (*a1)->GetStringUTFChars(a1, a3, 0LL);
  if ( InputUTF )
  {
    Input = InputUTF;
    // ......省略部分不太重要的代码, 大概是环境检测? 总之不重要
    v15 = (void (__fastcall *)(_QWORD *, const char *, __int64, _QWORD *))nullsub_1(*(_QWORD *)((unsigned __int64)v22 & 0xFFFFFFFFFFFFFFF7LL | (8LL * (((unsigned __int8)(v11 | v14) ^ (((unsigned int)ao ^ (unsigned int)a) >> 24)) & 1)))); // 计算v15的地址 (其实调用的就是ao, 但不一定能确定, 还得Unidbg)
    dword_16359C = -559038669;
    v21[0] = 0LL;
    v21[1] = 0LL;
    v16 = (_QWORD *)operator new[](0x13uLL);
    v15(v21, Input, 19LL, v16); // 调用v15获取v16 对应指令为BLR X22
    v19 = *v16 == 0x72ECF89BAF8F2748LL
       && v16[1] == 0xB63AE26B0C720798LL
       && *(_QWORD *)((char *)v16 + 11) == 0xF75942B63AE26B0CLL;  // 检验
    operator delete[](v16);
    (*a1)->ReleaseStringUTFChars(a1, a3, Input);
  }
  else
  {
    return 0;
  }
  return v19;
}

可以发现v15的地址不能确定, 直接用Unidbg断在调用v15的那里即可

emulator.attach().addBreakPoint(md.base+0xe8f44); // 0xe8f44为指令BLR X22的偏移量, 断下后看X22寄存器减掉基址就行

最后可以发现调用函数为ao (0xe9f60)

分析

// a2就是输入的字符串, a3为19, result为最后结果
void __fastcall ao(__int128 *a1, const char *a2, __int64 a3, _QWORD *result)
{
  __int64 i; // x23
  __int128 v8; // [xsp+0h] [xbp-20h] BYREF
  __int64 v9; // [xsp+18h] [xbp-8h]

  v9 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40); // 没用
  v8 = *a1; // 传入a1为定值, 故v8也为定值
  if ( a3 )
  {
    for ( i = 0LL; i != a3; ++i )  // 循环19次, 侧面说明Flag长度为19
    {
      if ( (i & 0xF) == 0 )
        sub_E9954(&v8);  // 这里的这个函数其实不用跟进去看...... (和输入啥的都没关系, v8一开始就是定值, v8更新的次数也固定)
      *((_BYTE *)result + i) = *(_BYTE *)((unsigned __int64)&v8 | i & 0xF) ^ a2[i]; // 实际上做的事情 ("^"左侧其实都是确定的值, 不随输入的改变而改变)
    }
  }
}

由此我们不难发现 (只要没有和我一样一开始就跑偏去看sub_E9954), 实际上就是将字符串的每一位与不同的数进行异或然后存入result而已

我们只要获取到i在0-18时*(_BYTE *)((unsigned __int64)&v8 | i & 0xF)的值就好, 我选择使用Unidbg对后面的EOR(ARM中异或指令)进行Hook

而在那里的汇编是这样的

; ......
ORR             X8, X24, X25
LDRB            W9, [X21,X23]
LDRB            W8, [X8]
EOR             W8, W8, W9  ; Addr=0xE9FB8
STRB            W8, [X19,X23]
ADD             X23, X23, #1
; ......

在这里先简单讲一下EOR指令

EOR                        W8, W8, W9  ; 将W8与W9进行异或, 结果存回W8 (在这里W8就是^左边那一长串, W9就是a2[i])

获取秘钥

Unidbg指令Hook

emulator.getBackend().hook_add_new(new CodeHook() {
        @Override
        public void hook(Backend backend, long address, int size, Object user) {
                if (address == md.base + 0xe9fb8) {  // 0xe9fb8就是刚才说的异或指令的偏移
            // 输出 W8, chr(a2[i])
                System.out.printf("0x%1$x, %2$c", emulator.getBackend().reg_read(Unicorn.UC_ARM64_REG_W8).intValue(), (char) emulator.getBackend().reg_read(Unicorn.UC_ARM64_REG_W9).intValue());
            System.out.println();
        }
    }

    @Override
    public void onAttach(UnHook unHook) {}
    @Override
        public void detach() {}
}, md.base, md.base + md.size, null);

然后我们可以得到每一位要与之异或的值

[0x2e, 0x4b, 0xee, 0xc8, 0xe0, 0x95, 0x88, 0x47, 0xb0, 0x72, 0x1b, 0x68, 0x40, 0xd0, 0xa, 0x84, 0x27, 0xaf, 0xf3]

那么现在我们有异或的值, 还需要知道正确的结果才能得到Flag

这时往前看, 回到刚才的函数Check (sub_E8C54), 有这样一段代码

_QWORD *v16  // Tips: 单个_QWORD 8个字节
v16 = (_QWORD *)operator new[](0x13uLL); // 这里申请了一块长度为0x13(19)的内存空间
v15(v21, Input, 19LL, v16); // v16是刚才的result
v19 = *v16 == 0x72ECF89BAF8F2748LL // v16的前8字节
   && v16[1] == 0xB63AE26B0C720798LL  // 又有8字节
   && *(_QWORD *)((char *)v16 + 11) == 0xF75942B63AE26B0CLL; // 这里有可能看不明白, 这其实是从11-19的8个字节, 也就是说包括第二个8字节尾部的5字节
return v19;

也就是, 输入正确的情况下v16里应该是这样的

v16:
0000: 48 27 8F AF 9B F8 EC 72 98 07 72 0C 6B E2 3A B6
0010: 42 59 F7 00 00 00 00 00 00 00 00 00 00 00 00 00

数字对应的字节 (小端序)
0x72ECF89BAF8F2748LL: 48 27 8F AF 9B F8 EC 72
0xB63AE26B0C720798LL: 98 07 72 0C 6B E2 3A B6
0xF75942B63AE26B0CLL: 0C 6B E2 3A B6 42 59 F7
后两个明显可以看到重合部分

正确结果有了, 异或的值有了, 直接写Python脚本解密

Flag = ''

for i in zip([0x48, 0x27, 0x8f, 0xaf, 0x9b, 0xf8, 0xec, 0x72, 0x98, 0x7, 0x72, 0xc, 0x6b, 0xe2, 0x3a, 0xb6, 0x42, 0x59, 0xf7], [0x2e, 0x4b, 0xee, 0xc8, 0xe0, 0x95, 0x88, 0x47, 0xb0, 0x72, 0x1b, 0x68, 0x40, 0xd0, 0xa, 0x84, 0x27, 0xaf, 0xf3]):
    # print(i[0] ^ i[1])
    Flag += chr(i[0] ^ i[1])
print(Flag)

最后输出结果

flag{md5(uid+202eö

好像最后那点不太对? 不过猜也能猜出来是uid+2025 毕竟2025年嘛

实际上应该是flag{md5(uid+2025)} ......吧? (但我从apk里输入进去也不对啊......)

(顺带提一嘴, 秘钥输入框需要在进入战斗后点右上角那个圆形才会出现... 应该不会有人和我一样直到快做完题才发现吧)

免费评分

参与人数 2威望 +1 吾爱币 +21 热心值 +2 收起 理由
正己 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
风子09 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

正己 发表于 2025-2-13 15:59
ao是错误方法,这里加密的秘钥和正确的秘钥只差几个字节,导致异或出来的结果可以猜出来。另外前面的检测方法没过掉就走ao这个错误方法
JOKERWING 发表于 2025-2-24 09:55
前面的思路基本和我一样,也到0xe8c54了,  本来是打算断掉调试看能不能行,但由于IDA捣鼓半天也没捣鼓出调试,就放弃了。光啃代码啃半天进度缓慢就放弃了,哎。。可能是我还在学,还有好多方法或者工具不知道,所以没啥思路了。  等我继续看完安卓逆向这档事吧,目前还没到frida
nanaqilin 发表于 2025-2-13 08:46
我也是搞到这儿,然后提交了好多次也没成功,其中也试过2025,看来这道题跟我无缘啊
 楼主| Command 发表于 2025-2-13 08:47
nanaqilin 发表于 2025-2-13 08:46
我也是搞到这儿,然后提交了好多次也没成功,其中也试过2025,看来这道题跟我无缘啊

你应该是把这个交上去了? 需要把UID和2025拼接到一起然后MD5再提交的
nanaqilin 发表于 2025-2-13 08:51
Command 发表于 2025-2-13 08:47
你应该是把这个交上去了? 需要把UID和2025拼接到一起然后MD5再提交的

是拼起来?我直接取的加和,然后再求的Md5
cattie 发表于 2025-2-13 08:56
那个2025不出来应该是你第二轮的异或密钥找错了,他在0x10位的时候重新获取了一次密钥。
可以看看我的writeup


还有建议把几个帖子全部合到一起,这样看起来更方便。
风子09 发表于 2025-2-13 10:09
安卓中级题在app上提交的flag的格式是什么样的  告诉我flag{md5(uid+2025)},提交半天都是错的

提交需要带flag吗?拼接时要带加号吗?
 楼主| Command 发表于 2025-2-13 10:13
本帖最后由 Command 于 2025-2-13 10:21 编辑
风子09 发表于 2025-2-13 10:09
安卓中级题在app上提交的flag的格式是什么样的  告诉我flag{md5(uid+2025)},提交半天都是错的

提交需要 ...

刚试了一下app上好像咋提交都不对; 但按照这个flag的格式往服务端上提交是没问题的
伤城幻化 发表于 2025-2-13 10:43
风子09 发表于 2025-2-13 10:09
安卓中级题在app上提交的flag的格式是什么样的  告诉我flag{md5(uid+2025)},提交半天都是错的

提交需要 ...

uid+2025 是字符串上的拼接
风子09 发表于 2025-2-13 10:48
app里面怎么提交都是错的,不知道为什么
LONG65041 发表于 2025-2-13 11:21
菜鸟围观一下!!!!!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-3-19 00:25

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表