写点什么

奇奇怪怪的编程语言:Malbolge

  • 2024-12-12
    福建
  • 本文字数:3755 字

    阅读完需:约 12 分钟

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),分别是 ac 和 da:Accumulator,主要用于存储计算结果和输入/输出数据。c:Code Pointer / Instruction Pointer,用于指向当前正在执行的代码的位置(即指令指针)。d:Data Pointer,用于指向内存中的数据位置。


其次,基于这些寄存器,Malbolge 提供了八条指令,分别为:jmpoutinrotrmovcrznopend。我们可以利用某些在线编译网站提供的规范化(Normalization)功能,将代码转换为由一组固定字符组成的指令字符集,从而更方便地进行调试(debug)。


例题


题目来源: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 webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECimport time
# 初始化浏览器(确保你有对应的浏览器驱动)# driver = webdriver.Chrome() # Chromedriver = webdriver.Firefox() # Firefoxtry: # 打开目标网页 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



https://tio.run/##y03MScrPSU/9/19DXU3VyjJWsa62psoorKK8TFcnwL@wID8vR8UjKzMjrcbJwcFOx9bG18pSzTxSOSzMKCQ4T88/VSvZWsNTTVVFWSnX2cnRvqqiokwrIMkioTDfxCg3yk2v2rNSyyHFRd3GOt5eMaYWYnpwcOD//wA



文章转载自:笔墨绘星河1

原文链接:https://www.cnblogs.com/bimohuixinghe/p/18600485

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
奇奇怪怪的编程语言:Malbolge_编程语言_不在线第一只蜗牛_InfoQ写作社区