[HDCTF2023]Re官方WP

导言

本次比赛HnuSec实验室的师傅们都准备了好久,可能经验不足,有好多问题,还望师傅们包容。再次感谢各位师傅们参加。
其次呢,因为我们实验室就两Re手,我还是个菜鸟,这次比赛让我负责Re方向,感觉压力很大,出题也没有经验,挺多问题的,也算是一次挑战吧。同时感谢shangwendada供题除了签到ez_re和Basketball都是他出的,帮了大忙。同时也感谢的帮助。

ez_re

不多说,upx脱壳后,base64
img
img

flag

NSSCTF{Y0u_h@v2_/\/\@57er3d_7he_r3v3rs3}

easy_asm

由Masm for Windows 集成实验环境 2015编译的一个汇编语言程序。直接进入ida分析汇编语言。
主要点如下
img
这里是在拿al寄存器于cl寄存器做异或处理
然后以$符号作为结尾符号.
cl赋值如下:

img
可以发现就是异或0x10
img
这里则是输入点与al寄存器作比较.
img
看到数据段.
img
显然不全是密文,有一段是用来判断是否相等的,之前的分析可以发现我们的字符结束符号是$,那么密文则为XTSDVkZecdOqOu#ciOqC}m
最后异或一个0x10输出即为flag

exp:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
            .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机     永无BUG
*/
int main ()
{
	char flag[]="XTSDVkZecdOqOu#ciOqC}m";
	for(int i = 0 ; i < strlen(flag) ; i ++ )
	{
		printf("%c",flag[i]^=0x10);
	}
	//HDCTF{Just_a_e3sy_aSm}
}

flag:

NSSCTF{Just_a_e3sy_aSm}

非预期

  • 有的师傅把汇编代码交给ChatGPT,让他输出C代码来阅读,挺不错的思路
  • 有的师傅直接所有数据扔进Cyberchef里面xor一把梭,就问你出没出吧
  • 有师傅通过分析16进制猜测单字符异或爆破出解
    img

double_code

img
打开ida,发现是一个非常典型的上线器,shellcode loader。
我们需要做的是找到其中的shellcode
img
此处加载了buf,也就是shellcode
点击查看
img
这就是shellcode的内容了,我们只需要将其复制下来
然后重新编辑一个文件使用010
再使用32位ida打开该文件分析
img
就可以发现文件的主要逻辑了。
是一个类似于虚拟机的操作
输出的数组已经给你,通过这个逆向操作将输出的数组还原回去
就可以拿到flag了

exp

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
            .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机     永无BUG
*/
int main ()
{
	int opcode[]={1,5,2,4,3};
	unsigned char flag[]={
		0x48,0x67,0x45,0x51,0x42,0x7b,0x70,0x6a,0x30,0x68,0x6c,0x60,0x32,0x61,0x61,0x5f,0x42,0x70,0x61,0x5b,0x30,0x53,0x65,0x6c,0x60,0x65,0x7c,0x63,0x69,0x2d,0x5f,0x46,0x35,0x70,0x75,0x7d
	};
	/*
	for(int i = 0 ; i < strlen((char *)flag) ; i ++ )
	{
		int tmp = i%5;
		if(tmp == 1)
		{
			flag[i] ^= 0x23;
		}
		else if(tmp == 2)
		{
			flag[i] += 2;
		}
		else if(tmp == 3)
		{
			flag[i] -=3;
		}
		else if (tmp == 4)
		{
			flag[i] -=4;
		}
		else if(tmp == 5)
		{
			flag[i]-=25;
		}
	}
	*/
	cout<<endl;
	for(int i = 0 ; i < strlen((char *)flag) ; i ++ )
	{
		int tmp = i%5;
		if(tmp == 1)
		{
			flag[i] ^= 0x23;
		}
		else if(tmp == 2)
		{
			flag[i] -= 2;
		}
		else if(tmp == 3)
		{
			flag[i] +=3;
		}
		else if (tmp == 4)
		{
			flag[i] +=4;
		}
		else if(tmp == 5)
		{
			flag[i]+=25;
		}
		printf("%c",flag[i]);
	}
}//HDCTF{Sh3llC0de_and_0pcode_al1_e3sy}

flag

NSSCTF{Sh3llC0de_and_0pcode_al1_e3sy}

非预期

好像没看到有非预期,要是有的话,欢迎师傅联系我,或者在NSSCTF上提交单题WP

fake_game

pyinstall解包

img
找到game.pyc,然后uncompyle6反编译
注较新版本pyinstall好像会自动修复Magic Number
如过遇到uncomyle6无法反编译,请手动修复Magic Number
得到如下代码

# uncompyle6 version 3.9.0
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)]
# Embedded file name: game.py
import pygame, random
from win32api import MessageBox
IMAGE_PATH = 'imgs/'
scrrr_width = 800
scrrr_height = 560
GAMEOVER = False
LOG = '文件:{}中的方法:{}出错'.format(__file__, __name__)

class Map:
    map_names_list = [
     IMAGE_PATH + 'map1.png', IMAGE_PATH + 'map2.png']

    def __init__(self, x, y, img_index):
        self.image = pygame.image.load(Map.map_names_list[img_index])
        self.position = (x, y)
        self.can_grow = True

    def load_map(self):
        MainGame.window.blit(self.image, self.position)


class Plant(pygame.sprite.Sprite):

    def __init__(self):
        super(Plant, self).__init__()
        self.live = True

    def load_image(self):
        if hasattr(self, 'image') and hasattr(self, 'rect'):
            MainGame.window.blit(self.image, self.rect)
        else:
            print(LOG)


class Sunflower(Plant):

    def __init__(self, x, y):
        super(Sunflower, self).__init__()
        self.image = pygame.image.load('imgs/sunflower.png')
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.price = 50
        self.hp = 100
        self.time_count = 0

    def produce_money(self):
        self.time_count += 1
        if self.time_count == 25:
            MainGame.money += 5
            self.time_count = 0

    def display_sunflower(self):
        MainGame.window.blit(self.image, self.rect)


class PeaShooter(Plant):

    def __init__(self, x, y):
        super(PeaShooter, self).__init__()
        self.image = pygame.image.load('imgs/peashooter.png')
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.price = 50
        self.hp = 200
        self.shot_count = 0

    def shot(self):
        should_fire = False
        for zombie in MainGame.zombie_list:
            if zombie.rect.y == self.rect.y and zombie.rect.x < 800 and zombie.rect.x > self.rect.x:
                should_fire = True
        else:
            if self.live:
                if should_fire:
                    self.shot_count += 1
                    if self.shot_count == 25:
                        peabullet = PeaBullet(self)
                        MainGame.peabullet_list.append(peabullet)
                        self.shot_count = 0

    def display_peashooter(self):
        MainGame.window.blit(self.image, self.rect)


