某校网安综合实践(三)-reserve

1.静态调试-baby

逆向入门题。

解题脚本

input_string = "welcome_to_2024hustncc!!!!"
str1 = [
    0x1, 0x8, 0x0F, 0x18, 0x1B, 0x5, 0x0C, 0x2C, 0x2B, 0x6, 
    0x2C, 0x6D, 0x51, 0x6D, 0x47, 0x1, 0x18, 0x3, 0x18, 0x0B, 
    0x3C, 0x11, 0x44, 0x57, 0x0F, 0x5C
]
# 执行逐位异或
result = []
for i in range(len(input_string)):
    result.append(chr(ord(input_string[i]) ^ str1[i]))
print(''.join(result))
# vmc{this_is_a_simple_rev.}

2.动态调试-game

逆向选修课原题,原本是用动态调试修改判断条件,绕过分数限制,直接打印flag。但还可以直接看打印flag的函数,重写成脚本即可获取flag。

解题脚本

def sub_403B93():
    v2 = [
        158, 149, 99, 155, 113, 157, 81, 156,
        109, 103, 97, 103, 110, 157, 150, 150,
        153, 103, 111, 92, 149, 109, 103, 105,
        147, 150, 124, 103, 105, 156, 133]
    for i in range(len(v2)):
        v1 = 0
        v1 ^= v2[i]
        v1 ^= 0x14
        v1 -= 20
        print(chr(v1), end='')  
if __name__ == "__main__":
    sub_403B93()
# vmc{Qu1te_a_funny_g4me_isnT_it}

3.迷宫逆向-maze

逆向选修课原题,题解参考 《maze程序分析》

解题脚本

from collections import deque
def find_shortest_path(s, start_index):
    # 定义四种可能的移动
    moves = {
        's': 13,  # 向前移动一步
        'd': 1,   # 向后移动一步
        'w': -13,  # 向前移动13步
        'a': -1   # 向后移动13步
    }
    # 队列保存 (当前索引, 当前路径)
    queue = deque([(start_index, '')])
    visited = set()  # 记录已访问的索引,防止重复访问
    while queue:
        index, path = queue.popleft()
        # 如果当前字符是 '#', 返回当前路径
        if s[index] == '#':
            return path
        # 遍历所有可能的移动
        for move, delta in moves.items():
            new_index = index + delta
            if 0 <= new_index < len(s) and s[new_index] != '*' and new_index not in visited:
                visited.add(new_index)
                queue.append((new_index, path + move))
    # 如果没有找到路径,返回空字符串
    return ""

# 定义输入字符串
s = "@@***********-***-**-*****--*****-*****-***#**-*****--**----******-*****-******-****--******---**-*******-*-----******-------*****************"
# 从第二个字符开始,即索引1
start_index = 1
# 查找最短路径
shortest_path = find_shortest_path(s, start_index)
# 打印最短路径
print("Shortest Path:", shortest_path)
# assssdsssddsdddwwdwwaaaw
# 最终flag是:vmc{assssdsssddsdddwwdwwaaaw}

4.c++语言特性-stl

逆向选修课原题,但是不会

5.加密算法-enc

这一题比较麻烦,需要分析多个函数,并写出其逆函数。我的做题过程是:

1. 进行静态分析,发现程序用sub_401CB1函数和char v5[176]数组对输入字符串Str进行加密,并把密文与Buf2比较。那么我们的思路就是写出sub_401CB1的逆函数,并输入char v5[176]Buf2进行解密。

2. 进行ida动态调试,定位 char v5[176] 在栈上的位置,用IDApython脚本输出内容,拿到 char v5[176] 数据。

3. 静态分析sub_401CB1函数,写出sub_401B47sub_4018C8sub_401975sub_40181E的逆函数。

– 由于sub_40181E直接进行异或操作,所以逆函数就是它本身 – 因为sub_401B47进行的是轮换操作,所以逆函数就是倒着轮换 – sub_4018C8函数进行查表代换,所以逆函数把值和索引调换重新建表,再查表就行 –sub_401975函数按照四个字符一组的顺序进行复杂的异或操作,这个我不知道怎么直接解开,只能枚举一个值,然后用代换关系推出其他三个值,再把这四个字符重新加密,如果与给定的密文相同,则成功解密

