|
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实际上是函数名还是变量名就可以通过,随后我进行的几次测试都符合这一点。当然我还没有用所有数据类型一一测试过。 */ |
|
|
|
对不起看错了 不是nmake文件 我看到的是另一个作品的makefile文件 sorry!
|
|
关注,mark
|
|
我跳楼
|
|
大哥,您确信通过链接了吗??
linker没有抱怨找不到一个类似于_main的符号吗? |
|
真的通过了呢!我试过了。关注!
|
|
试了一下,果然可以
|
|
也不是很反常啊
|
|
编译通过,但是运行出错:
3 [main] mullender 1784 handle_exceptions: Exception: STATUS_PRIVILEGED_INSTRUCTION 685 [main] mullender 1784 open_stackdumpfile: Dumping stack trace to mullender.exe.stackdump |
|
作了个大胆的猜想:
操作系统调用crt的代码去查找入口点并开始执行以一个程序时,在汇编层次上只是寻找_main这样一个symbol所以便造成了这个漏洞。 毕竟机器是死的人是活的 |
|
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/aaaa.exe : fatal error LNK1120: 1 unresolved externals |
|
不大可能把??
|
|
汇编了一下,全都变成了DW xxxx(xxxx就是那些常量),理论上如果组织的够严密应该可以执行的,不过不知道这个程序在什么编译器下曾经编译通过,并且可以运行?
|
|
不过很怀疑这个代码怎么可能得奖,显然不够混乱
|
|
同意BenWong1981126(肥牛)
|
|
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标准的一部分? |
|
这段代码可以运行很正常啊.因为老的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可以编译,只能说明它古老. |
|
可惜的是,我刚才在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 ..." |
|
可以想象main[]数组里存的是机器码。
谁能破译这些机器码?是8086的机器码吗? 我没有到老DOS的编译器,无法运行它。 |
|
redleaves(ID最吊的网友)你的机子上能正常运行么?
你是说要什么操作系统什么编译器才能运行呢? K&R C编译器哪里有下载么?真想试试。 |
|
用单步方式执行的时候,_main段的汇编代码如下
_main: 00424A30 adc eax,0DF09DD01h 00424A35 out dx,eax 00424A36 sbb dword ptr [eax],eax 00424A38 add byte ptr [eax],al 00424A3A fld qword ptr [ecx] 00424A3C sti 00424A3D add ebp,edi 00424A3F or eax,dword ptr [eax] 00424A41 add byte ptr [eax],al 00424A43 xor cl,byte ptr [edi-28AD8001h] 00424A49 push edx 可以正常执行到第二句,再执行下一条的时候就弹出对话框说 Unhandled exception in mullender.exe:0xc0000096:Privileged Instruction. 我是在guest下调试的 不知道有没有关系 |
|
没拷完全 是
_main: 00424A30 adc eax,0DF09DD01h 00424A35 out dx,eax 00424A36 sbb dword ptr [eax],eax 00424A38 add byte ptr [eax],al 00424A3A fld qword ptr [ecx] 00424A3C sti 00424A3D add ebp,edi 00424A3F or eax,dword ptr [eax] 00424A41 add byte ptr [eax],al 00424A43 xor cl,byte ptr [edi-28AD8001h] 00424A49 push edx 00424A4A adc bh,ah 00424A4C adc esp,esp 00424A4E add byte ptr [eax],al 00424A50 mov esp,20200404h 00424A55 cmp ch,byte ptr ds:[8080829h] 00424A5B or ah,al 00424A5D adc esp,esp 00424A5F or eax,esp 00424A61 in eax,9 00424A63 add byte ptr [edi],dh 00424A65 adc dword ptr [esi],eax 00424A67 add al,al 00424A69 adc eax,89040001h 00424A6E add byte ptr [eax],al 00424A70 or dword ptr [eax],eax 00424A72 ret 15h 00424A75 add ch,byte ptr ds:[0F07E8289h] 00424A7B add dword ptr [esi],eax 00424A7D add byte ptr [eax],al 00424A7F add byte ptr [eax+eax],al 00424A82 add byte ptr ds:[0Fh],al 00424A88 add al,0 00424A8A add byte ptr ds:[4],al 00424A90 add al,0 00424A92 add byte ptr [eax],al 00424A94 add byte ptr [eax],al 00424A96 add byte ptr [eax],al 00424A98 or byte ptr [eax],al 00424A9A add byte ptr [eax],al 00424A9C add al,0 00424A9E add byte ptr [eax],al 00424AA0 sub al,0 00424AA2 add byte ptr [eax],al 00424AA4 or al,0 00424AA6 add byte ptr [eax],al 00424AA8 add al,0 00424AAA add byte ptr [eax],al 00424AAC and eax,dword ptr [eax] 00424AAE add byte ptr [eax],al 00424AB0 adc byte ptr [eax],al 00424AB2 add byte ptr [eax],al 00424AB4 add al,0 00424AB6 add byte ptr [eax],al 00424AB8 push ds 00424AB9 add byte ptr [eax],al 00424ABB add byte ptr [esi],dl 00424ABD add byte ptr [eax],al 00424ABF add byte ptr [esi+61h],dh 00424AC2 js _main+94h (00424ac4) 00424AC4 jo __XcptActTab+2Ah (00424b2a) 00424AC6 jo _main+98h (00424ac8) 00424AC8 jae __XcptActTab+3Eh (00424b3e) 00424ACA jb _main+9Ch (00424acc) 00424ACC ja __XcptActTab+40h (00424b40) 00424ACE imul esi,dword ptr [ebp],0 00424AD6 add byte ptr [eax],al 00424AD8 add byte ptr [eax],al 00424ADA add byte ptr [eax],al 00424ADC add byte ptr [eax],al 00424ADE add byte ptr [eax],al 00424AE0 add byte ptr [eax],al 00424AE2 add byte ptr [eax],al 00424AE4 add byte ptr [eax],al 00424AE6 add byte ptr [eax],al 00424AE8 add byte ptr [eax],al 00424AEA add byte ptr [eax],al 00424AEC add byte ptr [eax],al 00424AEE add byte ptr [eax],al 00424AF0 add byte ptr [eax],al 00424AF2 add byte ptr [eax],al |
|
弓虽啊,就是运行出错了,郁闷啊
|
|
刚才在win XP的CMD下用TC2.0编译通过后,试着运行了一下,第一次运行的时候没有提示任何错误,屏幕上很规则地打出了一些类似音符的小方块图案(彩色),每个小方块中间有一个数字。再次运行时死机。
重启之后再运行,运行时操作系统会出现一个对话框提示“无效的指令”,点“略过”之后可以正常运行(运行了好几次都一样),但是这次出现的结果是CMD下的光标不停地按某一规则变化位置,按“pause”健光标停在当前位置,按其他任意键又继续光标的运动,看来main数组中的代码当时肯定是可以正确运行的!!! 奇怪的是,我在TC下用这样的代码 void fmain() { printf("Successful?"); } void (*main)() = fmain; 编译后居然会不能正常运行(总是说“无效的指令”)!按理说我的main指向一个有效的函数,应该运行正确啊!?谁能告诉我为什么他的能运行我的就不能呢? |
|
使用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调试器里的) |
|
关注楼上
|
|
哦 我把文件名更改为了invalid.c
所以汇编代码里有一行 ?debug S "source\invalid.c" |
|
mark
|
|
TO an_bachelor(AMD Duron):
K&R的C编译器只是说这个C编译器支持K&R的C语法.比如: void test(a) int a { } 要想看它的汇编,最简单的方法是把main[]的内容写到一个文件里,再用HIEW32反汇编成16位代码. |
|
这个好像还是挺容易的,我写了个在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) 或者直接去掉吧 |
|
Dev c++ 下通过,但生成的EXE文件运行三秒钟后提示遇到问题,非法关闭。
|
|
Dev c++ 下通过,但生成的EXE文件运行三秒钟后提示遇到问题,非法关闭。
|
|
实际上在 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 |
|
void fmain()
{ printf("Successful?"); } void (*main)() = fmain; 这样的代码当然不行的哦,你的main里只是保存了函数的地址而不是程序的机器码,如果fmain的地址刚好是一条合法的指令并且还能正确的跳转到fmain()那个几率真的是很小很小的,可以去买彩票去了 最上面的程序应该不是在x86上运行的,因为反汇编出来的代码好像是没有什么意义的,能贴出程序的出处来吗? |
|
出处我说了 是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. |
|
LLnju(LLnju)我还是有点不明白 为什么我的void (*main)()指向了一个有效的函数却不行呢?fmain这个地址应该也对应着一段有效的可执行代码阿!
|
|
我在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(AMD Duron):
main确实保存的是fmain的起始地址,然后呢? 程序开始跳到main执行,也就是说eip=main的地址(不是main保存的fmain的地址),取出此地址的值(也就是fmain的地址)作为机器代码来执行了。 所以是不行的。 |
|
写个这样的程序,就一行:
int main=195; 编译和执行都不会有问题,呵呵,可惜这个程序什么都没做。 |
|
谢谢大家的指导,尤其感谢allen_wang(IA32 architecture) 和LLnju(LLnju) 的细致解释。
|