LitCTF2023-部分Reserve复现

news2024/10/6 6:00:20

目录

一:[LitCTF 2023]snake

pyc文件magic修复:

pycdc工具转pyc文件为py文件:

[LitCTF 2023]enbase64

[LitCTF 2023]ez_XOR

[LitCTF 2023]For Aiur

在python38环境中将exe文件反编译为pyc文件

pycdc使用:

[LitCTF 2023]程序和人有一个能跑就行了

C++异常处理问题:

[LitCTF 2023]debase64


一:[LitCTF 2023]snake

打开附件,是一个pyc文件,尝试进行转py文件,没成功

后来,经过好学长提点,需要进行python文件magic的修复

pyc文件magic修复:

放到winhex里可以发现E3前面全是0,正常的pyc文件E3前面包含Magic Number和时间戳

Python3.3以下的版本中,只有Magic Number和四位时间戳
Python3.3到Python3.7(不包含3.7)版本中,只有Magic Number和八位时间戳 + 大小信息
Python3.7及以上版本的编译后二进制文件中,头部除了四字节Magic Number,还有四个字节的空位和八个字节的时间戳 + 大小信息,后者对文件反编译没有影响,全部填充0即可

不同的python版本对应不同的magic, 时间戳是这个文件处理的时间信息,其中magic影响文件的反编译,如果pyc文件缺失magic或者magic损坏,就可能出现无法反编译情况

