2023年春秋杯网络安全联赛春季赛 RE复盘(部分待补)

目录

sum

Pytrans

BWBA

Poisoned_tea_CHELL

第一种找程序加密函数的方法

第二种找程序加密函数的方法

解密


这次的春季赛仍是被打爆了,re只做出了一题,发现自己还是太菜了,好在在后期复盘中又收获了许多新知识了,不亏。

sum

获取题目附件,64位,无壳。

 用IDA对程序进行反编译,找到关键的main函数。

int __cdecl main(int argc, const char **argv, const char **envp)
{char *v3; // rbpint v4; // r14dunsigned int v5; // r12d__int64 i; // rbxchar v7; // alint v8; // eaxconst char *v9; // raxv3 = &matrix;v4 = 1;v5 = 0;puts("Welcome to Solver!");do{for ( i = 0LL; i != 9; ++i ){if ( !v3[i] ){v7 = getchar();if ( (v7 - 49) > 8u )v4 = 0;elsev3[i] = v7 - 48;}v8 = v3[i];v5 += v8;}v3 += 9;}while ( v3 != &matrix + 81 );if ( v4 && verify("Welcome to Solver!", argv) ){puts("You Win!");__snprintf_chk(buf, 32LL, 1LL, 32LL, "%d", v5);v9 = str2md5(buf, strlen(buf));__printf_chk(1LL, "flag is: flag{%s}\n\n", v9);exit(0);}puts("Again~");return 0;
}

v7是来获取输入内容的,matrix中存放着一些值,长度为81,一开始以为是一个9*9的迷宫题,但发现matrix中存放的不只有0和1,还有一些其他值,不像是迷宫题。

 用Chatgpt帮忙辅助分析了一下,才知道这是一个九宫格数独游戏。

 因此,明白了matrix中存放的是九宫格中已知的值,写脚本dump出数独:

maze = [5, 3, 0, 0, 7, 0, 0, 0, 0, 6, 0, 0, 1, 9, 5, 0, 0, 0, 0, 9, 8, 0, 0, 0, 0, 6, 0, 8, 0, 0, 0, 6, 0, 0, 0, 3, 4, 0, 0, 8, 0, 3, 0, 0, 1, 7, 0, 0, 0, 2, 0, 0, 0, 6, 0, 6, 0, 0, 0, 0, 2, 8, 0, 0, 0, 0, 4, 1, 9, 0, 0, 5, 0, 0, 0, 0, 8, 0, 0, 7, 9]for i in range(0,len(maze)):if i % 9 == 0:print('\n')print(maze[i],end=" ")
5 3 0 0 7 0 0 0 0 6 0 0 1 9 5 0 0 0 0 9 8 0 0 0 0 6 0 8 0 0 0 6 0 0 0 3 4 0 0 8 0 3 0 0 1 7 0 0 0 2 0 0 0 60 6 0 0 0 0 2 8 00 0 0 4 1 9 0 0 50 0 0 0 8 0 0 7 9 

数独中的0就是需要填的位置,找到一个在线解数独的网站在线数独求解器 (gwalker.cn)

 这里想吐槽一下自己太粗心了,把绿色块当成是解出来的值,实际白色块才是解出来的。

468912723481342575971422657913948591537428763345261

 接着将解出的值输入到程序中即可拿到flag。

 FLAG:flag{bbcbff5c1f1ded46c25d28119a85c6c2}

Pytrans

获取题目文件,64位,无壳。

 根据题目介绍猜测是用python编写的程序,所以可以直接用pyinstxtractor转成pyc文件,再用uncompyle6转成py文件。

# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: run.py
import base64, zlib, ctypes
try:mylib = ctypes.cdll.LoadLibrary('./mylib.so')
except:print('file no exit!')
else:a = []
try:sstr = input("Please enter the 10 digits and ending with '\\n': ").split(' ')if len(sstr) == 10:for i in sstr:a.append(int(i))mylib.check.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.c_int)mylib.check.restype = ctypes.c_char_pscrambled_code_string = mylib.check((ctypes.c_int * len(a))(*a), len(a))try:decoded_data = base64.b64decode(scrambled_code_string)uncompressed_data = zlib.decompress(decoded_data)exec(__import__('marshal').loads(uncompressed_data))except:print('Incorrect input caused decryption failure!')except:pass
# okay decompiling run.pyc

 可以看见程序调用了一个mylib.so库,从pyinstxtractor导出的文件中找到mylib.so库。

 放入IDA中进行分析,找到check函数。