class PeaBullet(pygame.sprite.Sprite):

    def __init__(self, peashooter):
        self.live = True
        self.image = pygame.image.load('imgs/peabullet.png')
        self.damage = 50
        self.speed = 10
        self.rect = self.image.get_rect()
        self.rect.x = peashooter.rect.x + 60
        self.rect.y = peashooter.rect.y + 15

    def move_bullet(self):
        if self.rect.x < scrrr_width:
            self.rect.x += self.speed
        else:
            self.live = False

    def hit_zombie(self):
        for zombie in MainGame.zombie_list:
            if pygame.sprite.collide_rect(self, zombie):
                self.live = False
                zombie.hp -= self.damage
                if zombie.hp <= 0:
                    zombie.live = False
                    self.nextLevel()

    def nextLevel(self):
        MainGame.score += 20
        MainGame.remnant_score -= 20
        for i in range(1, 100):
            if MainGame.score == 100 * i and MainGame.remnant_score == 0:
                MainGame.remnant_score = 100 * i
                MainGame.shaoguan += 1
                MainGame.produce_zombie += 50

    def display_peabullet(self):
        MainGame.window.blit(self.image, self.rect)


class Zombie(pygame.sprite.Sprite):

    def __init__(self, x, y):
        super(Zombie, self).__init__()
        self.image = pygame.image.load('imgs/zombie.png')
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.hp = 1000
        self.damage = 2
        self.speed = 1
        self.live = True
        self.stop = False

    def move_zombie(self):
        if self.live:
            if not self.stop:
                self.rect.x -= self.speed
                if self.rect.x < -80:
                    MainGame().gameOver()

    def hit_plant(self):
        for plant in MainGame.plants_list:
            if pygame.sprite.collide_rect(self, plant):
                self.stop = True
                self.eat_plant(plant)

    def eat_plant(self, plant):
        plant.hp -= self.damage
        if plant.hp <= 0:
            a = plant.rect.y // 80 - 1
            b = plant.rect.x // 80
            map = MainGame.map_list[a][b]
            map.can_grow = True
            plant.live = False
            self.stop = False

    def display_zombie(self):
        MainGame.window.blit(self.image, self.rect)


class MainGame:
    shaoguan = 1
    score = 0
    remnant_score = 100
    money = 200
    map_points_list = []
    map_list = []
    plants_list = []
    peabullet_list = []
    zombie_list = []
    count_zombie = 0
    produce_zombie = 100

    def init_window(self):
        pygame.display.init()
        MainGame.window = pygame.display.set_mode([scrrr_width, scrrr_height])

    def draw_text(self, content, size, color):
        pygame.font.init()
        font = pygame.font.SysFont('kaiti', size)
        text = font.render(content, True, color)
        return text

    def load_help_text(self):
        text1 = self.draw_text('1.按左键创建向日葵 2.按右键创建豌豆射手', 26, (255, 0, 0))
        MainGame.window.blit(text1, (5, 5))

    def init_plant_points(self):
        for y in range(1, 7):
            points = []
            for x in range(10):
                point = (
                 x, y)
                points.append(point)
            else:
                MainGame.map_points_list.append(points)
                print('MainGame.map_points_list', MainGame.map_points_list)

    def init_map(self):
        for points in MainGame.map_points_list:
            temp_map_list = list()
            for point in points:
                if (point[0] + point[1]) % 2 == 0:
                    map = Map(point[0] * 80, point[1] * 80, 0)
                else:
                    map = Map(point[0] * 80, point[1] * 80, 1)
                temp_map_list.append(map)
                print('temp_map_list', temp_map_list)
            else:
                MainGame.map_list.append(temp_map_list)

        else:
            print('MainGame.map_list', MainGame.map_list)

    def load_map(self):
        for temp_map_list in MainGame.map_list:
            for map in temp_map_list:
                map.load_map()

    def load_plants(self):
        for plant in MainGame.plants_list:
            if plant.live:
                if isinstance(plant, Sunflower):
                    plant.display_sunflower()
                    plant.produce_money()
                elif isinstance(plant, PeaShooter):
                    plant.display_peashooter()
                    plant.shot()
            else:
                MainGame.plants_list.remove(plant)

    def load_peabullets(self):
        for b in MainGame.peabullet_list:
            if b.live:
                b.display_peabullet()
                b.move_bullet()
                b.hit_zombie()
            else:
                MainGame.peabullet_list.remove(b)

    def deal_events(self):
        eventList = pygame.event.get()
        for e in eventList:
            if e.type == pygame.QUIT:
                self.gameOver()
            elif e.type == pygame.MOUSEBUTTONDOWN:
                print(e.pos)
                x = e.pos[0] // 80
                y = e.pos[1] // 80
                print(x, y)
                map = MainGame.map_list[y - 1][x]
                print(map.position)
                if e.button == 1:
                    if map.can_grow:
                        if MainGame.money >= 50:
                            sunflower = Sunflower(map.position[0], map.position[1])
                            MainGame.plants_list.append(sunflower)
                            print('当前植物列表长度:{}'.format(len(MainGame.plants_list)))
                            map.can_grow = False
                            MainGame.money -= 50
            elif e.button == 3 and map.can_grow and MainGame.money >= 50:
                peashooter = PeaShooter(map.position[0], map.position[1])
                MainGame.plants_list.append(peashooter)
                print('当前植物列表长度:{}'.format(len(MainGame.plants_list)))
                map.can_grow = False
                MainGame.money -= 50

    def init_zombies(self):
        for i in range(1, 7):
            dis = random.randint(1, 5) * 200
            zombie = Zombie(800 + dis, i * 80)
            MainGame.zombie_list.append(zombie)

    def load_zombies(self):
        for zombie in MainGame.zombie_list:
            if zombie.live:
                zombie.display_zombie()
                zombie.move_zombie()
                zombie.hit_plant()
            else:
                MainGame.zombie_list.remove(zombie)

    def start_game(self):
        global GAMEOVER
        xorr = [
         0] * 4
        ans = [0] * 55
        flag = [178868, 188, 56953, 2413, 178874, 131, 56957, 2313, 178867, 156, 
         56933, 2377, 178832, 202, 56899, 2314, 178830, 167, 56924, 
         2313, 178830, 167, 56938, 2383, 178822, 217, 56859, 2372]
        self.init_window()
        self.init_plant_points()
        self.init_map()
        self.init_zombies()
        while not GAMEOVER:
            MainGame.window.fill((255, 255, 255))
            MainGame.window.blit(self.draw_text('当前钱数$: {}'.format(MainGame.money), 26, (255,
                                                                                         0,
                                                                                         0)), (500,
                                                                                               40))
            MainGame.window.blit(self.draw_text('当前关数{},得分{},距离下关还差{}分'.format(MainGame.shaoguan, MainGame.score, MainGame.remnant_score), 26, (255,
                                                                                                                                                0,
                                                                                                                                                0)), (5,
                                                                                                                                                      40))
            self.load_help_text()
            xorr[0] = MainGame.money
            xorr[1] = MainGame.shaoguan
            xorr[2] = MainGame.score
            xorr[3] = MainGame.remnant_score
            if xorr[0] * 256 - xorr[1] / 2 + xorr[2] * 23 + xorr[3] / 2 == 47118166:
                if xorr[0] * 252 - xorr[1] * 366 + xorr[2] * 23 + xorr[3] / 2 - 1987 == 46309775:
                    if xorr[0] * 6 - xorr[1] * 88 + xorr[2] / 2 + xorr[3] / 2 - 11444 == 1069997:
                        if (xorr[0] - 652) * 2 - xorr[1] * 366 + xorr[2] * 233 + xorr[3] / 2 - 13333 == 13509025:
                            for i in range(len(flag)):
                                ans[i] = flag[i] ^ xorr[i % 4]
                            else:
                                with open('flag.txt', 'w') as (f):
                                    f.write(''.join([chr(a) for a in ans]))

            self.load_map()
            self.load_plants()
            self.load_peabullets()
            self.deal_events()
            self.load_zombies()
            MainGame.count_zombie += 1
            if MainGame.count_zombie == MainGame.produce_zombie:
                self.init_zombies()
                MainGame.count_zombie = 0
            pygame.time.wait(10)
            pygame.display.update()

    def gameOver(self):
        global GAMEOVER
        MainGame.window.blit(self.draw_text('游戏结束', 50, (255, 0, 0)), (300, 200))
        print('游戏结束')
        pygame.time.wait(400)
        GAMEOVER = True


