NSSCTF Round#18 RE WP 完整复现

1. GenshinWishSimulator

恶搞原神抽卡模拟器

看到软件的界面,大致有三种思路:

  1. 修改石头数量一直抽,如果概率正常肯定能抽到(但是估计设置的概率是0)
  2. 在源码里找flag的数据
  3. 把抽卡概率改成100%直接抽出来

Unity逆向,根据经验应该dnspy查看Assembly-CSharp.dll

上来看到SM4,但是别急

Gacha是抽卡的意思,这里应该是抽卡的主逻辑

Gift内可以看到主要逻辑,以array作为key"NSSCTF_2024_R#18" 为偏移(IV),用SM4/CBC/PKCS7Padding模式加密input数组,最后保存字符串到flag对象里

也就是说如果静态分析直接求flag就是一个SM4_CBC的解密过程,检查一下发现SM4没有经过魔改,应该容易解出

a. 静态分析解密

只要得到SM4解密需要的几个重要参数,再结合在线工具就能得出答案

  • 密文就是input数组

  • 密钥key是array数组,其计算方法很明显

然后找bucket数组,在Check()函数里发现了一堆方程,用于验证bucket数组的值

z3算一下,SM4的密钥长度只有16字节,设置变量类型时用Int,BitVec会因为位数多导致解出结果过大