解题脚本

#include <stdio.h>
#include <cstring>
unsigned char byte_405000[] = {
    0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B,
    0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0,
    0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26,
    0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
    0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2,
    0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0,
    0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED,
    0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
    0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F,
    0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5,
    0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC,
    0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
    0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14,
    0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C,
    0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D,
    0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
    0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F,
    0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E,
    0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11,
    0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
    0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F,
    0xB0, 0x54, 0xBB, 0x16};
char reverse_map[256];
void generate_reverse_map(void) {
    for (int i = 0; i < 256; i++) {
        reverse_map[(unsigned char)byte_405000[i]] = (char)i;
    }
}
void sub_40181E(int a1, char *a2, char *a3) {
    for (int i = 0; i <= 3; ++i)
        for (int j = 0; j <= 3; ++j) a2[j + 4 * i] ^= a3[4 * (4 * a1 + i) + j];
    return;
}
void sub_4018C8_reverse(char *a1) {
    for (int i = 0; i <= 3; ++i) {
        for (int j = 0; j <= 3; ++j) {
            a1[4 * j + i] = reverse_map[(unsigned char)a1[4 * j + i]];
        }
    }
}
void sub_401B47_reverse(char *a1) {
    char v2;
    v2 = a1[13];
    a1[13] = a1[9];
    a1[9] = a1[5];
    a1[5] = a1[1];
    a1[1] = v2;
    v2 = a1[10];
    a1[10] = a1[2];
    a1[2] = v2;
    v2 = a1[14];
    a1[14] = a1[6];
    a1[6] = v2;
    v2 = a1[7];
    a1[7] = a1[11];
    a1[11] = a1[15];
    a1[15] = a1[3];
    a1[3] = v2;
}
unsigned char sub_401948(unsigned char a1) {
    return (2 * a1) ^ (27 * (unsigned int)(a1 >> 7));
}
unsigned char sub_401948_reverse(unsigned char a1) {
    if (a1 & 1)
        return ((a1 ^ 27) >> 1) + 128;
    return a1 >> 1;
}
void sub_401975_part(char *a1) {
    char v2 = a1[0];
    char v1 = a1[0] ^ a1[1] ^ a1[2] ^ a1[3];
    a1[0] ^= v1 ^ sub_401948(a1[0] ^ a1[1]);
    a1[1] ^= v1 ^ sub_401948(a1[1] ^ a1[2]);
    a1[2] ^= v1 ^ sub_401948(a1[2] ^ a1[3]);
    a1[3] ^= v1 ^ sub_401948(a1[3] ^ v2);
}
void sub_401975_reverse(char *a1) {
    char v1, tmp[5] = {0};
    unsigned char ans[5] = {0};
    for (int i = 0; i <= 3; ++i) {
        v1 = a1[4 * i] ^ a1[4 * i + 1] ^ a1[4 * i + 2] ^ a1[4 * i + 3];
        for (int j = 0; j < 256; ++j) {
            ans[0] = j;
            ans[1] = sub_401948_reverse(a1[4 * i] ^ v1 ^ ans[0]) ^ ans[0];
            ans[2] = sub_401948_reverse(a1[4 * i + 1] ^ v1 ^ ans[1]) ^ ans[1];
            ans[3] = sub_401948_reverse(a1[4 * i + 2] ^ v1 ^ ans[2]) ^ ans[2];
            memcpy(tmp, (char *)ans, 5);
            sub_401975_part(tmp);
            if (memcmp(tmp, a1 + 4 * i, 4) == 0) {
                memcpy(a1 + 4 * i, ans, 4);
                break;
            }
        }
    }
}
void sub_401C2A_revese(char *a1, char *a2) {
    generate_reverse_map();
    sub_40181E(10, a1, a2);
    sub_401B47_reverse(a1);
    sub_4018C8_reverse(a1);
    for (int i = 9; i >= 1; --i) {
        sub_40181E(i, a1, a2);
        sub_401975_reverse(a1);
        sub_401B47_reverse(a1);
        sub_4018C8_reverse(a1);
    }
    sub_40181E(0, a1, a2);
    return;
}
unsigned char v5[] = {
    0x27, 0x76, 0xC4, 0xEA, 0x40, 0xC1, 0xB6, 0xD2, 0x83, 0x8F, 0x3D, 0xDB,
    0xFA, 0xB7, 0x8F, 0xB1, 0x8F, 0x05, 0x0C, 0xC7, 0xCF, 0xC4, 0xBA, 0x15,
    0x4C, 0x4B, 0x87, 0xCE, 0xB6, 0xFC, 0x08, 0x7F, 0x3D, 0x35, 0xDE, 0x89,
    0xF2, 0xF1, 0x64, 0x9C, 0xBE, 0xBA, 0xE3, 0x52, 0x08, 0x46, 0xEB, 0x2D,
    0x63, 0xDC, 0x06, 0xB9, 0x91, 0x2D, 0x62, 0x25, 0x2F, 0x97, 0x81, 0x77,
    0x27, 0xD1, 0x6A, 0x5A, 0x55, 0xDE, 0xB8, 0x75, 0xC4, 0xF3, 0xDA, 0x50,
    0xEB, 0x64, 0x5B, 0x27, 0xCC, 0xB5, 0x31, 0x7D, 0x90, 0x19, 0x47, 0x3E,
    0x54, 0xEA, 0x9D, 0x6E, 0xBF, 0x8E, 0xC6, 0x49, 0x73, 0x3B, 0xF7, 0x34,
    0x52, 0x71, 0x5F, 0xB1, 0x06, 0x9B, 0xC2, 0xDF, 0xB9, 0x15, 0x04, 0x96,
    0xCA, 0x2E, 0xF3, 0xA2, 0x23, 0x7C, 0x65, 0xC5, 0x25, 0xE7, 0xA7, 0x1A,
    0x9C, 0xF2, 0xA3, 0x8C, 0x56, 0xDC, 0x50, 0x2E, 0x25, 0x2F, 0x54, 0x74,
    0x00, 0xC8, 0xF3, 0x6E, 0x9C, 0x3A, 0x50, 0xE2, 0xCA, 0xE6, 0x00, 0xCC,
    0xB0, 0x4C, 0x1F, 0x00, 0xB0, 0x84, 0xEC, 0x6E, 0x2C, 0xBE, 0xBC, 0x8C,
    0xE6, 0x58, 0xBC, 0x40, 0xEC, 0x29, 0x16, 0x8E, 0x5C, 0xAD, 0xFA, 0xE0,
    0x70, 0x13, 0x46, 0x6C, 0x96, 0x4B, 0xFA, 0x2C};
