LitCTF2023 Reverse 题解及复现

一.enbase64

1.main函数

经典的base64换表,第10和11行作用重复,12行也没有实际作用,此处的base表根本没有变化
在这里插入图片描述

2.换表函数

在17行的base64函数中有一个basechange换表操作
在这里插入图片描述
basechange函数关键代码:

  result = strcpy(des, table);
  for ( i = 0; i <= 47; ++i )
  {
    for ( j = 0; j <= 63; ++j )
      table[j] = des[key[j]];		//换表1
    result = strcpy(des, table);	//换表2
  }
  return result;
}

3. check函数

basecheck函数:用于检查input经过加密后是否正确在这里插入图片描述
这里本来想动调得到base表,但是少了dll文件,所以只能用笨方法脚本硬解了

4. 解题脚本:

  1. 求更换后的base64表
#include <stdio.h>
#include <string.h>
void basechange(char* table)
{
    char* result; // eax
    char des[65]; // [esp+13h] [ebp-155h] BYREF
    int key[65]; // [esp+54h] [ebp-114h] BYREF
    int j; // [esp+158h] [ebp-10h]
    int i; // [esp+15Ch] [ebp-Ch]

    memset(key, 0, sizeof(key));
    key[0] = 16;
    key[1] = 34;
    key[2] = 56;
    key[3] = 7;
    key[4] = 46;
    key[5] = 2;
    key[6] = 10;
    key[7] = 44;
    key[8] = 20;
    key[9] = 41;
    key[10] = 59;
    key[11] = 31;
    key[12] = 51;
    key[13] = 60;
    key[14] = 61;
    key[15] = 26;
    key[16] = 5;
    key[17] = 40;
    key[18] = 21;
    key[19] = 38;
    key[20] = 4;
    key[21] = 54;
    key[22] = 52;
    key[23] = 47;
    key[24] = 3;
    key[25] = 11;
    key[26] = 58;
    key[27] = 48;
    key[28] = 32;
    key[29] = 15;
    key[30] = 49;
    key[31] = 14;
    key[32] = 37;
    key[34] = 55;
    key[35] = 53;
    key[36] = 24;
    key[37] = 35;
    key[38] = 18;
    key[39] = 25;
    key[40] = 33;
    key[41] = 43;
    key[42] = 50;
    key[43] = 39;
    key[44] = 12;
    key[45] = 19;
    key[46] = 13;
    key[47] = 42;
    key[48] = 9;
    key[49] = 17;
    key[50] = 28;
    key[51] = 30;
    key[52] = 23;
    key[53] = 36;
    key[54] = 1;
    key[55] = 22;
    key[56] = 57;
    key[57] = 63;
    key[58] = 8;
    key[59] = 27;
    key[60] = 6;
    key[61] = 62;
    key[62] = 45;
    key[63] = 29;
    strcpy(des, table);
    for (i = 0; i <= 47; ++i)
    {
        for (j = 0; j <= 63; ++j)
            table[j] = des[key[j]];
        strcpy(des, table);
    }
}
int main()
{
    unsigned char table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    basechange(table);
    printf("%s", table);
    return 0;
}
运行得到更换后的表:gJ1BRjQie/FIWhEslq7GxbnL26M4+HXUtcpmVTKaydOP38of5v90ZSwrkYzCAuND
  1. base64解码
import base64
str1 = "GQTZlSqQXZ/ghxxwhju3hbuZ4wufWjujWrhYe7Rce7ju"#待解密数据
string1 = "gJ1BRjQie/FIWhEslq7GxbnL26M4+HXUtcpmVTKaydOP38of5v90ZSwrkYzCAuND"#更改后的base64表
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print (base64.b64decode(str1.translate(str.maketrans(string1,string2))))
#LitCTF{python_snake_is_so_easy!}

二.snake

1. 修复MagicNumber

下载得到pyc文件,用010editor或者winhex打开可以发现第一行数据全为0,需要修复MagicNumber
在这里插入图片描述
前面四字节改为420D0D0A保存即可(注意不要改动了后面的数据,最初多添加了一个字节导致错误)
在这里插入图片描述

2. 反编译

然后用pycdc或者python在线反编译输出代码
如果不会使用pycdc等pyhton反编译操作可以看我的另一篇文章Python逆向基本操作步骤

反编译代码:

# Source Generated with Decompyle++
# File: game.cpython-37.pyc (Python 3.7)