from z3 import *
from Crypto.Util.number import *
bucket = [Int("bucket[%d]" % i) for i in range(16)]
s = Solver()
s.add(bucket[0] * 40 + bucket[1] * 65 + bucket[2] * -53 + bucket[3] * 70 + bucket[4] * -84 + bucket[5] * -38 + bucket[6] * 94 + bucket[7] * -39 + bucket[8] * -91 + bucket[9] * -35 + bucket[10] * 54 + bucket[11] * 17 + bucket[12] * 45 + bucket[13] * 92 + bucket[14] * -29 + bucket[15] * 61 == 3004)
s.add(bucket[0] * -15 + bucket[1] * 74 + bucket[2] * -89 + bucket[3] * -82 + bucket[4] * -92 + bucket[5] * 27 + bucket[6] * 21 + bucket[7] * -24 + bucket[8] * -82 + bucket[9] * -58 + bucket[10] * -36 + bucket[11] * 64 + bucket[12] * -49 + bucket[13] * -22 + bucket[14] * 59 + bucket[15] * -47 == -674)
s.add(bucket[0] * 67 + bucket[1] * -23 + bucket[2] * 63 + bucket[3] * -38 + bucket[4] * -32 + bucket[5] * 61 + bucket[6] * -71 + bucket[7] * 49 + bucket[8] * 83 + bucket[9] * -92 + bucket[10] * -16 + bucket[11] * 65 + bucket[12] * -22 + bucket[13] * 12 + bucket[14] * -85 + bucket[15] * 74 == 945 )
s.add(bucket[0] * -49 + bucket[1] * 48 + bucket[2] * -11 + bucket[3] * 20 + bucket[4] * -14 + bucket[5] * 92 + bucket[6] * -19 + bucket[7] * 32 + bucket[8] * 64 + bucket[9] * -77 + bucket[10] * 49 + bucket[11] * -19 + bucket[12] * 72 + bucket[13] * -64 + bucket[14] * 85 + bucket[15] * 54 == 1721)
s.add(bucket[0] * 36 + bucket[1] * -21 + bucket[2] * -59 + bucket[3] * -54 + bucket[4] * -96 + bucket[5] * -81 + bucket[6] * -33 + bucket[7] * 31 + bucket[8] * -41 + bucket[9] * -70 + bucket[10] * -27 + bucket[11] * 24 + bucket[12] * 95 + bucket[13] * -61 + bucket[14] * -17 + bucket[15] * -52 == -2198)
s.add(bucket[0] * 78 + bucket[1] * -62 + bucket[2] * 70 + bucket[3] * -69 + bucket[4] * 38 + bucket[5] * 90 + bucket[6] * -52 + bucket[7] * 41 + bucket[8] * 63 + bucket[9] * -65 + bucket[10] * -15 + bucket[11] * 59 + bucket[12] * -31 + bucket[13] * 54 + bucket[14] * 33 + bucket[15] * -57 == -1833)
s.add(bucket[0] * 56 + bucket[1] * 75 + bucket[2] * 71 + bucket[3] * 78 + bucket[4] * -39 + bucket[5] * -84 + bucket[6] * 55 + bucket[7] * 54 + bucket[8] * -12 + bucket[9] * -57 + bucket[10] * 32 + bucket[11] * -19 + bucket[12] * 13 + bucket[13] * -83 + bucket[14] * 11 + bucket[15] * -67 == 829)
s.add(bucket[0] * 10 + bucket[1] * -97 + bucket[2] * 56 + bucket[3] * -61 + bucket[4] * 45 + bucket[5] * -22 + bucket[6] * 33 + bucket[7] * 81 + bucket[8] * 32 + bucket[9] * 49 + bucket[10] * -19 + bucket[11] * -18 + bucket[12] * 80 + bucket[13] * -98 + bucket[14] * 79 + bucket[15] * -36 == -2551)
s.add(bucket[0] * 24 + bucket[1] * -61 + bucket[2] * 91 + bucket[3] * 93 + bucket[4] * 76 + bucket[5] * 54 + bucket[6] * -33 + bucket[7] * -29 + bucket[8] * -72 + bucket[9] * 20 + bucket[10] * 48 + bucket[11] * 79 + bucket[12] * 76 + bucket[13] * 68 + bucket[14] * 51 + bucket[15] * 25 == 2996)
s.add(bucket[0] * -83 + bucket[1] * -77 + bucket[2] * -64 + bucket[3] * -38 + bucket[4] * -13 + bucket[5] * -85 + bucket[6] * 33 + bucket[7] * -76 + bucket[8] * 27 + bucket[9] * 14 + bucket[10] * -79 + bucket[11] * -63 + bucket[12] * -78 + bucket[13] * 53 + bucket[14] * -73 + bucket[15] * 61 == -2315)
s.add(bucket[0] * 84 + bucket[1] * -67 + bucket[2] * 57 + bucket[3] * 26 + bucket[4] * 94 + bucket[5] * 20 + bucket[6] * -71 + bucket[7] * -88 + bucket[8] * -28 + bucket[9] * -13 + bucket[10] * -40 + bucket[11] * 76 + bucket[12] * -14 + bucket[13] * 33 + bucket[14] * 76 + bucket[15] * -75 == -150)
s.add(bucket[0] * -60 + bucket[1] * 88 + bucket[2] * -66 + bucket[3] * -72 + bucket[4] * 41 + bucket[5] * 49 + bucket[6] * 48 + bucket[7] * -77 + bucket[8] * -42 + bucket[9] * 25 + bucket[10] * -50 + bucket[11] * -84 + bucket[12] * 40 + bucket[13] * 50 + bucket[14] * -83 + bucket[15] * -27 == -1919)
s.add(bucket[0] * -16 + bucket[1] * -53 + bucket[2] * -21 + bucket[3] * -44 + bucket[4] * 26 + bucket[5] * -56 + bucket[6] * -90 + bucket[7] * -93 + bucket[8] * -73 + bucket[9] * 48 + bucket[10] * 15 + bucket[11] * -43 + bucket[12] * -61 + bucket[13] * -24 + bucket[14] * 71 + bucket[15] * 67 == -1199)
s.add(bucket[0] * 55 + bucket[1] * -34 + bucket[2] * -22 + bucket[3] * 60 + bucket[4] * 93 + bucket[5] * -95 + bucket[6] * 50 + bucket[7] * 36 + bucket[8] * -48 + bucket[9] * -26 + bucket[10] * -94 + bucket[11] * -35 + bucket[12] * 21 + bucket[13] * -27 + bucket[14] * 91 + bucket[15] * -76 == -1163)
s.add(bucket[0] * 64 + bucket[1] * -50 + bucket[2] * -23 + bucket[3] * -70 + bucket[4] * -78 + bucket[5] * 34 + bucket[6] * 26 + bucket[7] * 64 + bucket[8] * -72 + bucket[9] * 10 + bucket[10] * -96 + bucket[11] * 61 + bucket[12] * -15 + bucket[13] * 31 + bucket[14] * 36 + bucket[15] * 50 == -266)
s.add(bucket[0] * -27 + bucket[1] * 86 + bucket[2] * -61 + bucket[3] * 89 + bucket[4] * -53 + bucket[5] * 10 + bucket[6] * -42 + bucket[7] * 92 + bucket[8] * -48 + bucket[9] * 13 + bucket[10] * 84 + bucket[11] * -71 + bucket[12] * 93 + bucket[13] * 54 + bucket[14] * -69 + bucket[15] * -30 == 892)
if s.check() == sat:
    s = s.model()