if __name__ == '__main__':
    game = MainGame()
    game.start_game()
# okay decompiling .\game.pyc

分析游戏开始函数可以发现,当分数,距离下一关分数,金钱,关卡数,成特定约束关系时,他们将会作为异或因子,对flag的进行解密,然后将解密的文件写如本地文件,一个叫做flag.txt的文件中
那么我们需要使用z3方程来求解这些异或因子如何才能满足该约束关系
脚本如下:

from z3 import *

xorr = [BitVec("num[%d]" % i, 32) for i in range(4)]
s = Solver()
s.add(xorr[0] * 256 - xorr[1] / 2 + xorr[2] * 23 + xorr[3] / 2 == 47118166)
s.add(xorr[0] * 252 - xorr[1] * 366 + xorr[2] * 23 + xorr[3] / 2 - 1987 == 46309775)
s.add(xorr[0] * 6 - xorr[1] * 88 + xorr[2] / 2 + xorr[3] / 2 - 11444 == 1069997)
s.add((xorr[0] - 652) * 2 - xorr[1] * 366 + xorr[2] * 233 + xorr[3] / 2 - 13333 == 13509025)

print(s.check())
for i in xorr:
    print(s.model()[i].as_long(), end=",")

解出值为178940,248,56890,2361

exp

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
            .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机     永无BUG
*/
int flag2[]={0x2bab4,0xbc,0xde79,0x96d,0x2baba,0x83,0xde7d,0x909,0x2bab3,0x9c,0xde65,0x949,0x2ba90,0xca,0xde43,0x90a,0x2ba8e,0xa7,0xde5c,0x909,0x2ba8e,0xa7,0xde6a,0x94f,0x2ba86,0xd9,0xde1b,0x944};
int xorr[]={178940,248,56890,2361};
int main ()
{
	for(int i = 0 ; i < 28 ; i ++ )
	{
		printf("%c",flag2[i]^xorr[i%4]);
		
	}
}//HDCTF{G0Od_pl2y3r_f0r_Pvz!!}

flag:

NSSCTF{G0Od_pl2y3r_f0r_Pvz!!}

非预期&一些说明

  • 出题人的脚本精度有差,将xorr最后一位+1了,好多师傅解出来是2360,导致出错,再次道歉。
  • 有师傅直接猜HDCTF{}格式flag,然后用HDCT四个字符去倒推异或因子

买了些什么呢

01背包

其实所谓01背包就‘0’与’1‘拿与不拿的问题;
在解决问题之前,为描述方便,首先定义一些变量:\(V_i\)表示第 i 个物品的价值,\(W_i\)表示第 i 个物品的体积,定义\(V_{ij}\):当前背包容量 j,前 i 个物品最佳组合对应的价值,同时背包问题抽象化(\(X_1\)\(X_2\),…,\(X_n\),其中 \(X_i\) 取0或1,表示第 i 个物品选或不选)。

1、建立模型,即求\(\max_{i=1}^{n} \{X_i V_i\}\)
2、寻找约束条件,\(\sum_{i=1}^{n} W_i X_i < \text{capacity} \)
3、寻找递推关系式,面对当前商品有两种可能性:

包的容量比该商品体积小,装不下,此时的价值与前i-1个的价值是一样的,即\(V_{i,j} = V_{i-1,j}\)
还有足够的容量可以装该商品,但装了也不一定达到当前最优价值,所以在装与不装之间选择最优的一个,即\(V_{i,j}=\max\{V_{i-1,j},V_{i-1,j-W_i}+V_i\}\)
其中\(V_{i-1j}\)表示不装,\(V_{i-1,j-W_i}+V_i\) 表示装了第i个商品,背包容量减少\(W_i\),但价值增加了\(V_i\)

由此可以得出递推关系式:

\[\begin{cases} V_{i,j}=V_{i-1,j} & j<w_i \\ V_{i,j}=\max\{V_{i-1,j},V_{i-1,j-W_i}+V_i\} & j \geq w_i \end{cases} \]

因此状态转移方程为: max(dp[i-1][j], val[i-1] + dp[i-1][j-wt[i-1]])

但这样只能找到理论的最大值
在01背包算法中,我们创建了一个二维的动态规划数组 dp,其中 dp[i][j] 表示在前 i 个物品中选择总重量不超过 j 的最大价值。在填充 dp 数组的过程中,我们会根据当前物品的重量和价值,来决定是否选择该物品。

当我们求解完整个动态规划数组后,需要找到选择的物品的下标。为了实现这一步,我们可以从动态规划数组的右下角 dp[n][W] 开始,一步步往左上角移动,并判断当前位置是否被选中。我们从右下角开始往左上角移动的原因是,选中的物品的总重量一定是等于 W 的,因此我们需要从 dp[n][W] 开始反向推导,找出哪些物品被选中。

具体来说,我们定义一个变量 w,初始值为背包的容量 W,然后从 dp[n][W] 开始往左上角移动。每次移动的时候,我们需要判断当前位置 dp[i][w] 是否被选中。如果 dp[i][w] 的值等于 dp[i-1][w],说明第 i 个物品没有被选中;如果 dp[i][w] 的值等于 val[i-1] + dp[i-1][w-wt[i-1]],说明第 i 个物品被选中了,此时我们将 i-1 添加到一个向量 indices 中,表示选中的物品的下标。最后,我们从 indices 向量的末尾开始循环,输出选择的物品的下标即可。

