不可理喻——IOCCC 1984年作品mullender.c

an_bachelor 2004-08-01 04:26:47
//mullender.c
short main[] = {
277, 04735, -4129, 25, 0, 477, 1019, 0xbef, 0, 12800,
-113, 21119, 0x52d7, -1006, -7151, 0, 0x4bc, 020004,
14880, 10541, 2056, 04010, 4548, 3044, -6716, 0x9,
4407, 6, 5568, 1, -30460, 0, 0x9, 5570, 512, -30419,
0x7e82, 0760, 6, 0, 4, 02400, 15, 0, 4, 1280, 4, 0,
4, 0, 0, 0, 0x8, 0, 4, 0, ',', 0, 12, 0, 4, 0, '#',
0, 020, 0, 4, 0, 30, 0, 026, 0, 0x6176, 120, 25712,
'p', 072163, 'r', 29303, 29801, 'e'
};
/*
这个文件居然可以在VC下编译通过(只是执行的时候有问题)TC下没有作测试 估计也可以

GCC下也应该可以通过(因为从文件所附带的nmake文件看 这段代码最初是在unix/linux下编译的)

估计一个C程序只需要定义了符号main,而不管main实际上是函数名还是变量名就可以通过,随后我进行的几次测试都符合这一点。当然我还没有用所有数据类型一一测试过。
*/
...全文
1082 39 打赏 收藏 转发到动态 举报
写回复
用AI写文章
39 条回复
切换为时间正序
请发表友善的回复…
发表回复
an_bachelor 2004-08-05
  • 打赏
  • 举报
回复
谢谢大家的指导,尤其感谢allen_wang(IA32 architecture) 和LLnju(LLnju) 的细致解释。
allen_wang 2004-08-05
  • 打赏
  • 举报
回复
写个这样的程序,就一行:
int main=195;

编译和执行都不会有问题,呵呵,可惜这个程序什么都没做。
LLnju 2004-08-04
  • 打赏
  • 举报
回复
void fmain()
{
printf("Successful?");
}
void (*main)() = fmain;

这样的代码当然不行的哦,你的main里只是保存了函数的地址而不是程序的机器码,如果fmain的地址刚好是一条合法的指令并且还能正确的跳转到fmain()那个几率真的是很小很小的,可以去买彩票去了

最上面的程序应该不是在x86上运行的,因为反汇编出来的代码好像是没有什么意义的,能贴出程序的出处来吗?
LLnju 2004-08-04
  • 打赏
  • 举报
回复
实际上在 VC , gcc 上写应该更方便些,因为调试啊什么的条件要好些,不过在这些保护模式的系统下我实在想不到办法怎么把结果输出来,因此还是用古老的TC.

你可以先写个程序,编译后把机器码填到 main 里就可以运行了,程序里少用绝对跳转比较好,因为跳转地址与编译器相关的.
我上面的代码实际上是:

int 3
mov ax , 0b800h
mov es , ax
mov di , 160 * 5 + 20
mov ax , 2
mov byte ptr es:[di] , 'H'
add di , ax
mov byte ptr es:[di] , 'e'
add di , ax
mov byte ptr es:[di] , 'l'
add di , ax
mov byte ptr es:[di] , 'l'
add di , ax
mov byte ptr es:[di] , 'o'
int 3
ret
sandlovefree 2004-08-04
  • 打赏
  • 举报
回复
Dev c++ 下通过,但生成的EXE文件运行三秒钟后提示遇到问题,非法关闭。
sandlovefree 2004-08-04
  • 打赏
  • 举报
回复
Dev c++ 下通过,但生成的EXE文件运行三秒钟后提示遇到问题,非法关闭。
LLnju 2004-08-04
  • 打赏
  • 举报
回复
这个好像还是挺容易的,我写了个在TC下可以用的:

unsigned char main[] = {
0xcc ,
0xb8 , 0x00 , 0xb8 ,
0x8e , 0xc0 ,
0xbf , 0x34 , 0x03 ,
0xb8 , 0x02 , 0x00 ,
0x26 , 0xc6 , 0x05 , 'H' ,
0x01 , 0xc7 ,
0x26 , 0xc6 , 0x05 , 'e' ,
0x01 , 0xc7 ,
0x26 , 0xc6 , 0x05 , 'l' ,
0x01 , 0xc7 ,
0x26 , 0xc6 , 0x05 , 'l' ,
0x01 , 0xc7 ,
0x26 , 0xc6 , 0x05 , 'o' ,
0xcc ,
0xc3 };

