NewStarCTF2023-Reverse-Week2-WP

AndroDbgMe

使用JEB打开(jadx打开有乱码),主要逻辑如下:

应用会检测是不是调试环境,如果是调试环境就会输出一个固定的值s1、s2是固定的,执行RC4加密。不是调试就会用input作为RC4的参数加密。在手机上安装使用发现直接input输出会乱码,结合题目名猜测考点是Android的动调。因此直接动调就可以出结果,静态分析在网上找个RC4解密脚本也可以。

使用JEB动调

手机上点一下按钮就有flag了

 AndroGenshin

使用gadx-gui打开找到资源文件下的AndroidManifest.xml 找到MainActivity  如下

 有两个加密函数根据名字判断是魔改的RC4和魔改的Base64

RC4是对称加密所有直接用现有的码就行了

 


public class it_is_not_RC4 {
    public static String rc4(String keyStr, int[] data) {
        byte[] key = keyStr.getBytes();
        int[] s = new int[256];
        int[] k = new int[256];
        int j = 0;
        for (int i = 0; i < 256; i++) {
            s[i] = i;
            k[i] = key[i % key.length];
        }
        for (int i2 = 0; i2 < 256; i2++) {
            j = (s[i2] + j + k[i2]) & 255;
            int temp = s[i2];
            s[i2] = s[j];
            s[j] = temp;
        }
        StringBuilder result = new StringBuilder();
        int j2 = 0;
        int i3 = 0;
        for (int i4 : data) {
            i3 = (i3 + 1) & 255;
            j2 = (s[i3] + j2) & 255;
            int temp2 = s[i3];
            s[i3] = s[j2];
            s[j2] = temp2;
            int rnd = s[(s[i3] + s[j2]) & 255];
            result.append((char) (i4 ^ rnd));
        }
        return result.toString();
    }
    public static void main(String[] args) {
        String username = "genshinimpact";
        int[] base64_table = {125, 239, 101, 151, 77, 163, 163, 110, 58, 230, 186, 206, 84, 84, 189, 193, 30, 63, 104, 178, 130, 211, 164, 94, 75, 16, 32, 33, 193, 160, 120, 47, 30, 127, 157, 66, 163, 181, 177, 47, 0, 236, 106, 107, 144, 231, 250, 16, 36, 34, 91, 9, 188, 81, 5, 241, 235, 3, 54, 150, 40, 119, 202, 150};
        String ret1 =rc4(username, base64_table);
        System.out.println(ret1); // BADCFEHGJILKNMPORQTSVUXWZYbadcfehgjilknmporqtsvuxwzy1032547698/+
    }
}

 RC4加密username、base64table 获得的retval(BADCFEHGJILKNMPORQTSVUXWZYbadcfehgjilknmporqtsvuxwzy1032547698/+)当做Base64的码表 用这个码表解equals中的参数的base64即可

 C_C++

exeinfo 使用C#编写 使用32/64位dnspy打开

进入主函数

 就是一些加减操作的 自己仔细看一下就懂了

 反向解一下就OK了

data = [68, 75, 66, 72, 99, 19, 19, 78, 83, 74, 91,
        86, 35, 39, 77, 85, 44, 89, 47, 92, 49,
        88, 48, 91, 88, 102, 105, 51, 76, 115,
        0x84, 125, 79, 122, 0x99]
