CTF密码学题目深度解析:消失的岛屿(LostIslands)

admin 2025-12-22 04:31:34 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 这篇文章详细解析了一道名为消失的岛屿的CTF密码学题目,通过逆向工程分析了一个使用魔改Base64编码和字符映射变换的加密算法,文章使用radare2工具进行静态分析,深入理解了charEncrypt函数的五种转换规则,最终设计出完整的解密方案并提供了Python脚本,成功解密得到flag,是学习CTF密码学和逆向工程的优秀案例。 综合评分: 90 文章分类: CTF,二进制安全,WEB安全,逆向分析,密码学


cover_image

CTF密码学题目深度解析:消失的岛屿(LostIslands)

原创

破镜安全

破镜安全

2025年12月15日 08:00 四川

CTF密码学题目深度解析:消失的岛屿(LostIslands)

前言

在CTF(Capture The Flag)竞赛中,密码学题目往往需要我们综合运用逆向工程、密码学知识和编程能力。本文将详细分析一道名为”消失的岛屿”(LostIslands)的密码学题目,从零开始,一步步带领读者完成从逆向分析到成功解密的全过程。

这道题目巧妙地结合了Base64编码和多层字符映射变换,是学习密码学和逆向工程的优秀案例。文章将详细讲解每一步的分析思路、使用的工具和技术原理,确保即使是初学者也能够理解并掌握。

一、题目初探:了解我们面对的是什么

1.1 题目文件基本信息

拿到题目后,我们首先需要了解文件的基本属性。使用Linux系统自带的file命令:

$ file LostIslands.exe
LostIslands.exe: PE32 executable (console) Intel 80386, for MS Windows, 12 sections

这个命令告诉我们:

  • 这是一个Windows可执行文件(PE32格式)
  • 是32位程序(Intel 80386架构)
  • 是控制台程序(console),意味着它运行在命令行界面
  • 包含12个节(sections)

我们还可以计算文件的MD5哈希值,用于验证文件完整性:

$ md5sum LostIslands.exe
8ef175759b0abbfd8f613fa428c78cb9  LostIslands.exe

1.2 提取可读字符串:寻找线索

在逆向分析的第一步,我们通常使用strings命令提取程序中的可读字符串。这个命令可以快速找到程序中硬编码的文本信息,往往能提供重要线索。

$ strings LostIslands.exe | grep -E "(flag|base64|TABLE|tuvwx)" | head -20

执行这个命令后,我们发现了几个关键信息:

发现1:一个不寻常的字符串

tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/

这个字符串长度为64个字符,包含大小写字母、数字以及特殊字符”+”和”/”。熟悉Base64编码的读者会立即意识到:标准的Base64码表也是64个字符!但这个码表的顺序明显被打乱了,这是一个魔改的Base64码表

标准Base64码表应该是:

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/

发现2:疑似密文