前后两个 0xcc (int 3) 是为了调试方便加的,改成 0x90 (nop) 或者直接去掉吧

redleaves 2004-08-04
  • 打赏
  • 举报
回复
TO an_bachelor(AMD Duron):
K&R的C编译器只是说这个C编译器支持K&R的C语法.比如:
void test(a)
int a
{
}
要想看它的汇编,最简单的方法是把main[]的内容写到一个文件里,再用HIEW32反汇编成16位代码.
ningzhiyu 2004-08-04
  • 打赏
  • 举报
回复
mark
allen_wang 2004-08-04
  • 打赏
  • 举报
回复
an_bachelor(AMD Duron):

main确实保存的是fmain的起始地址,然后呢?
程序开始跳到main执行,也就是说eip=main的地址(不是main保存的fmain的地址),取出此地址的值(也就是fmain的地址)作为机器代码来执行了。

所以是不行的。
allen_wang 2004-08-04
  • 打赏
  • 举报
回复
我在linux下写了一个,用gcc编译通过的helloworld程序。

#include "stdio.h"


char main[100]={0x55, 0x89, 0xe5, 0xa1, 0xc4, 0x94, 0x04,
0x08, 0x50, 0xa1, 0xc8, 0x94, 0x04, 0x08,
0xff, 0xd0, 0x83, 0xc4, 0x04, 0xc9, 0xc3};

char *msg="Hello,world!\n";
int (*addr)()=printf;
an_bachelor 2004-08-04
  • 打赏
  • 举报
回复
LLnju(LLnju)我还是有点不明白 为什么我的void (*main)()指向了一个有效的函数却不行呢?fmain这个地址应该也对应着一段有效的可执行代码阿!
an_bachelor 2004-08-04
  • 打赏
  • 举报
回复
出处我说了 是IOCCC的作品啊,我是在http://learn.tsinghua.edu.cn/homepage/2001315450/src/all.tgz下载的
据说是1984-2000年的IOCCC获奖作品。

文件mullender.c 目录下还有一个文件mullender.hint 内容如下:
哦对了文中好像说到了只有Vax-11 or pdp-11能正确执行。

The Grand Prize: <vu44!{sjoerd,cogito}> Sjoerd Mullender & Robbert van Renesse

Without question, this C program is the most obfuscated C program that
has ever been received! Like all great contest entries, they result
in a change of rules for the following year. To prevent a flood of
similar programs, we requested that programs be non machine specific.

This program was selected for the 1987 t-shirt collection.

NOTE: If your machine is not a Vax-11 or pdp-11, this program will
not execute correctly. In later years, machine dependent
code was discouraged.

The C startup routine (via crt0.o) transfers control to a location
named main. In this case, main just happens to be in the data area.
The array of shorts, which has been further obfuscated by use of
different data types, just happens to form a meaningful set of PDP-11
and Vax instructions. The first word is a PDP-11 branch instruction
that branches to the rest of the PDP code. On the Vax main is called with
the calls instruction which uses the first word of the subroutine as a
mask of registers to be saved. So on the Vax the first word can be anything.
The real Vax code starts with the second word. This small program
makes direct calls to the write() Unix system call to produce a
message on the screen. Can you guess what is printed? We knew you
couldn't! :-)

Copyright (c) 1984, Landon Curt Noll.
All Rights Reserved. Permission for personal, educational or non-profit use is
granted provided this this copyright and notice are included in its entirety
and remains unaltered. All other uses must receive prior permission in writing
from both Landon Curt Noll and Larry Bassel.
improgrammer 2004-08-03
  • 打赏
  • 举报
回复
可以想象main[]数组里存的是机器码。
谁能破译这些机器码?是8086的机器码吗?
我没有到老DOS的编译器,无法运行它。
redleaves 2004-08-03
  • 打赏
  • 举报