void *__fastcall check(_DWORD *x)
{void *v2; // [rsp+18h] [rbp-28h]char v3[24]; // [rsp+20h] [rbp-20h] BYREFunsigned __int64 v4; // [rsp+38h] [rbp-8h]v4 = __readfsqword(0x28u);sub_1239();if ( -27 * x[7] + -11 * x[6] + 16 * x[5] + *x + 2 * x[1] - x[2] + 8 * x[3] - 14 * x[4] + 26 * x[8] + 17 * x[9] != 14462|| -30 * x[8] + 13 * x[5] + x[3] + x[1] + 2 * *x - 15 * x[4] - 24 * x[6] + 16 * x[7] + 36 * x[9] != -2591|| 16 * x[6] + -21 * x[5] + 7 * x[3] + 3 * x[1] - *x - x[2] + 12 * x[4] - 23 * x[7] + 25 * x[8] - 18 * x[9] != 2517|| -6 * x[6] + 2 * x[2] - x[1] + 2 * x[5] + 9 * x[7] + 2 * x[8] - 5 * x[9] != 203|| -5 * x[8] + 6 * x[7] + 3 * x[1] - x[3] - x[5] + x[6] + 5 * x[9] != 3547|| -9 * x[8] + x[4] + x[2] + x[7] - 5 * x[9] != -7609|| 2 * x[5] + -x[3] - x[4] + x[8] + 6 * x[9] != 4884|| x[6] - x[7] + 2 * x[8] != 1618|| x[4] - x[6] + 2 * x[9] != 1096|| x[8] + x[4] + x[3] + x[2] + x[1] + *x - x[5] - x[6] - x[7] - x[9] != 711|| 2 * (2 * x[4] + x[3]) + 5 * x[5] != 7151 ){return 0LL;}v3[0] = 0;v3[1] = 0;v3[2] = 0;v3[3] = 0;v3[4] = 0;v3[5] = 0;v3[6] = 0;v3[7] = 0;v3[8] = 0;v3[9] = 0;v3[10] = 0;v3[11] = 0;v3[12] = 0;v3[13] = 0;v3[14] = 0;v3[15] = x[4] % 255;v2 = malloc(0x4F0uLL);sub_27E4(&unk_62C0, v2, v3);return v2;
}

 这里对输入值进行了比较,可以用z3进行求解来得出正确的输入值,脚本如下:

from z3 import *
def main():x = [BitVec("x%d"%i,16)for i in range(10)]s = Solver()s.add( -27 * x[7] + -11 * x[6] + 16 * x[5] + x[0] + 2 * x[1] - x[2] + 8 * x[3] - 14 * x[4] + 26 * x[8] + 17 * x[9] == 14462)s.add( -30 * x[8] + 13 * x[5] + x[3] + x[1] + 2 * x[0] - 15 * x[4] - 24 * x[6] + 16 * x[7] + 36 * x[9] == -2591)s.add( 16 * x[6] + -21 * x[5] + 7 * x[3] + 3 * x[1] - x[0] - x[2] + 12 * x[4] - 23 * x[7] + 25 * x[8] - 18 * x[9] == 2517)    s.add( -6 * x[6] + 2 * x[2] - x[1] + 2 * x[5] + 9 * x[7] + 2 * x[8] - 5 * x[9] == 203)s.add( -5 * x[8] + 6 * x[7] + 3 * x[1] - x[3] - x[5] + x[6] + 5 * x[9] == 3547)s.add( -9 * x[8] + x[4] + x[2] + x[7] - 5 * x[9] == -7609)s.add( 2 * x[5] + -x[3] - x[4] + x[8] + 6 * x[9] == 4884)  s.add( x[6] - x[7] + 2 * x[8] == 1618)s.add( x[4] - x[6] + 2 * x[9] == 1096)   s.add( x[8] + x[4] + x[3] + x[2] + x[1] + x[0] - x[5] - x[6] - x[7] - x[9] == 711)    s.add( 2 * (2 * x[4] + x[3]) + 5 * x[5] == 7151)if s.check() == sat:value = s.model()for i in range(10):print(value[x[i]],end=" ")if __name__ == '__main__':main()

 获得正确的输入值。

511 112 821 949 517 637 897 575 648 738

 将这串值输入程序,以为能直接拿到flag了,结果发现还有内容需要输入。

根据内容,猜测是走迷宫,但在这里就被卡住了,一直在mylib.so中找实现走迷宫部分的函数,结果没找到,虽然注意到了check函数中的sub_27E4函数,但不知如何去动调获取数据。

后来,看到其他师傅写的wp时,我才知道dump出来的py文件实际就已经将内容解密并执行了。

    mylib.check.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.c_int)mylib.check.restype = ctypes.c_char_pscrambled_code_string = mylib.check((ctypes.c_int * len(a))(*a), len(a))try:decoded_data = base64.b64decode(scrambled_code_string)uncompressed_data = zlib.decompress(decoded_data)exec(__import__('marshal').loads(uncompressed_data))

 uncompressed_data 中存放的就是解密后的内容,输出uncompressed_data。

 猜测这是一个文件,将其写入进一个文件中。

# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: run.py
import base64, zlib, ctypes
try:mylib = ctypes.cdll.LoadLibrary('./mylib.so')
except:print('file no exit!')
else:a = []
try:sstr = input("Please enter the 10 digits and ending with '\\n': ").split(' ')if len(sstr) == 10:for i in sstr:a.append(int(i))mylib.check.argtypes = (ctypes.POINTER(ctypes.c_int), ctypes.c_int)mylib.check.restype = ctypes.c_char_pscrambled_code_string = mylib.check((ctypes.c_int * len(a))(*a), len(a))try:decoded_data = base64.b64decode(scrambled_code_string)uncompressed_data = zlib.decompress(decoded_data)# print(uncompressed_data)file1 = open("./second","+ab")file1.write(uncompressed_data)file1.close()exec(__import__('marshal').loads(uncompressed_data))except:print('Incorrect input caused decryption failure!')except:pass
# okay decompiling run.pyc

 写入后,将这个文件放入Winhex中查看,像是一个缺少文件头的pyc文件。

 将之前dump出来的pyc文件的文件头粘贴到这个文件里,再用uncompyle6来转成py文件,就可以得到以下内容:

# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: sample.py
footprint = '3qzqns4hj6\neeaxc!4a-%\nd735_@4l6g\nf1gd1v7hdm\n1+$-953}81\na^21vbnm3!\n-#*f-e1d8_\n2ty9uipok-\n6r1802f7d1\n9wez1c-f{0'
xx0000 = []
footprintlist = footprint.split('\n')
for i in range(len(footprintlist)):xx0000.append(list(footprintlist[i]))
else:def xxxx000x0(num):xx000000 = format(num, '010b')return xx000000oxooxxxxxoooo = []xx0000000 = input("Please enter the previous 10 digits again and ending with '\\n': ").split(' ')if len(xx0000000) == 10:try:for i in xx0000000:oxooxxxxxoooo.append(int(i))except:print('err input!')exit(-1)else:print('err input!')exit(-1)for i in range(len(oxooxxxxxoooo)):oxooxxxxxoooo[i] = list(xxxx000x0(oxooxxxxxoooo[i]))else:xx0000x000 = oxooxxxxxooooprint(xx0000x000)x, o = (0, 0)xx00x00x0xxx00 = [(x, o)]xx00x00x0xxx00input = list(input('input maze path:'))count = 0while (x, o) != (9, 9):if count < len(xx00x00x0xxx00input):xx0000x0xxx00 = xx00x00x0xxx00input[count]if xx0000x0xxx00 == 'a':if o > 0 and xx0000x000[x][o - 1] == '0':o -= 1count += 1xx00x00x0xxx00.append((x, o))else:print('wrong!')exit(-1)elif xx0000x0xxx00 == 'd':if o < 9 and xx0000x000[x][o + 1] == '0':count += 1o += 1xx00x00x0xxx00.append((x, o))else:print('wrong!')exit(-1)else:if xx0000x0xxx00 == 'w':if x > 0 and xx0000x000[x - 1][o] == '0':count += 1x -= 1xx00x00x0xxx00.append((x, o))else:print('wrong!')exit(-1)else:if xx0000x0xxx00 == 's':if x < 9 and xx0000x000[x + 1][o] == '0':count += 1x += 1xx00x00x0xxx00.append((x, o))else:print('wrong!')exit(-1)else:print('wrong!')exit(-1)else:print('wrong!')exit(-1)print('right! you maybe got it,flag is flag{the footprint of the maze path}')
# okay decompiling second.pyc

 数组oxooxxxxxoooo存放的是地图,将之前解出的10个值输入,再将oxooxxxxxoooo输出,即可获得地图。

[['0', '1', '1', '1', '1', '1', '1', '1', '1', '1'],['0', '0', '0', '1', '1', '1', '0', '0', '0', '0'],['1', '1', '0', '0', '1', '1', '0', '1', '0', '1'],['1', '1', '1', '0', '1', '1', '0', '1', '0', '1'],['1', '0', '0', '0', '0', '0', '0', '1', '0', '1'],['1', '0', '0', '1', '1', '1', '1', '1', '0', '1'],['1', '1', '1', '0', '0', '0', '0', '0', '0', '1'],['1', '0', '0', '0', '1', '1', '1', '1', '1', '1'],['1', '0', '1', '0', '0', '0', '1', '0', '0', '0'],['1', '0', '1', '1', '1', '0', '0', '0', '1', '0']]

按要求从左上角走到右下角即可。

sddsdssdddwwwddsssssaaaaassddsddwdds

 输入程序中,显示正确。

 接着按照程序给出的提示,将走过路径的对应字符取过来即可,脚本如下:

footprint = '3qzqns4hj6\neeaxc!4a-%\nd735_@4l6g\nf1gd1v7hdm\n1+$-953}81\na^21vbnm3!\n-#*f-e1d8_\n2ty9uipok-\n6r1802f7d1\n9wez1c-f{0'
footprintlist = footprint.split('\n')
cipher = "sddsdssdddwwwddsssssaaaaassddsddwdds"
x = 0
y = 0
flag = ""
flag += footprintlist[x][y]
for c in cipher:if c == 'w':x -= 1flag += footprintlist[x][y]elif c == 's':x += 1flag += footprintlist[x][y]elif c == 'a':y -= 1flag += footprintlist[x][y]elif c == 'd':y += 1flag += footprintlist[x][y]
print(flag)# 3eea35d-953744a-6d838d1e-f9802c-f7d10

 这一题学到了不少,通过这道题也顺便知道了如何去调用动态库中函数,也获得了一些解题经验,收获颇丰。

FLAG:flag{3eea35d-953744a-6d838d1e-f9802c-f7d10}

BWBA

获取题目附件,64位,无壳,而enc文件猜测是密文。

用IDA进行分析,查看main函数。

