CISCN2024初赛Reverse部分wp

Re

asm_re

txt找到密文

找到关键逻辑,*0x50 +0x14 ^0x4d +0x1e后面还有一段

也可以问一下gpt得到流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <string.h>

void main() {
char src[] = "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}";
char dst[0x98];
char result[0x50];

memcpy(dst, src, 0x98);

int len = strlen(dst);

for (int i = 0; i < len; i++) {
result[i] = ((dst[i] * 0x50) + 0x14) ^ 0x4D + 0x1E;
}

if (len >= 0x50) {
printf("The result array matches the expected array.\n");
} else {
printf("The result array does not match the expected array.\n");
}
}

直接除去密文然后爆破

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
numbers = [
0x1FD7,
0x21B7,
0x1E47,
0x2027,
0x26E7,
0x10D7,
0x1127,
0x2007,
0x11C7,
0x1E47,
0x1017,
0x1017,
0x11F7,
0x2007,
0x1037,
0x1107,
0x1F17,
0x10D7,
0x1017,
0x1017,
0x1F67,
0x1017,
0x11C7,
0x11C7,
0x1017,
0x1FD7,
0x1F17,
0x1107,
0xF47,
0x1127,
0x1037,
0x1E47,
0x1037,
0x1FD7,
0x1107,
0x1FD7,
0x1107,
0x2787
]
print(len(numbers))
flag=''
for i in range(len((numbers))):
for j in range(32,127):
tmp=(((j*0x50)+0x14)^0x4d)+0x1e
if tmp==numbers[i]:
#print(tmp)
flag+=chr(j)
break
print(flag)
flag{67e9a228e45b622c2992fb5174a4f5f5}

androidso_re-1

法1

已知flag格式和内部32位

先des再base64

so层两个函数尝试hook返回值

很奇怪竟然报错了

用脚本一直无法hook,用objection可以注入 objection -g com.example.re11113 explore

android hooking watch class_method com.example.re11113.jni.getiv –dump-args –dump-return –dump-backtrace

输入格式和长度相同的flag:flag{12345678901234567890123456789012}

一次只能注入一个,得到结果后要新开cmd

后面直接解密

1
flag{188cba3a5c0fbb2250b5a2e590c391ce}

上面的图说明frida的时候会读到非utf-8的字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function hook() {
let jni = Java.use("com.example.re11113.jni");
var iv = jni.getiv();
console.log("IV: "+iv);
var getKeyBase = Module.findExportByName("libSecret_entrance.so", "Java_com_example_re11113_jni_getkey");

Interceptor.attach(getKeyBase, {
onEnter: function(args) {
},
onLeave: function(retval) {
}
});
var key = jni.getKey();
console.log("Key: " + key);
}

法2

大部分师傅直接逆代码

上面搜到base64,猜测凯撒偏移

rc4解密之后再异或也行

只取前八位

法3

https://blog.csdn.net/Pisces50002/article/details/139580143

安装r0env集成环境:https://pan.baidu.com/share/init?surl=y6ceiOzWuv0Gl5FNksgciQ 提取码:v8at

把so和apk文件放到java文件夹下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package com.ciscn;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;


import java.io.*;

