i春秋夏令营Reverse

前言:第一次参加,只写了前两个,最后一个赛后复现。写的尽量详细一点,希望大家都能看懂

snake

运行贪吃蛇游戏

进行查壳

解包一下

在解包的文件中找到与文件名相同的.pyc文件\snake\snake.exe_extracted\snack.pyc

老本的pyinstxtractor.py可能生成的不是.pyc文件,需要用010补一下文件头
55 0D 0D 0A 00 00 00 00 00 00 00 00 00 00 00 00,再将末尾改为.pyc就可以使用工具了

snake.pyc包含了游戏逻辑与加密算法,简单筛选一下保留加密算法部分(游戏逻辑显示大于9999分就可以直接获得flag,so游戏佬除外)

#魔改RC4
import random
import key

def initialize(key):
    key_length = len(key)
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % key_length]) % 256
        S[i] = S[j]
        S[j] = S[i]
    return S

def generate_key_stream(S, length):
    i = 0
    j = 0
    key_stream = []
    for _ in range(length):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i] = S[j]
        S[j] = S[i]
        key_stream.append(S[(S[i] + S[j]) % 256])
    return key_stream

def decrypt(data, key):
    S = initialize(key)
    key_stream = generate_key_stream(S, len(data))
    decrypted_data = None((lambda .0 = None: [ i ^ data[i] ^ key_stream[i] for i in .0 ])(range(len(data))))
    return decrypted_data

key_bytes = bytes((lambda .0: [ ord(char) for char in .0 ])(key.xor_key))
data = [101,97,39,125,218,172,205,3,235,195,72,125,89,130,103,213,120,227,193,67,174,71,162,248,244,12,238,92,160,203,185,155]
decrypted_data = decrypt(bytes(data), key_bytes)

在解包的PYZ-00.pyz_extracted中找一下key.pyc,有的pyinstxtractor.py得到的文件夹为空,可以换一个版本进行解决,反编译得到key

推荐几个反编译pyc的网站:

python反编译 - 在线工具 (tool.lu)

在线Python pyc文件编译与反编译

pyc反编译 - 工具匠

exp:

def rc4_decrypt(key, length):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    i = j = 0
    text = []
    for _ in range(length):
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        text.append(S[(S[i] + S[j]) % 256])
    return text

key = b'V3rY_v3Ry_Ez'
data = [101, 97, 39, 125, 218, 172, 205, 3, 235, 195, 72, 125, 89, 130, 103, 213, 120, 227, 193, 67, 174, 71, 162, 248, 244, 12, 238, 92, 160, 203, 185, 155]
text = rc4_decrypt(key, len(data))
flag = ''
for i in range(len(data)):
    flag += chr(data[i] ^ text[i] ^ i)
print(flag)

得到flag{KMLTz3lT_MePUDa7A_P5LpzCBT}

HardSignin

无法运行程序,直接查壳——upx,直接使用工具脱不了,应该是被魔改了,010查看一下

使用另外一个查壳工具发现改成了vmp,修改回来(共三处UPX1,UPX2,UPX!)

使用UPX Unpacker或者指令upx -d HardSignin.exe都可以

使用ida32打开发现没有main函数

对_main进行交叉引用Upo.text:loc_401029mov dword ptr [ebp-4], offset _main,发现四个TLS回调函数,但是存在基础花指令修改一下

第一处:(右键->Patching->change byte->E2改为90->快捷键C转为汇编代码)

第二处:(右键->Patching->change byte->9A改为90->快捷键C转为汇编代码)

第二处:(右键->Patching->change byte->C2改为90->快捷键C转为汇编代码)

TlsCallback_0,TlsCallback_1,TlsCallback_2后按快捷键P转换为函数

TlsCallback_0函数存在一点问题,字节异或0x66

由于是可以运算,我们直接写exp,shift+F2打开编辑窗口,编写脚本

from ida_bytes import *
addr = 0x00401890
for i in range(170):
    patch_byte(addr + i,get_wide_byte(addr + i)^0x66)

