Raj*_*ngh 85 c obfuscation c-preprocessor
#include <stdio.h>
#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)
int begin()
{
printf("Ha HA see how it is?? ");
}
Run Code Online (Sandbox Code Playgroud)
这间接打电话main
吗?怎么样?
hac*_*cks 193
C语言将执行环境定义为两类:独立式和托管式.在两个执行环境中,环境都会调用函数来启动程序.
在独立环境中,程序启动功能可以在托管环境中实现定义main
.如果没有程序启动功能,C中的程序就无法在定义的环境中运行.
在您的情况下,main
由预处理器定义隐藏.begin()
将扩展到 decode(a,n,i,m,a,t,e)
将进一步扩展到main
.
int begin() -> int decode(a,n,i,m,a,t,e)() -> int m##a##i##n() -> int main()
Run Code Online (Sandbox Code Playgroud)
decode(s,t,u,m,p,e,d)
是一个带有7个参数的参数化宏.此宏的替换列表是m##s##u##t
.m, s, u
和t
是4 个,1 日,3 次和2 次在替换列表中使用的参数.
s, t, u, m, p, e, d
1 2 3 4 5 6 7
Run Code Online (Sandbox Code Playgroud)
休息是没用的(只是混淆).参数传递到decode
是" 一个,Ñ,我,米,A,T,E",所以,标识符m, s, u
和t
与参数替换m, a, i
和n
分别.
m --> m
s --> a
u --> i
t --> n
Run Code Online (Sandbox Code Playgroud)
jda*_*nay 71
尝试使用gcc -E source.c
,输出以:
int main()
{
printf("Ha HA see how it is?? ");
}
Run Code Online (Sandbox Code Playgroud)
所以main()
函数实际上是由预处理器生成的.
Nli*_*tis 37
有问题的程序会main()
因宏观扩展而调用,但您的假设是有缺陷的 - 它根本不需要调用main()
!
严格来说,你可以拥有一个C程序,并且能够在没有main
符号的情况下编译它.在完成自己的初始化之后,期望跳进去的main
东西c library
.通常你会main
从名为的libc符号跳转到_start
.总是可以有一个非常有效的程序,只需执行程序集,而不需要主程序.看看这个:
/* This must be compiled with the flag -nostdlib because otherwise the
* linker will complain about multiple definitions of the symbol _start
* (one here and one in glibc) and a missing reference to symbol main
* (that the libc expects to be linked against).
*/
void
_start ()
{
/* calling the write system call, with the arguments in this order:
* 1. the stdout file descriptor
* 2. the buffer we want to print (Here it's just a string literal).
* 3. the amount of bytes we want to write.
*/
asm ("int $0x80"::"a"(4), "b"(1), "c"("Hello world!\n"), "d"(13));
asm ("int $0x80"::"a"(1), "b"(0)); /* calling exit syscall, with the argument to be 0 */
}
Run Code Online (Sandbox Code Playgroud)
编译上面的内容gcc -nostdlib without_main.c
,并Hello World!
通过在内联汇编中发出系统调用(中断)来看它在屏幕上的打印.
有关此特定问题的更多信息,请查看ksplice博客
另一个有趣的问题是,您还可以拥有一个程序,该程序可以在没有main
符号对应C函数的情况下进行编译.例如,您可以将以下内容作为一个非常有效的C程序,只会使编译器在您启动警告级别时发出抱怨.
/* These values are extracted from the decimal representation of the instructions
* of a hello world program written in asm, that gdb provides.
*/
const int main[] = {
-443987883, 440, 113408, -1922629632,
4149, 899584, 84869120, 15544,
266023168, 1818576901, 1461743468, 1684828783,
-1017312735
};
Run Code Online (Sandbox Code Playgroud)
数组中的值是与屏幕上打印Hello World所需的指令相对应的字节.有关此特定程序如何工作的更详细说明,请查看此博客文章,这也是我首先阅读它的地方.
我想最后通知一下这些程序.我不知道他们是否根据C语言规范注册为有效的C程序,但是编译它们并运行它们肯定是非常可能的,即使它们违反了规范本身.
abh*_*ora 30
有人试图表现得像魔术师.他认为他可以欺骗我们.但我们都知道,程序执行开始于main()
.
的int begin()
将被替换为decode(a,n,i,m,a,t,e)
由预处理器级的一个通.然后,decode(a,n,i,m,a,t,e)
将替换为m ## a ## i ## n.通过宏调用的位置关联, s
将具有一个字符值a
.同样,u
将被'i' t
取代,并将被'n'取代.而且,这将是如何,m##s##u##t
将成为main
关于##
宏扩展中的符号,它是预处理运算符,它执行标记粘贴.扩展宏时,每个'##'运算符两侧的两个标记组合成一个标记,然后在宏扩展中替换'##'和两个原始标记.
如果您不相信我,可以使用-E
flag 编译代码.它将在预处理后停止编译过程,您可以看到令牌粘贴的结果.
gcc -E FILENAME.c
Run Code Online (Sandbox Code Playgroud)
Frx*_*rem 11
decode(a,b,c,d,[...])
将前四个参数混合并加入它们以获得新的标识符dacb
.(其余三个参数将被忽略.)例如,decode(a,n,i,m,[...])
给出标识符main
.请注意,这是begin
宏定义的内容.
因此,begin
宏被简单地定义为main
.