babyASM

txt文件

.intel_syntax noprefix
    .globl  data
    .data
data:
    .byte   20
    .byte   92
    .byte   43
    .byte   69
    .byte   81
    .byte   73
    .byte   95
    .byte   23
    .byte   72
    .byte   22
    .byte   24
    .byte   69
    .byte   25
    .byte   27
    .byte   22
    .byte   17
    .byte   23
    .byte   29
    .byte   24
    .byte   73
    .byte   17
    .byte   24
    .byte   85
    .byte   27
    .byte   112
    .byte   76
    .byte   15
    .byte   92
    .byte   24
    .byte   1
    .byte   73
    .byte   84
    .byte   13
    .byte   81
    .byte   12
    .byte   0
    .byte   84
    .byte   73
    .byte   82
    .byte   8
    .byte   82
    .byte   81
    .byte   76
    .byte   125

    .text
    .globl  printFLAG
printFLAG:
    sub esp, 28
    mov eax, DWORD PTR [esp+32]
    mov DWORD PTR [esp], eax
    call    printf
    nop
    add esp, 28
    ret

    .globl  main
main:
    push ebp
    mov ebp, esp
    and esp, -16
    sub esp, 32
    call    __x86.get_pc_thunk.ax
    mov DWORD PTR [esp+28], 0
    jmp L3
L4:
    mov eax, DWORD PTR [esp+28]
    add eax, OFFSET FLAT:data
    movzx eax, BYTE PTR [eax]
    add eax, 28
    mov edx, DWORD PTR [esp+28]
    add edx, OFFSET FLAT:data
    mov BYTE PTR [edx], al
    add DWORD PTR [esp+28], 1
L3:
    cmp DWORD PTR [esp+28], 21
    jle L4
L5:
    mov eax, DWORD PTR [esp+28]
    add eax, OFFSET FLAT:data
    movzx edx, BYTE PTR [eax]
    mov eax, DWORD PTR [esp+28]
    sub eax, 22
    movzx eax, BYTE PTR data[eax]
    xor edx, eax
    mov eax, DWORD PTR [esp+28]
    add eax, OFFSET FLAT:data
    mov BYTE PTR [eax], dl
    add DWORD PTR [esp+28], 1
    cmp DWORD PTR [esp+28], 42
    jle L5
    mov DWORD PTR [esp], OFFSET FLAT:data
    call    printFLAG
    mov eax, 0
    leave
    ret

纯汇编代码,前面数据部分是一个数组,后面代码部分写了一个简单加密

前22位加上28,后20位异或前22位

直接写脚本

data = [
    20, 92, 43, 69, 81, 73, 95, 23, 72, 22,
    24, 69, 25, 27, 22, 17, 23, 29, 24, 73,
    17, 24, 85, 27, 112, 76, 15, 92, 24, 1,
    73, 84, 13, 81, 12, 0, 84, 73, 82, 8,
    82, 81, 76, 125
]
for i in range(22):
    data[i] += 28
for i in range(22, 43):
    data[i] ^= data[i - 22]
print(''.join(chr(b) for b in data))

LittlePuzzle

看题目名字就能猜到是考数独

jar文件,使用jadx反编译

package p000;

import java.util.Scanner;

/* loaded from: Puzzle.jar:Puzzle.class */
public class Puzzle {
    static int[][] board = {new int[]{5, 7, 0, 9, 4, 0, 8, 0, 0}, new int[]{0, 0, 8, 0, 3, 0, 0, 0, 5}, new int[]{0, 1, 0, 2, 0, 0, 0, 3, 7}, new int[]{0, 0, 9, 7, 2, 0, 0, 0, 0}, new int[]{7, 3, 4, 0, 0, 8, 0, 0, 0}, new int[]{0, 0, 0, 0, 0, 0, 7, 5, 1}, new int[]{3, 0, 0, 0, 1, 4, 2, 0, 0}, new int[]{0, 6, 0, 0, 0, 2, 0, 4, 0}, new int[]{0, 2, 7, 0, 0, 9, 5, 0, 0}};

    public static void exit() {
        System.out.println("解谜失败");
        System.exit(1);
    }

    public static boolean check(int x, int y) {
        int i = board[x][y];
        int i2 = x - (x % 3);
        int i3 = y - (y % 3);
        for (int i4 = 0; i4 < 9; i4++) {
            if (i4 != x && i4 != y && (i == board[x][i4] || i == board[i4][y])) {
                return false;
            }
        }
        for (int i5 = 0; i5 < 3; i5++) {
            for (int i6 = 0; i6 < 3; i6++) {
                if (i2 + i5 != x && i3 + i6 != y && i == board[i2 + i5][i3 + i6]) {
                    return false;
                }
            }
        }
        return true;
    }