总结一下,找到选择的物品的下标的过程就是从 dp[n][W] 开始,根据动态规划数组的状态,一步步往左上角移动,并判断当前位置是否被选中。如果被选中,则将该物品的下标添加到一个向量中。最后,从该向量的末尾开始循环,输出选择的物品的下标。
因此可以使用如下代码

    for(int i = n; i > 0 && w > 0; i--)
    {
        if(dp[i][w] != dp[i-1][w])
        {
            indices.push_back(i-1);
            w -= wt[i-1];
        }
    }

解题过程

分析题目文件

img

可以发现题目的重量和价值是使用随机种子1生成的,因此我们可以使用同样的方法生成
img

然后分析
img

发现判断成功与否的条件是所收集到的最后价值是否等于v5然后v5是knapsack函数的返回值,我们查看knapsack函数

img
是一段dp算法,计算最大价值,我们只需要抄下来让他产生相同的dp数组
img
将其输出

img

这就是最大价值的选举过程
横坐标i为选取的物品,因此我们只需要向矩阵的左上角遍历果 dp[i][w] 的值等于 val[i-1] + dp[i-1][w-wt[i-1]],说明第 i 个物品被选中了
因为选举过程中的状态转移如下
img

exp

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
            .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机     永无BUG
*/
int dp[500][500];
int __cdecl knapsack(int W, int *wt, int *val, int n)
{
  int j_0; // [esp+0h] [ebp-10h]
  int i_0; // [esp+4h] [ebp-Ch]
  int j; // [esp+8h] [ebp-8h]
  int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i <= n; ++i )
  {
    for ( j = 0; j <= W; ++j )
      dp[i][j] = 0;
  }
  for ( i_0 = 1; i_0 <= n; ++i_0 )
  {
    for ( j_0 = 1; j_0 <= W; ++j_0 )
    {
      if ( j_0 < wt[i_0 - 1] )
      {
        dp[i_0][j_0] = dp[i_0 - 1][j_0];
      }
      else
      {
        dp[i_0][j_0] = dp[i_0 - 1][j_0];
        if ( dp[i_0 - 1][j_0 - wt[i_0 - 1]] + val[i_0 - 1] > dp[i_0][j_0] )
          dp[i_0][j_0] = val[i_0 - 1] + dp[i_0 - 1][j_0 - wt[i_0 - 1]];
      }
    }
  }

}
int main ()
{
	int W = 50;
	int n = 40;
	int wt[0xA0];
	int val[0xA0];
	srand(1);
	for(int i = 0 ; i < n ; i ++ )
	{
		wt[i] = rand() % 10 + 1;
		val[i] = rand() % 10 + 1;
		printf("|    %d    |   %d   |   %d   |\n", i + 1, wt[i], val[i]);
	}
	knapsack(W, wt, val, n);
    cout << "动态规划数组为: " << endl;
    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= W; j++)
        {
            cout << dp[i][j] << " ";
        }
        cout << endl;
    }
	  vector<int> indices;
	  int w=W;
	  for(int i = n; i > 0 && w > 0; i--)
	  {
	      if(dp[i][w] != dp[i-1][w])
	      {
	          indices.push_back(i-1);
	          w -= wt[i-1];
	      }
	  }
	  cout << "选择的物品的下标为: ";
      for(int i = indices.size()-1; i >= 0; i--)
      {
          cout << indices[i] << " ";
      }
}//HDCTF{0 4 6 10 11 13 16 18 21 22 24 26 31 33 34 36 39}

flag

NSSCTF{0 4 6 10 11 13 16 18 21 22 24 26 31 33 34 36 39}

非预期&一些说明

  • 这题前期没有沟通清楚下标问题,导致很多师傅因为下标从1开始一直无法解出,深感抱歉
  • 还有就是因为这题设置的是静态flag,出题人没考虑到有多解的情况,后面有解出别的解来问的师傅都直接告诉答案了,还是我们的问题,再次跪求原谅
  • 这题比较多的非预期就是01背包是一个经典的dp问题,问ChatGPT一般都能秒切
  • 有师傅是通过动调找到重量和价值之和,再去解的,不错的方法,有兴趣的师傅可以试一下

enc

32位无壳,直接分析
主函数:

__int64 __fastcall main()
{
  char *v0; // rdi
  __int64 i; // rcx
  std::ostream *v2; // rax
  char v4; // [rsp+20h] [rbp+0h] BYREF
  unsigned int v5[8]; // [rsp+24h] [rbp+4h] BYREF
  int key; // [rsp+44h] [rbp+24h]
  unsigned int k[12]; // [rsp+68h] [rbp+48h] BYREF
  unsigned int v; // [rsp+98h] [rbp+78h] BYREF
  int v9; // [rsp+9Ch] [rbp+7Ch]
  char _Str[456]; // [rsp+C0h] [rbp+A0h] BYREF

  v0 = &v4;
  for ( i = 110i64; i; --i )
  {
    *(_DWORD *)v0 = -858993460;
    v0 += 4;
  }
  j___CheckForDebuggerJustMyCode(&_B692EFB8_smctest_cpp);
  v2 = std::operator<<<std::char_traits<char>>(std::cout, "plesase input the key");
  std::ostream::operator<<(v2, std::endl<char,std::char_traits<char>>);
  std::istream::operator>>(std::cin, v5);
  key = v5[0];
  k[0] = 18;
  k[1] = 52;
  k[2] = 86;
  k[3] = 120;
  v = v5[0];
  v9 = 4;
  tea_encrypt(&v, k);
  if ( v == 1627184887 && v9 == 37149676 )
  {
    memset(_Str, 0, 0x100ui64);
    UnPack(key);
    j_printf("Right,please continue...\nPlease input your flag.\n");
    std::operator>><char,std::char_traits<char>>(std::cin, _Str);
    Fun1(_Str);
  }
  return 0i64;
}

需要输入一个key来启动程序,这个key是使用tea加密技术加密的,让我们看tea加密函数

void __fastcall tea_encrypt(unsigned int *v, unsigned int *k)
{
  unsigned int v2; // [rsp+24h] [rbp+4h]
  unsigned int v3; // [rsp+44h] [rbp+24h]
  int v4; // [rsp+64h] [rbp+44h]
  unsigned int i; // [rsp+84h] [rbp+64h]

  j___CheckForDebuggerJustMyCode(&_B692EFB8_smctest_cpp);
  v2 = *v;
  v3 = v[1];
  v4 = 0;
  for ( i = 0; i < 0x20; ++i )
  {
    v4 -= 1640531527;
    v2 += (k[1] + (v3 >> 5)) ^ (v4 + v3) ^ (*k + 16 * v3);
    v3 += (k[3] + (v2 >> 5)) ^ (v4 + v2) ^ (k[2] + 16 * v2);
  }
  *v = v2;
  v[1] = v3;
}

根据该函数以及上面的数字直接写出解密脚本:

#include <stdio.h>
#include <stdint.h>  // 使用uint32_t数据类型需要包含此头文件
#include <string.h>
 #include<iostream>
 using namespace std;
