荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: MUL (MUL), 信区: Program
标  题: 圣诞十二天
发信站: 荔园晨风BBS站 (2005年10月10日17:59:38 星期一), 站内信件

 有人分析了,哈哈!
发信人: LosComet (小七其实挺聪明的,就是人有点傻……), 信区: CProgramming
标  题: 圣诞十二天
发信站: 水木社区 (Mon Oct 10 13:20:20 2005), 站内

那天那个程序的分析,今天刚刚从硬盘里翻出来的~~,贴出来吧:

===============================================================================
=
==========
/***************************************************************
你能猜出这段程序的输出结果吗?

许多人都坚信,只要有源代码,他们就总能搞懂代码里所有的玄机。
下面这几行C语言代码就是对这一类人的最大嘲弄:

这只是一段普通的C语言代码罢了。我知道,它也许看上去不那么
好看,似乎这只是一堆无法通过编译的字符垃圾。不过,为什么
你不试一试呢?它的确能通过所有C语言编译器的编译。

我担保你猜不出它的运行结果,试着运行一下,你一定会被输出结
果惊得目瞪口呆。
/*   Write by CYNOSURE , cinasure#hotmail    bbs.cdut.edu.cn   */
****************************************************************/

#include <stdio.h>
main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_,
main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t,
"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\
;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \
){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \
iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \
;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \
}'+}##(!!/")
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
  :0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry"),a+1);}




上面这段代码,确实很好玩,这是1988年IOCCC大赛(国家C语言混乱大赛)的一段作品,
这段看起来杂乱无章的代码,它居然能输出一段诗:


On the first day of Christmas my true love gave to me
a partridge in a pear tree.

On the second day of Christmas my true love gave to me
two turtle doves
and a partridge in a pear tree.

On the third day of Christmas my true love gave to me
three french hens, two turtle doves
and a partridge in a pear tree.

On the fourth day of Christmas my true love gave to me
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the fifth day of Christmas my true love gave to me
five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the sixth day of Christmas my true love gave to me
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the seventh day of Christmas my true love gave to me
seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the eigth day of Christmas my true love gave to me
eight maids a-milking, seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the ninth day of Christmas my true love gave to me
nine ladies dancing, eight maids a-milking, seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the tenth day of Christmas my true love gave to me
ten lords a-leaping,
nine ladies dancing, eight maids a-milking, seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the eleventh day of Christmas my true love gave to me
eleven pipers piping, ten lords a-leaping,
nine ladies dancing, eight maids a-milking, seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.

On the twelfth day of Christmas my true love gave to me
twelve drummers drumming, eleven pipers piping, ten lords a-leaping,
nine ladies dancing, eight maids a-milking, seven swans a-swimming,
six geese a-laying, five gold rings;
four calling birds, three french hens, two turtle doves
and a partridge in a pear tree.


----------------------------------
我是先看到这个结果,然后才看到程序的,第一眼的反应:“人工智能”?!
这种杂乱无章的程序,居然能写出通顺的文章?!天啦,太牛了!!!

不过,我打死也不相信有人工智能可以达到这种境界。。。。。

所以俺决定和它较真儿!

既然是输出文字的东东,那首先看看字符串吧。。。。发现整个程序,只有四个字符串。


"%s %d %d\n"


"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\
;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K w'K:'+}e#';dq#'l \
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \
){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw' iK{;[{nl]'/w#q#n'wk nw' \
iwk{KK{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \
;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')# \
}'+}##(!!/"



"%s"



"!ek;dc i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m .vpbks,fxntdCeghiry"



很明显的,第二个字符串是重点怀疑对象,因为它太庞大了,我不相信那么短
的程序里面,如此长(占程序一大半)的东西会不起作用。。。。但它确实是
杂乱无章的呀。。。但是。。。。第二个hint发现了。。。一个符号经常性的
出现哦。。。那就是“#”。。。如果略微知道一点密码学,应该晓得,英文
里面最常出现的字母,一定是“e”!啊呀呀,啊呀呀。。。。是不是应该比
较一下这段文字和打印出来的正文呢?正文重复的太多,有理由相信是用循环
生成的。那么,比较最后一段吧。。。把“#”当作“e”来对比。。。。。