    static String flag(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.length(); i += 6) {
            sb.append(Integer.toHexString(Integer.parseInt(str.substring(i, i + 6))));
        }
        return sb.toString();
    }

    public static void main(String[] strArr) {
        System.out.println("请输入你的解谜结果:");
        Scanner scanner = new Scanner(System.in);
        String flag = scanner.nextLine();
        scanner.close();
        if (flag.length() != 48) {
            exit();
        }
        int i = 0;
        for (int i2 = 0; i2 < 9; i2++) {
            for (int i3 = 0; i3 < 9; i3++) {
                if (board[i2][i3] == 0) {
                    int charAt = flag.charAt(i) - '0';
                    if (charAt > 0 && charAt <= 9) {
                        board[i2][i3] = charAt;
                        i++;
                    } else {
                        exit();
                    }
                }
            }
        }
        for (int i4 = 0; i4 < 9; i4++) {
            for (int i5 = 0; i5 < 9; i5++) {
                if (!check(i4, i5)) {
                    exit();
                }
            }
        }
        System.out.println(String.format("0xGame{%s}", flag(flag)));
    }
}

简单分析了一下代码,这段代码是一个简单的数独解谜程序。它使用一个 9x9 的整数数组 board 来表示数独的棋盘。程序首先要求用户输入一个解谜结果,并检查输入的长度是否为 48。如果输入不符合要求,程序会调用 exit() 方法输出“解谜失败”并终止。

程序会遍历棋盘,将用户输入的数字填入原本为 0 的位置。然后,使用 check() 方法来验证每个数字是否符合数独的规则(即每行、每列和每个 3x3 方块中的数字不能重复)。如果发现任何不符合规则的情况,程序再次调用 exit()。

脚本解数独,得到解后的board

board = [
    [5, 7, 0, 9, 4, 0, 8, 0, 0],
    [0, 0, 8, 0, 3, 0, 0, 0, 5],
    [0, 1, 0, 2, 0, 0, 0, 3, 7],
    [0, 0, 9, 7, 2, 0, 0, 0, 0],
    [7, 3, 4, 0, 0, 8, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 7, 5, 1],
    [3, 0, 0, 0, 1, 4, 2, 0, 0],
    [0, 6, 0, 0, 0, 2, 0, 4, 0],
    [0, 2, 7, 0, 0, 9, 5, 0, 0]
]

def is_valid(row, col, num):
    # 检查行
    for i in range(9):
        if board[row][i] == num:
            return False
    # 检查列
    for i in range(9):
        if board[i][col] == num:
            return False
    # 检查3x3方块
    start_row, start_col = row - row % 3, col - col % 3
    for i in range(3):
        for j in range(3):
            if board[i + start_row][j + start_col] == num:
                return False
    return True

def solve_sudoku():
    for row in range(9):
        for col in range(9):
            if board[row][col] == 0:  # 找到一个空位置
                for num in range(1, 10):  # 填入数字1-9
                    if is_valid(row, col, num):
                        board[row][col] = num  # 填入数字
                        if solve_sudoku():  # 递归调用
                            return True
                        board[row][col] = 0  # 撤销
                return False  # 如果没有找到合适的数字,返回False
    return True  # 如果所有位置都填完,返回True


# 解数独
if solve_sudoku():
    print(board)
else:
    print("no")

输出0被替换的值,即flag

board1 = [
    [5, 7, 0, 9, 4, 0, 8, 0, 0],
    [0, 0, 8, 0, 3, 0, 0, 0, 5],
    [0, 1, 0, 2, 0, 0, 0, 3, 7],
    [0, 0, 9, 7, 2, 0, 0, 0, 0],
    [7, 3, 4, 0, 0, 8, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 7, 5, 1],
    [3, 0, 0, 0, 1, 4, 2, 0, 0],
    [0, 6, 0, 0, 0, 2, 0, 4, 0],
    [0, 2, 7, 0, 0, 9, 5, 0, 0]
]
board2 = [
    [5, 7, 3, 9, 4, 1, 8, 6, 2],
    [2, 4, 8, 6, 3, 7, 1, 9, 5],
    [9, 1, 6, 2, 8, 5, 4, 3, 7],
    [1, 5, 9, 7, 2, 6, 3, 8, 4],
    [7, 3, 4, 1, 5, 8, 6, 2, 9],
    [6, 8, 2, 4, 9, 3, 7, 5, 1],
    [3, 9, 5, 8, 1, 4, 2, 7, 6],
    [8, 6, 1, 5, 7, 2, 9, 4, 3],
    [4, 2, 7, 3, 6, 9, 5, 1, 8]
]
for i in range(len(board2)):
    for k in range(9):
        if board1[i][k] != board2[i][k]:
            print(board2[i][k], end='')

