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的记录文件在用一次