LOADING

加载过慢请开启缓存 浏览器默认开启

2024春秋杯Re-wp

2025/1/17 Wp Wp

day1

eare

custom_md5_init(seed);会检测断点,我们可以在输入和比较的数据下硬件断点,用写内存的方法把密文写回内存就可以在s1中看到flag。\n

密文:5C 76 4A 78 15 62 05 7C 6B 21 40 66 5B 1A 48 7A\n

1E 46 7F 28 02 75 68 2A 34 0C 4B 1D 3D 2E 6B 7A

17 45 07 75 47 27 39 78 61 0B

image

ko0h

前面的flag是假的,修一下jzjnz的花指令,可以看到主逻辑

image (1)

魔改RC4

image (2)

密文在sub_402A70()里,”DDDDAAAASSSS”

脚本如下

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

//s 表的长度取 256
#define size 256

unsigned char sbox[257] = { 0 };

// 初始化 s 表
void init_sbox(unsigned char* key) {
    unsigned int i, j, k;
    int tmp;

    for (i = 0; i < size; i++) {
        sbox[i] = i;
    }

    j =k = 0;
    for (i = 0; i < size; i++) {
        tmp = sbox[i];
        j = (j + tmp + key[i % strlen((char*)key)]) % size;  //注意此处的长度,自己计算一下密钥和数据的长度多次尝试
        sbox[i] = sbox[j];
        sbox[j] = tmp;
        /*if (++k >= 12)
            k = 0;*/
    }
}

// 加解密函数
void enc_dec(unsigned char* key, unsigned char* data) {
    int i, j, k, R, tmp;

    init_sbox(key);

    j = k = 0;
    for (i = 0; i < strlen((char*)data); i++) {
        j = (j + 1) % size;
        k = (k + sbox[j]) % size;

        tmp = sbox[j];
        sbox[j] = sbox[k];
        sbox[k] = tmp;

        R = sbox[(sbox[j] + sbox[k]) % size];

        data[i] += R;
    }
}

int main() {
    unsigned char key[100] = "DDDDAAAASSSS";
    unsigned char data[100] = { 24,-100,71,61,59,-31,41,39,-97,52,-125,-43,-19,-75,110,89,127,-34,71,-41,101,63,122,51,91,100,-74,-6,-108,85,-121,66,32,6,12,105,-2,114,-87,-28,-47,124 };
    enc_dec(key, data);
    printf("dec: %s\n", data);
    return 0;
}

day3

oooooore

全是花指令,大概分为两种,jzjnz和callret,用脚本去花(脚本1),去除花指令后可以看到main中的加密逻辑,看字符串表可以看到一个rc4密钥,还可以定位到输入的位置。

image-20250120001155602

mecpcy那里是密文,s是我们的输入。往下看我们的输入被传入了两个函数中,这应该就是加密函数了。第一个进去就是我们在字符串表看到的那个RC4。

image-20250120002002954

在第二个函数中我们可以在流程图中看到一些常数,按r可以看到他们的字符串”expand 32-byte k”,这个明显就是salsa20或者chacha20加密了,和RC4差不多本质就是一个异或,可以用写内存的方法解决。(其实这些找不到也没什么大问题,下面的动调可以解决一切)

image-20250120002445728

回到main函数,我们在输入的数据下硬件断点,输入一个正确长度的值进行动调,查看我们值的变化。这里我输入flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa},继续运行,同时看我们输入数据的变化。

第一次改变在这里,有一个异或,发现数据来源于byte ptr [rcx+rdx]和[rbp-70h],我们在00005555555593BF下断点就可以看到byte ptr [rcx+rdx]是我们的输入,[rbp-70h]是一个地址,指向的地址里面只有一个数据,我们给这个数据下硬件断点继续溯源(其实可以不用再溯源了,这里是为了深入了解)image-20250120004147145

这次断在这个位置,上面的[rcx]是我们追踪的值,这个值来自byte ptr [rcx+rdx],不过好像被覆盖了,我们下断点查看。里面就是一串表(有点像main函数里面第二个函数上面那些数据),下面还有RC4的密钥,但是不确定是哪个加密函数。取消全部的断点,只保留我们输入数据的硬件断点,继续运行。

image-20250120004807245

最后变化在这个位置,往前看看就能看到只有一个异或,还有一个RC4的盒,那么前面那个就是chacha20或者salsa20。(我们只需要知道加密只有两个异或就行)

image-20250120010127963

再进行一次动调,这次我们把我们把密文作为输入写内存,并在最后一个数据下硬件断点。运行到最后一步加密,再次查看就可以看到flag

81 ED 7E 2F 93 B6 6F 8D 43 E5 C9 11 A9 F4 2B DB 
AD CB 45 66 FA DF A9 61 28 65 31 D7 80 D5 18 FE 
25 6E 94 05 83 51 B5 42 D2 9D

image-20250120000848703

脚本1(去花)

import idautils
import idc
import idaapi