回复
可惜的是,我刚才在VC7下面试了一下,也可以编译通过,看来它并没有对C进行强类型检查(至少是main函数).
下面是C99标准的原话:
"The function called at program startup is named main. The implementation declares no prototype for this function. It shall be defined with a return type of int and with no parameters ..."
redleaves 2004-08-03
  • 打赏
  • 举报
回复
这段代码可以运行很正常啊.因为老的C编译器都是K&R编译器,而不是ISO的标准C编译器.K&R编译器对类型检查并不严格.而编译器通常是通过启动模块(比如start.obj之类的)调用main函数
.在目标文件里,这个main只是一个符号_main,连接器只是简单地把start.obj中所用到的main定位到正确的地方而已.所以以上代码可以正确地编译连接.
而在C的标准中,main只能是一个函数,而且必须是int main(void)或int main(int,char**)等几种形式.所以,比较新的编译器应该就编译不过去了.再加之把main定义成一种数据以后,通常会放在数据段,数据段的代码在有的编译器生成的文件中是没有执行权限的,这种代码也是会出错的.
在C++中就更不用说了,C++是强类型的语言,这种程序就更不可能编译通过了.
VC6可以编译,只能说明它古老.
an_bachelor 2004-08-03
  • 打赏
  • 举报
回复
TC和VC上都试过了可以编译(WinXP专业版 sp1 + VC6 sp6 + TC2.0)
都只是在运行时出错。

我想这可能不是编译器的疏漏,而是一种古老的合格的C语法
(在VC6的一个C++工程中测试时,没有通过编译(缺少symbol _main))!

关于BenWong1981126(肥牛) 的猜想我也测试过,
但是以下的代码:
//Sample1
void imain(){}
void (*main)() = imain;

//Sample2
void imain(){}
int main = ()imain;

//...
把main的类型换成int,int*,void*都是同样的情况即
只能编译通过而运行则有错误。

希望大牛指教:这到底是不是C标准的一部分?
an_bachelor 2004-08-03
  • 打赏
  • 举报
回复
哦 我把文件名更改为了invalid.c
所以汇编代码里有一行

?debug S "source\invalid.c"
vioy 2004-08-03
  • 打赏
  • 举报
回复
关注楼上
an_bachelor 2004-08-03
  • 打赏
  • 举报
回复
使用Tcc -1 -S mullender.c得到如下汇编代码
参数说明:
-1:80186/80286指令
-S:产生汇编代码

.186
ifndef ??version
?debug macro
endm
endif
?debug S "source\invalid.c"
_TEXT segment byte public 'CODE'
DGROUP group _DATA,_BSS
assume cs:_TEXT,ds:DGROUP,ss:DGROUP
_TEXT ends
_DATA segment word public 'DATA'
d@ label byte
d@w label word
_DATA ends
_BSS segment word public 'BSS'
b@ label byte
b@w label word
?debug C E9F79D033110736F757263655C696E76616C69642E63
_BSS ends
_DATA segment word public 'DATA'
_main label word
dw 277
dw 2525
dw -4129
dw 25
dw 0
dw 477
dw 1019
dw 3055
dw 0
dw 12800
dw -113
dw 21119
dw 21207
dw -1006
dw -7151
dw 0
dw 1212
dw 8196
dw 14880
dw 10541
dw 2056
dw 2056
dw 4548
dw 3044
dw -6716
dw 9
dw 4407
dw 6
dw 5568
dw 1
dw -30460
dw 0
dw 9
dw 5570
dw 512
dw -30419
dw 32386
dw 496
dw 6
dw 0
dw 4
dw 1280
dw 15
dw 0
dw 4
dw 1280
dw 4
dw 0
dw 4
dw 0
dw 0
dw 0
dw 8
dw 0
dw 4
dw 0
dw 44
dw 0
dw 12
dw 0
dw 4
dw 0
dw 35
dw 0
dw 16
dw 0
dw 4
dw 0
dw 30
dw 0
dw 22
dw 0
dw 24950
dw 120
dw 25712
dw 112
dw 29811
dw 114
dw 29303
dw 29801
dw 101
_DATA ends
?debug C E9
_DATA segment word public 'DATA'
s@ label byte
_DATA ends
_TEXT segment byte public 'CODE'
_TEXT ends
public _main
end

谁能告诉我 这段代码是否有问题么?(之先帖子里的汇编代码是VC6.0调试器里的)
加载更多回复(19)

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