为了缩小范围:我们把##形式的当作ee,能找到什么呢?分别有三段含有
##和ee:

w{%'l##w#' i;     §      +,}##'*}#nc     §        }'+}##(!!/

six geese a-layi  §  ds, three french    §   a pear tree.




比较以上片段,是不是很容易看出字符对应规则?

拿着这段鸡毛你就可以当令箭了,那段加密后的字符串,原来就是:

"On the
/first/second/third/fourth/fifth/sixth/seventh/eigth/ninth/tenth/elevent
h/twelfth/ day of Christmas my true love gave to me
/twelve drummers drumming, /eleven pipers piping, /ten lords a-leaping,
/nine ladies dancing, /eight maids a-milking, /seven swans a-swimming,
/six geese a-laying, /five gold rings;
/four calling birds, /three french hens, /two turtle doves
and /a partridge in a pear tree.

/"

啊哈,原来。。。原来。。。原来。。。。楼上的程序就是在故弄玄
虚嘛,假动作之一,发现了。。。。。呵呵

不过这才是万里长征,第一步呢。。。要想弄懂它,还得慢慢看。。。

怎么解密呢?程序里面使用了什么高明的算法吗?继续看下去吧。。。

嗯,输出嘛,总该有个东西来负责输出,没有print? 那么putchar也
凑合,果然有个putchar! 可是,为什么是putchar(31[a])呢?31?有
什么奥妙?

嗯嗯嗯。。。看看第四个字符串,长度?啊,不是刚刚好是31×2+1嘛
为什么多了个1?呀呀呀,字符串里面有个\n,不是刚好多1嘛,看看呢?
把这个字符串剖成两半呢?

 !ek;dc i@bK'(q)-[w]*%n+r3#l,{}:
\nuwloca-O;m .vpbks,fxntdCeghiry


哦?!这不正好是密码表吗?!Bingo!

putchar(31[a]),这正是指针的一个绝妙应用,原来,就等于putchar(a+31)啊。



原程序我们可以做个手术了:

#include <stdio.h>
main(t,_,a)char *a;{return!0<t?t<3?main(-79,-13,a+main(-87,1-_,
main(-86,0,a+1)+a)):1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13?
main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t,
"密文")
:t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1)
  :0<t?main(2,2,"%s"):*a=='/'||main(0,main(-61,*a,
"密码表"),a+1);}


可是即使是这样,这程序还是很难懂,没办法,我们来挨个修理那些问号冒号吧!
为便于理解,我还加上了些括号:

main(t,_,a)
char *a;
{
return !0 < t   ?       (t<3? main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a))
:
1),
                        (t<_?main(t+1,_,a ):0)  ,
                        (main(-94,t-27,a)&&t==2  ?   ( _ < 13 ?main(2,_+1,"%s
%d
 %d\n"):9)  : 16)
                :  t<0?  ( t<-72?    main(_,t, "加密文本")
                                :   (t<-50?  ( _==*a  ? putchar(密码表长度的一

[a])
                                                          :   main(-65,_,a+1)
                                                )
                                                 :  main((*a=='/')+t,_,a+1)
                                     )
                         )
                        :  t>0   ? main(2,2,"%s")
                                   :(*a=='/' || main(0,main(-61,*a,"密码表"),a+
1
));
}

试过几次后发现一个重要的问题:return!0<t,居然等价于return 1<t,这是把 !0 直接

作1了。。。
这是作者和我们开的第二个玩笑:)

( _ < 13 ?main(2,_+1,"%s %d %d\n"):9)  这里的13是不是与循环次数有关呢?改来看看

,果然就是。


由此可见,根据t的大小,程序分为几个流程:


  t<-72:main(_,t, "加密文本")  ,准备输出文本


  -72<=t<-50:_==*a时候输出解密字符并返回1,否则 main(-65,_,a+1),可见这里的参

数-65还是在-72和-50之间。
                                             这是在重复调用自身,指针a在寻找密




  -50<=t<0:main((*a=='/')+t,_,a+1)    和上面差不多,这个简单的说就是将指针移

到第-t个“/”后面。


  0==t:(*a=='/' || main(0,main(-61,*a,"密码表"),a+1)),顺序输出字符(调用main(
-
61....)),直到遇到“/”。
                                          注意:  || 运算符特点,左式为真的话,

不再判断右式。

  1==t:main(2,2,"%s")


  2=<t<循环数+1:       (t<3? main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a))