char Buf2[16] = {-6, -91, 6,  63, -3, -65, 37,  69,
                 29, -64, 94, 63, 94, -28, -71, -107};
int main() {
    sub_401C2A_revese(Buf2, (char *)v5);
    puts(Buf2);
    return 0;
}
// aa8e45738c8e5096
// 最终flag是:vmc{aa8e45738c8e5096}

6.飞翔的小鸟-bird

这一题感觉比前几题简单

解题脚本

def print_(a1):
    for char in a1:
        if ord(char) <= 96 or ord(char) > 122:
            print(char, end='')
        else:
            transformed_char = chr((ord(char) - 84) % 26 + 97)
            print(transformed_char, end='')
if __name__ == "__main__":
    flag = "izp{lbh_ner_fb_pyrire}"
    print_(flag)
# vmc{you_are_so_clever}

7.我的ida怎么坏了-asm-花指令

在分析这一题的反汇编时,会遇到下面问题:

  • 指令 jz short near ptr loc_4010AE+1 和 jnz short near ptr loc_4010AE+1 的跳转目标位置有点奇怪。
  • 在地址 loc_4010AE 处存在一条奇怪的汇编指令:jmp near ptr 33D45E40h,而 loc_4010AE 本身包含了一个字节,设置为 E8(即 jmp 指令)。
  • 此 jmp 指令使得反汇编工具在分析过程中将 0x4010AE 后的 4 个字节识别为地址,导致后续的汇编代码难以正常解析。