// 定义加密函数
void tea_decrypt(uint32_t *v, uint32_t *k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0xC6EF3720, i;  // 根据TEA算法,解密轮次的计算需要初始化sum
    uint32_t delta = 0x9e3779b9;
 
    for (i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
        sum -= delta;
    }
 
    v[0] = v0;
    v[1] = v1;
}
 
int main() {
    uint32_t enc[2]={0x60FCDEF7,0x236DBEC};
    uint32_t key[]={0x12,0x34,0x56,0x78};
    tea_decrypt(enc,key);
    cout<<enc[0];
    return 0;
}

输出为3
可以看到3被存入了key2然后传入了unpack函数
可以发现unpack函数实际上是调用了一个叫做smc的函数,查看其内容

void __fastcall SMC(char *pBuf, int key)
{
  __int16 v2; // [rsp+44h] [rbp+24h]
  char *Str1; // [rsp+A8h] [rbp+88h]
  int i; // [rsp+C4h] [rbp+A4h]

  j___CheckForDebuggerJustMyCode(&_B692EFB8_smctest_cpp);
  v2 = *(_WORD *)&pBuf[*((int *)pBuf + 15) + 6];
  Str1 = &pBuf[*((int *)pBuf + 15) + 264];
  for ( i = 0; i < v2; ++i )
  {
    if ( !j_strcmp_0(Str1, ".hdctf") )
    {
      xxor(&pBuf[*((unsigned int *)Str1 + 3)], *((_DWORD *)Str1 + 4), key);
      return;
    }
    Str1 += 40;
  }
}

典型的smc局部代码加密技术,此处加密的部分为.hdctf程序段
加密方法在xxor函数中

void __cdecl xxor(char *soure, int dLen, char key)
{
  int i; // [esp+D0h] [ebp-8h]

  __CheckForDebuggerJustMyCode(&B5276889_test1_cpp);
  for ( i = 0; i < dLen; ++i )
    soure[i] ^= key;
}

这其中的key就是之前tea解密出来的值
看到Fun函数可以发现ida无法识别 且字段位.hdctf,则是被smc加密了的字段,使用idapython解密回去即可

.hdctf:000000014001E000                               ; Section 3. (virtual address 0001E000)
.hdctf:000000014001E000                               ; Virtual size                  : 00001589 (   5513.)
.hdctf:000000014001E000                               ; Section size in file          : 00001600 (   5632.)
.hdctf:000000014001E000                               ; Offset to raw data for section: 0000D400
.hdctf:000000014001E000                               ; Flags E0000020: Text Executable Readable Writable
.hdctf:000000014001E000                               ; Alignment     : default
.hdctf:000000014001E000                               ; ===========================================================================
.hdctf:000000014001E000
.hdctf:000000014001E000                               ; Segment type: Pure code
.hdctf:000000014001E000                               ; Segment permissions: Read/Write/Execute
.hdctf:000000014001E000                               _hdctf segment para public 'CODE' use64
.hdctf:000000014001E000                               assume cs:_hdctf
.hdctf:000000014001E000                               ;org 14001E000h
.hdctf:000000014001E000                               assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.hdctf:000000014001E000
.hdctf:000000014001E000                               ; =============== S U B R O U T I N E =======================================
.hdctf:000000014001E000
.hdctf:000000014001E000
.hdctf:000000014001E000                               ; void __fastcall Fun1(char *data)
.hdctf:000000014001E000                               ?Fun1@@YAXQEAD@Z proc near              ; CODE XREF: Fun1(char * const)↑j
.hdctf:000000014001E000                                                                       ; DATA XREF: .pdata:0000000140027064↓o
.hdctf:000000014001E000                               ; __unwind { // j___GSHandlerCheck
.hdctf:000000014001E000 4B 8A 4F 27                   mov     cl, [r15+27h]
.hdctf:000000014001E004 0B 56 55                      or      edx, [rsi+55h]
.hdctf:000000014001E007 54                            push    rsp
.hdctf:000000014001E007
.hdctf:000000014001E007                               ; ---------------------------------------------------------------------------
.hdctf:000000014001E008 4B 82 EF 63 04 03 03 4B       dq 4B03030463EF824Bh
.hdctf:000000014001E010 8E 6F 27 23                   db 8Eh, 6Fh, 27h, 23h
.hdctf:000000014001E014 4B 8E 7F 27                   __$EncStackInitStart_4 dd 277F8E4Bh
.hdctf:000000014001E018 23 BA 43 02 03 03 BB CF       dq 0CFBB03030243BA23h
.hdctf:000000014001E020 CF CF CF F0 A8                db 3 dup(0CFh), 0F0h, 0A8h
.hdctf:000000014001E025 4B 88 8F                      __$EncStackInitEnd_4 db 4Bh, 88h, 8Fh
.hdctf:000000014001E028 27 8B 04 03 03 4B 88 06 D7 5C+dq 6884B0303048B27h, 4BC6304B03035CD7h, 8E4B0303043B868Ah, 37DAEB0303BCF20Eh, 46C50C0B46C5FCFCh, 46C5AD0946C5970Ah
.hdctf:000000014001E028 03 03 4B 30 C6 4B 8A 86 3B 04+dq 46C5C30F46C5F108h, 46C5C10D46C5540Eh, 46C5991346C5E30Ch, 46C5341146C54612h, 46C5F61746C55310h, 46C55D1546C5A316h
.hdctf:000000014001E028 03 03 4B 8E 0E F2 BC 03 03 EB+dq 46C52F1B46C5C814h, 46C52B1946C5151Ah, 46C5FD1F46C52A18h, 46C5301D46C5FC1Eh, 46C50D2346C5451Ch, 46C5812146C55422h
.hdctf:000000014001E028 DA 37 FC FC C5 46 0B 0C C5 46+dq 46C5512746C52120h, 46C5282546C52526h, 46C5E72B46C56D24h, 8E4B272946C5812Ah, 0BAC330FB884B5346h, 884BA9F003030203h
.hdctf:000000014001E028 0A 97 C5 46 09 AD C5 46 08 F1+dq 3262EB030304638Eh, 3030267868AFCFCh, 4B0303028B868E4Bh, 884B030331310E8Eh, 3030CBAF2884BFBh, 302B786C5A7F003h
.hdctf:000000014001E028 C5 46 0F C3 C5 46 0E 54 C5 46+dq 303077786C40303h, 73786C403030303h, 0DE8030303030303h, 0C3FC030307378688h, 0BE8203030737868Ah, 303020303030737h
.hdctf:000000014001E028 0D C1 C5 46 0C E3 C5 46 13 99+dq 3073786604B697Eh, 30307378EB50C03h, 4B030302E3068F8Bh, 8A4B030307378660h, 8E8E4B0303042B86h, 0FC33D9EB0303028Bh
.hdctf:000000014001E028 C5 46 12 46 C5 46 11 34 C5 46+dq 3030433868A4BFCh, 3042B8E884BD130h, 338E884BC2884B03h, 884BF2F44B030304h, 30307378E604BC1h, 303028B0687B50Ch
.hdctf:000000014001E028 10 53 C5 46 17 F6 C5 46 16 A3+dq 0EA030300030E878Bh, 73786C4FCFCFC7Fh, 0DE8030303030303h, 0C3FC030307378688h, 0BE8203030737868Ah, 303020303030737h
.hdctf:000000014001E028 C5 46 15 5D C5 46 14 C8 C5 46+dq 604B0303038F8E0Ch, 87B50C0303073786h, 778E88030302E306h, 4BC288CB00030307h, 0B50C030307378E60h, 0C200030300030E8Fh
.hdctf:000000014001E028 1B 2F C5 46 1A 15 C5 46 19 2B+dq 30303FCE1829Ah, 0C128030303FC26C1h, 604B03030777868Ah, 87B50C0303073786h, 17868B030302E306h, 77786604B030307h
.hdctf:000000014001E028 C5 46 18 2A C5 46 1F FD C5 46+dq 307378E604B0303h, 302E30687B50C03h, 30302E30E878B03h, 0C0303077786604Bh, 8F8B030307178EB5h, 0FC55EA030302E306h
.hdctf:000000014001E028 1E FC C5 46 1D 30 C5 46 1C 45+dq 303075786C4FCFCh, 757868803030303h, 3030737868A0303h, 3030303079786C4h, 79786880DE80303h, 797868AC3FC0303h
.hdctf:000000014001E028 C5 46 23 0D C5 46 22 54 C5 46+dq 303026786880303h, 8E0C03030797863Ah, 737868803030201h, 0FCE1829AC3FC0303h, 3FC26C100030303h, 737868AC1280303h
.hdctf:000000014001E028 21 81 C5 46 20 21 C5 46 27 51+dq 3073786604B0303h, 302E30687B50C03h, 30307578E8803h, 3FCE1829AC288CBh, 303FC26C1000303h, 30757868AC12803h
.hdctf:000000014001E028 C5 46 26 25 C5 46 25 28 C5 46+dq 303073786604B03h, 30302E30687B50Ch, 604B03030717868Bh, 8E604B0303075786h, 687B50C03030737h, 0E30E878B030302E3h
.hdctf:000000014001E028 24 6D C5 46 2B E7 C5 46 2A 81+dq 75786604B030302h, 307178EB50C0303h, 30302E3068F8B03h, 4B0303079786604Bh, 0BD0C030304638E88h, 3030427868A0207h
.hdctf:000000014001E028 C5 46 29 27 4B 8E 46 53 4B 88+dq 0C030307378E604Bh, 4B030302E30E8FB5h, 0B50C030307579660h, 0C900030302E31697h, 303FCE1829AC288h, 30303FC26C10003h
.hdctf:000000014001E028 FB 30 C3 BA 03 02 03 03 F0 A9+dq 687B50C9B4BC128h, 4278E88030302E3h, 604BC288CB300303h, 0E478B030307978Eh, 86C4FCFCFDDDEA53h, 3030302030307B7h
.hdctf:000000014001E028 4B 88 8E 63 04 03 03 EB 62 32+dq 0EB030304638E884Bh, 20FB804BFCFC2D8Ch, 30307B786C40977h, 7D786C403030303h, 0DE8030303030303h, 0C3FC030307D78688h
.hdctf:000000014001E028 FC FC 8A 86 67 02 03 03 4B 8E+dq 0BE80030307D7868Ah, 4B297E20030307D7h, 0B50C030307D78660h, 7D78E604B0B0647h, 38530E4FB50C0303h, 307B786C40F77C2h
.hdctf:000000014001E028 86 8B 02 03 03 4B 8E 0E 31 31+dq 0E801E80303030303h, 3030307B7BE80BCh, 32C1C0E8E4B0D77h, 0FE8FCFC2D46EB03h, 0EB03032C220E8E4Bh, 0E34E8E4BFCFC2D34h
.hdctf:000000014001E028 03 03 4B 88 FB 4B 88 F2 BA 0C+dq 0EB03032872168E4Bh, 3B8E884BFCFC3323h, 32EBCE304B030304h, 443A68E4BFCFC2Dh
.hdctf:000000014001E028 03 03 03 F0 A7 C5 86 B7 02 03+?Fun1@@YAXQEAD@Z endp ; sp-analysis failed
.hdctf:000000014001E028 03 03 C4 86 77 07 03 03 03 03+
.hdctf:000000014001E468 03 03 5C 5D 5E C0             db 2 dup(3), 5Ch, 5Dh, 5Eh, 0C0h
.hdctf:000000014001E468                               ; } // starts at 14001E000
.hdctf:000000014001E46E CF CF                         dw 0CFCFh                               ; DATA XREF: .pdata:0000000140027064↓o
.hdctf:000000014001E470 CF CF CF CF CF CF CF CF CF CF+dq 223h dup(0CFCFCFCFCFCFCFCFh), 3030303030303CFh, 0Eh dup(303030303030303h)
.hdctf:000000014001F600 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+dq 140h dup(?)
.hdctf:000000014001F600 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+_hdctf ends
.hdctf:000000014001F600 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+

