Brainfuck
20 年比赛已经出现了好几道 Brainfuck 的 vm pwn 了,补一下 Brainfuck 的知识。
简介
Brainfuck 是一种简单的、可以用最小的编译器来实现的、符合图灵完全思想的编程语言,它是由 Urban Müller 在 1993 年创造的,简称为 bf。Brainfuck 由八种运算符构成,它基于一个简单的机器模型,出了指令,这个机器还包括:一个字节为单位、被初始化为零的数组、一个指向该数组的指针(初始时指向数组的第一个字节)、以及用于输入输出的两个字节流。
指令
字符 | 含义 |
---|---|
> | 指针加一 |
< | 指针减一 |
+ | 指针指向的字节的值加一 |
- | 指针指向的字节的值减一 |
. | 输出指针指向的单元的内容 |
, | 输入内容到指针指向的单元 |
[ | 如果指针指向的单元值为零,向后跳转到对应的]指令的次一指令处 |
] | 如果指针指向的单元值不为零,向前跳转到对应的[指令的次一指令处 |
Brainfuck 程序可以用下面的替换方法翻译成 C 语言(假设 ptr 是 char* 类型)
Brainfuck | 含义 |
---|---|
> | ++ptr; |
< | --ptr; |
+ | ++*ptr; |
- | --*ptr; |
. | putchar(*ptr); |
, | *ptr = getchar(); |
[ | while(*ptr){ |
] | } |
例子 Hello World!
在屏幕上打印 “Hello World!” 的程序
++++++++++[>+++++++>++++++++++>+++>+<<<<-]
>++.>+.+++++++..+++.>++.<<+++++++++++++++.
>.+++.------.--------.>+.>.
分析
- ++++++++++ ,将 *ptr0 置为 10,用作后面循环的条件。
- [>+++++++>++++++++++>+++>+<<<<-] ,循环 10 次,每次执行
*(ptr0 + 1) += 7
*(ptr0 + 2) += 10
*(ptr0 + 3) += 3
*(ptr0 + 4) += 1
当循环完后
*(ptr0 + 1) = 70
*(ptr0 + 2) = 100
*(ptr0 + 3) = 30
*(ptr0 + 4) = 10
- >++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. ,这一串就是通过加减得到每个字母对应的 ascii 码,然后将其打印出来,大致过程如下
初始值:
*(ptr0 + 1) = 70
*(ptr0 + 2) = 100
*(ptr0 + 3) = 30
*(ptr0 + 4) = 10
代码计算过程:
*(ptr0 + 1) = 72 输出 'H'
*(ptr0 + 2) = 101 输出 'e'
*(ptr0 + 2) = 108 输出 'l'
*(ptr0 + 2) = 108 输出 'l'
*(ptr0 + 2) = 111 输出 'o'
*(ptr0 + 3) = 32 输出 ' '
*(ptr0 + 1) = 87 输出 'W'
*(ptr0 + 2) = 111 输出 'o'
*(ptr0 + 2) = 114 输出 'r'
*(ptr0 + 2) = 108 输出 'l'
*(ptr0 + 2) = 100 输出 'd'
*(ptr0 + 3) = 33 输出 '!'
*(ptr0 + 4) = 10 输出 '\n'
总结
在 ctf 中,大多是通过程序重新自定义运算符,但是功能还是类似的,知道这点,我们就可以大大减轻逆向分析的难度。