public class ciscna extends AbstractJni {
private final AndroidEmulator emulator; //android 模拟器 a
private final VM vm;//vm 虚拟机
private final Module module;
private final Memory memory;
private final DalvikModule dm;

//将该类封装起来,以后直接套用模板
public ciscna(String apkFilePath, String soFilePath, String apkProcessname) throws IOException {
// 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName(apkProcessname).build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File(apkFilePath));
vm.setVerbose(false); // 打印日志,会在调用初始化 JNI_unload 打印一些信息,默认:false
// 加载目标 SO
dm = vm.loadLibrary(new File(soFilePath), true); // 加载 so 到虚拟内存,第二个参数:是否需要初始化
//获取本 SO 模块的句柄
module = dm.getModule();
vm.setJni(this); //设置 Jni,防止报错
//创建完后,需要调用 JNI_onload 函数
dm.callJNI_OnLoad(emulator); // 调用 JNI OnLoad,进行动态注册某些函数。如果都是静态注册,那就不用调用这个函数
vm.setVerbose(true);
// Debugger debugger = emulator.attach();
// debugger.addBreakPoint(module.base + 0x1924C);
// debugger.addBreakPoint(module.base + 0x19240);
}

public ciscna(AndroidEmulator emulator, VM vm, Module module, Memory memory, DalvikModule dm) {
this.emulator = emulator;
this.vm = vm;
this.module = module;
this.memory = memory;
this.dm = dm;
}

public String func_getKey() {
DvmClass dvmClass = vm.resolveClass("com.example.re11113.jni");
DvmObject<?> object = dvmClass.newObject(null);
DvmObject<?> object1 = object.callJniMethodObject(emulator, "getkey()Ljava/lang/String;");
return object1.getValue().toString();
}

public String func_getiv() {
DvmClass dvmClass = vm.resolveClass("com.example.re11113.jni");
DvmObject<?> object = dvmClass.newObject(null);
DvmObject<?> object1 = object.callJniMethodObject(emulator, "getiv()Ljava/lang/String;");
return object1.getValue().toString();
}

//创建一个 main 函数
public static void main(String[] args) throws IOException {
// 1、需要调用的 so 文件所在路径
String soFilePath = "/root/Documents/unidbg/unidbg-android/src/test/java/com/ciscn/libSecret_entrance.so";

// 2、APK 的路径
String apkFilePath = "/root/Documents/unidbg/unidbg-android/src/test/java/com/ciscn/app-debug.apk";
// 3、apk 进程名
String apkProcessname = "com.tencent.testvuln";
ciscna myapp = new ciscna(apkFilePath, soFilePath, apkProcessname);
System.out.println("getKeyresult:" + myapp.func_getKey());
System.out.println("getivresult:" + myapp.func_getiv());
}
}

gdb_debug-1

伪随机数只取高位

xor rand->交换下标->xor rand->xor string

按理seed生成的伪随机数会很大,与调试结果不符,这也就是题目要求我们调试的原因

一种思路是输入一个flag,得到密文之后xor得到rand1,rand3和ptr,当然可以一位位提取数据

看汇编去找把rax作为地址的内存,记住开始位置后直接跳出循环,全部提取,这题神奇的点在双击数组进去看不到密文

而需要在调试时看看寄存器的值找内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

int main() {
char v28[38];
for (int i = 0; i < 1; i++) {
int a[16] = {0, 0x10000000, 0x20000000, 0x30000000, 0x40000000, 0x50000000, 0x60000000, 0x70000000, 0x80000000, 0x90000000, 0xa0000000, 0xb0000000, 0xc0000000, 0xd0000000, 0xe0000000, 0xf0000000};
srand(a[i]);
int v31[38] = {220, 184, 64, 189, 156, 201, 110, 101, 239, 18, 216, 152, 105, 208, 222, 252, 107, 174, 125, 139, 214, 141, 15, 208, 79, 102, 62, 157, 250, 195, 233, 36, 211, 239, 255, 157, 231, 1};
int rand1[38]={217, 15, 24, 189, 199, 22, 129, 190, 248, 74, 101, 242, 93, 171, 43, 51, 212, 165, 103, 152, 159, 126, 43, 93, 194, 175, 142, 58, 76, 165, 117, 37, 180, 141, 227, 123, 163, 100};
int rand3[38]={222, 170, 66, 252, 9, 8, 178, 6, 13, 147, 97, 244, 36, 73, 21, 1, 215, 171, 4, 24, 207, 233, 213, 150, 51, 202, 249, 42, 94, 234, 45, 60, 148, 111, 56, 157, 88, 234};
char ptr[38]={0x12, 0x0E, 0x1B, 0x1E, 0x11, 0x05, 0x07, 0x01, 0x10, 0x22, 0x06, 0x17, 0x16, 0x08, 0x19, 0x13, 0x04, 0x0F, 0x02, 0x0D, 0x25, 0x0C, 0x03, 0x15, 0x1C, 0x14, 0x0B, 0x1A, 0x18, 0x09, 0x1D, 0x23, 0x1F, 0x20, 0x24, 0x0A, 0x00, 0x21};
for (int i = 0; i < 38; i++) {
v31[i] = v31[i] ^ rand3[i];
//printf("%d ",rand());
//ptr[i] = i;
}

// for (int k = 37; k; --k) {
// int v18 = rand() % 38;
// //printf("%d ",v18);
// int tmp = ptr[k];
// ptr[k] = ptr[v18];
// ptr[v18] = tmp;
// }
//printf("\n");
for (int i = 0; i <38; i++) {
v28[ptr[i]] = v31[i];
}
for (int i = 0; i < 38; i++) {
v28[i] = v28[i] ^ rand1[i];
}
for (int i = 0; i < 38; i++) {
printf("%c", v28[i]&0xff);
}
printf("\n");

}

return 0;
}
//flag{�8bace5989660ee38f1fd980a4b4fbcd}