解密完后即为:

void __fastcall Fun1(char *data)
{
  char *v1; // rdi
  __int64 i; // rcx
  char v3; // [rsp+20h] [rbp+0h] BYREF
  char v4[72]; // [rsp+28h] [rbp+8h]
  char v5[276]; // [rsp+70h] [rbp+50h] BYREF
  int v6; // [rsp+184h] [rbp+164h]
  char v7[652]; // [rsp+1A8h] [rbp+188h] BYREF
  char v8; // [rsp+434h] [rbp+414h]
  int j; // [rsp+454h] [rbp+434h]
  int v10; // [rsp+474h] [rbp+454h]
  int v11; // [rsp+494h] [rbp+474h]
  int k; // [rsp+4B4h] [rbp+494h]
  BOOL v13; // [rsp+4D4h] [rbp+4B4h]
  int m; // [rsp+4F4h] [rbp+4D4h]
  int v15; // [rsp+744h] [rbp+724h]
  unsigned __int64 v16; // [rsp+748h] [rbp+728h]
  size_t v17; // [rsp+750h] [rbp+730h]

  v1 = &v3;
  for ( i = 320i64; i; --i )
  {
    *(_DWORD *)v1 = -858993460;
    v1 += 4;
  }
  j___CheckForDebuggerJustMyCode(&_B692EFB8_smctest_cpp);
  v4[0] = 15;
  v4[1] = -108;
  v4[2] = -82;
  v4[3] = -14;
  v4[4] = -64;
  v4[5] = 87;
  v4[6] = -62;
  v4[7] = -32;
  v4[8] = -102;
  v4[9] = 69;
  v4[10] = 55;
  v4[11] = 80;
  v4[12] = -11;
  v4[13] = -96;
  v4[14] = 94;
  v4[15] = -53;
  v4[16] = 44;
  v4[17] = 22;
  v4[18] = 40;
  v4[19] = 41;
  v4[20] = -2;
  v4[21] = -1;
  v4[22] = 51;
  v4[23] = 70;
  v4[24] = 14;
  v4[25] = 87;
  v4[26] = -126;
  v4[27] = 34;
  v4[28] = 82;
  v4[29] = 38;
  v4[30] = 43;
  v4[31] = 110;
  v4[32] = -28;
  v4[33] = -126;
  v4[34] = 36;
  memset(v5, 0, 0x100ui64);
  v6 = j_strlen_0(data);
  strcpy(v7, "you_are_master");
  v7[44] = 0;
  v11 = 0;
  for ( j = 0; j < 256; ++j )
  {
    v7[j + 88] = j;
    v16 = j;
    v17 = j_strlen_0(v7);
    v7[j + 376] = v7[v16 % v17];
  }
  for ( j = 0; j < 256; ++j )
  {
    v11 = ((unsigned __int8)v7[j + 376] + (unsigned __int8)v7[j + 88] + v11) % 256;
    v8 = v7[j + 88];
    v7[j + 88] = v7[v11 + 88];
    v7[v11 + 88] = v8;
  }
  v10 = 0;
  j = 0;
  for ( k = 0; k < v6; ++k )
  {
    j = (j + 1) % 256;
    v10 = ((unsigned __int8)v7[j + 88] + v10) % 256;
    v8 = v7[j + 88];
    v7[j + 88] = v7[v10 + 88];
    v7[v10 + 88] = v8;
    v15 = data[k];
    v5[k] = v7[((unsigned __int8)v7[v10 + 88] + (unsigned __int8)v7[j + 88]) % 256 + 88] ^ v15;
  }
  v13 = j_strlen_0(data) == 35;
  for ( m = 0; ; ++m )
  {
    if ( m >= 35 )
      goto LABEL_18;
    if ( v4[m] != v5[m] )
      break;
  }
  v13 = 0;
LABEL_18:
  if ( v13 )
    j_printf("right!!!!");
  else
    j_printf("please try agin~");
}