text2 = "NEWSTAR"
#text2转int好计算一点
data2= [78, 69, 87, 83, 84, 65, 82]
for i in range(7):
    data[i] -= (i ^ (-(data2[i] % 4)))
    data[i + 7] -= data2[i] % 5
    data[i + 14] -= (2 * i)
    data[i + 21] -= (i ^ 2)
    data[i + 28] -= (data2[i] // 5) + 10

for i in range(35):
    data[i] += 32 
    data[i] -= i
    print(chr(data[i]),end='')#flag{45dg_ng78_d8b5_1a7d_gh47_kd5b}

 easy_enc

64位IDA打开 创建一个线程然后对输入str执行4个加密函数(你要是不确定动调一下就知道四个函数执行顺序)

 数据部分

 加密函数都不复杂很容易懂,但是由于有很多乘、模操作会导致数据丢失。所以不能直接逆向求解,要用(Z3)爆破。

from z3 import *
import numpy as np

data = [0xE8, 0x80, 0x84, 0x08, 0x18, 0x3c, 0x78, 0x68, 0x00, 0x70, 0x7C, 0x94, 0xC8, 0xE0, 0x10,
        0xEC, 0xB4, 0xAC, 0x68, 0xA8, 0xC, 0x1C, 0x90, 0xCC, 0x54, 0x3C, 0x14, 0xDC, 0x30]
data_len = len(data)

# 第一个函数 z3求解
# 大写字母 -52(4) %26 + 65(A)

# 小写字母 -89(Y) %26 + 97(a)

# 数字 -45(-) %10 + 48(0)


# 第二个函数
# input[i] += str[i%len]


# 第三个函数
# 按位取反


# 第四个函数
# 每一位都 * 0x34

prefix = "NewStarCTF"
solver = Solver()

tmp = [BitVec(f'tmp_{i}', 8) for i in range(len(data))]

# 添加约束条件
for i in range(data_len):
    char_code = data[i]
    t1 = (tmp[i] - 52) % 26 + 65
    t2 = t1 +ord(prefix[i%10])
    t3 = ~t2
    t4 = t3 * 0x34
    t4 = t4 & 0xFF
    container1 = t4  == char_code

    t1 = (tmp[i] - 89) % 26 + 97
    t2 = t1 +ord(prefix[i%10])
    t3 = ~t2
    t4 = t3 * 0x34
    t4 = t4 & 0xFF
    container2 = t4  == char_code

    solver.add(Or(And(tmp[i] >= ord('A'), tmp[i] <= ord('Z'),container1),
                And(tmp[i]>= ord('a'), tmp[i] <= ord('z'), container2)
                    )  
                )

    # t1 = (tmp[i] - 52) % 26 + 65
    # t2 = t1 +ord(prefix[i%10])
    # t3 = ~t2
    # t4 = t3 * 0x34
    # t4 = t4 & 0xFF
    # container3 = t4  == char_code




if solver.check() == sat:
    # 获取解
    model = solver.model()
    # print(model)
    # # 打印解
    solution = [chr(model[tmp[i]].as_long()) if model[tmp[i]] is not None else None for i in range(len(data))]
    print("".join(solution[i] for i in range(len(data))))
else:
    print("无解")

# BruteForceIsAGoodwaytoGetFlag

Perals

使用IDA64打开发现有些地方爆红,查看汇编有加花指令导致IDA分析报错。我的解决方法是在所有JUMP()爆红的地方打上断点让后去动态调试一遍就可让IDA分析正常

主函数是这样

进入encode函数

v4的值由a2(定值25)获取确定的 可以直接从栈中拿到

 然后a1[j]=v4[a1[j]] 逆  v4.index(data[i])

import hashlib

def md5_encode(data):
    md5 = hashlib.md5()
    md5.update(data.encode('utf-8'))
    return md5.hexdigest()

data = [  0xD0, 0xD0, 0x85, 0x85, 0x80, 0x80, 0xC5, 0x8A, 0x93, 0x89, 
  0x92, 0x8F, 0x87, 0x88, 0x9F, 0x8F, 0xC5, 0x84, 0xD6, 0xD1, 
  0xD2, 0x82, 0xD3, 0xDE, 0x87]
table = [ 0xE6, 0xE7, 0xE4, 0xE5, 0xE2, 0xE3, 0xE0, 0xE1, 0xEE, 0xEF, 
  0xEC, 0xED, 0xEA, 0xEB, 0xE8, 0xE9, 0xF6, 0xF7, 0xF4, 0xF5, 
  0xF2, 0xF3, 0xF0, 0xF1, 0xFE, 0xFF, 0xFC, 0xFD, 0xFA, 0xFB, 
  0xF8, 0xF9, 0xC6, 0xC7, 0xC4, 0xC5, 0xC2, 0xC3, 0xC0, 0xC1, 
  0xCE, 0xCF, 0xCC, 0xCD, 0xCA, 0xCB, 0xC8, 0xC9, 0xD6, 0xD7, 
  0xD4, 0xD5, 0xD2, 0xD3, 0xD0, 0xD1, 0xDE, 0xDF, 0xDC, 0xDD, 
  0xDA, 0xDB, 0xD8, 0xD9, 0xA6, 0xA7, 0xA4, 0xA5, 0xA2, 0xA3, 
  0xA0, 0xA1, 0xAE, 0xAF, 0xAC, 0xAD, 0xAA, 0xAB, 0xA8, 0xA9, 
  0xB6, 0xB7, 0xB4, 0xB5, 0xB2, 0xB3, 0xB0, 0xB1, 0xBE, 0xBF, 
  0xBC, 0xBD, 0xBA, 0xBB, 0xB8, 0xB9, 0x86, 0x87, 0x84, 0x85, 
  0x82, 0x83, 0x80, 0x81, 0x8E, 0x8F, 0x8C, 0x8D, 0x8A, 0x8B, 
  0x88, 0x89, 0x96, 0x97, 0x94, 0x95, 0x92, 0x93, 0x90, 0x91, 
  0x9E, 0x9F, 0x9C, 0x9D, 0x9A, 0x9B, 0x98, 0x99, 0x66, 0x67, 
  0x64, 0x65, 0x62, 0x63, 0x60, 0x61, 0x6E, 0x6F, 0x6C, 0x6D, 
  0x6A, 0x6B, 0x68, 0x69, 0x76, 0x77, 0x74, 0x75, 0x72, 0x73, 
  0x70, 0x71, 0x7E, 0x7F, 0x7C, 0x7D, 0x7A, 0x7B, 0x78, 0x79, 
  0x46, 0x47, 0x44, 0x45, 0x42, 0x43, 0x40, 0x41, 0x4E, 0x4F, 
  0x4C, 0x4D, 0x4A, 0x4B, 0x48, 0x49, 0x56, 0x57, 0x54, 0x55, 
  0x52, 0x53, 0x50, 0x51, 0x5E, 0x5F, 0x5C, 0x5D, 0x5A, 0x5B, 
  0x58, 0x59, 0x26, 0x27, 0x24, 0x25, 0x22, 0x23, 0x20, 0x21, 
  0x2E, 0x2F, 0x2C, 0x2D, 0x2A, 0x2B, 0x28, 0x29, 0x36, 0x37, 
  0x34, 0x35, 0x32, 0x33, 0x30, 0x31, 0x3E, 0x3F, 0x3C, 0x3D, 
  0x3A, 0x3B, 0x38, 0x39, 0x06, 0x07, 0x04, 0x05, 0x02, 0x03, 
  0x00, 0x01, 0x0E, 0x0F, 0x0C, 0x0D, 0x0A, 0x0B, 0x08, 0x09, 
  0x16, 0x17, 0x14, 0x15, 0x12, 0x13, 0x10, 0x11, 0x1E, 0x1F, 
  0x1C, 0x1D, 0x1A, 0x1B, 0x18, 0x19] 

flag =''

for i in range(len(data)):
    flag +=chr(table.index(data[i]))
print(md5_encode(flag)) #d780c9b2d2aa9d40010a753bc15770de

Pzthon

python打包的exe 使用exeinfo可以看到更多信息

 要想要解包pyinstaller打包的程序需要对应的python版本去解包

使用010 打开PZthon.exe 查找关键字 MEI 找出Python版本为 3.9

我在虚拟机上安装python3.9 使用脚本 pyinstxtractor.py  解包获得pyc(虚拟机重置了没有留档)

在这个输出文件中找到PZthon.pyc文件 使用在线网站 python反编译 - 在线工具 (tool.lu)

 获得源码

逆向一下 

enc = [
    115,
    121,
    116,
    114,
    110,
    76,
    37,
    96,
    88,
    116,
    113,
    112,
    36,
    97,
    65,
    125,
    103,
    37,
    96,
    114,
    125,
    65,
    39,
    112,
    70,
    112,
    118,
    37,
    123,
    113,
    69,
    79,
    82,
    84,
    89,
    84,
    77,
    76,
    36,
    112,
    99,
    112,
    36,
    65,
    39,
    116,
    97,
    36,
    102,
    86,
    37,
    37,
    36,
    104]

flag = ""
for i in enc:
    flag += chr(i ^ 21) 
print(flag) #flag{Y0uMade1tThr0ughT2eSec0ndPZGALAXY1eve1T2at1sC001}

R4ndom

IDA64打开主程序逻辑明确

但是Rkey 是生成的rand() 第一眼傻眼了

但是随后便知道了伪随机(使用固定种子生成的随机数是固定的) 查找用于设定种子的Srand()函数,在b函数中

有个a函数中是反调试函数 如何要动调的话 直接使用改标志位就能跳过 

在win和linux中srand()函数生成随机数并不一样 使用这个脚本在linux下生成Rkey

//在linux系统中运行
#include<stdio.h>
#include<stdlib.h>
void main(){
	unsigned int seed = 0x5377654E;
	int i =0;
	srand(seed);
	for (i=0;i<42;i++){
		printf("0x%02x ,",rand()%255);
	}
}

然后又是移位和模操作加密 我直接Z3爆破

from z3 import *
flag =""
res = [153, 245, 13, 62, 207, 14, 130, 217, 106, 3, 18, 219, 28, 192, 83, 195, 205, 146, 20, 153,
        177, 225, 174, 255, 17, 0, 73, 226, 186, 155, 8, 118, 56, 36, 79, 139, 129, 36, 161, 187, 237, 197]

Rkey = [0x33, 0x89, 0xac, 0xd7, 0x54, 0xcc, 0x4a, 0xa5, 0x35, 0xd1, 0xdb, 0xa3, 0xe6, 0x93, 0x0f, 0x7f, 0x95, 0x4d, 0xe7, 0x65,
        0x80, 0xaf, 0x6b, 0xd2, 0xcc, 0xcd, 0x14, 0xad, 0x8d, 0x69, 0xc6, 0x40, 0xf2, 0xf2, 0x18, 0x47, 0x40, 0xe2, 0x6c, 0x75, 0xb4, 0x48]

# 创建符号变量 tmp
tmp = [BitVec(f'tmp_{i}', 8) for i in range(len(res))]

# 创建 Z3 求解器
solver = Solver()

# 添加约束条件
for i in range(len(res)):
    constraint = (16 * ((tmp[i] + Rkey[i]) >> 4) + 15) & (tmp[i] + Rkey[i]) == res[i]
    solver.add(constraint)

# 检查是否存在解
if solver.check() == sat:
    # 获取解
    model = solver.model()
    
    # 打印解
    for i in range(len(res)):
        # print(f"tmp[{i}] = {model[tmp[i]].as_long()}")
        flag+=(chr(model[tmp[i]].as_long()))
else:
    print("无解")
print(flag) #flag{B8452786-DD8E-412C-E355-2B6F27DAB5F9}

SMC

有一个反调试函数 更改标志位跳过

当运行到JZ 指令时更改ZF标志位为1

 JZ线变绿代表条件成立 即将跳转

 sub_401042()中就是一些对内存的XOR操作,类似解壳。

因为主要加密函数(byte_403040)还是一些数据。直接动调过去 。然后F7进入 发现在data端(爆红)。

因为IDA静态分析时把这分析为数据段了,我们进入加密函数后按P让IDA识别为函数F5看一下加密逻辑。

编写代码

data =[  0x7C, 0x82, 0x75, 0x7B, 0x6F, 0x47, 0x61, 0x57, 0x53, 0x25, 
  0x47, 0x53, 0x25, 0x84, 0x6A, 0x27, 0x68, 0x27, 0x67, 0x6A, 
  0x7D, 0x84, 0x7B, 0x35, 0x35, 0x48, 0x25, 0x7B, 0x7E, 0x6A, 
  0x33, 0x71]
flag =""
for i in data:
    flag += chr((i - 0x5)^ 0x11  )
print(flag) #flag{SMC_1S_1nt3r3sting!!R1ght?}

 这题写WP时,发现二次IDA打开就彻底乱了,只能用一次:)要删去IDA的记录文件在用一次

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值