'''\xe8\xb4\xaa\xe5\x90\x83\xe8\x9b\x87'''
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()

3. 解题脚本

关键代码是最后一段对flag的操作,不过这里可能是有意为之或者是反编译的问题,意思应该是flag[i]和flag[i+1]的数据异或后互换

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):
    tmp=flag[i]
    flag[i] = flag[i + 1] ^ 136
    flag[i + 1] = tmp ^ 119     #编译或者是故意这样,要添加一个tmp变量才可以输出正常的值
    print(chr(flag[i]),end='')
    print(chr(flag[i+1]),end='')
#LitCTF{python_snake_is_so_easy!}

三.For Aiur

这题经过其他师傅点拨才整出来,可以说出题人有点小坏坏

1. 注意点

使用解包工具时要注意环境问题,这题是python3.8编写的,所以要用python3.8的环境,否则会缺少输出文件,建议使用anaconda管理python版本
推荐文章:
anaconda的安装和使用(管理python环境看这一篇就够了)
Anaconda安装及配置(详细版)
安装好后打开Anaconda Prompt使用命令:
conda crate python38 python=3.8即可创建python3.8环境
使用activate python38切换到python3.8环境
可以使用uncompyle6或者pyinstxtractor.py这两个解包工具,使用方法就不再多赘述可以参考Python逆向基本操作步骤

2. 解包

将Probee.exe和解包工具放在一起,在conda环境中切换到文件夹中
在这里插入图片描述
使用命令:python pyinstxtractor.py Probe.exe
然后会输出Probe.exe_extracted文件夹,打开后可以找到Probee.pyc
在这里插入图片描述
不过关键在于其中的PYZ-00.pyz_extracted文件夹,里面有一个ch.pyc
在这里插入图片描述

3. 反编译

分别用pycdc输出Probee.pyc和ch.pyc的反编译代码
①Probee.pyc:

# Source Generated with Decompyle++
# File: Probee.pyc (Python 3.8)

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.pyc:

# Source Generated with Decompyle++
# File: ch.pyc (Python 3.8)

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()

4. 解题脚本

关键就在于Probee代码中调用了check函数,该函数定义在ch中,分析程序逻辑不难写出解题脚本

# Source Generated with Decompyle++
# File: ch.pyc (Python 3.8)

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 = []
num=0
while True :
    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 += '}'
        break
    num=num+1
print(flag)
#LitCTF{Pylon_OverCharge!!_We_Must_construc7_addition4l_pylons}

四.程序和人有一个能跑就行了

参考:其他师傅的wp得到思路LitCTF RE方向 WP

1. fakeflag

这题很显然有一个输入flag,然后进行RC4加密最后进行判断的过程
在这里插入图片描述
这个下面的操作就比较奇怪,无论字符串是否相等好像都会输出"U are right?"
反而是当字符串不相等时会分配一个新的指针mem用于保存Buf2(即input),然后在sub_475190函数中进行一些操作
在这里插入图片描述

2. 真flag

动态调试开干

  1. 在if判断处下断点,随便输入字符串让程序执行到断点
    在这里插入图片描述

  2. f8步过sub_475190后,跳转到一块区域

    比较异或的是main函数的v15变量保存了475B38处的地址,但是我没有找到具体调用这个位置的函数(如果有佬知道希望浇一浇,上面大佬的文章说是C++异常处理,搞不太懂hhh)
    在这里插入图片描述

3.继续跟进可以找到关键函数
在这里插入图片描述
这里类似主函数执行的一段流程,这里的Buf1和主函数的略有不同,这里的数据应该就可以解得真flag了

