吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3085|回复: 12
收起左侧

[CTF] 【2023春节】解题领红包 Windows 和 Android 初级解题过程记录

[复制链接]
Ganlv 发表于 2023-2-8 02:56
本帖最后由 Ganlv 于 2023-2-12 04:59 编辑

Windows 初级

拖到 ida 里,看着就像把输入值和一个常量的每个字节右移 2 位作比较,然后输出 Success。

Windows简单题.gif

Array.from(`98 01 00 00 B0 01 00 00  84 01 00 00 9C 01 00 00
EC 01 00 00 D4 00 00 00  C8 00 00 00 40 01 00 00
BC 01 00 00 28 01 00 00  A4 01 00 00 94 01 00 00
C8 00 00 00 C0 00 00 00  C8 00 00 00 CC 00 00 00
20 01 00 00 84 01 00 00  C0 01 00 00 C0 01 00 00
E4 01 00 00 38 01 00 00  94 01 00 00 DC 01 00 00
64 01 00 00 94 01 00 00  84 01 00 00 C8 01 00 00
F4 01 00 00 00 00 00 00  00 00 00 00 00 00 00 00`.matchAll(/.. .. .. ../g))
    .map(x => x[0])
    .map(x => x.split(' ').reverse().join(''))
    .map(x => parseInt(x, 16) >> 2)
    .map(x => String.fromCodePoint(x))
    .join('')

Android 初级题 1

拖到 jadx-gui 中,把那个字符串每个 ASCII 码减 2。

Android简单题1.gif

"hnci}|jwfclkczkppkcpmwckng\u007f".split('').map(x => x.codePointAt(0) - 2).map(x => String.fromCharCode(x)).join('')

Android 初级题 2

拖到 jadx-gui 中,uid + "Wuaipojie2023" 从后往前异或 5252...,然后 base64,然后 md5,然后恺撒密码挪动 5 位。

const script = document.createElement("script");
script.setAttribute("src", "https://unpkg.com/blueimp-md5/js/md5.min.js");
document.body.appendChild(script);
const uid = '765171';
`flag{${md5(btoa(`${uid}Wuaipojie2023`.split('').reverse().map((c, i) => String.fromCodePoint(c.codePointAt(0) ^ '52'.codePointAt(i % 2))).reverse().join(''))).replace(/[A-Za-z]/g, (c) => String.fromCodePoint((c.codePointAt(0) & 0xe0) + ((c.codePointAt(0) & 0x1f) + 5) % 26))}}`;

A 是 0b01000001,a 是 0b01100001,所以可以使用 0b11100000 (0xe0) 和 0b00011111 (0x1f) 进行位运算。

其他

Windows 中级和 Android 中级其实也做出来了,不调试过程太复杂了,不想写了。

Windows 中级

用 Rust 写的编码程序

fn new_uid_secret_table(k: u32) -> [u32; 4] {
    let mut result = [0u32; 4];
    for i in 0..4 {
        result[i] = (i as u32 + 1).wrapping_mul(k + 1);
    }
    result
}

const FLAG_TEMPLATE: &[u8] = b"flag{!!!!_HAPPY_NEW_YEAR_2023!!!!}";

fn main() {
    println!("Hello, world!");

    let flag = BASE_TABLE.iter().enumerate().map(|(i, x)| *x ^ ((i as u32 + 1) * 0x11111111)).collect::<Vec<u32>>();
    let flag_bytes = unsafe { &*slice_from_raw_parts(flag.as_ptr() as *const u16, BASE_TABLE.len() * 2) };
    println!("{}", String::from_utf16_lossy(flag_bytes));

    let k = generate_k(765171);
    let lparam = k.wrapping_mul(32);
    println!("k = 0x{:x} lparam = 0x{:x}", k, lparam);

    let uid_secret_table = new_uid_secret_table(k);
    println!("uid_secret_table = {:#x?}", uid_secret_table);

    let mut flag_u32v = Vec::from(unsafe { &*slice_from_raw_parts(FLAG_TEMPLATE.as_ptr() as *const u32, 8) });
    println!("flag_u32v = {:08x?}", flag_u32v);
    for i in (0..8).step_by(2) {
        let mask = encrypt_key_u32vec(&mut flag_u32v[i..], &uid_secret_table, k);
        println!("mask = {}", mask);
    }
    println!("flag_u32v = {:08x?}", flag_u32v);
    for i in (0..8).step_by(2) {
        println!("k.wrapping_mul(32) = {}", k.wrapping_mul(32));
        decrypt_key_u32vec(&mut flag_u32v[i..], &uid_secret_table, k, k.wrapping_mul(32));
    }
    println!("flag_u32v = {:08x?}", flag_u32v);
}

fn encrypt_key_u32vec(key_u32vec: &mut [u32], uid_secret_table: &[u32; 4], k: u32) -> u32 {
    let mut key0 = key_u32vec[0];
    let mut key1 = key_u32vec[1];
    let mut mask = 0u32;
    for _ in 0..32 {
        mask = mask.wrapping_add(k);
        key0 = key0.wrapping_add((uid_secret_table[1].wrapping_add(key1 >> 5)) ^ (mask.wrapping_add(key1)) ^ (uid_secret_table[0].wrapping_add(key1 << 4)));
        key1 = key1.wrapping_add((uid_secret_table[3].wrapping_add(key0 >> 5)) ^ (mask.wrapping_add(key0)) ^ (uid_secret_table[2].wrapping_add(key0 << 4)));
    }
    key_u32vec[0] = key0;
    key_u32vec[1] = key1;
    return mask;
}