典型的rc4

exp:

smc:

for i in range(0x14001e000,0x14001f600):
    patch_byte(i,get_wide_byte(i)^3)

解密:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<map>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<string>
#include<cstring>
#include<list>
#include<stdlib.h>
using namespace std;
typedef int status;
typedef int selemtype;
/*
                   _ooOoo_
                  o8888888o
                  88" . "88
                  (| -_- |)
                  O\  =  /O
               ____/`---'\____
            .'  \\|     |//  `.
            /  \\|||  :  |||//  \
           /  _||||| -:- |||||-  \
           |   | \\\  -  /// |   |
           | \_|  ''\---/''  |   |
           \  .-\__  `-`  ___/-. /
         ___`. .'  /--.--\  `. . __
      ."" '<  `.___\_<|>_/___.'  >'"".
     | | :  `- \`.;`\ _ /`;.`/ - ` : | |
     \  \ `-.   \_ __\ /__ _/   .-` /  /
======`-.____`-.___\_____/___.-`____.-'======
                   `=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    佛祖保佑       永不宕机     永无BUG
*/
unsigned char ida_chars[] =
{
	0xD4, 0x16, 0x87, 0xD6, 0x54, 0x68, 0xBC, 0x02, 0x15, 0x6D, 
	0x30, 0x08, 0x4B, 0x61, 0x4C, 0x5E, 0x42, 0xFD, 0x55, 0x61,
	0xB9, 0x27, 0x6F, 0xF5, 0xB6, 0x86, 0x23, 0xA9, 0xEF, 0x1C,
	0x04, 0x9F
};
typedef unsigned longULONG;
 
/*初始化函数*/
void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
    int i = 0, j = 0;
    char k[256] = { 0 };
    unsigned char tmp = 0;
    for (i = 0; i<256; i++)
    {
        s[i] = i;
        k[i] = key[i%Len];
    }
    for (i = 0; i<256; i++)
    {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];//交换s[i]和s[j]
        s[j] = tmp;
    }
}
 
/*加解密*/
void rc4_crypt(unsigned char*s, unsigned char*Data, unsigned long Len)
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k<Len; k++)
    {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];//交换s[x]和s[y]
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        Data[k] ^= s[t];
    }
}
 
int main()
{
    unsigned char s[256] = { 0 }, s2[256] = { 0 };//S-box
    char key[256] = { "you_are_master" };
   unsigned char pData[512] = {
		0xf,0x94,0xae,0xf2,0xc0,0x57,0xc2,0xe0,0x9a,0x45,
		0x37,0x50,0xf5,0xa0,0x5e,0xcb,0x2c,0x16,0x28,0x29,
		0xfe,0xff,0x33,0x46,0xe,0x57,0x82,0x22,0x52,0x26,
		0x2b,0x6e,0xe4,0x82,0x24
	};
    unsigned long len = 35;
    int i;
 
    printf("pData=%s\n", pData);
    printf("key=%s,length=%d\n\n", key, strlen(key));
    rc4_init(s, (unsigned char*)key, strlen(key));//已经完成了初始化
    printf("完成对S[i]的初始化,如下:\n\n");
    for (i = 0; i<256; i++)
    {
        printf("%02X", s[i]);
        if (i && (i + 1) % 16 == 0)putchar('\n');
    }
    printf("\n\n");
    for (i = 0; i<256; i++)//用s2[i]暂时保留经过初始化的s[i],很重要的!!!
    {
        s2[i] = s[i];
    }

    rc4_crypt(s, (unsigned char*)pData, len);//加密

    printf("现在解密:\n\n");
    //rc4_init(s,(unsignedchar*)key,strlen(key));//初始化密钥
    //rc4_crypt(s2, (unsigned char*)pData, len);//解密
    printf("pData=%s\n\n", pData);
    return 0;
}
//HDCTF{y0u_ar3_rc4_t3a_smc_m4ster!!}

flag

NSSCTF{y0u_ar3_rc4_t3a_smc_m4ster!!}

Basketball

64位无壳,直接逆

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int result; // eax
  int v5; // eax
  int v6; // edx
  int i_0; // [rsp+2Ch] [rbp-14h]
  bool q; // [rsp+33h] [rbp-Dh]
  int i; // [rsp+34h] [rbp-Ch]
  int j; // [rsp+38h] [rbp-8h]
  char c; // [rsp+3Fh] [rbp-1h]
  char ca; // [rsp+3Fh] [rbp-1h]

  _main(argc, argv, envp);
  puts("Please intput two keys within 100 to encrypt the data");
  x = read() % 300;
  y = read() % 300;
  puts("Please intput the data that you want encrypt");
  for ( c = getchar(); c != 10; c = getchar() )
  {
    v3 = len++;
    s[v3] = c;
  }
  f(x, y);
  if ( !strcmp(s, Str2) )
  {
    puts("you get a hint,keep going!");
    puts("Please intput the message you get from the array,notice that all the input should be English.");
    for ( ca = getchar(); ca != 10; ca = getchar() )
    {
      v5 = l2++;
      str[v5] = ca;
    }
    len_flag = 28;
    j = 0;
    while ( l2 <= len_flag )
    {
      v6 = l2++;
      str[v6] = str[j++];
    }
    puts("Please intput your flag");
    scanf("%s", flag);
    for ( i = 0; i < len_flag; ++i )
      num[i] = (char)(flag[i] ^ str[i]);
    q = 1;
    for ( i_0 = 0; i_0 < len_flag; ++i_0 )
    {
      if ( code[i_0] != num[i_0] )
      {
        q = 0;
        break;
      }
    }
    if ( q )
      puts("You get the right flag");
    else
      puts("You are wrong,try again");
    result = 0;
  }
  else
  {
    puts("Try again or you can choose to solve this problem without the hint I give.");
    result = 0;
  }
  return result;
}