print(s)
'''
[bucket[15] = 16,
 bucket[13] = 0,
 bucket[10] = 1,
 bucket[11] = 17,
 bucket[14] = 2,
 bucket[4] = 2,
 bucket[2] = 1,
 bucket[1] = 14,
 bucket[5] = 1,
 bucket[7] = 0,
 bucket[12] = 2,
 bucket[6] = 1,
 bucket[3] = 17,
 bucket[8] = 2,
 bucket[9] = 4,
 bucket[0] = 1]
 '''

然后每个+53就可以得到array也就是key了

  • IV很显然是"NSSCTF_2024_R#18"

用脚本转化一下形式使得数据容易套进在线工具里


#genshin wp
enc = [145,
		118,
		31,
		48,
		103,
		110,
		52,
		82,
		113,
		19,
		83,
		44,
		176,
		130,
		138,
		129,
		115,
		110,
		38,
		10,
		42,
		100,
		193,
		105,
		125,
		61,
		7,
		229,
		230,
		180,
		68,
		133,
		11,
		177,
		210,
		122,
		161,
		60,
		129,
		140,
		16,
		45,
		224,
		83,
		238,
		0,
		213,
		157,
		121,
		193,
		135,
		197,
		87,
		118,
		155,
		110,
		90,
		91,
		30,
		158,
		248,
		44,
		95,
		215,
		166,
		247,
		239,
		43,
		228,
		114,
		227,
		146,
		164,
		137,
		111,
		79,
		143,
		17,
		132,
		14]
print("enc: ")
for i in enc:  
    print(hex(i), end=' ')