fn decrypt_key_u32vec(key_u32vec: &mut [u32], uid_secret_table: &[u32; 4], k: u32, mut mask: u32) -> u32 {
    let mut key0 = key_u32vec[0];
    let mut key1 = key_u32vec[1];
    for i in 0..32 {
        key1 = key1.wrapping_sub((uid_secret_table[3].wrapping_add(key0 >> 5)) ^ (mask.wrapping_add(key0)) ^ (uid_secret_table[2].wrapping_add(key0 << 4)));
        key0 = key0.wrapping_sub((uid_secret_table[1].wrapping_add(key1 >> 5)) ^ (mask.wrapping_add(key1)) ^ (uid_secret_table[0].wrapping_add(key1 << 4)));
        mask = mask.wrapping_sub(k);
    }
    key_u32vec[0] = key0;
    key_u32vec[1] = key1;
    mask
}

Android 中级

用 Golang 写的解码程序。

package main

import (
        "bytes"
        "crypto/aes"
        "encoding/base64"
        "encoding/hex"
        "fmt"
        "io"
        "os"
)

func main() {
        image1, err := os.ReadFile("aes.png")
        if err != nil {
                panic(err)
                return
        }
        image2, err := base64.StdEncoding.DecodeString(string(image1))
        if err != nil {
                return
        }
        err = os.WriteFile("aes2.png", image2, 0644)
        if err != nil {
                panic(err)
                return
        }

        key1 := []byte("|wfkuqokj4548366")
        key2 := []byte{0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe, 0xfb, 0xfe}
        key := []byte{}
        for i, b := range key1 {
                key = append(key, b+key2[i])
        }
        fmt.Println(string(key))

        block, err := aes.NewCipher(key)
        if err != nil {
                panic(err)
                return
        }
        image3 := make([]byte, len(image2))
        for i := 0; i < len(image2); i += block.BlockSize() {
                block.Decrypt(image3[i:i+block.BlockSize()], image2[i:i+block.BlockSize()])
        }
        image3 = image3[:(len(image3) - int(image3[len(image3)-1]))]

        err = os.WriteFile("aes3.png", image3, 0644)
        if err != nil {
                panic(err)
                return
        }

        decoder := hex.NewDecoder(bytes.NewReader(image3))
        f, err := os.OpenFile("aes4.png", os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
        if err != nil {
                panic(err)
                return
        }
        _, err = io.Copy(f, decoder)
        if err != nil {
                panic(err)
                return
        }
}

关于 Android 调试的一点有趣的事

jadx-gui 和 ida 真的太牛逼了,Android 简单和中等题我根本都没安装过 apk,也没装 Android Studio,直接看 java 或者 so 代码就行。

Android 高级题我就不会了,ida 反编译出错,我自己也不会 arm64 指令集,指令都不认识。

想过利用 https://crates.io/crates/jni-sys 自己实现一下 JavaVM 的 RegisterNatives 接口,放在安卓手机上跑,JNI_OnLoad 确实可以正常调用,但是再往下就麻烦了,动不动就 Segment fault。感觉工作量太大了,就算了。而且就算找到了 RegisterNatives 函数的位置,ida 依然没法反编译,我依然看不懂指令。

这时我已经装了 ndk 和 platform-tools(这两个下载解压就行),但是仍然不想装 Android Studio。

后来看别人的题解才知道有 unidbg 这种东西。估计调试量也不小。

免费评分

参与人数 4威望 +1 吾爱币 +22 热心值 +4 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhangxiaoxiao + 1 谢谢@Thanks!
a1142382573 + 1 + 1 谢谢@Thanks!
xiaohanjss + 1 + 1 谢谢@Thanks!

查看全部评分

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

爱飞的猫 发表于 2023-2-8 06:47
本帖最后由 爱飞的猫 于 2023-2-8 06:49 编辑

可以让 IDA 直接导出 DWORD 数组
双击跳到 IDA View 界面,类型改为 DWORD[29](可能还需要让 IDA 识别为数组),按 Shift-E 导出选 initialized C variable

[JavaScript] 纯文本查看 复制代码
[
  408, 432, 388, 412, 492, 212, 200, 320, 444, 296, 420, 404, 200, 192, 200,
  204, 288, 388, 448, 448, 484, 312, 404, 476, 356, 404, 388, 456, 500,
]
  .map((x) => x >> 2)
  .map((x) => String.fromCharCode(x))
  .join("");


ida_export.png

免费评分

参与人数 3吾爱币 +3 热心值 +3 收起 理由
Ganlv + 1 + 1 谢谢@Thanks!
侃遍天下无二人 + 1 + 1 谢谢@Thanks!
Fanoteria + 1 + 1 谢谢@Thanks!

查看全部评分

努力画图 发表于 2023-2-8 05:35
nmkj888 发表于 2023-2-8 07:22
wushengli 发表于 2023-2-8 10:05
咱也学习下,谢谢分享!
xiaohanjss 发表于 2023-2-8 10:07
先收藏学习一下,可惜我都还没入门
jackyyue_cn 发表于 2023-2-8 10:49
非常简洁明了,学习了~
 楼主| Ganlv 发表于 2023-2-8 10:56
爱飞的猫 发表于 2023-2-8 06:47
可以让 IDA 直接导出 DWORD 数组
双击跳到 IDA View 界面,类型改为 DWORD[29](可能还需要让 IDA 识别为 ...

学到了,感谢教学
头像被屏蔽
hetingting 发表于 2023-2-9 16:24
提示: 作者被禁止或删除 内容自动屏蔽
zdq001 发表于 2023-2-13 16:18
我就跟看天书一样,慢慢学,看看小白啥时候能学会。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-15 12:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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