int __cdecl main(int argc, const char **argv, const char **envp)
{__int64 v3; // raxint v4; // ebxunsigned int v5; // eax__int64 v6; // rax__int64 v7; // raxunsigned __int64 v8; // rbxdouble v10; // [rsp+8h] [rbp-488h]char v11[248]; // [rsp+10h] [rbp-480h] BYREF__int64 v12; // [rsp+108h] [rbp-388h] BYREFchar v13[256]; // [rsp+210h] [rbp-280h] BYREF__int64 v14; // [rsp+310h] [rbp-180h] BYREFchar v15[32]; // [rsp+420h] [rbp-70h] BYREFchar v16[32]; // [rsp+440h] [rbp-50h] BYREFchar v17[16]; // [rsp+460h] [rbp-30h] BYREFchar v18[12]; // [rsp+470h] [rbp-20h] BYREFint i; // [rsp+47Ch] [rbp-14h]std::ifstream::basic_ifstream(v13, "flag.txt", 8LL);if ( std::ios::operator!(&v14) ){v3 = std::operator<<<std::char_traits<char>>(&std::cerr, "Failed to open input file.");std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);v4 = 1;}else{std::string::string(v18);std::getline<char,std::char_traits<char>,std::allocator<char>>(v13, v18);std::ifstream::close(v13);pad_string(v17);str_to_ascii(v16);encrypt(v15, v16);v5 = std::operator|(16LL, 32LL);std::ofstream::basic_ofstream(v11, "enc", v5);if ( std::ios::operator!(&v12) ){v6 = std::operator<<<std::char_traits<char>>(&std::cerr, "Failed to open output file.");std::ostream::operator<<(v6, &std::endl<char,std::char_traits<char>>);v4 = 1;}else{for ( i = 0; ; ++i ){v8 = i;if ( v8 >= std::vector<double>::size(v15) )break;v10 = *std::vector<double>::operator[](v15, i);v7 = std::ostream::operator<<(v11, v10);std::operator<<<std::char_traits<char>>(v7, 32LL);}std::ofstream::close(v11);v4 = 0;}std::ofstream::~ofstream(v11);std::vector<double>::~vector(v15);std::vector<int>::~vector(v16);std::string::~string(v17);std::string::~string(v18);}std::ifstream::~ifstream(v13);return v4;
}

可以看出关键在encrypt函数里,查看encrypt函数。

__int64 __fastcall encrypt(__int64 a1, __int64 a2)
{double *v2; // raxdouble *v3; // raxdouble v5; // [rsp+8h] [rbp-38h]double v6; // [rsp+8h] [rbp-38h]double v7; // [rsp+8h] [rbp-38h]char v8; // [rsp+23h] [rbp-1Dh] BYREFint v9; // [rsp+24h] [rbp-1Ch]int j; // [rsp+28h] [rbp-18h]int i; // [rsp+2Ch] [rbp-14h]v9 = std::vector<int>::size(a2);std::allocator<double>::allocator(&v8);std::vector<double>::vector<int>(a1, v9, 0LL, &v8);std::allocator<double>::~allocator(&v8);for ( i = 0; i < v9; ++i ){for ( j = 0; j < v9; ++j ){v5 = *std::vector<int>::operator[](a2, j);v6 = cos((j + 0.5) * (3.141592653589793 * i) / v9) * v5;v2 = std::vector<double>::operator[](a1, i);*v2 = *v2 + v6;}if ( i )v7 = sqrt(2.0 / v9);elsev7 = sqrt(1.0 / v9);v3 = std::vector<double>::operator[](a1, i);*v3 = *v3 * v7;}return a1;
}

是一个没见过的算法,一开始想尝试着将它逆过来,但是失败了,在这里被困住了。

后来看到其他师傅的wp时才知道,这是一个DCT(离散余弦变换),属于是自己的知识盲区了,知道sqrt是平方根,知道cos是余弦,知道3.14是圆周率,但是凑在却是一点也看不明白了,看来要学的知识还有很多很多。

了解到了IDCT(离散余弦逆变换)是DCT的逆算法,在scipy库和cv2库中都有这两种算法,这里调用cv2库,脚本如下:

import cv2
import numpy
def main():cipher = [370.75, 234.362, -58.0834, 59.8212, 88.8221, -30.2406, 21.8316, 49.9781, -33.5259, 2.69675, 43.5386, -30.2925, -28.0754, 27.593, -2.53962, -27.1883, -5.60777, -0.263937, 6.80326, 8.03022, -6.34681, -0.89506, -6.80685, -13.6088, 27.0958, 29.8439, -21.7688, -20.6925, -13.2155, -37.0994, 2.23679, 37.6699, -3.5, 9.85188, 57.2806, 13.5715, -20.7184, 8.6816, 3.59369, -4.5302, 4.22203, -28.8166, -23.695, 31.2268, 6.58823, -39.9966, -20.7877, -19.7624, -22.031, 16.3285, 2.07557, -26.2521, 16.1914, 18.3976, -26.9295, 3.03769, 41.0412, 20.2598, 14.991, 6.99392, -22.3752, -7.24466, 8.96299, -10.4874, ]value = numpy.array(cipher)value1 = cv2.idct(value)    # value1接受到的是一个二维数组value2 = value1.ravel()     # 将二维数组转成一维数组for i in range(len(value2)):print(chr(round(value2[i])),end="")     # 用round函数对数组中的值进行四舍五入
if __name__ == '__main__':main()# flag{9ab488a7-5b11-1b15-04f2-c230704ecf72}

FLAG:flag{9ab488a7-5b11-1b15-04f2-c230704ecf72}

Poisoned_tea_CHELL

获取题目附件,通过ExeinfoPe进行查壳发现没有壳。

 但用die进行查壳时发现了UPX壳。

 可以猜测出修改了UPX壳的特征码。用UPX指令无法对其进行脱壳。

 这里学到了两种方法来找到程序中的加密函数。