print("\n")
from z3 import *
from Crypto.Util.number import *
bucket = [Int("bucket[%d]" % i) for i in range(16)]
s = Solver()
s.add(bucket[0] * 40 + bucket[1] * 65 + bucket[2] * -53 + bucket[3] * 70 + bucket[4] * -84 + bucket[5] * -38 + bucket[6] * 94 + bucket[7] * -39 + bucket[8] * -91 + bucket[9] * -35 + bucket[10] * 54 + bucket[11] * 17 + bucket[12] * 45 + bucket[13] * 92 + bucket[14] * -29 + bucket[15] * 61 == 3004)
s.add(bucket[0] * -15 + bucket[1] * 74 + bucket[2] * -89 + bucket[3] * -82 + bucket[4] * -92 + bucket[5] * 27 + bucket[6] * 21 + bucket[7] * -24 + bucket[8] * -82 + bucket[9] * -58 + bucket[10] * -36 + bucket[11] * 64 + bucket[12] * -49 + bucket[13] * -22 + bucket[14] * 59 + bucket[15] * -47 == -674)
s.add(bucket[0] * 67 + bucket[1] * -23 + bucket[2] * 63 + bucket[3] * -38 + bucket[4] * -32 + bucket[5] * 61 + bucket[6] * -71 + bucket[7] * 49 + bucket[8] * 83 + bucket[9] * -92 + bucket[10] * -16 + bucket[11] * 65 + bucket[12] * -22 + bucket[13] * 12 + bucket[14] * -85 + bucket[15] * 74 == 945 )
s.add(bucket[0] * -49 + bucket[1] * 48 + bucket[2] * -11 + bucket[3] * 20 + bucket[4] * -14 + bucket[5] * 92 + bucket[6] * -19 + bucket[7] * 32 + bucket[8] * 64 + bucket[9] * -77 + bucket[10] * 49 + bucket[11] * -19 + bucket[12] * 72 + bucket[13] * -64 + bucket[14] * 85 + bucket[15] * 54 == 1721)
s.add(bucket[0] * 36 + bucket[1] * -21 + bucket[2] * -59 + bucket[3] * -54 + bucket[4] * -96 + bucket[5] * -81 + bucket[6] * -33 + bucket[7] * 31 + bucket[8] * -41 + bucket[9] * -70 + bucket[10] * -27 + bucket[11] * 24 + bucket[12] * 95 + bucket[13] * -61 + bucket[14] * -17 + bucket[15] * -52 == -2198)
s.add(bucket[0] * 78 + bucket[1] * -62 + bucket[2] * 70 + bucket[3] * -69 + bucket[4] * 38 + bucket[5] * 90 + bucket[6] * -52 + bucket[7] * 41 + bucket[8] * 63 + bucket[9] * -65 + bucket[10] * -15 + bucket[11] * 59 + bucket[12] * -31 + bucket[13] * 54 + bucket[14] * 33 + bucket[15] * -57 == -1833)
s.add(bucket[0] * 56 + bucket[1] * 75 + bucket[2] * 71 + bucket[3] * 78 + bucket[4] * -39 + bucket[5] * -84 + bucket[6] * 55 + bucket[7] * 54 + bucket[8] * -12 + bucket[9] * -57 + bucket[10] * 32 + bucket[11] * -19 + bucket[12] * 13 + bucket[13] * -83 + bucket[14] * 11 + bucket[15] * -67 == 829)
s.add(bucket[0] * 10 + bucket[1] * -97 + bucket[2] * 56 + bucket[3] * -61 + bucket[4] * 45 + bucket[5] * -22 + bucket[6] * 33 + bucket[7] * 81 + bucket[8] * 32 + bucket[9] * 49 + bucket[10] * -19 + bucket[11] * -18 + bucket[12] * 80 + bucket[13] * -98 + bucket[14] * 79 + bucket[15] * -36 == -2551)
s.add(bucket[0] * 24 + bucket[1] * -61 + bucket[2] * 91 + bucket[3] * 93 + bucket[4] * 76 + bucket[5] * 54 + bucket[6] * -33 + bucket[7] * -29 + bucket[8] * -72 + bucket[9] * 20 + bucket[10] * 48 + bucket[11] * 79 + bucket[12] * 76 + bucket[13] * 68 + bucket[14] * 51 + bucket[15] * 25 == 2996)
s.add(bucket[0] * -83 + bucket[1] * -77 + bucket[2] * -64 + bucket[3] * -38 + bucket[4] * -13 + bucket[5] * -85 + bucket[6] * 33 + bucket[7] * -76 + bucket[8] * 27 + bucket[9] * 14 + bucket[10] * -79 + bucket[11] * -63 + bucket[12] * -78 + bucket[13] * 53 + bucket[14] * -73 + bucket[15] * 61 == -2315)
s.add(bucket[0] * 84 + bucket[1] * -67 + bucket[2] * 57 + bucket[3] * 26 + bucket[4] * 94 + bucket[5] * 20 + bucket[6] * -71 + bucket[7] * -88 + bucket[8] * -28 + bucket[9] * -13 + bucket[10] * -40 + bucket[11] * 76 + bucket[12] * -14 + bucket[13] * 33 + bucket[14] * 76 + bucket[15] * -75 == -150)
s.add(bucket[0] * -60 + bucket[1] * 88 + bucket[2] * -66 + bucket[3] * -72 + bucket[4] * 41 + bucket[5] * 49 + bucket[6] * 48 + bucket[7] * -77 + bucket[8] * -42 + bucket[9] * 25 + bucket[10] * -50 + bucket[11] * -84 + bucket[12] * 40 + bucket[13] * 50 + bucket[14] * -83 + bucket[15] * -27 == -1919)
s.add(bucket[0] * -16 + bucket[1] * -53 + bucket[2] * -21 + bucket[3] * -44 + bucket[4] * 26 + bucket[5] * -56 + bucket[6] * -90 + bucket[7] * -93 + bucket[8] * -73 + bucket[9] * 48 + bucket[10] * 15 + bucket[11] * -43 + bucket[12] * -61 + bucket[13] * -24 + bucket[14] * 71 + bucket[15] * 67 == -1199)
s.add(bucket[0] * 55 + bucket[1] * -34 + bucket[2] * -22 + bucket[3] * 60 + bucket[4] * 93 + bucket[5] * -95 + bucket[6] * 50 + bucket[7] * 36 + bucket[8] * -48 + bucket[9] * -26 + bucket[10] * -94 + bucket[11] * -35 + bucket[12] * 21 + bucket[13] * -27 + bucket[14] * 91 + bucket[15] * -76 == -1163)
s.add(bucket[0] * 64 + bucket[1] * -50 + bucket[2] * -23 + bucket[3] * -70 + bucket[4] * -78 + bucket[5] * 34 + bucket[6] * 26 + bucket[7] * 64 + bucket[8] * -72 + bucket[9] * 10 + bucket[10] * -96 + bucket[11] * 61 + bucket[12] * -15 + bucket[13] * 31 + bucket[14] * 36 + bucket[15] * 50 == -266)
s.add(bucket[0] * -27 + bucket[1] * 86 + bucket[2] * -61 + bucket[3] * 89 + bucket[4] * -53 + bucket[5] * 10 + bucket[6] * -42 + bucket[7] * 92 + bucket[8] * -48 + bucket[9] * 13 + bucket[10] * 84 + bucket[11] * -71 + bucket[12] * 93 + bucket[13] * 54 + bucket[14] * -69 + bucket[15] * -30 == 892)
if s.check() == sat:
    s = s.model()
