– 引子 –
在今年年初的VNCTF 2025中,遇到了一个这样的题目(不知道是不是第一次遇到了,感觉以前也见过几次,但是最有印象的是这一次了):
1 | v1 = rand(); |
在某个函数的结尾有一个诡异的return 6(还见过return 39之类的),而这时如果你查看汇编会发现不对劲的地方:
1 | ...... |
可以看到这里有一个突然插入的汇编段,对rax进行了一大串计算后返回,后续又是正常的函数逻辑了,但从ida的反编译来看,后续的逻辑并没有显示出来,而是直接在这里诡异地return了一个值。
– 分析 –
实际上这是一种花指令,对rax进行初始化(这里初始化值是1)后用call的指令对其进行了一些计算,之后进行比较,如果结果不对就return,否则跳转到后面的正常逻辑。问题就出在这个return上,ida似乎无法识别出来这个return是return到上面的call,而不是整个函数的return,所以误以为函数在这里中断,直接返回了第一次的计算结果(可以看到流程图上也没有建立联系):
– 改进 –
那么我们可以自然而然的想到并发问:如果一定要用上各种复杂的指令,像这样的循环多吗?能否自由快捷地构建任意我想要构建的循环?有的兄弟,有的,像这样的循环还有无数个。实际上,我们可以编写一个python脚本来快速构建我们想要的循环:
1 | import random |
输出类似于:
1 | 已找到符合要求的opcode序列(71872次尝试): |
这样我们就能随意地构建我们想要的花指令了,我们还可以写一个脚本打印和验证我们的花指令是否正确:
1 | def add(a, b): |
运行效果:
1 | "add $1, %%rax\n\t" |
其中带引号的行可以直接粘贴进c语言或者c++的脚本中,用asm volatile();包裹:
1 | asm volatile ( |
这样我们就能获得一个任意的二次计算型花指令了!你可以随意修改其中的任何值,比如…:
——在操作数上搞怪🤪🤪🤪(比如像我一样把操作数改成homo特有的恶臭数字),藏彩蛋,甚至藏密钥和flag
——尝试有没有无解的情况🤔🤔🤔(除非你闲得慌,但我觉得只要循环段够短就大概率无解)
——不建议:把显示返回值改成0什么的😰😰😰,大家可能要找半天才能发现你藏东西了
——非常不建议:在隐藏段加上反调试并在无调试器时加密😨😨😨,想出来这个的家里要请哈基高了
– 改进??? –
那么我们可以更自然而然的想到并发问:现在这个花指令还是太简单、太乏味、太无趣了,能否再改改,改成反编译器不敢反编译的样子?有的兄弟,有的,像这样的改法还有无数个。实际上,我们可以编写一个python脚本来…好吧,我做不到,还是太菜了😭,不过,我这里提供了一种改法,需要手搓汇编,有兴趣的可以也像我一样试着瞎改改(前提是改完能如期运行):
1 | asm volatile ( |
效果展示:
– ¿ –
这是我第一次在网站上发这类灵感(大粪),以后如果有新的灵感应该也会发的,希望我的灵感能一次比一次新颖(更加大粪)😋😋😋