最后一位爆破即可:flag{78bace5989660ee38f1fd980a4b4fbcd}

还有一种是直接用时间戳,注意要在linux下运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <vector>
#include <algorithm>
#include <stack>
#include <set>
#include <map>
#include <ctime>
#include <unistd.h>

using namespace std;
typedef long long LL;
typedef long double DD;

int main()
{
srand(((int)time(0))& 0xF0000000);
char s2[] = "congratulationstoyoucongratulationstoy";
unsigned char byte_10A0[] = {
0xBF, 0xD7, 0x2E, 0xDA, 0xEE, 0xA8, 0x1A, 0x10, 0x83, 0x73, 0xAC, 0xF1, 0x06, 0xBE, 0xAD, 0x88,
0x04, 0xD7, 0x12, 0xFE, 0xB5, 0xE2, 0x61, 0xB7, 0x3D, 0x07, 0x4A, 0xE8, 0x96, 0xA2, 0x9D, 0x4D,
0xBC, 0x81, 0x8C, 0xE9, 0x88, 0x78, 0x00, 0x00
};
char rand1[38];
unsigned int rand2[38],rand3[38];
int len = strlen(s2);
cout << "len:" << len << endl;
for(int i=0;i<len;i++)
{
rand1[i] = rand();
// cout << (unsigned int)(rand1[i]&0xff) << " ";
}
for(int i=len-1;i;--i)
{
rand2[i] = rand()%(i+1);
// cout << (unsigned int)(rand2[i]&0xff) << " ";
}
for(int i=0;i<len;i++)
{
rand3[i] = rand();
}

for(int i=0;i<len;i++)
{
s2[i] ^= byte_10A0[i];
}

for(int i=0;i<len;i++)
{
s2[i] ^= rand3[i];
}

int str3[39];
for(int i=0;i<len;i++)
{
str3[i] = i;
}

for(int i=len-1;i;--i)
{
int temp = str3[i];
str3[i] = str3[rand2[i]];
str3[rand2[i]] = temp;
}

//s2[i]=str2[str3[i]];
char str2[39] = {0};
for(int i=0;i<len;i++)
{
str2[str3[i]] = s2[i];
}
for(int i=0;i<len;i++)
{
str2[i] ^= rand1[i];
}
cout << str2;

return 0; //-22 61 13 92
}

