奇奇怪怪的编程语言:Malbolge
Malbolge
除了我们日常使用的Python、Java、C等主流编程语言外,还存在这么一类极为晦涩难懂的编程语言,被称为深奥的编程语言(Esoteric programming language,简称Esolang)。它们被设计用于测试计算机语言表达的极限,所以不会考虑它们的实用性。今天我们来看其中一个非常典型的例子:Malbolge。
Malbolge由Ben Olmstead 在1998年发明,其名字来自于但丁的《神曲》中的第八层地狱“Malebolge”,意大利语中意为“邪恶的沟渠”(male bolge)。

Hello World
下面这段Malbolge代码会输出“Hello, World.”
(=<`#9]~6ZY327Uv4-QsqpMn&+Ij"'E%e{Ab~w=_:]Kw%o44Uqp0/Q?xNvL:`H%c#DD2^WV>gY;dts76qKJImZkj
而这段代码则会输出“Hello, World!”
('&%:9]!~}|z2Vxwv-,POqponl$Hjihf|B@@>,=<M:9&7Y#VV2TSn.Oe*c;(I&%$#"mCBA?zxxv*Pb8`qo42mZF.{Iy*@dD'<;_?!\}}|z2VxSSQ
我们可以看到,光是一个标点符号的改变,就导致代码发生了天翻地覆的变化。
CTF
大多数涉及 Malbolge 的基础 CTF 题目会提供一段看似乱码的内容。此时,需要识别出这实际上是 Malbolge 代码,并通过编译器将其编译出来。
比如说这段代码会输出“flag{this_is_a_flag}”
D'`;qLo=I;|XyhCwStcr=NL-,I$)"XW21A/c>,v_)\xqYonsrqj0hPlkdcb(`Hd]#a`_A@VzZY;QuUTSRQJImGLEJIBAeED&B;_9>7<;:921U54ts10)M'&Jkj"F&%|#z@~}vu;y[Zvo5Vlkjongf,Miha'Hd]\[ZY}W?UZYRQuU7SLp]

有些题目可能需要对编译后的结果再进行一次处理,比如使用 base64 解码等。
但是这些内容都不需要了解任何 Malbolge 语言的特性,只需要找到一个编译器即可。因此,接下来我会介绍一些 Malbolge 的特点,并分享一道较难的 CTF 比赛真题。
Malbolge的特点
首先,Malbolge会使用 三个寄存器(register),分别是 a、c 和 d。
a:Accumulator,主要用于存储计算结果和输入/输出数据。
c:Code Pointer / Instruction Pointer,用于指向当前正在执行的代码的位置(即指令指针)。
d:Data Pointer,用于指向内存中的数据位置。
其次,基于这些寄存器,Malbolge提供了八条指令,分别为:jmp,out,in,rotr,mov,crz,nop,end。我们可以利用某些在线编译网站提供的规范化(Normalization)功能,将代码转换为由一组固定字符组成的指令字符集,从而更方便地进行调试(debug)。
更多详细信息可以参考
https://en.wikipedia.org/wiki/Malbolge
例题
题目来源:Platypwn 2024 CTF
链接:https://platypwn.ctf.platypwnies.de/
题面:

下载下来的文件内容为:
D'`__LK!mY:jiy6Be3cPa)onKI[#j4&DUBzcx>_;):'Zputml21onPlkd*hJIedcb[!YXW{[TYXWVUNrRQPImGFEDIBfFED=<;:?8\<;432V65ut,P*/('&J*j(!~}C#cy~}v<tsxwvon4Ukji/Plejc)gIedcb[!_^]\U=SwWVONMqQPONMFjJIHGF(>b<$:?876ZG
这段内容看似是乱码,但根据题面的提示“the worst of esoteric programming languages” (正如前文所提到的),我们可以判断出它实际上是一段Malbolge的代码。
直接编译这段代码无法得到任何结果,因此我们可以选择使用某个在线编译工具对其进行规范化处理,使代码转换为更标准的指令字符集,从而便于调试(debug):


normalized 之后的结果为:
ojii<vvj/io//jov/o/</p**<i/pjvo</<pjp<*iv<v*poopp<<oo*oop<o**oooop<ppp<opooooop<ooop<ppppop<oooppppop<ooppp<oo**p<poppp<o*oppp<o*poop<ppooopp<*ppp<*opop<o*oooop<oooop*p<ooppp<ooooop<ooooo*p<p*poppp<v
当我们运行这段代码时,会发现它在执行到中间某处时意外停止了:

大概是在这个位置:

所以我们猜测(尝试)需要将这一项改成其他的命令。
而在将其改正为p了之后会得到:

订正后的内容
ojii<vvj/io//jov/o/</p**<i/pjvo</<pjp<*iv<p*poopp<<oo*oop<o**oooop<ppp<opooooop<ooop<ppppop<oooppppop<ooppp<oo**p<poppp<o*oppp<o*poop<ppooopp<*ppp<*opop<o*oooop<oooop*p<ooppp<ooooop<ooooo*p<p*poppp<v
D'`__LK!mY:jiy6Be3cPa)onKI[#j4&DUBzcx>_;):'Zputml21onPlkd*hJIedcb[!YXW{[TYXWVUNrRQPImGFEDIBfFED=<;:?8\<;432V65ut,P*/('&J*j(!~}C#cy~}v<tsxwvon4Ukji/Plejc)gIedcb[!_^]\U=SwWVONMqQPONMFjJIHGF(>b<$:?876ZG
具体改动:

这样一来我们就成功获取到了flag。
其他办法
1. 暴力破解
当然,如果我们知道(或猜测)这段代码中只有一个地方存在问题,可以尝试使用暴力破解(brute force)的方法进行修正。这种方法的核心是将每条指令逐一修改为其他可能的指令,并观察编译结果。由于不需要理解 Malbolge 的具体特性,因此这种方法非常简单。
为实现这一目标,我们可以利用支持在线编译 Malbolge 的网站,以及 Python 中的 Selenium 库。Selenium 提供了浏览器自动化操作功能,能够帮助我们完成网页上的勾选、输入操作,并提取输出内容,从而实现自动化调试。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 初始化浏览器(确保你有对应的浏览器驱动)
# driver = webdriver.Chrome() # Chrome
driver = webdriver.Firefox() # Firefox
try:
# 打开目标网页
driver.get("https://lutter.cc/malbolge/debugger.html")
# 等待页面加载
wait = WebDriverWait(driver, 30)
# 勾选所有复选框
checkboxes_ids = ['until_in', 'until_out', 'until_crz', 'until_rotr', 'until_jmp', 'until_mov', 'until_nop']
for checkbox_id in checkboxes_ids:
checkbox = wait.until(EC.element_to_be_clickable((By.ID, checkbox_id)))
if not checkbox.is_selected():
checkbox.click()
# 勾选Normalized
normalized_checkbox = wait.until(EC.element_to_be_clickable((By.ID, 'normalizedcode')))
if not normalized_checkbox.is_selected():
normalized_checkbox.click()
# 原始Malbolge代码
original_code = "ojii<vvj/io//jov/o/</p**<i/pjvo</<pjp<*iv<v*poopp<<oo*oop<o**oooop<ppp<opooooop<ooop<ppppop<oooppppop<ooppp<oo**p<poppp<o*oppp<o*poop<ppooopp<*ppp<*opop<o*oooop<oooop*p<ooppp<ooooop<ooooo*p<p*poppp<v"
# 替换的字符列表
replace_chars = ['i', 'j', 'o', 'p', '/', '*']
# 标志变量,用于退出循环
found_flag = False
# 循环替换并执行代码
for i in range(1, len(original_code)):
if found_flag: # 如果找到结果,退出外层循环
break
for char in replace_chars:
modified_code = list(original_code)
modified_code[i] = char # 替换第i个字符
modified_code = ''.join(modified_code)
# 输入修改后的Malbolge代码
program_textarea = driver.find_element(By.ID, "program")
program_textarea.clear()
program_textarea.send_keys(modified_code)
# 点击Load/Reset按钮
load_button = driver.find_element(By.ID, "load")
load_button.click()
# 点击Execute按钮
execute_button = wait.until(EC.element_to_be_clickable((By.ID, "run")))
execute_button.click()
# 等待执行完成并获取Output内容
time.sleep(1) # 根据需要调整等待时间
output_div = driver.find_element(By.ID, "output")
output_content = output_div.text
# 仅当output内容包含"flag"时打印结果
if "pp{" in output_content.lower():
print(f"将第 {i + 1} 位修改成 '{char}' 后成功编译出flag。")
print(f"编译成功的代码: {modified_code}")
found_flag = True # 设置标志变量,标记已找到结果
break # 退出内层循环
finally:
# 关闭浏览器
driver.quit()
# 运行成功后会得到:
"""将第 43 位修改成 'p' 后成功编译出flag。
编译成功的代码: ojii<vvj/io//jov/o/</p**<i/pjvo</<pjp<*iv<p*poopp<<oo*oop<o**oooop<ppp<opooooop<ooop<ppppop<oooppppop<ooppp<oo**p<poppp<o*oppp<o*poop<ppooopp<*ppp<*opop<o*oooop<oooop*p<ooppp<ooooop<ooooo*p<p*poppp<v
"""
2. 修改解释器源代码
有一位参赛选手分享了一个非常巧妙的解法,具体如下:
首先他找到了一个用 C 语言编写的原始 Malbolge 解释器(https://github.com/bipinu/malbolge)。接着,他将第 131 行的return 改为 break,以避免 exec() 函数提前结束。
最后,他使用修改后的解释器运行题目中提供的 Malbolge 代码,成功得到了 flag。
工具(网站)
最后附上一些网页,可以用来生成,编译,或者debug。
https://lutter.cc/malbolge/debugger.html

https://zb3.me/malbolge-tools/#generator

奇奇怪怪的编程语言:Malbolge的更多相关文章
- Meow 攻击会删除不安全(开放的)的Elasticsearch(及MongoDB) 索引,然后建一堆以Meow结尾的奇奇怪怪的索引(如:m3egspncll-meow)
07月29日,早上照例一来,先连接Elasticsearch查看日志[禁止转载,by @CoderBaby],结果,咦,什么情况,相关索引被删除了,产生了一堆以Meow开头的奇奇怪怪的索引,如下图: ...
- ios ZBar扫二维码奇奇怪怪的错误
Undefined symbols for architecture armv7: "_CVPixelBufferGetHeight", referenced from: -[ZB ...
- 微信小程序奇奇怪怪的语法
这... <view class="body"> <view class="nav bc_white"> <view class= ...
- 【奇奇怪怪的bug系列】微信小程序
今天修改代码的时候,发现了一件让我很恐慌的事情,在app.json中修改页面路径顺序不起作用了: 这样我根本就看不到页面的效果啊??? 在折腾了半天后,才发现是一个比较乌龙的事情:昨天修改完首页后顺手 ...
- Latex编译过程中遇到的奇奇怪怪的问题及解决方案
标签(空格分隔): 杂七杂八的问题 有必要写一个博文记录自己在Latex编译时遇到的各种问题,希望可以帮到遇到同样错误的亲故.讲真,一直没有系统的学习Latex,都是投哪个会直接拿那个会的模板来套,然 ...
- 一些说明&其他奇奇怪怪的东西
NOIP考完了,这篇博客彻底咕了.
- jQuery取值的一些奇奇怪怪的操作
语法解释:1. $("#select_id").change(function(){//code...}); //为Select添加事件,当选择其中一项时触发2. var ch ...
- 奇奇怪怪的冒泡排序 TOJ 2014: Scramble Sort
粘贴两个特别简单的冒泡排序 2014: Scramble Sort Description In this problem you will be given a series of lists co ...
- [luoguP1922] 女仆咖啡厅桌游吧(奇奇怪怪的树形DP)
传送门 什么鬼的题? 代码 #include <cstdio> #include <cstring> #include <iostream> #define N 1 ...
- 一些DP上的奇奇怪怪的东西
单调队列&单调栈: 有手就行.jpg 四边形不等式: 若\(w(i,j)\)满足\(\forall a\le b<c\le d,w(a,c)+w(b,d)\le w(b,c)+w(a,d ...
随机推荐
- EF Core – Get Started 搭建单侧环境
有时候想搭个环境做测试, 又记不住那些 command, 官方教程又啰嗦. git clone 模板又不太好管理, 索性记入在这里吧. 创建项目 dotnet new webapp -o Simple ...
- CSP初赛知识点:Linux 系统
CSP初赛知识点:Linux 系统 前言 近年 CSP 初赛几乎前 5 道选择题都有一两道有关 Linux 系统的使用,所以作为备战 CSP-J/S 2024 的资料,整理下来啦. 祝各位今年所有考试 ...
- Wpf使用NLog将日志输出到LogViewer
1 LogViewer LogViewer是通过UDP传输的高性能实时log查看器. 具有一下特性: 通过UDP读取日志 通过文件导入日志 导出日志到一个文件中 排序.过滤(日志树,日志等级)和查找 ...
- 课时05:Linux必备系统命令
- CTF中特别小的EXE是怎么生成的
我们在打CTF时候,出题的爷爷们给出的exe都很小 就10k左右,有的甚至就5k,那时候我很郁闷啊.现在我也能了啊哈哈 不多bb按如下操作: 我们来看看正常的release生成的代码 #include ...
- 通过一个题目三种变式讲清楚go接口类型断言
[第一种]一种类型实现多个接口,各个接口变量调用各自的方法 type Work struct { i int } func (w Work) ShowA() int { return w.i + 10 ...
- ORM的性能争议
1 二次封装---业务的执行,步骤多一些 2 映射的过程---必然从类到Sql语句变化---类---Sql语句---必然会有大量的反 射(损耗性能) 3 Sql语句僵化---数据库执行有性能损耗 部分 ...
- stm32开发
基于寄存器开发 新建工程 添加C/C++识别路径 : 防止中文乱码 - 改变编码格式 基于库函数开发
- 关于 vue3 中的 fragment 组件
vue3 中的模板中只能返回一个元素 ,否则报错,使用 fragment 组件可以返回多个元素标签
- OAS常见错误
body { font-family: Arial, sans-serif; line-height: 1.6; margin: 20px } h1, h2 { color: rgba(51, 51, ...
