Ganlv 发表于 2023-2-8 02:56

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

本帖最后由 Ganlv 于 2023-2-12 04:59 编辑

## Windows 初级

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



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

## Android 初级题 1

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



```javascript
"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 位。

```javascript
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(//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 写的编码程序

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

const FLAG_TEMPLATE: & = 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, &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, &uid_secret_table, k, k.wrapping_mul(32));
    }
    println!("flag_u32v = {:08x?}", flag_u32v);
}

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

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

### Android 中级

用 Golang 写的解码程序。

```go
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)
      }
      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, image2)
      }
      image3 = image3[:(len(image3) - int(image3))]

      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 这种东西。估计调试量也不小。

爱飞的猫 发表于 2023-2-8 06:47

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

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

[
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("");


努力画图 发表于 2023-2-8 05:35

用的很开心    感谢分享

nmkj888 发表于 2023-2-8 07:22

如果用维吉尼亚密码是不是就稍微会好点{:1_904:}

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(可能还需要让 IDA 识别为 ...

学到了,感谢教学

hetingting 发表于 2023-2-9 16:24

zdq001 发表于 2023-2-13 16:18

我就跟看天书一样{:1_937:},慢慢学,看看小白啥时候能学会。
页: [1] 2
查看完整版本: 【2023春节】解题领红包 Windows 和 Android 初级解题过程记录