call    sub_4744B0
.text:00475B85
.text:00475B8A mov     [esp+4], eax                    ; Source
.text:00475B8E lea     eax, [esp+2ACh+Litctf]
.text:00475B95 mov     [esp], eax                      ; Destination
.text:00475B98 call    strcpy
.text:00475B98
.text:00475B9D lea     eax, [esp+2ACh+var_229]
.text:00475BA4 mov     [esp+4], eax                    ; Buf2
.text:00475BA8 lea     eax, [esp+2ACh+Litctf]
.text:00475BAF mov     dword ptr [esp+8], 1Dh          ; Size
.text:00475BB7 mov     [esp], eax                      ; Buf1
.text:00475BBA mov     byte ptr [esp+83h], 8Dh
.text:00475BC2 mov     byte ptr [esp+84h], 6Ch ; 'l'
.text:00475BCA mov     byte ptr [esp+85h], 85h
.text:00475BD2 mov     byte ptr [esp+86h], 76h ; 'v'
.text:00475BDA mov     byte ptr [esp+87h], 32h ; '2'
.text:00475BE2 mov     byte ptr [esp+88h], 72h ; 'r'
.text:00475BEA mov     byte ptr [esp+89h], 0B7h
.text:00475BF2 mov     byte ptr [esp+8Ah], 43h ; 'C'
.text:00475BFA mov     byte ptr [esp+8Bh], 85h
.text:00475C02 mov     byte ptr [esp+8Ch], 7Bh ; '{'
.text:00475C0A mov     byte ptr [esp+8Dh], 85h
.text:00475C12 mov     byte ptr [esp+8Eh], 0DEh
.text:00475C1A mov     byte ptr [esp+8Fh], 0C1h
.text:00475C22 mov     byte ptr [esp+90h], 0FBh
.text:00475C2A mov     byte ptr [esp+91h], 2Eh ; '.'
.text:00475C32 mov     byte ptr [esp+92h], 64h ; 'd'
.text:00475C3A mov     byte ptr [esp+93h], 7
.text:00475C42 mov     byte ptr [esp+94h], 0C8h
.text:00475C4A mov     byte ptr [esp+95h], 5Fh ; '_'
.text:00475C52 mov     byte ptr [esp+96h], 9Ah
.text:00475C5A mov     byte ptr [esp+97h], 35h ; '5'
.text:00475C62 mov     byte ptr [esp+98h], 18h
.text:00475C6A mov     byte ptr [esp+99h], 0ADh
.text:00475C72 mov     byte ptr [esp+9Ah], 0B5h
.text:00475C7A mov     byte ptr [esp+9Bh], 15h
.text:00475C82 mov     byte ptr [esp+9Ch], 92h
.text:00475C8A mov     byte ptr [esp+9Dh], 0BEh
.text:00475C92 mov     byte ptr [esp+9Eh], 1Bh
.text:00475C9A mov     byte ptr [esp+9Fh], 88h
.text:00475CA2 call    memcmp

解题脚本:

from Crypto.Cipher import ARC4
arr = [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]
arrBytes = bytes(arr)
key = b"litctf"
enc = ARC4.new(key)
flag = enc.decrypt(arrBytes)
print(flag)

五.debase64

1. encode函数

关键在于encode函数
在这里插入图片描述
encode函数:

int __cdecl encode(unsigned __int8 *input, int str)
{
  unsigned __int8 *v2; // ebp
  unsigned __int8 *input2; // ecx
  int iii; // ebx
  int m; // eax
  int i; // edx
  unsigned __int8 *input3; // edx
  int j; // ecx
  unsigned __int8 *input4; // ecx
  int k; // ebx
  int len; // [esp+0h] [ebp-38h]
  int v13; // [esp+4h] [ebp-34h]
  unsigned int number; // [esp+Ch] [ebp-2Ch]

  if ( !*input )
    return 0;
  v2 = input + 4;
  input2 = input;
  iii = 0;
  m = 0;
  v13 = 0;
  while ( 1 )
  {
    number = 0xFFFFFFFF;
    for ( i = 0; i != 64; ++i )
    {
      while ( Base64Table[i] != *input2 )
      {
        if ( ++i == 64 )                        // 查找该字符在base表中的位置
          goto LABEL_7;
      }
      LOBYTE(number) = i;                       // 将位置赋给number
    }
LABEL_7:
    LOBYTE(i) = 0;                              // 清零
    do
    {
      while ( Base64Table[i] != input[iii + 1] )
      {
        if ( ++i == 64 )
          goto LABEL_11;
      }
      BYTE1(number) = i++;
    }
    while ( i != 64 );
LABEL_11:
    input3 = &input[iii + 2];
    for ( j = 0; j != 64; ++j )
    {
      while ( Base64Table[j] != *input3 )
      {
        if ( ++j == 64 )
          goto LABEL_15;
      }
      BYTE2(number) = j;
    }
LABEL_15:
    input4 = &input[iii + 3];
    for ( k = 0; k != 64; ++k )
    {
      while ( Base64Table[k] != *input4 )
      {
        if ( ++k == 64 )
          goto LABEL_19;
      }
      HIBYTE(number) = k;
    }
LABEL_19:                                       // number保存了四个字符在base表中的偏移值
    len = m + 1;
    *(str + m) = (4 * HIBYTE(number)) | (BYTE2(number) >> 4) & 3;
    if ( *input3 == '=' )
      return len;
    len = m + 2;
    *(str + m + 1) = (16 * BYTE2(number)) | (BYTE1(number) >> 2) & 0xF;
    if ( *input4 == 0x3D )
      return len;
    m += 3;
    input2 = v2;
    v2 += 4;
    v13 += 4;
    iii = v13;
    *(str + m - 1) = (BYTE1(number) << 6) | number & 0x3F;// 高2低6
    if ( !*(v2 - 4) )
      return m;
  }
}