print(s)
bucket[15] = 16
bucket[13] = 0
bucket[10] = 1
bucket[11] = 17
bucket[14] = 2
bucket[4] = 2
bucket[2] = 1
bucket[1] = 14
bucket[5] = 1
bucket[7] = 0
bucket[12] = 2
bucket[6] = 1
bucket[3] = 17
bucket[8] = 2
bucket[9] = 4
bucket[0] = 1
array = [0]*16
print(bucket)
for i in range(len(bucket)):
    array[i] = bucket[i] + 53
key = ''
for i in array:
	key += chr(i)
print("key:",key)
#iv = "NSSCTF_2024_R#18"  
'''
enc: 
0x91 0x76 0x1f 0x30 0x67 0x6e 0x34 0x52 0x71 0x13 0x53 0x2c 0xb0 0x82 0x8a 0x81 0x73 0x6e 0x26 0xa 0x2a 0x64 0xc1 0x69 0x7d 0x3d 0x7 0xe5 0xe6 0xb4 0x44 0x85 0xb 0xb1 0xd2 0x7a 0xa1 0x3c 0x81 0x8c 0x10 0x2d 0xe0 0x53 0xee 0x0 0xd5 0x9d 0x79 0xc1 0x87 0xc5 0x57 0x76 0x9b 0x6e 0x5a 0x5b 0x1e 0x9e 0xf8 0x2c 0x5f 0xd7 0xa6 0xf7 0xef 0x2b 0xe4 0x72 0xe3 0x92 0xa4 0x89 0x6f 0x4f 0x8f 0x11 0x84 0xe

[bucket[15] = 16,
 bucket[13] = 0,
 bucket[10] = 1,
 bucket[11] = 17,
 bucket[14] = 2,
 bucket[4] = 2,
 bucket[2] = 1,
 bucket[1] = 14,
 bucket[5] = 1,
 bucket[7] = 0,
 bucket[12] = 2,
 bucket[6] = 1,
 bucket[3] = 17,
 bucket[8] = 2,
 bucket[9] = 4,
 bucket[0] = 1]
[1, 14, 1, 17, 2, 1, 1, 0, 2, 4, 1, 17, 2, 0, 2, 16]
key: 6C6F7665796F757E
'''

几个参数注意一下就可以了

b. 修改逻辑(动态分析)

但考虑修改抽卡概率的方法可能更方便

尝试在这里下断点但是直接抽卡不运行这一段代码,无法在这里断下来

只能修改代码逻辑

我们找到这个GetRandomItem函数,发现要经过Check才可以出发生成flag的Gift函数

即bucket必须满足上面我们z3解出来的那个值,我们直接把bucket赋值

在GachaHistoryBucket中,修改GetBucket()或者直接修改bucket初始值都可以

修改初始值的话记得选择编辑类,不然编译不通过

抽取的部分也要改一下

我们要的flag在这里,修改一下使得它必然被执行

保存一下运行

OCR提取一下文字,少数几个错字自己改一下

2. NewYearGift

IDA打开来没什么头绪,不过似乎和dll有关

尝试从程序中提取dll文件

这里用静态提取即可

用010Editor打开,搜索PE文件头,开头有一个中间有一个,应该是打包在exe里的dll