!NGV%,$h1f4S3%2P(hkQ94==

这个字符串有几个Base64的典型特征:

  • 末尾有两个等号(==),这是Base64的填充符号
  • 长度为24个字符(Base64编码后的长度通常是4的倍数)
  • 包含各种奇怪的字符

但它显然不是标准Base64编码,因为标准Base64只包含字母、数字、+、/和=,而这里出现了”!”、”%”、”,”、”$”等字符。

发现3:相关函数名

_flag
base64_encode
_base64_encode
charEncrypt

这些函数名透露了程序的核心功能:

  • base64_encode:进行Base64编码
  • charEncrypt:进行字符加密/转换
  • _flag:可能与flag相关

发现4:用户交互信息

please enter Serial:
Success
Please Try Again
error

这说明程序会提示用户输入一个Serial(序列号),然后进行验证。

1.3 初步分析总结

通过简单的字符串提取,我们已经得到了重要信息:

  1. 程序使用了魔改的Base64编码
  2. 存在一个需要验证的密文
  3. 有一个名为charEncrypt的函数进行字符转换
  4. 程序会读取用户输入并进行验证

现在,我们需要深入分析程序的逻辑,理解加密算法的具体实现。

二、深入分析:使用radare2逆向工程

2.1 为什么选择radare2

在逆向工程中,我们有多种工具可选:

  • IDA Pro:功能强大但商业软件,价格昂贵
  • Ghidra:NSA开源的反编译器,功能完善
  • radare2:开源、免费、命令行工具,功能强大

本文选择radare2进行分析,它可以帮助我们:

  • 反汇编程序代码
  • 分析程序结构
  • 查看函数调用关系
  • 追踪数据流

2.2 定位关键函数

首先,我们使用radare2分析程序,找出所有函数:

$ r2 -q -c "aaa; afl" LostIslands.exe | grep -E "(main|charEncrypt|base64)"

命令解释:

  • r2:启动radare2

  • -q:安静模式(减少输出)

  • -c "aaa; afl":执行两个命令

  • aaa:分析所有函数(analyze all)

  • afl:列出所有函数(list functions)

输出结果:

0x004013c0   15 136          dbg.charEncrypt
0x00401448    8 474          dbg.base64_encode
0x00401622    6 224          dbg.main

这个输出给了我们三个关键信息:

| 函数名 | 地址 | 大小(字节) | 分析 | | — | — | — | — | | charEncrypt | 0x004013c0 | 136 | 相对较小,可能是简单的字符转换 | | base64_encode | 0x00401448 | 474 | 较大,包含完整的编码逻辑 | | main | 0x00401622 | 224 | 主函数,协调整体流程 |

函数大小可以帮助我们判断其复杂度。charEncrypt只有136字节,说明它的逻辑相对简单,是个突破口。

2.3 分析charEncrypt函数:核心加密逻辑

现在,让我们深入分析charEncrypt函数。这个函数名暗示它负责字符的加密转换。

$ r2 -q -c "aaa; s dbg.charEncrypt; pdf" LostIslands.exe

命令解释:

  • s dbg.charEncrypt:跳转(seek)到charEncrypt函数
  • pdf:打印反汇编代码(print disassemble function)

2.3.1 函数序言:建立栈帧

0x004013c0:  push ebp                     ; 保存调用者的栈帧基址
0x004013c1:  mov ebp, esp                 ; 建立新的栈帧
0x004013c3:  sub esp, 0x10                ; 在栈上分配16字节的局部变量空间
0x004013c6:  mov dword [var_4h], 0x404064 ; 将地址0x404064存入局部变量

让我解释这段代码在做什么:

栈帧的概念:在x86架构中,每个函数调用都会在栈上建立一个”栈帧”,用于存储:

  • 函数参数
  • 返回地址
  • 局部变量

关键发现0x404064这个地址! 这正是我们之前发现的魔改Base64码表的存储位置。程序将这个地址保存到局部变量中,说明函数会用到这个码表。

2.3.2 读取码表字符

0x004013cd:  mov edx, dword [arg_8h]      ; edx = 函数参数(索引值)
0x004013d0:  mov eax, dword [var_4h]      ; eax = 码表地址 (0x404064)
0x004013d3:  add eax, edx                 ; eax = 码表地址 + 索引
0x004013d5:  movzx eax, byte [eax]        ; 从计算出的地址读取一个字节(字符)
0x004013d8:  movsx eax, al                ; 将字节符号扩展为32位整数
0x004013db:  mov dword [arg_8h], eax      ; 将字符的ASCII值保存回参数位置

这段代码的逻辑:

  1. 从参数中获取一个索引值
  2. 用这个索引在魔改码表中查找对应的字符
  3. 将字符的ASCII值保存起来

为什么要这样做?这是典型的数组访问操作:char = table[index]。函数接收一个索引,返回码表中该位置的字符,然后对这个字符进行转换。

2.3.3 第一种转换:大写字母(A-Z)

0x004013de:  cmp dword [arg_8h], 0x40     ; 比较:字符 > 64 (ASCII '@')
0x004013e2: &nbsp;jle 0x4013fa &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果 <= 64,跳过这段代码
0x004013e4: &nbsp;cmp dword [arg_8h], 0x5a &nbsp; &nbsp; ; 比较:字符 <= 90 (ASCII 'Z')
0x004013e8: &nbsp;jg 0x4013fa &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 如果 > 90,跳过这段代码
0x004013ea: &nbsp;mov eax, 0x9b &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; eax = 155
0x004013ef: &nbsp;sub eax, dword [arg_8h] &nbsp; &nbsp; &nbsp;; eax = 155 - 字符的ASCII值
0x004013f2: &nbsp;mov dword [arg_8h], eax &nbsp; &nbsp; &nbsp;; 保存转换结果
0x004013f5: &nbsp;mov eax, dword [arg_8h]
0x004013f8: &nbsp;jmp 0x401446 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 跳转到函数返回

让我详细解释这段代码的逻辑:

条件判断

  • 第一个判断:字符 > 64(即 >= 65)
  • 第二个判断:字符 <= 90
  • 综合起来:65 <= 字符 <= 90

查看ASCII表,我们知道:

  • ‘A’ = 65
  • ‘Z’ = 90

所以这段代码处理的是大写字母A-Z

转换公式

new_char = 155 - old_char

让我们验证几个例子:

  • ‘A’ (65) → 155 – 65 = 90 → ‘Z’
  • ‘T’ (84) → 155 – 84 = 71 → ‘G’
  • ‘Z’ (90) → 155 – 65 = 65 → ‘A’

为什么用155?这是一个精心设计的数字。注意:65 + 90 = 155。这个公式实际上是在做镜像映射

  • A ↔ Z
  • B ↔ Y
  • C ↔ X

这是一种对称的转换,大写字母表被”翻转”了。

2.3.4 第二种转换:小写字母(a-z)

0x004013fa: &nbsp;cmp dword [arg_8h], 0x60 &nbsp; &nbsp; ; 比较:字符 > 96 (ASCII '`')
0x004013fe: &nbsp;jle 0x40140f &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果 <= 96,跳过
0x00401400: &nbsp;cmp dword [arg_8h], 0x7a &nbsp; &nbsp; ; 比较:字符 <= 122 (ASCII 'z')
0x00401404: &nbsp;jg 0x40140f &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 如果 > 122,跳过
0x00401406: &nbsp;subl dword [arg_8h], 0x40 &nbsp; &nbsp;; 字符 = 字符 - 64
0x0040140a: &nbsp;mov eax, dword [arg_8h]
0x0040140d: &nbsp;jmp 0x401446 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 跳转到函数返回

条件判断

  • 97 <= 字符 <= 122
  • 这对应小写字母 ‘a’ (97) 到 ‘z’ (122)

转换公式

new_char = old_char - 64

让我们验证几个例子:

  • ‘a’ (97) → 97 – 64 = 33 → ‘!’ (感叹号)
  • ‘t’ (116) → 116 – 64 = 52 → ‘4’
  • ‘u’ (117) → 117 – 64 = 53 → ‘5’
  • ‘z’ (122) → 122 – 64 = 58 → ‘:’

为什么减64?64正好是大写字母’@’的ASCII值。减去64后:

  • ‘a’变成了33(’!’)
  • ‘z’变成了58(’:’)

小写字母被映射到了可打印的特殊字符区域。这种映射使得结果看起来像是随机字符,增加了破解难度。

2.3.5 第三种转换:数字(0-9)

0x0040140f: &nbsp;cmp dword [arg_8h], 0x2f &nbsp; &nbsp; ; 比较:字符 > 47 (ASCII '/')
0x00401413: &nbsp;jle 0x401424 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果 <= 47,跳过
0x00401415: &nbsp;cmp dword [arg_8h], 0x39 &nbsp; &nbsp; ; 比较:字符 <= 57 (ASCII '9')
0x00401419: &nbsp;jg 0x401424 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 如果 > 57,跳过
0x0040141b: &nbsp;add dword [arg_8h], 0x32 &nbsp; &nbsp; ; 字符 = 字符 + 50
0x0040141f: &nbsp;mov eax, dword [arg_8h]
0x00401422: &nbsp;jmp 0x401446 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 跳转到函数返回

条件判断

  • 48 <= 字符 <= 57
  • 这对应数字 ‘0’ (48) 到 ‘9’ (57)

转换公式

new_char = old_char + 50

让我们验证几个例子:

  • ‘0’ (48) → 48 + 50 = 98 → ‘b’
  • ‘7’ (55) → 55 + 50 = 105 → ‘i’
  • ‘9’ (57) → 57 + 50 = 107 → ‘k’

为什么加50?加上50后,数字被映射到了小写字母区域:

  • ‘0’变成了’b’
  • ‘9’变成了’k’

这种映射使得原本的数字在最终密文中变成了字母,进一步混淆了原始数据。

2.3.6 第四种转换:特殊字符’+’

0x00401424: &nbsp;cmp dword [arg_8h], 0x2b &nbsp; &nbsp; ; 比较:字符 == 43 (ASCII '+')
0x00401428: &nbsp;jne 0x401436 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果不等于43,跳过
0x0040142a: &nbsp;mov dword [arg_8h], 0x77 &nbsp; &nbsp; ; 字符 = 119 (ASCII 'w')
0x00401431: &nbsp;mov eax, dword [arg_8h]
0x00401434: &nbsp;jmp 0x401446 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 跳转到函数返回

转换规则

'+' (43) → 'w' (119)

为什么单独处理’+’?在标准Base64编码中,’+’是码表的第62个字符。这里将它固定映射为’w’,确保在最终密文中不会出现’+’字符。

2.3.7 第五种转换:特殊字符’/’

0x00401436: &nbsp;cmp dword [arg_8h], 0x2f &nbsp; &nbsp; ; 比较:字符 == 47 (ASCII '/')
0x0040143a: &nbsp;jne 0x401443 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果不等于47,跳过
0x0040143c: &nbsp;mov dword [arg_8h], 0x79 &nbsp; &nbsp; ; 字符 = 121 (ASCII 'y')

转换规则

'/' (47) → 'y' (121)

为什么单独处理’/’?在标准Base64编码中,’/’是码表的第63个字符。将它映射为’y’,同样是为了混淆最终结果。

2.3.8 charEncrypt函数总结

通过完整的汇编分析,我们完全理解了charEncrypt函数的5种转换规则:

| 序号 | 输入字符类型 | ASCII范围 | 汇编特征 | 转换公式 | 实例 | | — | — | — | — | — | — | | 1 | 大写字母 | 65-90 | mov $0x9b; sub | chr(155 – ascii) | T(84) → G(71) | | 2 | 小写字母 | 97-122 | subl $0x40 | chr(ascii – 64) | t(116) → 4(52) | | 3 | 数字 | 48-57 | add $0x32 | chr(ascii + 50) | 7(55) → i(105) | | 4 | ‘+’ | 43 | mov $0x77 | 固定为’w'(119) | + → w | | 5 | ‘/’ | 47 | mov $0x79 | 固定为’y'(121) | / → y |

设计目的分析

  1. 混淆原始数据:通过不同的数学变换,使得原始字符与转换后字符之间的关系不明显
  2. 保持可打印性:所有转换后的字符都是可打印字符,便于存储和传输
  3. 增加分析难度:使用不同的变换规则处理不同类型的字符,增加了逆向分析的复杂度

2.4 分析main函数:程序主流程

了解了charEncrypt的工作原理后,我们需要理解程序的整体流程。让我们分析main函数:

$ r2 -q -c&nbsp;"aaa; s dbg.main; pdf"&nbsp;LostIslands.exe

2.4.1 用户输入部分

0x00401630: &nbsp;mov dword [esp], str.please_enter_Serial:
0x00401637: &nbsp;call sym._printf &nbsp; &nbsp; &nbsp; &nbsp;; 打印提示信息
0x0040163c: &nbsp;lea eax, [str1] &nbsp; &nbsp; &nbsp; &nbsp; ; 获取输入缓冲区地址
0x00401640: &nbsp;mov dword [size], eax
0x00401644: &nbsp;mov dword [esp], 0x4040ba &nbsp; ; " %s" 格式字符串
0x0040164b: &nbsp;call sym._scanf &nbsp; &nbsp; &nbsp; &nbsp; ; 读取用户输入

这段代码很直观:

  1. 打印提示信息”please enter Serial:”
  2. 准备一个缓冲区
  3. 使用scanf读取用户输入的字符串

2.4.2 输入验证

0x00401650: &nbsp;lea eax, [str1] &nbsp; &nbsp; &nbsp; &nbsp; ; 获取输入字符串地址
0x00401657: &nbsp;call sym._strlen &nbsp; &nbsp; &nbsp; ; 计算字符串长度
0x0040165c: &nbsp;cmp eax, 0x31 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 比较长度是否 <= 49
0x0040165f: &nbsp;jbe 0x40166d &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 如果 <= 49,继续执行
0x00401661: &nbsp;mov dword [esp], str.error &nbsp;; 否则输出"error"

长度检查:程序检查输入长度是否不超过49字节(0x31 = 49)。这是为了防止缓冲区溢出。

2.4.3 内存分配

0x0040166d: &nbsp;mov dword [size], 0x400 &nbsp; &nbsp; ; 大小 = 1024字节
0x00401675: &nbsp;mov dword [esp], 1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 元素数量 = 1
0x0040167c: &nbsp;call sym._calloc &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 分配内存
0x00401681: &nbsp;mov dword [base64_str], eax ; 保存指针

程序调用calloc分配了1024字节的内存,用于存储Base64编码后的结果。

为什么是1024字节?Base64编码会使数据量增加约33%。如果输入最多49字节,编码后大约65字节,1024字节足够了。

2.4.4 Base64编码

0x00401685: &nbsp;lea eax, [str1] &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 输入字符串地址
0x00401689: &nbsp;mov dword [esp], eax &nbsp; &nbsp; &nbsp; &nbsp;; 参数1:输入
0x0040168c: &nbsp;call sym._strlen &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 获取长度
0x00401691: &nbsp;mov dword [var_8h], eax &nbsp; &nbsp;; 参数2:长度
0x00401695: &nbsp;mov eax, dword [base64_str]
0x00401699: &nbsp;mov dword [size], eax &nbsp; &nbsp; &nbsp;; 参数3:输出缓冲区
0x004016a4: &nbsp;call dbg.base64_encode &nbsp; &nbsp; &nbsp;; 调用编码函数

这里调用了base64_encode函数,传入三个参数:

  1. 输入字符串
  2. 输入长度
  3. 输出缓冲区

2.4.5 密文比较

0x004016a9: &nbsp;mov dword [str], str._NGV% &nbsp;; "!NGV%,$h1f4S3%2P(hkQ94=="
0x004016b1: &nbsp;mov eax, dword [base64_str] ; 编码结果
0x004016b5: &nbsp;mov dword [size], eax
0x004016b9: &nbsp;mov eax, dword [str] &nbsp; &nbsp; &nbsp; ; 硬编码的密文
0x004016bd: &nbsp;mov dword [esp], eax
0x004016c0: &nbsp;call sym._strcmp &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 字符串比较
0x004016c5: &nbsp;test eax, eax &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 检查比较结果
0x004016c7: &nbsp;jne 0x4016d7 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 如果不相等,跳转到失败分支
0x004016c9: &nbsp;mov dword [esp], str.Success &nbsp;; 相等则输出"Success"

关键逻辑

  1. 将用户输入进行魔改Base64编码
  2. 将编码结果与硬编码密文 !NGV%,$h1f4S3%2P(hkQ94== 比较
  3. 如果相等,输出”Success”;否则输出”Please Try Again”

2.5 分析base64_encode函数:编码实现

base64_encode函数实现了完整的Base64编码流程,但使用了经过charEncrypt转换的码表。

0x00401448: &nbsp;push ebp
0x00401449: &nbsp;mov ebp, esp
0x0040144b: &nbsp;push ebx
0x0040144c: &nbsp;sub esp, 0x14 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;; 分配局部变量空间

函数的核心循环逻辑(简化说明):

  1. 读取3个字节的输入
   0x00401462: &nbsp;mov edx, dword [var_8h] &nbsp; &nbsp; &nbsp;; i = 当前位置
   0x00401465: &nbsp;mov eax, dword [arg_8h] &nbsp; &nbsp; &nbsp;; 输入数据地址
   0x00401468: &nbsp;add eax, edx &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ; 当前字节地址
   0x0040146a: &nbsp;movzx eax, byte [eax] &nbsp; &nbsp; &nbsp; &nbsp;; 读取一个字节
  1. Base64编码转换(3字节→4字符)
  • 将3个字节(24位)分成4组,每组6位
  • 每组6位的值范围是0-63,正好对应Base64码表的索引
  1. 调用charEncrypt转换
   0x0040148f: &nbsp;call dbg.charEncrypt &nbsp; &nbsp; &nbsp; &nbsp; ; 第1次调用
   ...
   0x004014cd: &nbsp;call dbg.charEncrypt &nbsp; &nbsp; &nbsp; &nbsp; ; 第2次调用
   ...
   0x00401533: &nbsp;call dbg.charEncrypt &nbsp; &nbsp; &nbsp; &nbsp; ; 第3次调用

在编码过程中,每个Base64索引值都会通过charEncrypt函数转换。

  1. 处理填充
   0x004014e4: &nbsp;mov byte [eax], 0x3d &nbsp; &nbsp; &nbsp; &nbsp; ; 写入 '=' 填充符

工作流程总结

输入数据
&nbsp; &nbsp;↓
标准Base64编码算法(3字节→4索引)
&nbsp; &nbsp;↓
用索引从魔改码表TABLE1取字符
&nbsp; &nbsp;↓
对每个字符调用charEncrypt转换
&nbsp; &nbsp;↓
生成最终密文

三、理解加密流程:两层混淆

通过对三个关键函数的深入分析,我们现在可以完整理解这道题目的加密流程。

3.1 加密流程图解

用户输入明文:"KanXue2019ctf_st"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
第一步:标准Base64编码原理
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;将每3个字节转换为4个6位索引
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
第二步:使用魔改码表TABLE1查找
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TABLE1 = "tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;根据6位索引值在TABLE1中取字符
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
第三步:对TABLE1中的每个字符应用charEncrypt
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;经过5种变换规则处理
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
第四步:生成最终码表TABLE2
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;TABLE2 = "45678GF,-./0123iBA!"#$%&'()*j9:bcdefghEDC+ZYXWVUTSRQPONMLKJIHkwy="
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;↓
最终密文:"!NGV%,$h1f4S3%2P(hkQ94=="

3.2 为什么这样设计?双层混淆的巧妙之处

这道题目使用了双层混淆策略:

第一层:魔改Base64码表

  • 打乱了标准码表的顺序
  • 使得无法直接用标准Base64解码

第二层:charEncrypt字符转换

  • 对魔改码表再次进行字符级别的转换
  • 即使找到了魔改码表TABLE1,也无法直接解密
  • 必须理解charEncrypt的转换规则

这种设计的优点:

  1. 增加破解难度:需要逆向两层算法
  2. 隐藏真实码表:程序中存储的TABLE1不是真正使用的TABLE2
  3. 考察综合能力:需要逆向分析、密码学和编程三方面能力

3.3 生成真正的编码表TABLE2

现在我们理解了charEncrypt的工作原理,可以生成真正使用的码表TABLE2。

让我们手工计算TABLE1的前几个字符:

TABLE1[0] = ‘t’ (116)

  • 小写字母规则:116 – 64 = 52 → ‘4’
  • TABLE2[0] = ‘4’

TABLE1[1] = ‘u’ (117)

  • 小写字母规则:117 – 64 = 53 → ‘5’
  • TABLE2[1] = ‘5’

TABLE1[2] = ‘v’ (118)

  • 小写字母规则:118 – 64 = 54 → ‘6’
  • TABLE2[2] = ‘6’

TABLE1[5] = ‘T’ (84)

  • 大写字母规则:155 – 84 = 71 → ‘G’
  • TABLE2[5] = ‘G’

TABLE1[17] = ‘7’ (55)

  • 数字规则:55 + 50 = 105 → ‘i’
  • TABLE2[17] = ‘i’

TABLE1[62] = ‘+’ (43)

  • 特殊字符规则:固定为 ‘w’ (119)
  • TABLE2[62] = ‘w’

TABLE1[63] = ‘/’ (47)

  • 特殊字符规则:固定为 ‘y’ (121)
  • TABLE2[63] = ‘y’

完整的TABLE2(通过程序计算):

TABLE2 = "45678GF,-./0123iBA!"#$%&'()*j9:bcdefghEDC+ZYXWVUTSRQPONMLKJIHkwy="

四、设计解密方案:逆向思维

理解了加密流程后,我们需要设计解密方案。

4.1 解密的关键思路

加密流程:

明文 → 标准Base64索引 → TABLE1取字符 → charEncrypt转换 → TABLE2字符 → 密文

解密流程(逆向):

密文 → 在TABLE2中查找位置 → 得到标准Base64索引 → TABLE0取字符 → 标准Base64密文 → Base64解码 → 明文

为什么可以这样解密?

关键理解:

  1. Base64编码的本质是索引映射
  2. 每个密文字符在TABLE2中的位置,就是原始的Base64索引值
  3. 用这个索引值去查标准码表TABLE0,就得到了标准Base64
  4. 标准Base64可以直接解码

4.2 详细解密步骤

步骤1:生成TABLE2

TABLE1 =&nbsp;"tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/"
TABLE2 =&nbsp;""

for&nbsp;char&nbsp;in&nbsp;TABLE1:
&nbsp; &nbsp; ascii_val = ord(char)

&nbsp; &nbsp;&nbsp;if&nbsp;65&nbsp;<= ascii_val <=&nbsp;90: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 大写字母
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += chr(155&nbsp;- ascii_val)
&nbsp; &nbsp;&nbsp;elif&nbsp;97&nbsp;<= ascii_val <=&nbsp;122: &nbsp; &nbsp; &nbsp;&nbsp;# 小写字母
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += chr(ascii_val -&nbsp;64)
&nbsp; &nbsp;&nbsp;elif&nbsp;48&nbsp;<= ascii_val <=&nbsp;57: &nbsp; &nbsp; &nbsp; &nbsp;# 数字
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += chr(ascii_val +&nbsp;50)
&nbsp; &nbsp;&nbsp;elif&nbsp;ascii_val ==&nbsp;43: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# '+'
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += chr(119) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 'w'
&nbsp; &nbsp;&nbsp;elif&nbsp;ascii_val ==&nbsp;47: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# '/'
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += chr(121) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 'y'

TABLE2 +=&nbsp;'='&nbsp;&nbsp;# 添加填充字符

步骤2:准备标准Base64码表

TABLE0 =&nbsp;'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

步骤3:密文映射

cip =&nbsp;"!NGV%,$h1f4S3%2P(hkQ94=="
cip_origin =&nbsp;""

for&nbsp;char&nbsp;in&nbsp;cip:
&nbsp; &nbsp;&nbsp;# 在TABLE2中找到密文字符的位置(索引)
&nbsp; &nbsp; index = TABLE2.find(char)
&nbsp; &nbsp;&nbsp;# 用这个索引从TABLE0中取出标准Base64字符
&nbsp; &nbsp; cip_origin += TABLE0[index]

让我们详细追踪第一个字符:

  • 密文字符:’!’
  • 在TABLE2中查找:TABLE2.find(‘!’) = 18
  • 在TABLE0中取字符:TABLE0[18] = ‘S’

逐字符处理:

| 密文 | TABLE2位置 | TABLE0字符 | 说明 | | — | — | — | — | | ! | 18 | S | 索引18对应标准base64的’S’ | | N | 54 | 2 | | | G | 5 | F | | | V | 46 | u | | | % | 22 | W | | | , | 7 | H | | | $ | 21 | V | | | h | 37 | l | |

完整映射后得到:S2FuWHVlMjAxOWN0Zl9zdA==

步骤4:标准Base64解码

import&nbsp;base64
flag = base64.b64decode("S2FuWHVlMjAxOWN0Zl9zdA==")
print(flag.decode('utf-8')) &nbsp;# 输出:KanXue2019ctf_st

五、编写完整解密脚本

5.1 完整Python脚本

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import&nbsp;base64

print("="&nbsp;*&nbsp;70)
print("消失的岛屿 CTF 题目解密脚本")
print("="&nbsp;*&nbsp;70)

# 步骤1:从程序中提取的魔改base64码表TABLE1
TABLE1 =&nbsp;"tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/"

print("\n[步骤1] 提取的魔改Base64码表(TABLE1)")
print(f"TABLE1 =&nbsp;{TABLE1}")
print(f"长度:{len(TABLE1)}&nbsp;字符")

# 步骤2:根据charEncrypt函数的5种变换规则,生成最终码表TABLE2
print("\n[步骤2] 应用charEncrypt变换规则生成TABLE2")
print("变换规则:")
print(" &nbsp;1. 大写字母(A-Z): chr(155 - ascii)")
print(" &nbsp;2. 小写字母(a-z): chr(ascii - 64)")
print(" &nbsp;3. 数字(0-9): chr(ascii + 50)")
print(" &nbsp;4. '+': chr(119) = 'w'")
print(" &nbsp;5. '/': chr(121) = 'y'")

TABLE2 =&nbsp;""
for&nbsp;i&nbsp;in&nbsp;range(len(TABLE1)):
&nbsp; &nbsp; char = TABLE1[i]
&nbsp; &nbsp; ascii_val = ord(char)

&nbsp; &nbsp;&nbsp;if&nbsp;65&nbsp;<= ascii_val <=&nbsp;90: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 大写字母A-Z
&nbsp; &nbsp; &nbsp; &nbsp; new_char = chr(155&nbsp;- ascii_val)
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += new_char
&nbsp; &nbsp;&nbsp;elif&nbsp;97&nbsp;<= ascii_val <=&nbsp;122: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 小写字母a-z
&nbsp; &nbsp; &nbsp; &nbsp; new_char = chr(ascii_val -&nbsp;64)
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += new_char
&nbsp; &nbsp;&nbsp;elif&nbsp;48&nbsp;<= ascii_val <=&nbsp;57: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 数字0-9
&nbsp; &nbsp; &nbsp; &nbsp; new_char = chr(ascii_val +&nbsp;50)
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += new_char
&nbsp; &nbsp;&nbsp;elif&nbsp;ascii_val ==&nbsp;43: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# '+'符号
&nbsp; &nbsp; &nbsp; &nbsp; new_char = chr(119) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 'w'
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += new_char
&nbsp; &nbsp;&nbsp;elif&nbsp;ascii_val ==&nbsp;47: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# '/'符号
&nbsp; &nbsp; &nbsp; &nbsp; new_char = chr(121) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 'y'
&nbsp; &nbsp; &nbsp; &nbsp; TABLE2 += new_char

TABLE2 +=&nbsp;'='&nbsp;&nbsp;# 添加Base64填充字符

print(f"\nTABLE2 =&nbsp;{TABLE2}")
print(f"长度:{len(TABLE2)}&nbsp;字符(包含填充符'=')")

# 步骤3:标准base64码表TABLE0
TABLE0 =&nbsp;'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

print("\n[步骤3] 标准Base64码表(TABLE0)")
print(f"TABLE0 =&nbsp;{TABLE0}")

# 步骤4:从程序中提取的密文
cip =&nbsp;"!NGV%,$h1f4S3%2P(hkQ94=="

print("\n[步骤4] 程序中硬编码的密文")
print(f"密文 =&nbsp;{cip}")
print(f"长度:{len(cip)}&nbsp;字符")

# 步骤5:将密文从TABLE2映射回标准base64(TABLE0)
print("\n[步骤5] 密文字符映射过程")
print("="&nbsp;*&nbsp;70)

cip_origin =&nbsp;""
for&nbsp;i, char&nbsp;in&nbsp;enumerate(cip):
&nbsp; &nbsp;&nbsp;# 在TABLE2中找到密文字符的位置(这就是Base64索引)
&nbsp; &nbsp; index = TABLE2.find(char)
&nbsp; &nbsp;&nbsp;# 使用该索引从标准base64码表TABLE0中取出对应字符
&nbsp; &nbsp; standard_char = TABLE0[index]
&nbsp; &nbsp; cip_origin += standard_char

&nbsp; &nbsp; print(f"密文[{i:2d}]: '{char}' → TABLE2位置&nbsp;{index:2d}&nbsp;→ TABLE0字符 '{standard_char}'")

print("="&nbsp;*&nbsp;70)
print(f"\n还原后的标准Base64密文:{cip_origin}")

# 步骤6:使用标准base64解码
print("\n[步骤6] 标准Base64解码")
flag_bytes = base64.b64decode(cip_origin)
flag = flag_bytes.decode('utf-8')

print("="&nbsp;*&nbsp;70)
print(f"解密成功!FLAG =&nbsp;{flag}")
print("="&nbsp;*&nbsp;70)

5.2 脚本执行结果

$ python solve.py
======================================================================
消失的岛屿 CTF 题目解密脚本
======================================================================

[步骤1] 提取的魔改Base64码表(TABLE1)
TABLE1 = tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/
长度:64 字符

[步骤2] 应用charEncrypt变换规则生成TABLE2
变换规则:
&nbsp; 1. 大写字母(A-Z): chr(155 - ascii)
&nbsp; 2. 小写字母(a-z): chr(ascii - 64)
&nbsp; 3. 数字(0-9): chr(ascii + 50)
&nbsp; 4.&nbsp;'+': chr(119) =&nbsp;'w'
&nbsp; 5.&nbsp;'/': chr(121) =&nbsp;'y'

TABLE2 = 45678GF,-./0123iBA!"#$%&'()*j9:bcdefghEDC+ZYXWVUTSRQPONMLKJIHkwy=
长度:65 字符(包含填充符'=')

[步骤3] 标准Base64码表(TABLE0)
TABLE0 = ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=

[步骤4] 程序中硬编码的密文
密文 = !NGV%,$h1f4S3%2P(hkQ94==
长度:24 字符

[步骤5] 密文字符映射过程
======================================================================
密文[ 0]: '!' → TABLE2位置 18 → TABLE0字符 'S'
密文[ 1]: 'N' → TABLE2位置 54 → TABLE0字符 '2'
密文[ 2]: 'G' → TABLE2位置 &nbsp;5 → TABLE0字符 'F'
密文[ 3]: 'V' → TABLE2位置 46 → TABLE0字符 'u'
密文[ 4]: '%' → TABLE2位置 22 → TABLE0字符 'W'
密文[ 5]: ',' → TABLE2位置 &nbsp;7 → TABLE0字符 'H'
密文[ 6]: '$' → TABLE2位置 21 → TABLE0字符 'V'
密文[ 7]: 'h' → TABLE2位置 37 → TABLE0字符 'l'
密文[ 8]: '1' → TABLE2位置 12 → TABLE0字符 'M'
密文[ 9]: 'f' → TABLE2位置 35 → TABLE0字符 'j'
密文[10]: '4' → TABLE2位置 &nbsp;0 → TABLE0字符 'A'
密文[11]: 'S' → TABLE2位置 49 → TABLE0字符 'x'
密文[12]: '3' → TABLE2位置 14 → TABLE0字符 'O'
密文[13]: '%' → TABLE2位置 22 → TABLE0字符 'W'
密文[14]: '2' → TABLE2位置 13 → TABLE0字符 'N'
密文[15]: 'P' → TABLE2位置 52 → TABLE0字符 '0'
密文[16]: '(' → TABLE2位置 25 → TABLE0字符 'Z'
密文[17]: 'h' → TABLE2位置 37 → TABLE0字符 'l'
密文[18]: 'k' → TABLE2位置 61 → TABLE0字符 '9'
密文[19]: 'Q' → TABLE2位置 51 → TABLE0字符 'z'
密文[20]: '9' → TABLE2位置 29 → TABLE0字符 'd'
密文[21]: '4' → TABLE2位置 &nbsp;0 → TABLE0字符 'A'
密文[22]: '=' → TABLE2位置 64 → TABLE0字符 '='
密文[23]: '=' → TABLE2位置 64 → TABLE0字符 '='
======================================================================

还原后的标准Base64密文:S2FuWHVlMjAxOWN0Zl9zdA==

[步骤6] 标准Base64解码
======================================================================
解密成功!FLAG = KanXue2019ctf_st
======================================================================

5.3 验证解密结果

我们可以验证解密结果是否正确:

import&nbsp;base64

# 将明文编码为标准Base64
plaintext =&nbsp;"KanXue2019ctf_st"
standard_base64 = base64.b64encode(plaintext.encode()).decode()
print(f"标准Base64编码:{standard_base64}")
# 输出:S2FuWHVlMjAxOWN0Zl9zdA==

确认无误!我们成功解密了这道题目。

六、技术总结与学习要点

6.1 核心技术点回顾

1. Base64编码原理

  • 将二进制数据编码为可打印字符
  • 每3个字节(24位)转换为4个字符(每个6位)
  • 使用64个字符的码表进行映射
  • 不足3字节时用’=’填充

2. 魔改Base64的常见手法

  • 打乱码表顺序
  • 使用自定义码表
  • 叠加其他变换算法

3. 字符映射变换

  • 利用ASCII值的数学关系
  • 不同类型字符使用不同变换规则
  • 保持可逆性或单向性

4. 双层混淆策略

  • 第一层:码表替换
  • 第二层:字符转换
  • 大幅增加破解难度

6.2 逆向分析方法总结

第一步:信息收集

  • 使用file、strings等基础工具
  • 快速获取程序特征和关键字符串
  • 建立初步假设

第二步:静态分析

  • 使用反汇编工具(IDA、radare2、Ghidra)
  • 定位关键函数
  • 分析函数调用关系

第三步:代码分析

  • 逐行分析汇编代码
  • 理解算法逻辑
  • 识别关键常数和魔数

第四步:算法还原

  • 将汇编代码还原为高级语言逻辑
  • 绘制流程图
  • 理解整体工作流程

第五步:编写解密脚本

  • 根据理解的算法编写逆向程序
  • 验证结果
  • 优化代码

6.3 学习建议

对于初学者

  1. 先学习Base64编码的基本原理
  2. 熟悉常用的逆向工具(radare2、IDA)
  3. 掌握基本的x86汇编语言
  4. 练习Python编程

对于进阶者

  1. 深入学习密码学理论
  2. 研究更复杂的混淆技术
  3. 学习动态分析方法(调试器使用)
  4. 了解反反调试技术

6.4 常见错误与注意事项

错误1:直接尝试标准Base64解码

  • 问题:看到”==”就以为是标准Base64
  • 教训:要检查字符集是否符合标准

错误2:忽略字符映射变换

  • 问题:找到魔改码表就直接解码
  • 教训:要分析程序是否有额外的变换

错误3:手动计算容易出错

  • 问题:在纸上计算ASCII值转换
  • 教训:编写程序自动化处理

错误4:忘记添加填充字符

  • 问题:生成的TABLE2缺少’=’
  • 教训:Base64码表必须是65个字符

6.5 拓展思考

如果题目变得更复杂

  1. 多轮变换:多次应用charEncrypt
  2. 动态码表:根据密钥生成不同的码表
  3. 混合加密:Base64 + AES + 自定义变换
  4. 反调试:增加反调试代码
  5. 代码混淆:使用花指令、虚假跳转等

实际应用场景

  1. 软件保护:序列号验证系统
  2. 数据传输:自定义编码协议
  3. 恶意代码:C&C通信加密
  4. 游戏保护:存档加密

七、工具使用指南

7.1 radare2 常用命令速查

# 基本操作
r2 file &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 打开文件
aaa &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 分析所有(analyze all)
afl &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 列出所有函数
s address &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 跳转到地址
pdf &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 打印当前函数反汇编
V &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 进入可视模式
q &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 退出

# 信息查看
ii &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 列出导入函数
is &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 列出符号
iz &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 列出字符串
afvd &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 列出函数变量

# 搜索
/ string &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;# 搜索字符串
/x 90 90 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;# 搜索十六进制

7.2 Python Base64 库使用

import&nbsp;base64

# 编码
plaintext =&nbsp;"Hello"
encoded = base64.b64encode(plaintext.encode())
print(encoded) &nbsp;# b'SGVsbG8='

# 解码
decoded = base64.b64decode(encoded)
print(decoded.decode()) &nbsp;# Hello

# 处理URL安全的Base64
url_encoded = base64.urlsafe_b64encode(plaintext.encode())
url_decoded = base64.urlsafe_b64decode(url_encoded)

7.3 ASCII 值速查表

| 范围 | 描述 | 示例 | | — | — | — | | 0-31 | 控制字符 | \n, \r, \t | | 32 | 空格 | ‘ ‘ | | 33-47 | 标点符号 | !, “, #, $, %, & | | 48-57 | 数字 | 0-9 | | 58-64 | 标点符号 | :, ;, <, =, >, ?, @ | | 65-90 | 大写字母 | A-Z | | 91-96 | 标点符号 | [, , ], ^, _, ` | | 97-122 | 小写字母 | a-z | | 123-126 | 标点符号 | {, |, }, ~ |

八、总结

通过对”消失的岛屿”这道CTF题目的完整分析,我们学习了:

  1. 逆向工程基础
  • 如何使用radare2进行静态分析
  • 如何阅读和理解x86汇编代码
  • 如何定位和分析关键函数
  1. 密码学知识
  • Base64编码的原理和实现
  • 魔改编码的常见手法
  • 多层混淆的设计思路
  1. 问题解决能力
  • 从混乱的汇编代码中提取算法逻辑
  • 设计逆向解密方案
  • 编写自动化解密脚本
  1. 技术细节
  • 字符映射变换的5种规则
  • 双层码表的生成和使用
  • ASCII值的数学关系应用

这道题目虽然不算复杂,但涵盖了CTF密码学和逆向工程的核心知识点。通过系统的学习和实践,我们不仅解决了这道题目,更重要的是掌握了一套完整的分析方法论。

在实际的安全研究工作中,我们经常会遇到各种各样的加密和混淆技术。保持好奇心,善用工具,耐心分析,相信每个人都能成为优秀的安全研究员。

最终FLAG:flag{KanXue2019ctf_st}

附录

A. 完整解密脚本

详见本文第五章节的完整Python脚本。

B. 相关资源

工具下载

  • radare2: https://rada.re/
  • IDA Pro: https://hex-rays.com/
  • Ghidra: https://ghidra-sre.org/
  • Python: https://www.python.org/

学习资源

  • Base64编码原理:https://zh.wikipedia.org/wiki/Base64
  • x86汇编教程:https://www.cs.virginia.edu/~evans/cs216/guides/x86.html
  • CTF Wiki:https://ctf-wiki.org/

题目来源

  • 看雪论坛:https://www.kanxue.com/
  • 题目年份:2019年看雪CTF比赛

感谢您的阅读,希望这篇文章能帮助您更好地理解CTF密码学题目的分析方法。如有任何疑问或建议,欢迎交流讨论!


查看原文:《CTF密码学题目深度解析:消失的岛屿(LostIslands)》

评论:0   参与:  2