Tea

无壳,ida反编译进入主函数

分析:

__int64 sub_140014E70()
{
  char *v0; // rdi
  __int64 i; // rcx
  __int64 v2; // rax
  __int64 v3; // rax
  __int64 v4; // rax
  char v6[32]; // [rsp+0h] [rbp-20h] BYREF
  char v7; // [rsp+20h] [rbp+0h] BYREF
  _DWORD Buf1[21]; // [rsp+30h] [rbp+10h] BYREF
  char v9; // [rsp+84h] [rbp+64h]
  int j; // [rsp+A4h] [rbp+84h]
  char *v12; // [rsp+198h] [rbp+178h]

  v0 = &v7;
  for ( i = 54i64; i; --i )
  {
    *(_DWORD *)v0 = -858993460;
    v0 += 4;
  }
  sub_140011514(&unk_140028068);
  j_memset(Buf1, 0, 0x40ui64);
  v2 = sub_1400110AA(std::cout, aEnterFlag);
  std::ostream::operator<<(v2, sub_140011046);
  sub_140011550(std::cin, (__int64)Buf1);       // 输入flag
  v9 = sub_140011532(Buf1, 40i64);
  sub_1400113B6(&unk_140022000, 40i64);         // 比较对象加密部分
  if ( v9 == 1 )
  {
    for ( j = 0; j < 5; ++j )
      sub_140011429(&Buf1[8 * j], &unk_140022030);// 标准tea加密,后面是key
    if ( j_memcmp(Buf1, &unk_140022000, 0x28ui64) )// 加密后的flag
      v12 = aPleaseTryAgain;
    else
      v12 = aCorrectFlagNow;
    v3 = sub_1400110AA(std::cout, v12);
    std::ostream::operator<<(v3, sub_140011046);
  }
  else
  {
    v4 = sub_1400110AA(std::cout, aLengthError);
    std::ostream::operator<<(v4, sub_140011046);
  }
  sub_140011474(v6, &unk_14001EE30);
  return 0i64;
}

key可以直接看到,为[2,0,2,4]

在第28行下断点,动调得到比较对象加密后的值

0x85336DD3, 0x2A7A7C3B, 0x64306238, 0x36396434, 0x62336364, 0x38376533, 0x37323664, 0x33363463, 0xF8EE8EA2, 0xC9B65CCE

写tea解密脚本

#include<stdio.h>
#include<stdint.h>
#include<string.h>

void encrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
    uint32_t delta = 0x9e3779b9;
    for (i = 0; i < 32; i++) {
        sum += delta;
        v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
    }
    v[0] = v0; v[1] = v1;
}

void decrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = 0x9e3779b9;
    uint32_t sum = delta * 32;
    for (i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
    v[0] = v0; v[1] = v1;
}

int main() {
    uint32_t key[4] = { 2,0,2,4 };
    uint32_t v[10] = { 0x85336DD3, 0x2A7A7C3B, 0x64306238, 0x36396434, 0x62336364, 0x38376533, 0x37323664, 0x33363463, 0xF8EE8EA2, 0xC9B65CCE };
    uint32_t temp[2]; //定义存放解密后的值

    for (int i = 0; i < 10; i += 2)
    {
        temp[0] = v[i];
        temp[1] = v[i + 1];
        decrypt(temp, key);
        for (int j = 0; j < 2; j++)
        {
            printf("%c", temp[j] & 0xff);
            printf("%c", (temp[j] >> 8) & 0xff);
            printf("%c", (temp[j] >> 16) & 0xff);
            printf("%c", (temp[j] >> 24) & 0xff);
        }
    }
    return 0;
}

然而结果只有头和尾8字节正确,中间部分全错误

重新动调,在28和32行下断点,发现中间部分根本没有加密,只有前8位和后8位加密了,中间24位不变

加密前:

加密后:

手动拼一下flag
0xGame{e8b0d4d96dc3b3e78d627c463c9953a1}