先要读入两个100以内数字和一句话加密,加密的话是一句hint
加密函数如下text_66点进去可以发现是一个gcd,加密后为

Uihx!hr!`!ibeu!|iju!H!idmq+xnt!hr+sdlhoe!xnt!un!bIdbj!uid!`ss`x!`
void __cdecl f(int k1_0, int k2_0)
{
  int i; // [rsp+2Ch] [rbp-4h]

  for ( i = 0; i < len; ++i )
  {
    k1_0 = (s[i] + k1_0) % 300;
    k2_0 = (s[i] + k2_0) % 300;
    s[i] ^= text_66(k1_0, k2_0);
  }
}

考虑暴力破解枚举k1,k2
可以随便输两个数字可以得到Thiy is a hcdt jhkt I help*you is * remind you to cHeck the array and three nugbers can vcew as a group等等类似结果
大致可以猜到hint就是告诉你数组中三个数字为一组
写个暴力循环模拟加密过程应该是可以完美解出s,我就不给出暴力了
给出一个可以猜出s的脚本,并给出s="This is a hint what I help you is remind you to check the array and three numbers can view as a group"

import math
for k1 in range(1, 101):
    for k2 in range(1, 101):
        s = "Uihx!hr!`!ibeu!|iju!H!idmq+xnt!hr+sdlhoe!xnt!un!bIdbj!uid!`ss`x!`oe!uisdd!otfcdsr!b`o!wbdv!`r!`!fsntq"
        plaintext = ''
        for i in range(len(s)):
            k1 = (k1 + ord(s[i])) % 300
            k2 = (k2 + ord(s[i])) % 300
            key = math.gcd(k1, k2)
            plaintext += chr(ord(s[i]) ^ key)
        if plaintext.startswith("This is a "):
            print("s = %s" % (plaintext))
    else:
        continue
    break

然后看看array.txt,里面一堆的300以内数字,结合三个一组,猜想应该是RGB值。
然后结合我后面给出的3 * 637 * 561,构造出图片

from PIL import Image
import numpy as np
# 打开文件并读取内容
with open('array.txt', 'r') as f:
    content = f.read()

# 将文件内容转换为一维数组
data = np.fromstring(content, dtype=np.uint8, sep=' ')

# 将一维数组转换为[561][637][3]的三维数组
data = data.reshape((561, 637, 3))

# 创建一个空白的图像宽637高561
img = Image.new('RGB', (637, 561))

# 将三维数组中的RGB值写入图像中
for y in range(561):
    for x in range(637):
        r, g, b = data[y][x]
        img.putpixel((x, y), (r, g, b))

# 保存图像
img.save('message.png')

可以看到是一张三井寿说教练我想打篮球的名场面
img
从中提取出message,结合message长度25是一句英文
得到"I want to play basketball"
然后补足28位"I want to play basketballI w"
和code异或即可得出flag

exp

from PIL import Image
import numpy as np
import math
for k1 in range(1, 101):
    for k2 in range(1, 101):
        s = "Uihx!hr!`!ibeu!|iju!H!idmq+xnt!hr+sdlhoe!xnt!un!bIdbj!uid!`ss`x!`oe!uisdd!otfcdsr!b`o!wbdv!`r!`!fsntq"
        plaintext = ''
        for i in range(len(s)):
            k1 = (k1 + ord(s[i])) % 300
            k2 = (k2 + ord(s[i])) % 300
            key = math.gcd(k1, k2)
            plaintext += chr(ord(s[i]) ^ key)
        if plaintext.startswith("This is a "):
            print("s = %s" % (plaintext))
    else:
        continue
    break

# 打开文件并读取内容
with open('array.txt', 'r') as f:
    content = f.read()

# 将文件内容转换为一维数组
data = np.fromstring(content, dtype=np.uint8, sep=' ')

# 将一维数组转换为[561][637][3]的三维数组
data = data.reshape((561, 637, 3))

# 创建一个空白的图像宽637高561
img = Image.new('RGB', (637, 561))

# 将三维数组中的RGB值写入图像中
for y in range(561):
    for x in range(637):
        r, g, b = data[y][x]
        img.putpixel((x, y), (r, g, b))

# 保存图像
img.save('message.png')
code = [1,100,52,53,40,15,4,69,46,109,47,40,55,55,92,94,62,70,23,72,8,82,29,65,16,117,117,10]
str = 'I want to play basketballI w'
flag = ''
for i in range(28):
    flag += chr(ord(str[i]) ^ code[i])
print(flag)
#HDCTF{$1AM_DVN|<_5|-|0|-|<U}

flag

NSSCTF{$1AM_DVN|<_5|-|0|-|<U}

跪求原谅说明

这题是我出题出道半夜,人快昏了,脑子不清醒,出了挺多问题的,跪求原谅。

  • 首先就是没给分辨率问题,我当时想的是给了分辨率会不会太明显,就没给,后面考虑到就算知道RGB值,没分辨率也无法还原图片。所以最后就给了一个数组共有3 * 637 * 561个数据的hint,貌似给了这个前面第一步的加密也不用解了,可能在源代码中加一个无意义的[637][561]的二维数组会好一点。
  • 其次就是message长度问题,我本来是想给的,半夜写代码写昏了,给的是flag的长度,flag的长度可以由code数组直接得到,我真该死啊。所以后面又给了message长度为25的hint。
  • 有师傅说我谜语人,我觉得倒也还好吧,把图片搞出来,图片中就三井寿的一句台词,我题目也提示就是一句英文,长度也给了,不难得出是I want to play basketball吧。比赛中有解出图片不明白的师傅我也提示了,真没想难为大家。
  • 再者是flag,有师傅吐槽是乱码一样HDCTF{$1AM_DVN|<_5|-|0|-|<U}
    这应该能看出来是SLAM_DUNK_SHOHKU灌篮高手和湘北的英文名吧。

非预期

  • 直接通过搜索我题目简述,可以搜到图片
  • 通过猜想flag是以HDCTF开头,直接和code异或得出前几位"I want"进而继续猜想得出"I want to play basketball"

鸣谢

再次感谢shangwendada

还有NSSCTF平台的两位运营
还有HnuSec的每一位师傅

posted @ 2023-04-23 16:27  Tree_24  阅读(1228)  评论(1编辑  收藏  举报