def nop_jzjnz(start_addr,endaddr,pattern):
    length =len(pattern)
    flag = 0
    while start_addr < endaddr:
        getbytes =idc.get_bytes(start_addr,length) #获取指定范围内指定地址的字节码并返回成列表
        if(getbytes[0]==pattern[0] and getbytes[3]==pattern[3] and getbytes[6] == pattern[6] ):  #匹配花指令模板
            flag = 1
            for i in range(length):        
                idc.patch_byte(start_addr + i,0x90)  #nop
            print(f"success! nop 0x{hex(start_addr)} to 0x{hex(start_addr + length -1)} length:{length}")
            start_addr += length - 1
        start_addr += 1
    if(flag == 0):
        print("Not find!!!")
        
def nop_range(start_addr,end_addr):
    while(start_addr <= end_addr):
        idc.patch_byte(start_addr,0x90)
        start_addr += 1
    print(f"success! nop 0x{start_addr} to 0x{end_addr}")

def remount():
    try:
        addr = idc.get_screen_ea()
        func = idaapi.get_func(addr)
        func_start = func.start_ea
        idaapi.del_func(func_start)
        idaapi.add_func(func_start)
        print(f"success!! remount the func in 0X{func}")
    except Exception as a:
        print("some errors occur, remount falrue")
        print(f"error : {a}")

start_addr = 0x0001000 
end_addr = 0x07378   

pattern_callret = [0xE8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x83, 0x04, 0x24, 0x08, 0xC3, 0xE8, 0x90, 0x90, 0x90]
pattern_jzjnz3 = [0x0F, 0x85, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x84, 0x01, 0x00, 0x00, 0x00, 0x21]

nop_jzjnz(start_addr,end_addr,pattern_jzjnz3)
nop_jzjnz(start_addr,end_addr,pattern_callret)

easyasm

sub_102C0

seg002:0000 sub_102C0       proc near               ; CODE XREF: start+F↓p
seg002:0000                 push    ax
seg002:0001                 push    bx
seg002:0002                 push    cx
seg002:0003                 push    si
seg002:0004
seg002:0004 loc_102C4:                              ; CODE XREF: sub_102C0+29↓j
seg002:0004                 mov     bx, 1      ;设置标志默认为1,用于判断是否产生交换
seg002:0007                 mov     cx, ds:0   ;设置循环次数为cx为一个地址ds:0,loop默认cx统计次数
seg002:000B                 dec     cx			;cx减一
seg002:000C                 lea     si, ds:2   ;加载ds:2的地址到si
seg002:0010
seg002:0010 loc_102D0:                              ; CODE XREF: sub_102C0+22↓j
seg002:0010                 mov     ax, [si]    
seg002:0012                 cmp     ax, [si+2]  	;比较[si]和[si+2],即2字节组合比较
seg002:0015                 jbe     short loc_102DF  ;若[si]<[si+2],跳转
seg002:0017                 xchg    ax, [si+2]  ;交换
seg002:001A                 mov     [si], ax
seg002:001C                 mov     bx, 0 ;设置bx为0记录产生交换
seg002:001F
seg002:001F loc_102DF:                              ; CODE XREF: sub_102C0+15↑j
seg002:001F                 add     si, 2
seg002:0022                 loop    loc_102D0  ;cx != 0时跳转,遍历数据
seg002:0024                 cmp     bx, 1    ;bx为1表示未产生交换,排序完毕
seg002:0027                 jz      short loc_102EB ;排序完毕
seg002:0029                 jmp     short loc_102C4 ;继续排序
seg002:002B ; ---------------------------------------------------------------------------
seg002:002B
seg002:002B loc_102EB:                              ; CODE XREF: sub_102C0+27↑j
seg002:002B                 pop     si
seg002:002C                 pop     cx
seg002:002D                 pop     bx
seg002:002E                 pop     ax
seg002:002F                 retn
seg002:002F sub_102C0       endp
seg002:002F

start