此题是由pyhton.3.7版本编写,查看python.3.7的魔数,修复文件(这里可以参考一位大佬的文章:https://www.cnblogs.com/Here-is-SG/p/15885799.html)

修复成功,注意:可以只修复前四位magic值,后面的时间戳数据可以直接为0

现在可以进行pyc文件转py,使用pycdc工具

pycdc工具转pyc文件为py文件:

使用指令

pycdc.exe 文件名.pyc

直接得到源码

import random
import sys
import time
import pygame
from pygame.locals import *
from collections import deque
SCREEN_WIDTH = 600
SCREEN_HEIGHT = 480
SIZE = 20
LINE_WIDTH = 1
SCOPE_X = (0, SCREEN_WIDTH // SIZE - 1)
SCOPE_Y = (2, SCREEN_HEIGHT // SIZE - 1)
FOOD_STYLE_LIST = [
    (10, (255, 100, 100)),
    (20, (100, 255, 100)),
    (30, (100, 100, 255))]
LIGHT = (100, 100, 100)
DARK = (200, 200, 200)
BLACK = (0, 0, 0)
RED = (200, 30, 30)
BGCOLOR = (40, 40, 60)

def print_text(screen, font, x, y, text, fcolor = ((255, 255, 255),)):
    imgText = font.render(text, True, fcolor)
    screen.blit(imgText, (x, y))


def init_snake():
    snake = deque()
    snake.append((2, SCOPE_Y[0]))
    snake.append((1, SCOPE_Y[0]))
    snake.append((0, SCOPE_Y[0]))
    return snake


def create_food(snake):
    food_x = random.randint(SCOPE_X[0], SCOPE_X[1])
    food_y = random.randint(SCOPE_Y[0], SCOPE_Y[1])
    while (food_x, food_y) in snake:
        food_x = random.randint(SCOPE_X[0], SCOPE_X[1])
        food_y = random.randint(SCOPE_Y[0], SCOPE_Y[1])
    return (food_x, food_y)


def get_food_style():
    return FOOD_STYLE_LIST[random.randint(0, 2)]


def main():
    pygame.init()
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption('\xe8\xb4\xaa\xe5\x90\x83\xe8\x9b\x87')
    font1 = pygame.font.SysFont('SimHei', 24)
    font2 = pygame.font.Font(None, 72)
    (fwidth, fheight) = font2.size('GAME OVER')
    b = True
    snake = init_snake()
    food = create_food(snake)
    food_style = get_food_style()
    pos = (1, 0)
    game_over = True
    start = False
    score = 0
    orispeed = 0.5
    speed = orispeed
    last_move_time = None
    pause = False
    while None:
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
                continue
            if event.type == KEYDOWN or event.key == K_RETURN or game_over:
                start = True
                game_over = False
                b = True
                snake = init_snake()
                food = create_food(snake)
                food_style = get_food_style()
                pos = (1, 0)
                score = 0
                last_move_time = time.time()
                continue
                if not event.key == K_SPACE or game_over:
                    pause = not pause
                    continue
                    if not (event.key in (K_w, K_UP) or b) and pos[1]:
                        pos = (0, -1)
                        b = False
                        continue
                        if not (event.key in (K_s, K_DOWN) or b) and pos[1]:
                            pos = (0, 1)
                            b = False
                            continue
                            if not (event.key in (K_a, K_LEFT) or b) and pos[0]:
                                pos = (-1, 0)
                                b = False
                                continue
                                if not event.key in (K_d, K_RIGHT) and b and pos[0]:
                                    pos = (1, 0)
                                    b = False
                                screen.fill(BGCOLOR)
                                for x in range(SIZE, SCREEN_WIDTH, SIZE):
                                    pygame.draw.line(screen, BLACK, (x, SCOPE_Y[0] * SIZE), (x, SCREEN_HEIGHT), LINE_WIDTH)

        for y in range(SCOPE_Y[0] * SIZE, SCREEN_HEIGHT, SIZE):
            pygame.draw.line(screen, BLACK, (0, y), (SCREEN_WIDTH, y), LINE_WIDTH)

        if not game_over:
            curTime = time.time()
            if not curTime - last_move_time > speed and pause:
                b = True
                last_move_time = curTime
                next_s = (snake[0][0] + pos[0], snake[0][1] + pos[1])
                if next_s == food:
                    snake.appendleft(next_s)
                    score += food_style[0]
                    speed = orispeed - 0.03 * (score // 100)
                    food = create_food(snake)
                    food_style = get_food_style()
                elif next_s[0] <= next_s[0] or next_s[0] <= SCOPE_X[1]:
                    pass
                else:
                    SCOPE_X[0]
            elif next_s[1] <= next_s[1] or next_s[1] <= SCOPE_Y[1]:
                pass
            else:
                SCOPE_Y[0]
        elif next_s not in snake:
            snake.appendleft(next_s)
            snake.pop()
        else:
            game_over = True
        if not game_over:
            pygame.draw.rect(screen, food_style[1], (food[0] * SIZE, food[1] * SIZE, SIZE, SIZE), 0)
        for s in snake:
            pygame.draw.rect(screen, DARK, (s[0] * SIZE + LINE_WIDTH, s[1] * SIZE + LINE_WIDTH, SIZE - LINE_WIDTH * 2, SIZE - LINE_WIDTH * 2), 0)

        print_text(screen, font1, 450, 7, f'''\xe5\xbe\x97\xe5\x88\x86: {score}''')
        if score > 1000:
            flag = [
                30,
                196,
                52,
                252,
                49,
                220,
                7,
                243,
                3,
                241,
                24,
                224,
                40,
                230,
                25,
                251,
                28,
                233,
                40,
                237,
                4,
                225,
                4,
                215,
                40,
                231,
                22,
                237,
                14,
                251,
                10,
                169]
            for i in range(0, len(flag), 2):
                flag[i] = flag[i + 1] ^ 136
                flag[i + 1] = flag[i] ^ 119

            print_text(screen, font2, (SCREEN_WIDTH - fwidth) // 2, (SCREEN_HEIGHT - fheight) // 2, bytes(flag).decode(), RED)
            pygame.display.update()
        if game_over and start:
            print_text(screen, font2, (SCREEN_WIDTH - fwidth) // 2, (SCREEN_HEIGHT - fheight) // 2, 'GAME OVER', RED)
        pygame.display.update()

if __name__ == '__main__':
    main()

写脚本解密:

#include<stdio.h>
#include<string.h>
int main(){
	          char   flag [] = {
                30,
                196,
                52,
                252,
                49,
                220,
                7,
                243,
                3,
                241,
                24,
                224,
                40,
                230,
                25,
                251,
                28,
                233,
                40,
                237,
                4,
                225,
                4,
                215,
                40,
                231,
                22,
                237,
                14,
                251,
                10,
                169};
            
            int i,j;
            for(i=0;i<strlen(flag);i+=2){
            	j=flag[i];
            	flag[i] = flag[i + 1] ^ 136;
				flag[i + 1] = j ^ 119;
			}
             puts(flag);   
                
	return 0;
}

[LitCTF 2023]enbase64

打开文件,查壳,发现无壳

 __main();
  memset(Str, 0, 1000);
  memset(Str1, 0, sizeof(Str1));
  *Source = *"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  strcpy(v5, "9+/");
  qmemcpy(&Source[1], &aAbcdefghijklmn[-(Source - &Source[1])], 4 * (((Source - &Source[1] + 65) & 0xFFFFFFFC) >> 2));
  puts("Please input flag:");
  gets(Str);
  if ( strlen(Str) == 33 )
  {
    base64(Source, Str, Str1);
    basecheck(Str1);
  }
  return 0;
}

分析代码可以发现,strcp(v5..)qmemcpy函数没有实际作用, &aAbcdefghijklmn里面还是base64表,qmemcpy函数实际上是qmemcpy(&Source[1],&aAbcdefghijklmn[1]..)所以base64表根本没变

但看到下面的函数bas64()可以发现里面有换表操作

{
  signed int result; // eax
  signed int v4; // [esp+14h] [ebp-14h]
  signed int i; // [esp+18h] [ebp-10h]
  int v6; // [esp+1Ch] [ebp-Ch]

  basechange(Source);
  v4 = strlen(Str);
  v6 = 0;
  for ( i = 0; ; i += 3 )
  {
    result = i;
    if ( i >= v4 )
      break;
    a3[v6] = Source[Str[i] >> 2];
    a3[v6 + 1] = Source[(16 * Str[i]) & 0x30 | (Str[i + 1] >> 4)];
    a3[v6 + 2] = Source[(4 * Str[i + 1]) & 0x3C | (Str[i + 2] >> 6)];
    a3[v6 + 3] = Source[Str[i + 2] & 0x3F];
    v6 += 4;
  }
  return result;
}

可以发现,刚开始会有一个basechange()函数,然后下面是base64加密过程,进入basechang()函数查看

char *__cdecl basechange(char *Source)
{
  char *result; // eax
  char Destination[65]; // [esp+13h] [ebp-155h] BYREF
  int v3[65]; // [esp+54h] [ebp-114h] BYREF
  int j; // [esp+158h] [ebp-10h]
  int i; // [esp+15Ch] [ebp-Ch]

  memset(v3, 0, sizeof(v3));
  v3[0] = 16;
  v3[1] = 34;
  v3[2] = 56;
  v3[3] = 7;
  v3[4] = 46;
  v3[5] = 2;
  v3[6] = 10;
  v3[7] = 44;
  v3[8] = 20;
  v3[9] = 41;
  v3[10] = 59;
  v3[11] = 31;
  v3[12] = 51;
  v3[13] = 60;
  v3[14] = 61;
  v3[15] = 26;
  v3[16] = 5;
  v3[17] = 40;
  v3[18] = 21;
  v3[19] = 38;
  v3[20] = 4;
  v3[21] = 54;
  v3[22] = 52;
  v3[23] = 47;
  v3[24] = 3;
  v3[25] = 11;
  v3[26] = 58;
  v3[27] = 48;
  v3[28] = 32;
  v3[29] = 15;
  v3[30] = 49;
  v3[31] = 14;
  v3[32] = 37;
  v3[34] = 55;
  v3[35] = 53;
  v3[36] = 24;
  v3[37] = 35;
  v3[38] = 18;
  v3[39] = 25;
  v3[40] = 33;
  v3[41] = 43;
  v3[42] = 50;
  v3[43] = 39;
  v3[44] = 12;
  v3[45] = 19;
  v3[46] = 13;
  v3[47] = 42;
  v3[48] = 9;
  v3[49] = 17;
  v3[50] = 28;
  v3[51] = 30;
  v3[52] = 23;
  v3[53] = 36;
  v3[54] = 1;
  v3[55] = 22;
  v3[56] = 57;
  v3[57] = 63;
  v3[58] = 8;
  v3[59] = 27;
  v3[60] = 6;
  v3[61] = 62;
  v3[62] = 45;
  v3[63] = 29;
  result = strcpy(Destination, Source);
  for ( i = 0; i <= 47; ++i )
  {
    for ( j = 0; j <= 63; ++j )
      Source[j] = Destination[v3[j]];
    result = strcpy(Destination, Source);
  }
  return result;
}

是换表没错了, 每次将v3的值作为下标在base64表中查找,查找到的值赋给base64密码表,重复64次,可以写一个脚本,解出新的base64密码表

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
	int i=0,j;
char a[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char b[65];
unsigned int v3[65];
char Source[65];
  v3[0] = 16;
  v3[1] = 34;
  v3[2] = 56;
  v3[3] = 7;
  v3[4] = 46;
  v3[5] = 2;
  v3[6] = 10;
  v3[7] = 44;
  v3[8] = 20;
  v3[9] = 41;
  v3[10] = 59;
  v3[11] = 31;
  v3[12] = 51;
  v3[13] = 60;
  v3[14] = 61;
  v3[15] = 26;
  v3[16] = 5;
  v3[17] = 40;
  v3[18] = 21;
  v3[19] = 38;
  v3[20] = 4;
  v3[21] = 54;
  v3[22] = 52;
  v3[23] = 47;
  v3[24] = 3;
  v3[25] = 11;
  v3[26] = 58;
  v3[27] = 48;
  v3[28] = 32;
  v3[29] = 15;
  v3[30] = 49;
  v3[31] = 14;
  v3[32] = 37;
  v3[34] = 55;
  v3[35] = 53;
  v3[36] = 24;
  v3[37] = 35;
  v3[38] = 18;
  v3[39] = 25;
  v3[40] = 33;
  v3[41] = 43;
  v3[42] = 50;
  v3[43] = 39;
  v3[44] = 12;
  v3[45] = 19;
  v3[46] = 13;
  v3[47] = 42;
  v3[48] = 9;
  v3[49] = 17;
  v3[50] = 28;
  v3[51] = 30;
  v3[52] = 23;
  v3[53] = 36;
  v3[54] = 1;
  v3[55] = 22;
  v3[56] = 57;
  v3[57] = 63;
  v3[58] = 8;
  v3[59] = 27;
  v3[60] = 6;
  v3[61] = 62;
  v3[62] = 45;
  v3[63] = 29;
  	strcpy(b, a);
for(i=0;i<=47;i++){
for ( j = 0; j <= 63; ++j )
	a[j] =b[v3[j]];
	strcpy(b, a);
}

puts(a);
return 0;
}

得到新表值为: gJ1BRjQie/FIWhEslq7GxbnL26M4+HXUtcpmVTKaydOP38of5v90ZSwrkYzCAuND

base()函数分析完,再返回主函数分析basecheck()函数

int __cdecl basecheck(char *Str1)
{
  if ( !strcmp(Str1, "GQTZlSqQXZ/ghxxwhju3hbuZ4wufWjujWrhYe7Rce7ju") )
    return puts("You are right!");
  else
    return puts("False");
}

直接找到了加密后的数据,上脚本解密咯

import base64
import string

str = "GQTZlSqQXZ/ghxxwhju3hbuZ4wufWjujWrhYe7Rce7ju"   # 欲解密的字符串
outtab  = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"  #  原生字母表
intab   = "gJ1BRjQie/FIWhEslq7GxbnL26M4+HXUtcpmVTKaydOP38of5v90ZSwrkYzCAuND"  #  换表之后的字母表

print (base64.b64decode(str.translate(str.maketrans(intab,outtab))))

[LitCTF 2023]ez_XOR

  __main();
  strcpy(Str2, "E`}J]OrQF[V8zV:hzpV}fVF[t");
  v9 = 0;
  v10 = 0;
  v11 = 0;
  v12 = 0;
  v13 = 0;
  v14 = 0;
  v15 = 0;
  printf("Enter The Right FLAG:");
  scanf("%s", Str1);
  XOR(Str1, 3);
  if ( !strcmp(Str1, Str2) )
  {
    printf("U Saved IT!\n");
    return 0;
  }
  else
  {
    printf("Wrong!Try again!\n");
    return main(v4, v5, v6);
  }
}

进入xor函数查看

size_t __cdecl XOR(char *Str, char a2)
{
  size_t result; // eax
  unsigned int i; // [esp+2Ch] [ebp-Ch]

  for ( i = 0; ; ++i )
  {
    result = strlen(Str);
    if ( i >= result )
      break;
    Str[i] ^= 3 * a2;
  }
  return result;
}

一个异或过程,写脚本解密

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
	int i;
	unsigned char arr[]="E`}J]OrQF[V8zV:hzpV}fVF[t";
	for(i=0;i<25;i++){
			arr[i] ^= 9;
			printf("%c",arr[i]);
	}

	return 0;
}
//LitCTF{XOR_1s_3asy_to_OR}

[LitCTF 2023]For Aiur

打开附件,可以看到是一个exe文件,用exeinfope查看可以看到是python编写的文件

在python38环境中将exe文件反编译为pyc文件

 需要将exe文件反编译为pyc文件,进而得到py文件,拿到python源码

我们可以使用pyinstxtractor.py来将exe文件反编译为pyc文件。

注意:

此题必须使用python38环境,否则会导致编译文件缺失

如果原本是其它python环境,可以使用anaconda来切换环境

下载配置可以看这个文章:Anaconda安装及配置(详细版)_旅途中的宽~的博客-CSDN博客

相关指令可以看这篇文章:anaconda常用命令_anaconda指令-CSDN博客

  • 将exe和pyinstxtractor.py放在同一级文件夹中
  • 在下载配置好的anaconda中使用conda activate python38把环境换到python38中
  • 在conda环境中切换到pyinstxtractor文件夹
  • 使用指令:python pyinstxtractor 文件名.exe

 这样再回到pyinstxtractor文件中就可以看到生成了extracted文件,进入查看可以找到同名.pyc文件struct.py文件

  • 将同名.py文件和struct.py文件放到winhex中
  • 将struct.py文件头中E3前的数据复制到同名.py文件的E3前(此题的同名.py文件没有缺失magic,不用进行这两步)

 

 Probee.pyc文件的magic没有缺失,可以直接编译

接着使用pycdc工具将pyc文件反编译为py文件,得到python源码

pycdc使用:

1.将文件放到pycdc同级文件夹中

2.使用指令pycdc.exe 文件.pyc

这样就可以得到python源码

注意:单看这一个源码无法得到flag,还生成了一个extracted文件(如果之前不是在python3.8环境中反编译exe文件,这个ectracted文件夹里就不会有内容哦),里面有需要的ch.pyc文件(大坑,好难想到)

重复上面pycdc工具使用操作,将ch.pyc编译为ch,py,得到ch.py源码

Probee源码:

from cv2 import imread, imshow, namedWindow, WINDOW_NORMAL, FONT_HERSHEY_SIMPLEX, getTickCount, getTickFrequency, putText, LINE_AA, waitKey, getTextSize, resize, moveWindow, IMREAD_UNCHANGED, destroyAllWindows
from numpy import uint8, zeros
from ch import check
Mineral = 100
Pylonnum = 0

def buildPylon():
    global Mineral, Pylonnum
    if Mineral < 100:
        warn_img = imread('source/warn1.png')
        imshow('warning', warn_img)
        return None
    None -= 100
    img1 = imread('source/warpin.png')
    namedWindow('Pylon' + str(Pylonnum), WINDOW_NORMAL)
    imshow('Pylon' + str(Pylonnum), img1)
    font = FONT_HERSHEY_SIMPLEX
    pos = (img1.shape[1] - 300, 50)
    color = (0, 0, 0)
    thickness = 2
    timer = getTickCount() + 18 * getTickFrequency()
    if getTickCount() < timer:
        img1_copy = img1.copy()
        time_left = int((timer - getTickCount()) / getTickFrequency())
        text = 'Time left: {}s'.format(time_left)
        putText(img1_copy, text, pos, font, 1, color, thickness, LINE_AA)
        imshow('Pylon' + str(Pylonnum), img1_copy)
        if waitKey(1) & 255 == ord('q'):
            pass

    img2 = imread('source/Pylon.png')
    imshow('Pylon' + str(Pylonnum), img2)
    waitKey(1)
    Pylonnum += 1


def gather():
    global Mineral
    digit_value = Mineral
    icon_img = imread('source/jingtikuang.png', IMREAD_UNCHANGED)
    icon_img = resize(icon_img, (120, 120))
    bg_img = zeros(icon_img.shape, uint8, **('dtype',))
    bg_img[(0:icon_img.shape[0], 0:icon_img.shape[1], :)] = icon_img
    digit_text = str(digit_value)
    digit_size = getTextSize(digit_text, FONT_HERSHEY_SIMPLEX, 1, 2)[0]
    digit_x = bg_img.shape[1] - digit_size[0]
    digit_y = digit_size[1] + 10
    putText(bg_img, digit_text, (digit_x, digit_y), FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
    imshow('Mineral', bg_img)
    moveWindow('Mineral', 1200, 100)
    Mineral += 5

img = imread('source/Probe.png')
(new_width, new_height) = (200, 200)
img = resize(img, (new_width, new_height))
(screen_width, screen_height) = (800, 120)
(x, y) = (600, 100)
(dx, dy) = (0, 5)
namedWindow('Probe', WINDOW_NORMAL)
imshow('Probe', img)
check(Pylonnum)
imshow('Probe', img)
if y < screen_height:
    dy = 5
if y > screen_height:
    dy = -5
x = x + dx
y = y + dy
moveWindow('Probe', x, y)
if waitKey(50) & 255 == ord('g'):
    gather()
if waitKey(50) & 255 == ord('b'):
    buildPylon()
if waitKey(50) & 255 == ord('e'):
    pass

destroyAllWindows()

ch.py源码:

enc = [
    98,
    77,
    94,
    91,
    92,
    107,
    125,
    66,
    87,
    70,
    113,
    92,
    83,
    70,
    85,
    81,
    19,
    21,
    109,
    99,
    87,
    107,
    127,
    65,
    65,
    64,
    109,
    87,
    93,
    90,
    65,
    64,
    64,
    65,
    81,
    3,
    109,
    85,
    86,
    80,
    91,
    64,
    91,
    91,
    92,
    0,
    94,
    107,
    66,
    77,
    94,
    91,
    92,
    71]
lis = []

def check(num):
    flag = 'LitCTF{'
    if num % 2 == 0 and num % 4 == 0 and num % 6 == 0 and num % 8 == 0 and num % 12 == 0 and num % 13 == 11:
        k = str(num)
        for i in range(len(enc)):
            flag += chr(ord(k[i % len(k)]) ^ enc[i])
            lis.append(ord(k[i % len(k)]) ^ enc[i])
        flag += '}'
        imread = imread
        imshow = imshow
        namedWindow = namedWindow
        WINDOW_NORMAL = WINDOW_NORMAL
        FONT_HERSHEY_SIMPLEX = FONT_HERSHEY_SIMPLEX
        getTickCount = getTickCount
        getTickFrequency = getTickFrequency
        putText = putText
        LINE_AA = LINE_AA
        waitKey = waitKey
        getTextSize = getTextSize
        resize = resize
        moveWindow = moveWindow
        IMREAD_UNCHANGED = IMREAD_UNCHANGED
        destroyAllWindows = destroyAllWindows
        import cv2
        uint8 = uint8
        zeros = zeros
        import numpy
        img = zeros((200, 20000, 3), uint8)
        img.fill(255)
        text = flag
        font = FONT_HERSHEY_SIMPLEX
        pos = (50, 120)
        color = (0, 0, 0)
        thickness = 2
        putText(img, text, pos, font, 1, color, thickness, LINE_AA)
        imshow('flag', img)
        waitKey(0)
        destroyAllWindows()

两个源码对比来看,该函数将数字num作为输入并检查它是否满足某些条件。如果满足条件,该函数通过在字符串的每个字符与加密列表的相应元素之间执行按位异或运算来生成标志字符串

写解密脚本,得到flag

enc = [
    98,
    77,
    94,
    91,
    92,
    107,
    125,
    66,
    87,
    70,
    113,
    92,
    83,
    70,
    85,
    81,
    19,
    21,
    109,
    99,
    87,
    107,
    127,
    65,
    65,
    64,
    109,
    87,
    93,
    90,
    65,
    64,
    64,
    65,
    81,
    3,
    109,
    85,
    86,
    80,
    91,
    64,
    91,
    91,
    92,
    0,
    94,
    107,
    66,
    77,
    94,
    91,
    92,
    71
]

flag = 'LitCTF{'
lis = []

for num in range(0, 120):
    if num % 2 == 0 and num % 4 == 0 and num % 6 == 0 and num % 8 == 0 and num % 12 == 0 and num % 13 == 11:
        k = str(num)
        for i in range(len(enc)):
            flag += chr(ord(k[i % len(k)]) ^ enc[i])
            lis.append(ord(k[i % len(k)]) ^ enc[i])
flag += '}'
print(flag)

[LitCTF 2023]程序和人有一个能跑就行了

下载附件,查壳,无壳,32位,ida打开

找到主函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _DWORD *v3; // eax
  _DWORD *v5; // eax
  char *v6; // eax
  int v7; // [esp+0h] [ebp-2ACh] BYREF
  int v8; // [esp+14h] [ebp-298h]
  int *v9; // [esp+18h] [ebp-294h]
  int v10; // [esp+1Ch] [ebp-290h] BYREF
  int v11; // [esp+20h] [ebp-28Ch]
  int (__cdecl *v12)(int, int, __int64, _BYTE *, int); // [esp+34h] [ebp-278h]
  int *v13; // [esp+38h] [ebp-274h]
  int *v14; // [esp+3Ch] [ebp-270h]
  void *v15; // [esp+40h] [ebp-26Ch]
  int *v16; // [esp+44h] [ebp-268h]
  char Buf1[27]; // [esp+68h] [ebp-244h] BYREF
  char data[256]; // [esp+A0h] [ebp-20Ch] BYREF
  char key[268]; // [esp+1A0h] [ebp-10Ch] BYREF
  int savedregs; // [esp+2ACh] [ebp+0h] BYREF

  v9 = &v10;
  v12 = sub_4752F0;
  v13 = dword_476078;
  v14 = &savedregs;
  v15 = &loc_475B38;
  v16 = &v7;
  sub_40A8F0(&v10);
  sub_409B80();
  v11 = -1;
  sub_472810(&dword_47DD80, data);
  strcpy(key, "litctf");
  function(data, strlen(data), key, 6u);
  Buf1[0] = 0x8D;
  Buf1[1] = 0x6C;
  Buf1[2] = 0x85;
  Buf1[3] = 0x76;
  Buf1[4] = 0x32;
  Buf1[5] = 0x72;
  Buf1[6] = 0xB7;
  Buf1[7] = 0x40;
  Buf1[8] = 0x88;
  Buf1[9] = 0x7E;
  Buf1[10] = 0x95;
  Buf1[11] = 0xEE;
  Buf1[12] = 0xC5;
  Buf1[13] = 0xED;
  Buf1[14] = 0x2E;
  Buf1[15] = 0x71;
  Buf1[16] = 0x37;
  Buf1[17] = 0xF1;
  Buf1[18] = 0x4A;
  Buf1[19] = 0x99;
  Buf1[20] = 0x35;
  Buf1[21] = 0x18;
  Buf1[22] = 0xA7;
  Buf1[23] = 0xB0;
  Buf1[24] = 0;
  Buf1[25] = 0x96;
  Buf1[26] = 0xB7;
  v8 = memcmp(Buf1, data, 0x1Bu);
  if ( v8 )                                     // 不相等时执行
  {
    v11 = 1;
    v5 = sub_471AE0(&dword_47DF60, "U are wrong?");
    sub_46FBA0(v5);                             // 字符串转换为相应的数字
    v6 = sub_474310(4);                         // 为加密操作分配一块内存,并返回指向该内存块的指针。
    *v6 = data;                                 // 数据传给分配的内存
    sub_475190(v6, &off_483660, 0);             // 抛出一个异常,提示出现了无法处理的错误
  }
  v11 = 1;
  v3 = sub_471AE0(&dword_47DF60, "U are right?");
  sub_46FBA0(v3);
  sub_40AA70(v9);
  return v8;
}

 可以看到主函数中有function函数,function函数中进行了rc4加密,进入查看,

int __cdecl function(int data, int datalen, int key, unsigned int len)
{
  unsigned int i; // ecx
  char *v5; // eax
  int v6; // ecx
  char v7; // si
  int ii; // eax
  int jj; // ecx
  int v10; // edx
  char v11; // di
  char v12; // si
  int v13; // edi
  int v14; // [esp+0h] [ebp-214h]
  char S[256]; // [esp+4h] [ebp-210h] BYREF
  char K[272]; // [esp+104h] [ebp-110h] BYREF

  for ( i = 0; i != 256; ++i )                  // 初始化S表和K表
  {
    S[i] = i;
    K[i] = *(key + i % len);
  }
  v5 = S;
  LOBYTE(v6) = 0;
  do
  {
    v7 = *v5++;                                 // 先取*再++
    v6 = (v5[255] + v7 + v6);
    *(v5 - 1) = S[v6];                          // s[i],交换
    S[v6] = v7;
  }
  while ( v5 != K );
  ii = datalen;
  if ( datalen )
  {
    LOBYTE(jj) = 0;
    LOBYTE(ii) = 0;
    v10 = 0;
    v14 = 0;
    do
    {
      ++v10;
      ii = (ii + 1);
      v11 = S[ii];
      v12 = v11;
      jj = (v11 + jj);                          // 交换
      S[ii] = S[jj];
      S[jj] = v11;
      v13 = v14;
      v14 = v10;                                // 相当于n++
      *(data + v13) ^= S[(S[ii] + v12)];
    }
    while ( v10 != datalen );
  }
  return ii;
}

使用主函数下面的Buf数据进行rc4解密,得到错误的flag

 这个BUF数据得到的是错误的flag

def rc4(key, ciphertext):
    # 初始化S盒
    sbox = list(range(256))
    j = 0
    for i in range(256):
        j = (j + sbox[i] + key[i % len(key)]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]

    # 生成密钥流
    i = 0
    j = 0
    keystream = []
    for _ in range(len(ciphertext)):
        i = (i + 1) % 256
        j = (j + sbox[i]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]
        k = sbox[(sbox[i] + sbox[j]) % 256]
        keystream.append(k)

    # 解密密文
    plaintext = []
    for i in range(len(ciphertext)):
        m = ciphertext[i] ^ keystream[i]
        plaintext.append(m)

    # 将明文转换为字符串
    return ''.join([chr(p) for p in plaintext])



# 测试
key = b'litctf'
ciphertext = [0x8D,0x6C,0x85,0x76,0x32,0x72,0xB7,0x40,0x88,0x7E,0x95,0xEE,0xC5,0xED,0x2E,0x71,0x37,0xF1,0x4A,0x99,0x35,0x18,0xA7,0xB0,0x0,0x96,0xB7]
plaintext = rc4(key, ciphertext)
print(plaintext)
#LitCTF{this_is_a_fake_flag}

接着回到主程序查看,当判断不相等时还对数据进行了操作

通过调试查看具体作用

下断点:

 任意输入一个值,程序进入U are wrong的部分

 一直执行,可以看到还有一个Buf数据,这里的数据和主函数不同,拿去解密得到真正的flag

def rc4(key, ciphertext):
    # 初始化S盒
    sbox = list(range(256))
    j = 0
    for i in range(256):
        j = (j + sbox[i] + key[i % len(key)]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]

    # 生成密钥流
    i = 0
    j = 0
    keystream = []
    for _ in range(len(ciphertext)):
        i = (i + 1) % 256
        j = (j + sbox[i]) % 256
        sbox[i], sbox[j] = sbox[j], sbox[i]
        k = sbox[(sbox[i] + sbox[j]) % 256]
        keystream.append(k)

    # 解密密文
    plaintext = []
    for i in range(len(ciphertext)):
        m = ciphertext[i] ^ keystream[i]
        plaintext.append(m)

    # 将明文转换为字符串
    return ''.join([chr(p) for p in plaintext])



# 测试
key = b'litctf'
ciphertext = [0x8D, 0x6C, 0x85, 0x76, 0x32, 0x72, 0xB7, 0x43, 0x85, 0x7B, 0x85, 0xDE, 0xC1, 0xFB, 0x2E, 0x64, 0x07, 0xC8, 0x5F, 0x9A, 0x35, 0x18, 0xAD, 0xB5, 0x15, 0x92, 0xBE, 0x1B, 0x88]
plaintext = rc4(key, ciphertext)
print(plaintext)

后来知道这题考察的是C++异常处理,补充上

C++异常处理问题:

当程序出现 C++ 异常时,通常需要对异常进行处理,以便尽可能地避免程序崩溃或产生不可预料的错误行为。这种异常可能是除0错误。处理方法有

  • 使用try-catch块:在可能抛出异常的代码块中使用try-catch块来捕获和处理异常。try块用于包裹可能会抛出异常的代码,catch块则用于捕获和处理异常。在catch块中,可以输出错误信息,或者采取其他措施来处理异常。注意,必须是包含在try块中的代码出现异常时才会使用catch捕捉处理,try块外面的使用其他方式,可能有多个catch
  • 抛出异常:在出现错误时,可以抛出自定义的异常,以提供更详细的错误信息。在 C++ 中,可以使用throw语句抛出异常,抛出的异常可以是任何类型,包括内置类型、自定义类型、指针等。
  • 终止程序:在某些情况下,程序出现异常后可能无法正常运行,这时可以选择终止程序。在 C++ 中,可以使用abort函数或exit函数来终止程序。abort函数会向操作系统发送一个中断信号,导致程序立即终止;exit函数则会正常退出程序,但是可以通过设置退出码来指示程序出现了异常。

此题出现的就是try-catch块捕捉处理的方法,抛出异常,实现程序的大跳转,将真正的flag数据隐藏起来

得到flag:LitCTF{welcome_to_the_litctf}

[LitCTF 2023]debase64

打开附件,找到主函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE result[14]; // [esp+10h] [ebp-44h] BYREF
  char v5; // [esp+1Eh] [ebp-36h]
  char v6; // [esp+20h] [ebp-34h]
  __int16 v7; // [esp+21h] [ebp-33h]
  __int16 v8; // [esp+23h] [ebp-31h]
  __int16 v9; // [esp+25h] [ebp-2Fh]
  __int16 v10; // [esp+27h] [ebp-2Dh]
  __int16 v11; // [esp+29h] [ebp-2Bh]
  __int16 v12; // [esp+2Bh] [ebp-29h]
  char v13; // [esp+2Dh] [ebp-27h]
  char v14; // [esp+2Eh] [ebp-26h]
  char input[20]; // [esp+3Ch] [ebp-18h] BYREF

  sub_402290();
  puts(&Buffer);                                // 请开始你的表演
  memset(result, 0, sizeof(result));
  v5 = 0;
  memset(input, 0, sizeof(input));
  scanf("%s", input);
  if ( strlen(input) != 20 )
  {
    puts(&aE_0);                                // 长度不对哦
    return 0;
  }
  v6 = 0x46;
  v7 = 0x18ED;
  v8 = 0x5696;
  v9 = 0xD29E;
  v10 = 0xB272;
  v11 = 0x80B3;
  v12 = 0xFF70;
  v13 = 0;
  v14 = 0;
  function(input, result);                      // 加密函数
  if ( v6 != result[0]                          // 最后结果判断
    || v7 != *&result[1]
    || v8 != *&result[3]
    || v9 != *&result[5]
    || v10 != *&result[7]
    || v11 != *&result[9]
    || v12 != *&result[11]
    || v13 != result[13]
    || v14 != v5 )
  {
    puts("不对哦");
    return 0;
  }
  return puts(asc_404065);                      // 恭喜你答对了
}

 可以看到程序先输入一串数据,然后在function函数中进行加密,加密后的数据与v6~v14进行比较,相等则输出,恭喜答对了,否则输出 不对哦。

进入加密function函数查看

int __cdecl sub_401520(_BYTE *a1, int a2)
{
  _BYTE *v2; // ebp
  _BYTE *v3; // ecx
  int v4; // ebx
  int v5; // eax
  int i; // edx
  _BYTE *v7; // edx
  int j; // ecx 每四个一组查找字符在base64表中的下标位置,将下标位置写入数组中,
  _BYTE *v9; // ecx 最后将数组元素的四个字节变换为3个数据(类似base64)
  int k; // ebx
  int v12; // [esp+0h] [ebp-38h]
  int v13; // [esp+4h] [ebp-34h]
  int v14; // [esp+Ch] [ebp-2Ch]

  if ( !*a1 )
    return 0;
  v2 = a1 + 4;
  v3 = a1;
  v4 = 0;
  v5 = 0;
  v13 = 0;
  while ( 1 )
  {
    v14 = -1;
    for ( i = 0; i != 64; ++i )
    {
      while ( aAbcdefghijklmn[i] != *v3 )
      {
        if ( ++i == 64 )
          goto LABEL_7;
      }
      LOBYTE(v14) = i;
    }
LABEL_7:
    LOBYTE(i) = 0;                              // 最低位字节,查找第一个字符
    do
    {
      while ( aAbcdefghijklmn[i] != a1[v4 + 1] )
      {
        if ( ++i == 64 )
          goto LABEL_11;
      }                                         // 查找第二个字符
      BYTE1(v14) = i++;                         // 第二位字节
    }
    while ( i != 64 );
LABEL_11:
    v7 = &a1[v4 + 2];
    for ( j = 0; j != 64; ++j )
    {
      while ( aAbcdefghijklmn[j] != *v7 )
      {
        if ( ++j == 64 )
          goto LABEL_15;
      }                                         // 查找第三个字符
      BYTE2(v14) = j;                           // 第三位字节
    }
LABEL_15:
    v9 = &a1[v4 + 3];
    for ( k = 0; k != 64; ++k )
    {
      while ( aAbcdefghijklmn[k] != *v9 )
      {
        if ( ++k == 64 )
          goto LABEL_19;
      }                                         // 查找第四个字符
      HIBYTE(v14) = k;                          // 最高位字节
    }
LABEL_19:
    v12 = v5 + 1;
    *(a2 + v5) = (4 * HIBYTE(v14)) | (BYTE2(v14) >> 4) & 3;
    if ( *v7 == 61 )
      return v12;
    v12 = v5 + 2;
    *(a2 + v5 + 1) = (16 * BYTE2(v14)) | (BYTE1(v14) >> 2) & 0xF;
    if ( *v9 == 61 )
      return v12;
    v5 += 3;
    v3 = v2;
    v2 += 4;
    v13 += 4;
    v4 = v13;
    *(a2 + v5 - 1) = (BYTE1(v14) << 6) | v14 & 0x3F;
    if ( !*(v2 - 4) )
      return v5;
  }
}

分析函数,具体是将Base64编码字符串每四个一组进行处理。

首先,代码找到第一个字符在字符表中的下标,将其存储在v14的最低位字节中。

然后,代码找到第二个字符在字符表中的下标,将其存储在v14的第二位字节中。

接着,代码找到第三个和第四个字符在字符表中的下标,并将它们分别存储在v14的第三位字节和最高位字节中。当查找不到时使用goto语句来跳过下面的循环

具体参考chatgpt解读:

  • 首先判断输入的字符串是否为空,如果为空则返回0。
  • 然后从输入字符串的第一个字符开始,每四个字符一组,查找每个字符在base64表中的下标位置,并将这四个下标位置存储到一个32位的整数变量v14中,其中最低位字节存储第一个字符在base64表中的下标位置,最高位字节存储第四个字符在base64表中的下标位置。
  • 将v14中的四个字节变换为三个数据,每个数据占8位,类似于base64的编码方式,存储到输出缓冲区中。
  • 如果在输入字符串中出现了'='字符,则说明输入字符串的长度不是4的倍数,需要特殊处理。
  • 如果已经处理完了整个输入字符串,则返回输出数据的长度。
  • 函数使用了一些位运算技巧来将v14中的四个字节变换为三个数据,例如 (4 * HIBYTE(v14)) | (BYTE2(v14) >> 4) & 3 将v14的最高位字节左移两位乘以4,并与v14的第三位字节右移4位后的结果取低两位进行或运算,得到一个8位的数据

写脚本解密(借鉴一位大佬的脚本,具体文章参考:https://blog.csdn.net/m0_72989226/article/details/130673183)

import base64
​
list = [0x46, 0xED, 0x18, 0x96, 0x56, 0x9E, 0xD2, 0x72, 0xB2, 0xB3, 0x80, 0x70, 0xFF]
list_b = bytes(list)
print(base64.b64encode(list_b))

最后一位根据提示爆破

import hashlib
​
key = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
for i in key:
    str1 = "Y0uReallyKn0wB4s" + i + "==="
    if hashlib.md5(str1.encode('utf-8')).hexdigest() == "5a3ebb487ad0046e52db00570339aace":
        print(str1)
        break

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iptables防火墙1

iptables防火墙 iptables概述 Linux 系统的防火墙 &#xff1a;IP信息包过滤系统&#xff0c;它实际上由两个组件netfilter 和 iptables组成。 主要工作在网络层&#xff0c;针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。 netfilter/iptables 关系&#xf…

ADC模数转换器

目录 逐次逼近型ADC 原理图 ADC基本框图​编辑 输入通道 ​编辑 转换模式 ADC触发控制​编辑 数据对齐 ​编辑 采样时间 校准 硬件电路 来源b站江科大stm32入门教程 逐次逼近型ADC 原理图 规则组最好要和DMA一起使用 ADDCLK来自ADC预分频器 &#xff0c;根据下图看出&…

OpenGL之绘制三角形

目录 OpenGL绘制图形的流程 标准化设备坐标 VAO和VBO VBO(顶点缓冲对象) VBO(顶点缓冲对象)创建流程 VAO(顶点数组对象) 绘制三角形 ​编辑给三角形添加颜色 顶点着色器 片段着色器 编译着色器 使用着色器为三角形添加颜色 OpenGL绘制图形的流程 在OpenGL中&…

二、Django REST Framework (DRF)序列化反序列化

参考&#xff1a; 为什么要学DRF和什么是REST API | 大江狗的博客 上一章&#xff1a; 一、Django REST Framework (DRF)& RESTful 风格api_做测试的喵酱的博客-CSDN博客 一、DRF框架介绍 1.1 介绍 Django REST Framework (DRF)这个神器我们可以快速开发出优秀而且…

基于SpringBoot的网吧管理系统的设计与实现

背景 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&#xff0c;各行各业相继进入信息管理时代&a…

ICR-预测三种医学状况 #$60,000 #Kaggle

CompHub[1] 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 以下信息由AI辅助创作&#xff0c;仅供参考 比赛名称 ICR - Identifying Age-Related Conditions[2] (见文末阅读原…

chatgpt赋能Python-pythonforrange

Python中的Range函数 在Python编程语言中&#xff0c;range()是一个用于生成一系列数字的函数。它可以接受1至3个整型参数&#xff1a;起始值、终止值和步长。生成的数字包括起始值&#xff0c;但不包括终止值。步长默认为1。 Range函数的语法 Python中range()函数的常规语法…

C++视角下的Qt按钮:从基础应用到高级定制

C视角下的Qt按钮&#xff1a;从基础应用到高级定制 一、Qt按钮基础 (Qt Button Basics)1.1 Qt按钮的定义与创建 (Definition and Creation of Qt Buttons)1.2 Qt按钮的属性 (Properties of Qt Buttons)文本 (Text)图标 (Icon)大小 (Size)样式 (Style)是否可用 (Enabled) 1.3 Qt…

Java【TCP 协议3】提高效率的五大机制

文章目录 前言一、滑动窗口与高速重传1, 什么是滑动窗口2, 什么是高速重传2.1, ack 丢包2.2, 数据丢包 二、流量控制1, 什么是流量控制 三、拥塞控制1, 什么是拥塞控制 四、延迟应答1, 什么是延迟应答 五、捎带应答1, 什么是捎带应答 总结 前言 各位读者好, 我是小陈, 这是我的…

JavaScript实现1-100之间所有的素数的代码

以下为实现1-100之间所有的素数的程序代码和运行截图 目录 前言 一、1-100之间所有的素数 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据题目要求实现…

防火墙(二)

进一步了解防火墙 一、SNAT原理与应用模拟实验 二、DNAT的原理与应用模拟实验 三、抓包四、防火墙规则的备份和还原 一、SNAT原理与应用 SNAT应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff08;私有不能早Internet中正常路由&#xff09; SNAT原理&am…

【2023 · CANN训练营第一季】新手班 昇腾AI入门课(PyTorch)

1 昇腾AI全栈架构 昇腾计算产业是基于昇腾系列处理器和基础软件构睫的全栈Al计算基础设施&#xff0e;行业应用及服务&#xff0c;包括昇腾系列处理器、Atlas系列硬件、CANN (Compute Architecture for Neural Networks&#xff0c;异构计算架构》、Al计算框架、应用使能、全流…

nRF52832 定时器REPEATED模式,导致异常重启的问题排查全过程

文章目录 一、遇到问题二、JLink连接时&#xff0c;无法复现三、查看日志四、回退改动五、解决问题六、问题剖析 一、遇到问题 nRF52832项目增加一个功能&#xff0c;自测没问题就发出去了。结果300台机器&#xff0c;有7台出现异常&#xff0c;无法正常使用。细看了一遍提价上…

论文阅读_音频生成_AudioLM

论文信息 name_en: AudioLM: a Language Modeling Approach to Audio Generation name_ch: AudioLM&#xff1a;一种音频生成的语言建模方法 paper_addr: http://arxiv.org/abs/2209.03143 doi: https://doi.org/10.48550/arXiv.2209.03143 date_read: 2023-04-25 date_publis…

打开数据结构大门——实现小小顺序表

文章目录 前言顺序表的概念及分类搭建项目&#xff08;Seqlist&#xff09;:apple:搭建一个顺序表结构&&定义所需头文件&&函数:banana:初始化:pear:打印:watermelon:数据个数:smile:检查容量:fireworks:判空:tea:在尾部插入数据:tomato:在尾部删除数据:lemon:在…

封装Appium启动参数,提高自动化测试效率的关键

目录 前言&#xff1a; 一、开发环境搭建 二、代码实现 1.导入Appium相关的库文件。 2.创建Appium的启动参数对象&#xff0c;并设置相关参数。 3.启动测试服务。 4.执行测试用例。 5.结束测试服务。 三、总结 前言&#xff1a; Appium是一款广泛使用的自动化测试工具…

Microsoft Office 2007的安装

哈喽&#xff0c;大家好。今天一起学习的是office2007的安装&#xff0c;有兴趣的小伙伴也可以来一起试试手。 一、测试演示参数 演示操作系统&#xff1a;Windows 7 不建议win10及以上操作系统使用 系统类型&#xff1a;64位 演示版本&#xff1a;cn_office_ultimate_2007_D…

从 SIEM 到下一代 SIEM 的演变

在此文中&#xff0c;我们详细介绍了下一代 SIEM 的演变。传统的 SIEM 主要用于提高网络可见性和网络安全性&#xff0c;同时支持合规性。它们跨应用程序、网络和系统摄取、收集和存储日志数据。 SIEM 使捕获和搜索数据变得更加容易&#xff0c;这些数据有助于组织进行审计、取…

详解RGB和XYZ色彩空间转换之下

前言 首先需要指明本文中描述的R,G,B并非通常的sRGB中的三个分量R,G,B&#xff0c;而是波长分别为700nm&#xff0c;546.1nm&#xff0c;435.8nm的单色红光&#xff0c;单色绿光&#xff0c;单色蓝光。sRGB中的RGB中的红色、绿色、蓝色已经不是单色光了。虽然习惯上大家都叫RGB…

Docker数据目录迁移方法

文章目录 前言一、停掉Docker服务&#xff1f;二、迁移docker数据到数据盘目三、备份原数据目录四、添加软链接五、重启docker服务六、确认服务没有问题后&#xff0c;删除备份的目录总结 前言 服务器上安装的docker服务&#xff0c;数据默认存储在/var/lib/docker目录&#x…