C+P转换得到主函数main:

分析encrypt函数:

其余密文的脚本(由于XTEA加密是四字节所以64//4):

from ida_bytes import *
from idaapi import *
addr = 0x00404000
enc = []
for i in range(64//4):
    enc.append(get_dword(addr + i * 4))
print(enc)
#[3036486489, 3653154923, 3598177203, 408905200, 1396350368, 645614189, 1318861428, 3625534240, 3046501746, 1445070236, 2433841867, 213678751, 3463276874, 699118653, 845347425, 3058494644]

两个加密的key都是随机生成的,交叉索引找到TLS1和TLS2

srand(0x114514u)是mz_base64的随机种子

srand(0x1919810u)是mz_RC4和mz_XTEA的随机数种子

  1. 解密mz_XTEA:
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>

void XTEA_decrypt(uint32_t* enc, uint32_t* key);

int main() {
    uint8_t RC4_key[16] = { 0 };
    char XTEA_key[16] = { 0 };
    uint32_t enc[] = { 3036486489, 3653154923, 3598177203, 408905200, 1396350368, 645614189, 1318861428, 3625534240, 3046501746, 1445070236, 2433841867, 213678751, 3463276874, 699118653, 845347425, 3058494644 };
    srand(0x1919810u);
    for (int i = 0; ; ++i){
        if (i >= 16)
            break;
        RC4_key[i] = rand() % 255;
        XTEA_key[i] = rand() % 255;
    }
    XTEA_decrypt(enc, (uint32_t*)XTEA_key);//指针强转
    //uint8_t* temp = (uint8_t*)enc;
    for (int i = 0; i < 16; i++) {
        //printf("%d, ", temp[i]);
        printf("%d, ", RC4_key[i]);
    }
    return 0;
}
void XTEA_decrypt(uint32_t* enc, uint32_t* XTEA_key) {
    uint32_t v7, v6, v5;
    for (int i = 0; i < 16; i += 2){
        v7 = enc[i];
        v6 = enc[i + 1];
        v5 = 0x9E3779B9 * 0x64;
        for (int j = 0; j < 0x64; ++j)
        {
            v6 -= (XTEA_key[(v5 >> 11) & 3] + v5) ^ (v7 + ((v7 >> 5) ^ (16 * v7)));
            v5 -= 0x9E3779B9;
            v7 -= (XTEA_key[v5 & 3] + v5) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
        }
        enc[i] = v7;
        enc[i + 1] = v6;
    }
}

得到:188, 237, 0, 123, 134, 244, 22, 147, 149, 249, 135, 220, 103, 168, 162, 127, 77, 226, 98, 159, 123, 52, 174, 233, 69, 3, 126, 53, 66, 208, 139, 112, 240, 251, 46, 199, 221, 233, 185, 115, 227, 204, 26, 117, 173, 220, 253, 20, 168, 200, 69, 22, 49, 110, 42, 8, 44, 15, 29, 159, 7, 186, 213, 239

  1. 解密mz_RC4
def rc4_decrypt(ciphertext, key):
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    i = j = 0
    plaintext = []
    for byte in ciphertext:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        plaintext.append(byte ^ k)

    return bytes(plaintext)
enc = [188, 237, 0, 123, 134, 244, 22, 147, 149, 249, 135, 220, 103, 168, 162, 127, 77, 226, 98, 159, 123, 52, 174, 233,
       69, 3, 126, 53, 66, 208, 139, 112, 240, 251, 46, 199, 221, 233, 185, 115, 227, 204, 26, 117, 173, 220, 253, 20,
       168, 200, 69, 22, 49, 110, 42, 8, 44, 15, 29, 159, 7, 186, 213, 239]
RC4_key = [118, 137, 51, 73, 25, 19, 195, 199, 173, 216, 228, 104, 252, 72, 4, 188]

decrypted_data = rc4_decrypt(enc, RC4_key)
print(decrypted_data)
#b'C+vFCnHRGPghbmyQMXvFMRNd7fNCG8jcU+jcbnjRJTj2GTCOGUvgtOS0CTge7fNs'
  1. 解密mz_base64

求变表

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

void swap(char* a, char* b) {
    char temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    char base64table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    int v6, v4;
    srand(0x114514u);
    for (int i = 0; i < 100; ++i) {
        v6 = rand() % 64;
        v4 = rand() % 64;
        swap(&base64table[v6], &base64table[v4]);
    }
    printf("%s\n", base64table);
    return 0;
}
//4yZRiNP8LoK/GSA5ElWkUjXtJCz7bMYcuFfpm6+hV0rxeHIdwv32QOTnqg1BDsa9

解密:

import base64
text1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
text2 = '4yZRiNP8LoK/GSA5ElWkUjXtJCz7bMYcuFfpm6+hV0rxeHIdwv32QOTnqg1BDsa9'
enc = 'C+vFCnHRGPghbmyQMXvFMRNd7fNCG8jcU+jcbnjRJTj2GTCOGUvgtOS0CTge7fNs'

decoded_bytes = base64.b64decode(enc.translate(str.maketrans(text2, text1)))

print(decoded_bytes.decode("utf-8"))

flag{C0ngr@tulat1on!Y0u_Re_suCces3fu1Ly_Signln!}

bedtea

运行代码,无壳直接用IDA64打开,一个C++程序

main函数还是比较复杂的

逻辑:flag->魔改TEA加密->二叉树倒叙->异或0x33->enc

难点在于密文和密钥都是隐藏的,需要进行动态调试,但是动调调试会出现不正确的密文,需要修改检测动态调试的值,在此之前看一下魔改的TEA是怎么样的吧!

本地动态调试就可以,在此下断点

发现RAX寄存器为1,修改一下

去sub_401E80()函数观察xmmword_408040(TEA_key)的变化,F9运行,两个地址来回断

记录一下:0x03050D08,0x15223759,0x90E9179262

密文地址:

写脚本提取一下(为了方便起见直接倒叙加异或):

from idaapi import *
import ida_bytes
enc = []
addr = 0x0000000000405080
for i in range(24):
    enc.append(ida_bytes.get_word(addr + i * 2))
enc.reverse()
dec = [x ^ 0x33 for x in enc]
print(dec)

[211, 109, 186, 193, 54, 101, 42, 137, 120, 145, 245, 49, 250, 190, 194, 49, 144, 12, 68, 67, 212, 174, 66, 69]

exp:(本来想写个自动化脚本,但是时间成本太高了,一共加密三大轮回,替换都比解决bug快,难受)

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

int main() {
    uint8_t enc[] = { 211, 109, 186, 193, 54, 101, 42, 137 };
                    //{ 120, 145, 245, 49, 250, 190, 194, 49 }
                    //{ 144, 12, 68, 67, 212, 174, 66, 69}
    uint32_t* denc = (uint32_t*)enc;
    uint32_t v15 = denc[0];
    uint32_t v13 = denc[1];
    uint32_t v14 = 0x9E3449B8 * 22;
    //key1[] = {0x03, 0x05, 0x0D, 0x08}
    //key2[] = {0x15, 0x22, 0x37, 0x59}
    //key3[] = {0x90, 0xE9, 0x179, 0x262};
    for (int i = 0; i < 22; i++) {
        v13 -= (v14 + v15) ^ (0x0D + (v15 >> 4)) ^ (0x08 + 32 * v15);
        v15 -= (v14 + v13) ^ (0x05 + (v13 >> 4)) ^ (0x03 + 32 * v13);
        v14 -= 0x9E3449B8;
    }
    denc[0] = v15;
    denc[1] = v13;
    printf("%s\n", enc);
    return 0;
}

得到:

flag{y0ux戸1??DC援BE烫烫嚈`榾

_reallyl烫烫?騡待?

1ke_te@}烫烫獞胇X鷞

手动拼接:flag{y0u_reallyl1ke_te@}(flag{y0ux_reallyl1ke_te@}不对一下就知道了,在提交答案时还设置了坑)

  • 27
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值