复制MZ到结尾的部分

后面可以看到一些相关的字段

新建一个十六进制文件粘贴进去

然后就可以在dnspy分析,主逻辑很清晰

比较简便的方法就是把Program的代码都抠出来,去掉if判断的部分直接编译

3. SenpaiCalc

IDA反编译非常难看,dnspy、ILspy都不行,因为使用了NativeAOT编译

NativeAOT 是 dotnet 新增加的运行模式,自.NET7之后便开始支持。其中,AOT是 Ahead-Of-Time 的缩写,和 JIT 边运行边编译不同,NativeAOT 直接将 IL 代码编译为目标平台的机器码发布,它的文件大小、启动时间和内存占用均比 JIT 低。但与之相对的是,逆向难度也要比以前更加高,以往使用DnSpy、ILSpy等工具在这里通通失效。因此,在没有更好的针对性逆向工具诞生之前,IDA仍然是优选。

不过题目提供了pdb文件,可以恢复符号表

尽管如此,符号表依然是依托答辩

搜一下Main,看一下相邻地址的函数

就可疑的函数名查看,最终锁定SenpaiCalc_Program__Senpai函数

还是一坨,不过只关注if判断部分,只是将高低四位交换

密文在SenpaiCalc__PrivateImplementationDetails____278942435594DEEBEFE7E3ACD891A0F13784E54403702D22BEF1387DEB336845

脚本就很简单了

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    char enc[] = { 0xE4, 0x35, 0x35, 0x34, 0x45, 0x64, 0xB7, 0x63, 0x37, 0xE6,
                  0x76, 0x93, 0x56, 0x96, 0x93, 0xB6, 0x93, 0x93, 0x03, 0x93,
                  0xE6, 0x33, 0x36, 0x17, 0x53, 0xE6, 0x23, 0x27, 0x37, 0xC6,
                  0x76, 0x97, 0x23, 0x33, 0x86, 0x87, 0xB6, 0x63, 0xC6, 0xF6,
                  0x33, 0x86, 0x56, 0xC6, 0x23, 0x03, 0xD6, 0x86, 0xC6, 0x96,
                  0x67, 0x33, 0xE6, 0x07, 0x96, 0x47, 0x23, 0x57, 0x83, 0x97,
                  0x26, 0xB6, 0x43, 0xD7 };
    char flag[65];
    int i;
    for (i = 0; i < 64; i++)
    {
        flag[i] = (((enc[i] & 0b1111) << 4) | ((enc[i] & 0b11110000) >> 4));
    }
    printf("%s", flag);
}
//NSSCTF{6sng9ei9k9909n3cq5n2rslgy23hxk6lo3hel20mhliv3npit2u8ybk4}

4. 炒鸡常见的编码哇

主逻辑如图,类似魔改base64

类似rc4初始化的函数,实际上只是打乱了bin内容

然后用爆破逆向

#swbase wp
enc = [0xE2, 0xF7, 0xD3, 0xE2, 0xC8, 0xB4, 0xD8, 0xC5, 0xCF, 0xB4, 
       0xE7, 0xEE, 0xE1, 0xD9, 0xF1, 0xEF, 0xCB, 0xEB, 0xD9, 0xC9, 
       0xCE, 0xC5, 0xD9, 0xE5, 0xCC, 0xB7, 0xD1, 0xED, 0xE0, 0xB4, 
       0xF1, 0xEE, 0xE0, 0xE7, 0xD2, 0xF6, 0xCB, 0xF3, 0xC9, 0xF3, 
       0xD3, 0xD5, 0xEF]
map = [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, 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, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 
       0xB7, 0xB8, 0xAA, 0xAE]

v1 = []
for i in enc:
    v1.append(map.index(i)) #计算索引
print("v1:",v1)

bins = []
for i in v1:
    bins.append(bin(i)[2:].zfill(6)) #十进制转二进制,去掉开头的0b,补齐到6位
print("bins:",bins)

res = ""
for i in bins:
    for j in range(0b111111):
        tem = list(bin(j)[2:].zfill(6))
        v3 = 0
        v4 = 0
        for k in range(0,256,2):
            v3 = (k + 1)%256
            v4 = (v3 + v4 + 1) % 256
            tem[v3 % 6],tem[v4 % 6] = tem[v4 % 6],tem[v3 % 6]
        d = "".join(tem)
        if d == i:
            res += bin(j)[2:].zfill(6)
            break