个人比赛的时候就用了这种,但是rand2的生成循环写反了,以及rand1和rand3的位置没考虑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>
#include <string.h>
#include <time.h>
#include<stdlib.h>
#include<stdint.h>
int main() {
char v28[38];
for(int i=0;i<1;i++){
//int a[16]={0,0x10000000,0x20000000,0x30000000,0x40000000,0x50000000,0x60000000,0x70000000,0x80000000,0x90000000,0xa0000000,0xb0000000,0xc0000000,0xd0000000,0xe0000000,0xf0000000};
srand(((int)time(0))&0xf0000000);
unsigned char v31[38]={220, 184, 64, 189, 156, 201, 110, 101, 239, 18, 216, 152, 105, 208, 222, 252, 107, 174, 125, 139, 214, 141, 15, 208, 79, 102, 62, 157, 250, 195, 233, 36, 211, 239, 255, 157, 231, 1};
char s[50]="congratulationstoyoucongratulationstoy";
int ptr[38];
unsigned int rand1[38];
unsigned int rand2[38];
unsigned int rand3[38];
for(int i =0;i<38;i++){
rand1[i]=rand();
//printf("%d ",rand1[i]);
}
printf("\n\n");
for(int i=0;i<37;i++){
rand2[i]=rand();
//printf("%d ",rand2[i]);
}
printf("\n\n");
for(int i =0;i<38;i++){
rand3[i]=rand();
//printf("%d ",rand3[i]);
}
for(int i=0;i<38;i++){
v31[i]=v31[i]^rand3[i];
printf("%d ",rand3[i]);
ptr[i]=i;
}

for(int k=37,j=0;k>=0;k--,j++){
uint32_t t=rand2[j]%(k+1);
printf("%d ",t);
char tmp=ptr[k];
ptr[k]=ptr[t];
ptr[t]=tmp;
}

for(int i=0;i<38;i++){
v28[ptr[i]]=v31[i];
}
for(int i=0;i<38;i++){
v28[i]=v28[i]^rand1[i];
}
//for(int i=0;i<38;i++){
// printf("%c",v28[i]&0x7f);
//}
printf("%s",v28);
printf("\n");
}

return 0;
}

whereThel1b-1

法1

Cpython题,比赛的时候一直想着是import的问题,后面要用kali已经来不及了

找base64的时候能找到Pyx_CreateStringTabAndInitStrings,有base和random

__pyx_n_s_pla通常用于存储字符串常量

参数0引用过去,在同一个函数下找

找到random

猜测seed为0

同样的方法找到base64

感觉随机数最大56应该是猜的,因为刚好56位

1
2
3
4
5
6
7
8
9
10
11
12
import base64
import random

random.seed(0)
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

for i in range(56):
temp = random.randint(0,56)
encry[i] ^= temp
#print(chr(encry[i]),end='')
print(base64.b64decode('ZmxhZ3s3ZjlhMmQzYy0wN2RlLTExZWYtYmU1ZS1jZjFlODg2NzRjMGJ9'.encode()))
# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

法2

还有直接爆破的,python要为3.10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import whereThel1b
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127,
68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80,
102, 101, 75, 93, 68, 121, 26]
flag = ''
for i in range(14):
for a in range(32, 126):
for b in range(32, 126):
for c in range(32, 126):
abc = chr(a) + chr(b) + chr(c)
flag_test = (flag + abc).ljust(42, '1')
result = whereThel1b.trytry(flag_test.encode())
position = i * 4
ret_substring = result[position:position + 4]
encry_substring = encry[position:position + 4]
if ret_substring == encry_substring:
flag += abc

print(flag)
# flag{7f9a2d3c-07de-11ef-be5e-cf1e88674c0b}

法3

黑盒测试,猜测与base64有关

输入42个a,得到ret与base64后的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

a = [111, 79, 118, 80, 67, 85, 86, 72, 70, 78, 116, 93, 74, 73, 80, 77, 84, 119, 78, 122, 81, 103, 64, 79, 106, 71, 100, 69, 106, 113, 79, 123, 95, 121, 66, 94, 114, 66, 88, 75, 95, 65, 93, 124, 126, 127, 75, 75, 71, 75, 113, 73, 73, 84, 117, 75]
b = [89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 89, 87, 70, 104, 10]

ret = []

for i in range(0, len(encry)):
ret += [a[i] ^ b[i] ^ encry[i]]

print(ret)
for i in ret:
print(chr(i),end="")a

发现输出有明显的Zmxh,为flag的base64编码,解密得到flag

法4

字符之间相互不影响,针对调用库的子函数,考虑用模拟退火算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import random
import math

import whereThel1b

def shared(lhs, rhs):
return sum([ x == y for x, y in zip(lhs, rhs) ])


encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