第一种找程序加密函数的方法

将程序放入WinHex中进行查看,看见明显被修改的痕迹。

 将其中所有的“VMP”全部修改为“UPX”,像这样

 这样即可对程序进行脱壳。

 接下来放到IDA中即可进行正常的分析了。

 加密函数就在sub_1536函数中的sub_1403函数中

__int64 sub_1536()
{int v1; // [rsp+8h] [rbp-468h]int i; // [rsp+Ch] [rbp-464h]int j; // [rsp+10h] [rbp-460h]int v4; // [rsp+14h] [rbp-45Ch] BYREFint v5; // [rsp+18h] [rbp-458h]int v6; // [rsp+1Ch] [rbp-454h]int v7[8]; // [rsp+20h] [rbp-450h] BYREFint v8; // [rsp+40h] [rbp-430h]int v9; // [rsp+44h] [rbp-42Ch]int v10; // [rsp+48h] [rbp-428h]int v11; // [rsp+4Ch] [rbp-424h]int v12; // [rsp+50h] [rbp-420h]int v13[258]; // [rsp+60h] [rbp-410h] BYREFunsigned __int64 v14; // [rsp+468h] [rbp-8h]v14 = __readfsqword(0x28u);v1 = 1;v7[0] = 5;v7[1] = 2;v7[2] = dword_4020;v7[3] = dword_4010;v7[4] = 0;memset(v13, 0, 0x400uLL);puts("##############");printf("\ninput flag: ");__isoc99_scanf("%s", v13);getchar();v4 = 0;v5 = 0;v6 = 0;for ( i = 0; v13[i]; i += 2 ){v4 = v13[i];v5 = v13[i + 1];sub_1403(dword_4030, &v4, v7);v13[i] = v4;v13[i + 1] = v5;}v8 = 0;v9 = 0;v10 = 0;v11 = 0;v12 = 0;for ( j = 0; v13[j]; j += 2 ){v8 = dword_41E0[j];v9 = dword_41E0[j + 1];v10 = v13[j];v11 = v13[j + 1];if ( v8 != v10 || v9 != v11 ){v1 = 0;break;}}if ( v1 )puts("Your input is correct.");elseputs("Your input is incorrect.");return 0LL;
}
__int64 __fastcall sub_1403(int a1, unsigned int *a2, __int64 a3)
{__int64 result; // raxint i; // [rsp+24h] [rbp-14h]unsigned int v5; // [rsp+28h] [rbp-10h]unsigned int v6; // [rsp+2Ch] [rbp-Ch]unsigned int v7; // [rsp+30h] [rbp-8h]v5 = *a2;v6 = a2[1];v7 = 0;for ( i = 0; i < a1; ++i ){v5 += (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*(4LL * (v7 & 3) + a3) + v7);v7 -= 0x41104111;v6 += (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (*(4LL * ((v7 >> 11) & 3) + a3) + v7);}*a2 = v5;result = v6;a2[1] = v6;return result;
}

第二种找程序加密函数的方法

不对程序进行UPX壳特征码修复,打开IDA,不往其中放文件。通过远程连接来抓执行的进程进行动态调试。

因为是elf文件,所以需要linux系统进行执行,选择“Remote Linux debugger”,填上用来执行程序的虚拟机ip地址。

 

 同时在虚拟机中执行题目程序。

 找到虚拟机中执行的进程,附加到IDA中。

 进来后就到了这个位置。

 虚拟机中程序选择第二个选项。

 然后即可开始动调,进行单步步入,直到找到这一处地方。

 按P生成函数,再按F5转成伪C代码,即可找到加密函数。

__int64 __fastcall sub_7FC907D16536(__int64 a1, __int64 a2)
{__int64 result; // raxint v3; // [rsp+8h] [rbp-468h]int i; // [rsp+Ch] [rbp-464h]int j; // [rsp+10h] [rbp-460h]int v6; // [rsp+14h] [rbp-45Ch] BYREFint v7; // [rsp+18h] [rbp-458h]int v8; // [rsp+1Ch] [rbp-454h]int v9[8]; // [rsp+20h] [rbp-450h] BYREFint v10; // [rsp+40h] [rbp-430h]int v11; // [rsp+44h] [rbp-42Ch]int v12; // [rsp+48h] [rbp-428h]int v13; // [rsp+4Ch] [rbp-424h]int v14; // [rsp+50h] [rbp-420h]int v15[258]; // [rsp+60h] [rbp-410h] BYREFunsigned __int64 v16; // [rsp+468h] [rbp-8h]v16 = __readfsqword(0x28u);v3 = 1;v9[0] = 5;v9[1] = 2;v9[2] = dword_7FC907D19020;v9[3] = dword_7FC907D19010;v9[4] = 0;memset(v15, 0, 0x400uLL);(unk_7FC907D160E0)("##############", a2, v15);(unk_7FC907D16110)("\ninput flag: ");(unk_7FC907D16150)(&unk_7FC907D17025, v15);(unk_7FC907D16130)();v6 = 0;v7 = 0;v8 = 0;for ( i = 0; v15[i]; i += 2 ){v6 = v15[i];v7 = v15[i + 1];(unk_7FC907D16403)(dword_7FC907D19030, &v6, v9);v15[i] = v6;v15[i + 1] = v7;}v10 = 0;v11 = 0;v12 = 0;v13 = 0;v14 = 0;for ( j = 0; v15[j]; j += 2 ){v10 = dword_7FC907D191E0[j];v11 = dword_7FC907D191E0[j + 1];v12 = v15[j];v13 = v15[j + 1];if ( v10 != v12 || v11 != v13 ){v3 = 0;break;}}if ( v3 )(unk_7FC907D160E0)("Your input is correct.");else(unk_7FC907D160E0)("Your input is incorrect.");result = 0LL;if ( v16 != __readfsqword(0x28u) )return (unk_7FC907D16100)();return result;
}