print("res:",res)
#爆破求打乱前的二进制数组

binflag = [res[i:i+8] for i in range(0, len(res), 8)]
print("binflag:",binflag)
flag = ''
for i in binflag:
    flag += chr(int(i,2))
print("flag:",flag)
'''
v1: [28, 49, 19, 28, 8, 57, 24, 5, 15, 57, 33, 40, 27, 25, 43, 41, 11, 37, 25, 9, 14, 5, 25, 31, 12, 60, 17, 39, 26, 57, 43, 40, 26, 33, 18, 48, 11, 45, 9, 45, 19, 21, 41]
bins: ['011100', '110001', '010011', '011100', '001000', '111001', '011000', '000101', '001111', '111001', '100001', '101000', '011011', '011001', '101011', '101001', '001011', '100101', '011001', '001001', '001110', '000101', '011001', '011111', '001100', '111100', '010001', '100111', '011010', '111001', '101011', '101000', '011010', '100001', '010010', '110000', '001011', '101101', '001001', '101101', '010011', '010101', '101001']
res: 010011100101001101010011010000110101010001000110011110110101100100110000011101010101111100110100011100100110010101010100011010000110010101011111010010110011000101101110011001110101111100110000011001100100001001100001011100110110010100110110001101000111110100
binflag: ['01001110', '01010011', '01010011', '01000011', '01010100', '01000110', '01111011', '01011001', '00110000', '01110101', '01011111', '00110100', '01110010', '01100101', '01010100', '01101000', '01100101', '01011111', '01001011', '00110001', '01101110', '01100111', '01011111', '00110000', '01100110', '01000010', '01100001', '01110011', '01100101', '00110110', '00110100', '01111101', '00']
flag: NSSCTF{Y0u_4reThe_K1ng_0fBase64}
'''

5. EzADVM

主逻辑在libezadvm.so里,需要解压apk

用IDA分析,是vm,但是并没有模拟到汇编层面

提出opcode打印指令,大部分是循环

opcode = [0x01, 0x01, 0x21, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 
  0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 
  0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 
  0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 
  0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61, 
  0x67, 0x7B, 0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 
  0x75, 0x74, 0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 
  0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 
  0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 
  0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66, 
  0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 
  0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 
  0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 
  0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 0x57, 0x44, 0x61, 0x44, 
  0x64, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69, 0x73, 
  0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 0x6C, 
  0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 
  0x57, 0x44, 0x61, 0x44, 0x64, 0x66, 0x6C, 0x61, 0x67, 0x7B, 
  0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 
  0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 
  0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x66, 0x6C, 0x61, 0x67, 0x7B, 
  0x59, 0x75, 0x69, 0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 
  0x69, 0x66, 0x75, 0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xF1, 
  0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 
  0xE5, 0xD4, 0xA1, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 0xA1, 0x53, 
  0x57, 0x44, 0x61, 0x44, 0x64, 0xF1, 0xC3, 0xB2, 0xE5, 0xD4, 
  0xA1, 0x99, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0x66, 0x6C, 0x61, 0x67, 0x7B, 0x59, 0x75, 0x69, 
  0x73, 0x61, 0x62, 0x65, 0x61, 0x75, 0x74, 0x69, 0x66, 0x75, 
  0x6C, 0x67, 0x69, 0x72, 0x6C, 0x7D, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 
  0xBF, 0xBB, 0xBF, 0xBB, 0xBF, 0xBB, 0x53, 0x57, 0x44, 0x44, 
  0xBF, 0xBB, 0xBF, 0xBB, 0x99, 0x53, 0x57, 0x44, 0x44, 0x53, 
  0x57, 0x44, 0x44, 0x88, 0xFF, 0x53, 0x57, 0x44, 0x44]

for i in opcode:
    match i:
        case 0xA1:
            print("enc[i - 1] = v25 & v22")
        case 0xC3:
            print("v22 = v17 | v18")
        case 0xB2:
            print("v23 = ~v17")
        case 0xE5:
            print("v24 = ~v18")
        case 0xF1:
            print("v17 = input[i]")
            print("v18 = input[++i]")
        case 0xD4:
            print("v25 = v24 | v23")
        case 0xBF:
            print("v6 = i++")
            print("v21[0] = enc[v6]")
        case 0x99:
            print("i = 0")
        case 0xBB:
            print("enc[i - 1] = i + v21[0] - 1")