flag = [ 0 ] * 42
ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&()*+,-./:;<=>?@[]^_`{|}~'

t = 0.7
i = 0
flag_val = 0
while True:
t *= 0.99998
i += 1
if i & 511 == 0:
print(bytes(flag), flag_val, t)

current = flag[:]
j = random.randrange(0, len(current))
current[j] = ord(random.choice(ALPHABET))

seed = random.randrange(0, 1 << 64)
current_val = shared(whereThel1b.trytry(bytes(current)), encry)
random.seed(seed)

delta = current_val - flag_val
if math.exp(delta / t) > random.random():
flag_val = current_val
flag = current

rust_baby-1

法1

有一些字符串去解密

交叉引用上述字符串找到关键函数,往下翻

调试发现数据在v21,不足8为位用E填充,8C3函数没逆,经过尝试发现

1
2
3
4
0~1  -1
2~3 +0
4~5 +1
6~7 +2

之后^0x33,总共104个字节

下内存断点,找到下一次用该内存的地方

直接到下一段处理的地方,v96貌似是定值

后面的0xAB是填充位

根据上面104字节,异或的密钥也要104字节,但是这里只有8字节,用0去异或任何值都不影响结果

在xor那里下断点,然后把输入的全改为0

再取消断点,运行到cmp处,提取密钥

这是base64,狠人

上面有段解密不了的base64就是密文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import base64

encoded_string = "igdydo19TVE13ogW1AT5DgjPzHwPDQle1X7kS8TzHK8S5KCu9mnJ0uCnAQ4aV3CSYUl6QycpibWSLmqm2y/GqW6PNJBZ/C2RZuu+DfQFCxvLGHT5goG8BNl1ji2XB3x9GMg9T8Clatc="
xor_key = "3F8gIsJ5GVY12otH0xn8VRTN0ntYWQlC3iy0SNnyG6lA4ab7/zjB1eLod3hvIgTmFj4MNVJc/cHlWRzQrlqy3Rn4QuYsiVnlEZzIe4Fwf2+8bwKP9/TIcK4C+FvicggJb79LObXQHqM="

# 步骤1: base64解码
decoded_bytes = base64.b64decode(encoded_string)
decoded_keys = base64.b64decode(xor_key)

xor_bytes = []
# 步骤2: 异或密钥
for i in range(len(decoded_bytes)):
xor = decoded_bytes[i] ^ decoded_keys[i]
xor_bytes.append(xor)

# 步骤3: 异或0x33
xor_0x33_bytes = bytes([b ^ 0x33 for b in xor_bytes])

# 8个字符一组进行加减运算
processed_bytes = []
for i in range(0, len(xor_0x33_bytes), 8):
group = list(xor_0x33_bytes[i:i+8])
for j in range(-1, 3):
processed_group1 = group[(j + 1) * 2] - j
processed_group2 = group[(j + 1) * 2 + 1] - j
processed_bytes.append(processed_group1)
processed_bytes.append(processed_group2)

processed_bytes = bytes(processed_bytes)
flag = processed_bytes.decode('ascii', errors='ignore')
print(flag)
# flag{6e2480b3-4f02-4cf1-9bc0-123b75f9a922}EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

法2

用frida插桩爆破

cmp的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var number = 0
// frida -l .\hook.js -n rust_baby.exe
function main()
{
// 寻找“rust_baby.exe”的基础地址
var base = Module.findBaseAddress("rust_baby.exe")

if(base){
// 在找到的基础地址上添加拦截器,拦截指定函数
Interceptor.attach(base.add(0x3EA8), { //序号加1的位置
onEnter: function(args) {
// console.log("成功!");
// 当函数被调用时执行的逻辑,这里增加了全局变量number的值并发送
number += 1;
// 发送数据,这里给 python 脚本发送,如果是命令行,会有发送的回显
send(number);
// 实现延迟,延迟时间单位为毫秒,延迟 4 毫秒
var delay = 0x4;
var start = new Date().getTime();
while (new Date().getTime() < start + delay);
// console.log("Delay complete.");
}
});
}
}
setImmediate(main);

可以采取一点点增加的方式,每次爆2字节然后报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import subprocess
import frida

# 定义目标文件名和初始命令行参数
filename = "rust_baby.exe"
cmd = ['./rust_baby.exe']

# 从文件读取JavaScript代码
jscode = open("Hook.js", "rb").read().decode()

# 自定义异常类,用于表示成功找到flag
class SUCCESSFLAG(Exception):
pass

def brute(F):
"""
通过Frida对目标程序进行动态分析,使用爆破方式获取正确的flag。

参数:
F: 用于传递给程序的输入,类型为bytes。

返回值:
result: 运行程序后的结果。

抛出:
SUCCESSFLAG: 当找到正确的flag时抛出。
"""
def on_message(message, data):
global result
if message['type'] == 'send':
result = message['payload']
else:
print(message)
# 创建子进程运行目标程序,并使用Frida附加到进程上
process = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True)
session = frida.attach(filename)
script = session.create_script(jscode)
script.on('message', on_message)
script.load()

# 向进程输入F,并等待进程结束
process.stdin.write(F.decode())
output, error = process.communicate()

# 若输出中包含"correct flag",则打印并抛出SUCCESSFLAG异常
if "correct flag" in output:
print("正确的flag是:", F.decode())
raise SUCCESSFLAG("抛出flag成功的异常!!!")
process.terminate()
return result

def bp(startflag, old_number1, tag1):
"""
基于brute函数的辅助爆破函数,用于逐字符爆破并处理特殊情况。

参数:
startflag: 当前已知的flag部分,列表形式。
old_number1: 上一次爆破得到的数字结果。
tag1: 标志位,用于控制爆破逻辑。

返回值:
startflag: 更新后的flag部分。
old_number1: 更新后的数字结果。
tag1: 更新后的标志位。
"""
global temptag
global maxnumber
idx = 0
temp = old_number1
targetidx = -1
table = "-0123456789abcdefghijklmnopqrstuvwxyz}"

# 循环遍历可能的字符,尝试爆破
for ch in table:
startflag.append(ord(ch))
startflag.append(10)
my_bytearray = bytearray(startflag)
new_number = brute(my_bytearray)

if(new_number > old_number1):
if tag1 == 0:
old_number1 = new_number
targetidx = idx
if(tag1 > 0):
tag1 -= 1

idx += 1
startflag = startflag[:-2]

# 若未找到目标字符,进行回溯处理
if(targetidx == -1):
print("错误的:")
my_bytearray = bytearray(startflag)
print(my_bytearray.decode())
print("去除错误字符!")
startflag = startflag[:-1]
print("magic:", temp-1)
temptag = temptag + 1
print("magictag:", temptag+1)
return startflag, temp-1, temptag

# 找到目标字符,进行相应处理
bpret = table[targetidx:targetidx+1]
print("目标数据:", bpret, ord(bpret))
startflag.append(ord(bpret))
my_bytearray = bytearray(startflag)
print(my_bytearray.decode())
return startflag, old_number1, 0

# 初始化参数并开始爆破流程
# startflag1 = [102, 108, 97, 103, 123] # flag的格式是固定的,因此初始值已知
startflag = [102, 108, 97, 103, 123, 54, 101, 50, 52, 56, 48, 98, 51, 45, 52, 102, 48, 50, 45, 52, 99, 102]
startflag.append(10)
my_bytearray = bytearray(startflag)

old_number = brute(my_bytearray)
startflag = startflag[:-1] # 去除回车
temptag = 0
tag = 0
idx = 0
temp = len(startflag) # 初始长度

cleartag = 5
while True:
startflag, old_number, tag = bp(startflag, old_number, tag) # 执行爆破逻辑

if(temp < len(startflag)):
temp = len(startflag)
print("----->", temp-5)
if((temp-cleartag)%3 == 0):
print(">>>>清理temptag")
temptag = 0
cleartag += 2

CISCN2024初赛Reverse部分wp
https://j1ya-22.github.io/2024/05/21/CISCN2024初赛Reverse部分wp/
作者
j1ya
发布于
2024年5月21日
许可协议