经过进一步分析,我们发现可以通过将 0x4010AE 字节修改为 nop,使得反汇编能够正常进行并恢复原有的指令流。

解题脚本

ida_chars = bytearray([
    0x3E, 0x0D, 0xED, 0xBE, 0x4A, 0x8A, 0x7D, 0xBC, 0x7C, 0xFC,
    0x2E, 0x2A, 0x79, 0x9D, 0x6A, 0x1A, 0xCC, 0x3D, 0x4A, 0xF8,
    0x3C, 0x79, 0x69, 0x39, 0xD9, 0xDD, 0x9D, 0xA9, 0x69, 0x4C,
    0x8C, 0xDD, 0x59, 0xE9, 0xD7
])
def swap_nibbles_and_subtract():
    # 交换每个字节的高 4 位和低 4 位
    for i in range(len(ida_chars)):
        ida_chars[i] = ((ida_chars[i] & 0x0F) << 4) | (
            (ida_chars[i] & 0xF0) >> 4)
    # 从倒数第二个字节开始,依次执行 ida_chars[i] -= ida_chars[i+1]
    for i in range(len(ida_chars) - 2, -1, -1):
        ida_chars[i] -= ida_chars[i + 1]
if __name__ == "__main__":
    swap_nibbles_and_subtract()
    for char in ida_chars:
        print(chr(char), end='')  # 将每个字节转换为字符输出
# vmc{p4tch_pr0gr4m_t0_d3c0mpi1e_it!}

8.朴实无华的vm-vm-虚拟机逆向

这一题较为繁琐,首先看懂vm指令集,再看懂要执行的指令,再写出逆函数进行还原。我的做题过程是: 1. 静态分析vm,发现经典的switch结构,总共12条指令,较为关键的是指令2指令10指令11指令2从输入数组dest中获取值,指令10进行判断并设置标志位、指令11查看标志位并进行跳转。

2. 分析ptr中虚拟机代码:

"03 00 00 00 00 00"     // mov $0, %%reg0
"03 00 00 00 00 01"     // mov $0, %%reg1
"03 00 00 00 00 02"     // mov $0, %%reg2
"03 47 00 00 00 03"     // mov $0x47, %%reg3
"03 25 5b 00 00 04"     // mov $0x5B25, %%reg4
"03 91 e5 25 01 05"     // mov $0x0125E591, %%reg5
"03 7f 00 00 00 06"     // mov $0x7F, %%reg6
"03 18 00 00 00 07"     // mov $0x18, %%reg7
check:
"02 02 00"              // mov (%%reg2), %%reg0
"09 02"                 // inc %%reg2
"01 03 01"              // mov %%reg3, %%reg1
"06 04 01"              // mul %%reg4, %%reg1
"04 05 01"              // add %%reg5, %%reg1
"08 06 01"              // mod %%reg6, %%reg1
"01 01 03"              // mov %%reg1, %%reg3
"0A 00 01"              // cmp %reg0, %reg1
"0B 0A"                 // jnz exit
"0A 02 07"              // cmp %reg2, %reg7
"0B de"                 // jnz check
"0C"                    // tag = 1
exit:
" 00 00";               // end
// 以伪代码形式表示
random = 0x47
for i in range(24):
    random = (random * 0x5B25 + 0x0125E591) % 0x7F
    assest(dest[i] == random)

解题脚本

dest = [
    0x42, 0x3F, 0x53, 0x24, 0x2C, 0x66, 0x5D, 0x5F, 0x7F, 0x0A,
    0x27, 0x39, 0x5F, 0x1D, 0x0B, 0x0F, 0x2E, 0x00, 0x6B, 0x2B,
    0x5B, 0x19, 0x5C, 0x41
]
output = []
random = 0x47
for i in range(24):
    random = (random * 0x5B25 + 0x0125E591) % 0x7F
    char = chr(random ^ dest[i])
    output.append(char)

print("".join(output))
# vmc{It_is_an_amazing_vm}
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