'''
v17 = input[i]
v18 = input[++i]
v22 = v17 | v18
v23 = ~v17
v24 = ~v18
v25 = v24 | v23
enc[i - 1] = v25 & v22    
#((~input[i])|(~input[i+1]))&((input[i])|(input[i+1])) 相当于input[i]^input[i+1]
…………
i = 0
v6 = i++
v21[0] = enc[v6]
enc[i - 1] = i + v21[0] - 1       #enc[i - 1] = i + enc[i] - 1
…………
i = 0
'''

主要逻辑就是一个 ((~input[i])|(~input[i+1]))&((input[i])|(input[i+1])) 相当于input[i]^input[i+1]以及 +i的操作

enc = [  0x1D, 0x01, 0x12, 0x1A, 0x16, 0x42, 0x39, 0x0F, 0x38, 0x09, 
  0x13, 0x31, 0x28, 0x38, 0x67, 0x6E, 0x1B, 0x61, 0x7C, 0x24, 
  0x1F, 0x47, 0x44, 0x81, 0x6A, 0x2C, 0x6D, 0x2B, 0x2C, 0x2D, 
  0x6A, 0x9C]
for i in range(32):
    enc[i] -= i
print(enc)
for i in range(30,-1,-1):
    enc[i] ^= enc[i + 1]
print(enc)
flag = ''
for i in enc:
    flag += chr(i)
print(flag)
#NSSCTF{H@ppy_Ch1ne5_NEwY3ar!1!1}
### 关于NSSCTF Round#28的比赛详情 目前尚未发现有关NSSCTF Round#28的具体官方公告或详细资料[^1]。然而,基于以往的赛事记录和模式分析,可以推测此轮比赛可能延续了之前几轮的特点,即注重实际操作能力以及对漏洞利用的深入理解。 #### 比赛参与方式 通常情况下,NSSCTF系列竞赛会开放在线注册通道供参赛者报名参加。具体步骤如下: - **访问官网**:前往NSSCTF官方网站或者其指定平台页面。 - **完成注册**:填写必要个人信息并确认提交表单。 - **获取资格**:成功登记后将收到进一步通知邮件说明后续流程安排。 对于最新一轮次(Round#28),建议密切关注主办方发布的动态更新消息渠道,例如社交媒体账号、论坛帖子或是往期选手分享的经验总结文档等资源来掌握确切日期与规则变化情况[^4]。 #### 题目解析方向预测 依据前序多场次活动案例来看,预计本轮也将围绕以下几个方面展开挑战项目设计: 1. **Web安全领域** - 文件上传机制缺陷检测 如同引用提到过的`Golang`环境下的文件处理不当实例[^3],此类场景下需仔细审查服务端验证逻辑是否存在疏漏之处允许非法脚本执行权限提升攻击行为发生。 2. **本地文件包含(LFI)** - 探讨绕过传统防护手段方法论探讨 结合hxp CTF 2021中的"Includer's Revenge"难题解决方案思路启发,在不控制任何外部可控实体的前提下实现敏感数据读取目标达成目的[^2]. 以下是针对上述两种典型问题类型的简单示例代码片段展示: ```python import os from flask import Flask, request, send_file app = Flask(__name__) @app.route('/upload', methods=['POST']) def upload(): file = request.files['file'] filename = secure_filename(file.filename) # Ensure safe filenames are used. filepath = f'uploads/{filename}' file.save(filepath) if not allowed_file(filename): # Custom function to check extensions whitelist/blacklist policies etc.. return 'Invalid File Type!', 400 process_image(filepath) # Hypothetical image processing routine call here... return 'File uploaded successfully!' ``` 以上Python程序展示了基本的文件接收存储过程需要注意的安全要点;而下面这段PHP则演示了一个简单的LFI风险示范: ```php <?php if(isset($_GET['page'])){ $page = $_GET['page']; }else{ die('No page specified'); } // No proper sanitization performed on user input leading directly into include statement below causing potential Local File Inclusion vulnerability issues. include($page . '.php'); ?> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值