2. 函数逻辑

这个函数的逻辑一开始我看的也云里雾里,询问gpt之后有点思路了
这是gpt给出的分析结果:
请添加图片描述
简单而言:

  1. 是将输入的字符串以四个为一组,在base64表中查找这组每个字符的位置
  2. 将四个字符的位置值分别保存在一个int类型变量的四个字节中
  3. 将这四个字节进行类base64编码的方式存储到str字符串中
  4. 这题给出了flag的md5值,以及提示最后四个字符有三个’‘=’,所以还需要爆破
  5. tips:程序中的HIBYTE(),BYTE2(),BYTE1(),LOBYTE()是宏函数,在ida的defs.h文件中可以找到
    这里我也有介绍过:IDA常用宏定义函数
    具体的功能就是分别取最高位字节~最低位字节
#define BYTEn(x, n)   (*((_BYTE*)&(x)+n))
#define WORDn(x, n)   (*((_WORD*)&(x)+n))
#define DWORDn(x, n)  (*((_DWORD*)&(x)+n))

#define LOBYTE(x)  BYTEn(x,LOW_IND(x,_BYTE))
#define LOWORD(x)  WORDn(x,LOW_IND(x,_WORD))
#define LODWORD(x) DWORDn(x,LOW_IND(x,_DWORD))
#define HIBYTE(x)  BYTEn(x,HIGH_IND(x,_BYTE))
#define HIWORD(x)  WORDn(x,HIGH_IND(x,_WORD))
#define HIDWORD(x) DWORDn(x,HIGH_IND(x,_DWORD))
#define BYTE1(x)   BYTEn(x,  1)         // byte 1 (counting from 0)
#define BYTE2(x)   BYTEn(x,  2)

3. 根据程序逻辑得到前16个字符

#include <stdio.h>
int main()
{
	unsigned char basetable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	unsigned char str[16] = { 0 };
	str[0] = 0x46;
	*(unsigned short*)& str[1] = 0x18ED;
	*(unsigned short*)&str[3] = 0x5696;
	*(unsigned short*)&str[5] = 0xD29E;
	*(unsigned int*)&str[7] = 0x80B3B272;
	*(unsigned short*)&str[11] = 0x70;
	*&str[13] = 0;
	str[14]=0;
	unsigned char number = 0;
	for (int i = 0; i < 15; i+=3)
	{
		number = 0;
		number += str[i + 2] & 0x3f;
		printf("%c", basetable[number]);
		number = 0;
		number += str[i + 1] & 0xf;
		number <<= 2;
		number += str[i + 2] >> 6;
		printf("%c", basetable[number]);
		number = 0;
		number += str[i] & 0x3;
		number <<= 4;
		number += str[i + 1] >> 4;
		printf("%c", basetable[number]);
		number = 0;
		number += str[i] >> 2;
		printf("%c", basetable[number]);
	}
	//Y0uReallyKn0wB4sAAAA
	return 0;
}

4. 爆破后四个字符得到flag

import hashlib
 # 已知的前 16 个字符
prefix = 'Y0uReallyKn0wB4s'
 # 目标 MD5 值
target_md5 = '5a3ebb487ad0046e52db00570339aace'
 # 构造所有可能的字符串
possible_chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/='
for s in possible_chars:
    for t in possible_chars:
        for u in possible_chars:
            for v in possible_chars:
                candidate = prefix + s + t + u + v
                md5 = hashlib.md5(candidate.encode()).hexdigest()
                if md5 == target_md5:
                    print("Found string:", candidate)
                    exit(0)
                    #Y0uReallyKn0wB4s3===
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OrientalGlass

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值