而进行tea系列算法加密的位置就在unk_7FC907D16403中,将其中内容按P生成函数后再用F5转成伪C代码,即可看见整个tea系列算法加密过程。

__int64 __fastcall sub_7FC907D16403(int a1, unsigned int *a2, __int64 a3)
{__int64 result; // raxint i; // [rsp+24h] [rbp-14h]unsigned int v5; // [rsp+28h] [rbp-10h]unsigned int v6; // [rsp+2Ch] [rbp-Ch]unsigned int v7; // [rsp+30h] [rbp-8h]v5 = *a2;v6 = a2[1];v7 = 0;for ( i = 0; i < a1; ++i ){v5 += (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*(4LL * (v7 & 3) + a3) + v7);v7 -= 1091584273;v6 += (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (*(4LL * ((v7 >> 11) & 3) + a3) + v7);}*a2 = v5;result = v6;a2[1] = v6;return result;
}

解密

这里拿脱完壳的程序进行分析,程序很明显可以找到一个像是用来参与加密的v7数组(key数组),此时数组中内容为[5, 2, 8, 7],但通过动调后会发现,后面数组中内容被改成了[5, 2, 9, 7]。

 接着就是后面的两个,一个是算法加密的函数,一个是放密文的数组。

 先查看sub_1403函数,可以看出函数的第一个参数是来确定加密循环次数的,其中存放的值为0x1F(也就是31),但是通过动调发现,这个也被修改了,实际循环次数是0x24(也就是36)。

__int64 __fastcall sub_1403(int a1, unsigned int *a2, __int64 a3)
{__int64 result; // raxint i; // [rsp+24h] [rbp-14h]unsigned int v5; // [rsp+28h] [rbp-10h]unsigned int v6; // [rsp+2Ch] [rbp-Ch]unsigned int v7; // [rsp+30h] [rbp-8h]v5 = *a2;v6 = a2[1];v7 = 0;for ( i = 0; i < a1; ++i ){v5 += (v6 + ((v6 >> 5) ^ (16 * v6))) ^ (*(4LL * (v7 & 3) + a3) + v7);v7 -= 0x41104111;v6 += (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (*(4LL * ((v7 >> 11) & 3) + a3) + v7);}*a2 = v5;result = v6;a2[1] = v6;return result;
}

tea系列算法会有一个DELTA值,而程序中的0x41104111就是其中的DELTA值,"v7 -= 0x41104111"也可以看作"v7 += 0xBEEFBEEF"(将0x41104111进行了取反)。

接着即可开始写解密脚本了。脚本如下:

C语言版解密脚本:

#include<stdio.h>
#include<Windows.h>
void main()
{UINT32 value[] = { 0xECFDA301, 0x61BECDF5, 0xB89E6C7D, 0xCE36DC68, 0x4B6E539E, 0x642EB504, 0x54F9D33C, 0x6D06E365, 0xEA873D53,0xA4618507, 0xD7B18E30, 0xC45B4042 };UINT32 v0 = 0;UINT32 v1 = 0;UINT32 sum = 0xbeefbeef * 36;UINT32 DELTA = 0xbeefbeef;UINT32 k[] = { 5,2,9,7 };int i = 0;int j = 0;for (i = 0; i < 12; i+=2){v0 = value[i];v1 = value[i+1];sum = 0xbeefbeef * 36;for (j = 0; j < 36; j++){v1 -= (v0 + ((v0 >> 5) ^ (16 * v0))) ^ (*(((sum >> 11) & 3) + k) + sum);sum -= 0xbeefbeef;v0 -= (v1 + ((v1 >> 5) ^ (16 * v1))) ^ (*((sum & 3) + k) + sum);}printf("%c", (v0 & 0xff) >> (4 * 8));printf("%c", (v0 & 0xff00) >> (5 * 8));printf("%c", (v0 & 0xff0000) >> (6 * 8));printf("%c", (v0 & 0xff000000) >> (7 * 8));printf("%c", (v1 & 0xff) >> (4 * 8));printf("%c", (v1 & 0xff00) >> (5 * 8));printf("%c", (v1 & 0xff0000) >> (6 * 8));printf("%c", (v1 & 0xff000000) >> (7 * 8));}
}

python版解密脚本:

import ctypes
def tea_decode(v,k): flag = []DELTA = 0xbeefbeefsum = DELTA * 36sum = ctypes.c_uint32(sum)for i in range(0,12,2):v0 = ctypes.c_uint32(v[i])v1 = ctypes.c_uint32(v[i+1])sum.value = DELTA * 36for j in range(36):v1.value -= (v0.value + ((v0.value >> 5) ^ (16 * v0.value))) ^ (k[(((sum.value >> 11) & 3))] + sum.value)sum.value -= DELTAv0.value -= (v1.value + ((v1.value >> 5) ^ (16 * v1.value))) ^ (k[((sum.value & 3))] + sum.value)flag.append(v0.value)flag.append(v1.value)return flagdef main():value = [0xECFDA301, 0x61BECDF5, 0xB89E6C7D, 0xCE36DC68, 0x4B6E539E, 0x642EB504, 0x54F9D33C, 0x6D06E365, 0xEA873D53,0xA4618507, 0xD7B18E30, 0xC45B4042]key = [5,2,9,7]flag = tea_decode(value,key)flag_hex = ""flag_value = ""for i in range(len(flag)):flag_hex += hex((flag[i] & 0xff) >> (0 * 8))[2:].zfill(2)flag_hex += hex((flag[i] & 0xff00) >> (1 * 8))[2:].zfill(2)flag_hex += hex((flag[i] & 0xff0000) >> (2 * 8))[2:].zfill(2)flag_hex += hex((flag[i] & 0xff000000) >> (3 * 8))[2:].zfill(2)for j in range(0,len(flag_hex),2):print(chr(int(flag_hex[j] + flag_hex[j+1], 16)),end="")
if __name__ == '__main__':main()

做题的时候写解密脚本总出问题,就尝试了C语言和python语言两种语言来写两个版本的解密脚本,后来仍然出问题,最后发现问题所在,解密算法中取key数组中值那部分,也就是伪代码程序中“v6 += (v5 + ((v5 >> 5) ^ (16 * v5))) ^ (*(4LL * ((v7 >> 11) & 3) + a3) + v7);”这里要将其中“4LL * ”给在脚本中删去。

大致想到为什么伪代码中需要“4LL * ”了,因为伪代码中是用地址去取值,int型数组中每个元素占4个字节,则需要“4LL * ”才能找到对应位置的元素,而在脚本中直接用数组下标了,所以无需再进行“4LL * ”。

这一道题学到了不少新东西,原先做题的时候还以为题目文件是个动态链接库,因此陷入难题,不知道密文,看到其他师傅写的wp才知道是个去掉UPX特征码的程序,也是第一次知道IDA可以通过远程调试抓到虚拟机中的进程。

FLAG:Thisisflag{cdfec405-3f4b-457e-92fe-f6446098ee2e}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/18259.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023春秋杯春季赛WP-REVERSE(AK)

REVERSE sumPoisoned_tea_CHELLBWBAPytransEmoji ConnectOldSymbolicCode 浅浅写一下RE的WP~ sum 根据代码&#xff0c;可以看出程序只能输入数字&#xff0c;判断matrix数组对应下标的值是否为0&#xff0c;如果为0&#xff0c;则可以输入一个字符&#xff0c;根据代码逻辑&a…

Servlet基础学习

什么是Servlet&#xff1f; Servlet是一种用于接收web网页传回和输出到web的一个Java类&#xff0c;根据不同的实际需要&#xff0c;实现不同的Servlet来对网页数据进行处理。 Servlet的基本处理流程 在网页发起请求之后&#xff0c;编译器首先去寻找web网页的配置文件&…

react仿微信聊天室|react即时聊天IM系统|react群聊

reactredux仿微信聊天IM实战|react仿微信界面|react多人群聊天室 最近一直捣鼓react开发&#xff0c;就运用react开发了个仿微信聊天室reactChatRoom项目&#xff0c;基于reactreact-domreact-router-domreduxreact-reduxwebpack2.0antdesignwcPop等技术混合开发&#xff0c;实…

DES加密解密 Feistel算法网络结构 详讲

文章目录 简单知识导入&#xff1a;具体过程&#xff1a;IP置换&#xff08; 64 − > 64 64->64 64−>64&#xff09;轮函数--E扩展置换&#xff08; 32 − > 48 32->48 32−>48&#xff09;轮函数--与子密钥异或&#xff08; 48 − > 48 48->48 48−&…

2023年春秋杯网络安全联赛春季赛Reverse题目复现

文章目录 一.sum1. 分析程序逻辑2.解数独矩阵3.解题脚本 二.Poisoned_tea_CHELL1. 重新识别函数及程序逻辑分析2.IDA动态调试(attach附加调试)3. 输入选项进行单步调试4.解题脚本 BWBAOldSymbolicCode 一.sum 1. 分析程序逻辑 这里直接贴上当时分析的结果,根据程序行为不难猜…

NLP相关知识点(慢慢更新)

一、基础概念 1.1. NLP 中的 Tokenization 是什么? NLP技术中 Tokenization 也可以被称作是“word segmentation”&#xff0c;直译为中文是指 分词。 分词是NLP的基础任务&#xff0c;按照特定需求能把文本中的句子、段落切分成一个字符串序列&#xff08;其中的元素通常称…

SpringCould+Vue3-Element-Admin 登录接口,用户信息接口以及Token验证的实现【Taurus教育平台】

文章目录 一.SpringCouldVue3-Element-Admin 登录接口&#xff0c;用户信息接口以及Token验证的实现【Taurus教育平台】1.1 背景1.2 数据库 二、登录接口及其Token实现2.1 前端2.2 后端2.2.1 控制层2.2.2 service层2.2.3 工具类&#xff1a;CreateJwt2.2.4 Dao-Mapper 三、用户…