seg002:004C                 public start
seg002:004C start           proc near
seg002:004C                 mov     ax, seg seg000
seg002:004F                 mov     ss, ax
seg002:0051                 mov     sp, 200h
seg002:0054                 mov     ax, seg seg001
seg002:0057                 mov     ds, ax
seg002:0059                 assume ds:seg001
seg002:0059                 mov     es, ax
seg002:005B                 assume es:seg001
seg002:005B                 call    sub_102C0 ;调用排序
seg002:005E                 mov     dx, 0B2h ;输入存储位置,前两个字节存储数据的属性,所以数据从B4h开始
seg002:0061                 mov     ah, 0Ah
seg002:0063                 int     21h      ;阻断程序,用于输入       ; DOS - BUFFERED KEYBOARD INPUT
seg002:0063                                         ; DS:DX -> buffer
seg002:0065                 mov     bl, byte ptr aWelcomeToChunq+5Dh ; ""
seg002:0069                 mov     byte ptr [bx+0B4h], 24h ; '$'
seg002:006E                 mov     cx, word ptr byte_10200  ;21
seg002:0072                 add     cx, cx  ;42
seg002:0074                 mov     bx, 0   ;偏移量
seg002:0077
seg002:0077 loc_10337:                              ; CODE XREF: start+3C↓j
seg002:0077                 mov     al, [bx+0B4h] ;输入的数据
seg002:007B                 mov     ah, [bx+2]  ;ds:(bx+2) 指向的数据(这里是单字节)
seg002:007F                 xor     al, ah  ;两者异或
seg002:0081                 mov     [bx+0B4h], al ;修改输入数据
seg002:0085                 add     bx, 1  ;计数器(偏移量)加一
seg002:0088                 loop    loc_10337 ;cx 不为0时继续异或,cx会被减一
seg002:008A                 lea     di, word_102B4  ;加载输入
seg002:008E                 lea     si, byte_1022C  ;加载密文
seg002:0092                 call    sub_102F0  ;比较函数
seg002:0095                 jnz     short loc_1035A  ;根据返回判断结果,错误
seg002:0097                 jmp     short loc_10364  ;正确
seg002:0097 ; ---------------------------------------------------------------------------
seg002:0099                 align 2
seg002:009A
seg002:009A loc_1035A:                              ; CODE XREF: start+49↑j
seg002:009A                 mov     dx, 71h ; 'q'
seg002:009D                 mov     ah, 9
seg002:009F                 int     21h             ; DOS - PRINT STRING
seg002:009F                                         ; DS:DX -> string terminated by "$"
seg002:00A1                 jmp     short loc_1036E
seg002:00A1 ; ---------------------------------------------------------------------------
seg002:00A3                 align 2
seg002:00A4
seg002:00A4 loc_10364:                              ; CODE XREF: start+4B↑j
seg002:00A4                 mov     dx, 9Bh
seg002:00A7                 mov     ah, 9
seg002:00A9                 int     21h             ; DOS - PRINT STRING
seg002:00A9                                         ; DS:DX -> string terminated by "$"
seg002:00AB                 jmp     short loc_1036E
seg002:00AB ; ---------------------------------------------------------------------------
seg002:00AD                 align 2
seg002:00AE
seg002:00AE loc_1036E:                              ; CODE XREF: start+55↑j
seg002:00AE                                         ; start+5F↑j
seg002:00AE                 mov     dx, 57h ; 'W'
seg002:00B1                 mov     ah, 9
seg002:00B3                 int     21h             ; DOS - PRINT STRING
seg002:00B3                                         ; DS:DX -> string terminated by "$"
seg002:00B5                 mov     ax, 4C00h
seg002:00B8                 int     21h             ; DOS - 2+ - QUIT WITH EXIT CODE (EXIT)
seg002:00B8 start           endp                    ; AL = exit code
seg002:00B8
seg002:00B8 seg002          ends
seg002:00B8
seg002:00B8
seg002:00B8                 end start

解密脚本

data = [
    0x2030, 0x3040, 0x4050, 0x1022, 0x2011, 0x1666, 0x1522, 0x8899,
    0x4155, 0x4044, 0x4288, 0x3321, 0x6033, 0xFFFF, 0x2221, 0x3366,
    0x222C, 0x2CCC, 0x22CC, 0xCC22, 0xC2C2
]
box = [
    0x44, 0x7C, 0x43, 0x72, 0x1D, 0x72, 0x74, 0x41, 0x05, 0x14, 0x19, 0x1A,
    0x19, 0x0F, 0xF5, 0x10, 0xAE, 0x18, 0x6D, 0x01, 0x10, 0x56, 0x00, 0x1E,
    0x26, 0x71, 0x65, 0x73, 0x78, 0x72, 0xEB, 0x72, 0x52, 0x06, 0xAA, 0xBB,
    0xA3, 0xA4, 0x1B, 0xFC, 0xC7, 0x82
]
for i in range(len(data)):
    for j in range(len(data) - i -1):
        if data[j] > data[j+1]:
            tmp = data[j]
            data[j] = data[j+1]
            data[j+1] = tmp

# for i in data:
#      print(f"{hex(i)},",end='')

f = b""

for x in data:
    b = x.to_bytes(2,"little")  #小端序拆分成单字节
    f += b

#print(f.hex())
hex_str = f.hex()

# 将十六进制字符串按每8字节(即16个字符)分块,转为列表
chunk_size = 2
chunks = [hex_str[i:i+chunk_size] for i in range(0, len(hex_str), chunk_size)]
# 将每个块转换为十进制并存入列表
decimal_list = [int(chunk, 16) for chunk in chunks]
result =list(i*0 for i in range(42))

# 打印结果
for d,b,i in zip(decimal_list,box,range(42)):
    result[i] = d^b

for i in result:
    print(chr(i),end='')
#flag{dea54885-92b4-11ef-b153-3c0af33af908}