:
1),
                        (t<_?main(t+1,_,a ):0)  ,
                        (main(-94,t-27,a)&&t==2  ?   ( _ < 13 ?main(2,_+1,"%s
%d
 %d\n"):9)  : 16)

    注意这是个逗号表达式,表示顺序执行,那就是从左往右一个一个的求值,最后整个

达式的结果
    是最后一个求值的结果。

  循环数+1=<t:不用管它内容了,整个程序没有机会进入它。


  第一次调用的时候,t和_都等于1,所以先进入 main(2,2,"%s");,   顺序执行程序,

  注意到在t<-72或t=2的时候,第三个参数a是没用的,所以,"%s %d %d\n"和"%s",在程

序中是没有用处的,
main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)) : 1)也就等于main(-79,-13,main(-
8
7,1-_,main(-86,0,随便什么)) : 1)


接下来就很好理解了,可以把“/”当作数组的分隔符,那么把密文看作个数组:

str[0]=On the
str[1]=first
str[2]=second
str[3]=third
str[4]=fourth
str[5]=fifth
str[6]=sixth
str[7]=seventh
str[8]=eigth
str[9]=ninth
str[10]=tenth
str[11]=eleventh
str[12]=twelfth
str[13]= day of Christmas my true love gave to me \n
str[14]=twelve drummers drumming,
str[15]=eleven pipers piping,
str[16]=ten lords a-leaping, \n
str[17]=nine ladies dancing,
str[18]=eight maids a-milking,
str[19]=seven swans a-swimming,\n
str[20]=six geese a-laying,
str[21]=five gold rings; \n
str[22]=four calling birds,
str[23]=three french hens,
str[24]=two turtle doves \nand
str[25]=a partridge in a pear tree. \n\n


程序的主题就在于这个分支:2=<t<循环数+1,从main(2,2,"%s")进入,t,_初始值都是2:


(t<3? main(-79,-13,                           再然后,打印str[13],

    main(-87,1-_,                    然后执行这里,打印str[_-1],因为后面有个循

环,_是从2到13。

    main(-86,0,1))) : 1), 首先执行这里,输出数组元素,第二个参数乘-1便是下标。

里为0,打印str[0]=On the

                        (t<_?main(t+1,_,a ):0)  ,     然后,循环str[27-t'],t'

t循环到_,第一次因为t=_就跳过这里,继续后面的。
                                                        在每次循环中由于t'>=2,

所以后面的操作,只打印str[27-t'],并不执行
                                                        分支 main(2,_+1,"%s
%d %
d\n");

    (main(-94,t-27,a)&&t==2?( _ < 13 ?main(2,_+1,""):9)  : 16)   当上面的循环完

了后,再执行此判断,
                                                               也就是,打印str[
2
7-2]=a partridge in a pear tree. \n\n
                                                               最后,循环调用ma
i
n(2,3,"")一直到main(2,13,"")
                                                               依次打印12段的内

容。




ok,这就是程序全部流程了。。。。。。打完收工~~~


这作者实在爱和大家开玩笑,文本加密不说,还用!0,逗号,问号,一堆东西,总是让人

入霏霏呀:)


/*   Write by CYNOSURE , cinasure#hotmail    bbs.cdut.edu.cn   */
===============================================================================
=
==========
--
main(_){for(--_;putchar(_++["J!Mpwf!Zpv\1"]-1););}


※ 来源:·水木社区 http://newsmth.net·[FROM: 221.2.163.*]

--
                     VENI  VIDE  VICI
※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.1.106]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店