【Bert、T5、GPT】fine tune transformers 文本分类/情感分析

【Bert、T5、GPT】fine tune transformers 文本分类/情感分析 0、前言text classificationemotions 数据集data visualization analysisdataset to dataframelabel analysistext length analysis text > tokenstokenize the whole dataset fine-tune transformersdistilbert…

Django通过nginx 部署(成功运行)

配置python版本和ssh启用root参考其它文章 完整项目路径 /root/projects/mysutra_pro 下级目录/root/projects/mysutra_pro/mysutra是源码目录 rootVM-12-2-debian:~/projects/mysutra_pro# ls db.sqlite3 manage.py ms_env mysutra rootVM-12-2-debian:~/projects/mysutr…

es Elasticsearch的增删改查(含数组操作)(类型,原理) - from chatgpt

父文章 算法中的特征的保存, es 和 mysql 和 odps hadoop hbase的区别_个人渣记录仅为自己搜索用的博客-CSDN博客 Elasticsearch如何做到数十亿数据查询毫秒级响应&#xff1f; - 知乎 ES系列之利用filter让你的查询效率飞起来_es filter_lucasma.eth的博客-CSDN博客 ES查询…

文心一言对比chatgpt

文章目录 一、 介绍二、 回复速度比较三、 写代码能力四、 做表格能力五、 写文案能力六、 解决数学问题能力七、 绘画能力八、 实时更新信息九、 总结 一、 介绍 文心一言对比chatgpt。 测试了上百个案例&#xff0c;挑选几个经典的案例。 二、 回复速度比较 百度文心耗时10…

CSDN chatGPT初体验

我的问题&#xff1a;用java实现一个B树 public class BTree {private int t;private Node root;private class Node {private int n;private boolean leaf;private int[] keys;private Node[] children;public Node(boolean leafNode) {this.n 0;this.leaf leafNode;this.ke…

chatgpt赋能python:Python岗位需求日渐增加

Python岗位需求日渐增加 Python编程语言在当前的IT行业中越来越受欢迎。其灵活性和易用性使得Python在各种领域中使用广泛&#xff0c;比如Web开发、数据科学、人工智能等。作为一名有10年Python编程经验的工程师&#xff0c;我认为Python是一种非常有前途的编程语言&#xff…

ChatGPT可能被滥用于网络犯罪

科技是一把双刃剑 ChatGPT一经发布&#xff0c;短时间内就成为了现象级的应用。其清晰明确的回答和丰富的知识&#xff0c;昭示着AI技术造福人类&#xff0c;彻底简化我们生活工作这一美好的未来愿景。对于解决各行各业的许多问题&#xff0c;ChatGPT也提供了另外一种思路&…

用ChatGPT构建网络设备表,并根据设备关系生成网络拓扑

构造一个数据表&#xff0c;存储包括交换机、路由器、防火墙、入侵检测、上网行为管理等设备的编号、序列号、IP、MAC、访问地址、用户名、密码、管理员、物理位置、上联设备ip等信息 下面是一个示例数据表&#xff1a; Device IDSerial NumberIPMACAccess URLUsernamePassword…

七大语言模型“偏见与毒性”的角逐,ChatGpt3.5综合表现优良

颠覆性的技术进步和人工智能的快速发展&#xff0c;催生了现如今LLM&#xff08;大型语言模型&#xff09;和AIGC&#xff08;AI生成内容&#xff09;的盛行。这些创新性的模型和算法不仅能够理解、生成和处理人类语言&#xff0c;还能够模拟智能思维和创造力&#xff0c;成为各…

阿里版ChatGPT已接入钉钉,张勇:未来所有业务都有大模型加持

机器之心报道 机器之心编辑部 阿里&#xff1a;大模型也是基础设施。 4 月 7 日下午&#xff0c;阿里云没有一点预告的突然宣布&#xff0c;自研类 ChatGPT 产品开启企业邀测&#xff0c;模型名为「通义千问」。 虽然是非常小范围的测试&#xff0c;但邀测消息刚放出&#xff…

阿里巴巴开源Chat2DB v1.0.11 初体验

阿里巴巴开源Chat2DB v1.0.11 初体验 前言什么是Chat2DB下载安装安装配置Chat2DB初体验配置数据源准备测试数据认识几个功能菜单开始测试自然语言转SQLSQL解释SQL优化 使用总结后续功能结语 前言 作为一名阿里巴巴开源项目的拥护者&#xff0c;从Chat2DB开源至今都有关注这个开…

估值 2 个月从 11 亿美元降到 3 亿美元,投资人清仓跑路,国产大模型创业遇冷...

图片来源&#xff1a;由无界 AI生成 创业未半&#xff0c;而中道崩殂。 6 月 29 日&#xff0c;美团发布公告以 20.65 亿元全资收购光年之外全部权益&#xff0c;距离光年之外正式营业刚过去 84 天。 这是目前中国大模型创业领域最大的收购案&#xff0c;光年之外也在 4 个月时…

英伟达帝国的一道裂缝

2012年&#xff0c;AI圈发生了两件大事&#xff0c;按时间顺序&#xff0c;第一件是谷歌组团已久的Google Brain发布“出道作”——一个能够识别猫的深度学习网络“谷歌猫”&#xff0c;74.8%的识别准确率&#xff0c;比知名识别图像大赛ImageNet前一年获胜算法的74%还要高出0.…