我的程序怎么进不了main函数 进程

C语言程序可以没有main函数
学习C语言的同学都知道,每个C程序要有一个main函数,程序从main函数开始执行,在main函数中结束。但事实上,C程序也可以没有main函数,或者说自己可以指定入口函数。下面这篇文章介绍了如何实现这一过程。这篇文章转自:http://www.codeweblog.com,作者不详。学习这个内容对程序设计没啥影响,但能更深入地了解程序编译和链接的原理。
这篇文章主要介绍了C语言之没有main函数的helloworld示例,本文分解了带main函数的helloworld示例,从而分析出不需要main函数的helloworld示例,需要的朋友可以参考下。
几乎所有程序员的第一堂课都是学习helloworld程序,下面我们先来重温一下经典的C语言helloworld。
/* hello.c */
#include &stdio.h&
int main()
printf("helloworld!\n");
这是一个简单的不能再简单的程序,但它包含有一个程序最重要的部分,那就是我们在几乎所有代码中都能看到的main函数,我们编译成可执行文件并查看符号表,过滤出里面的函数如下(为了方便查看我手动调整了grep的输出的格式,所以和你的输出格式是不一样的)
$ gcc hello.c -o hello
$ readelf -s hello | grep FUNC
13 call_gmon_start
13 __do_global_dtors_aux
13 frame_dummy
13 __do_global_ctors_aux
13 __libc_csu_fini
UND puts@@GLIBC_2.2.5
UND __libc_start_main@@GLIBC_
13 __libc_csu_init
大家都知道用户的代码是从main函数开始执行的,虽然我们只写了一个main函数,但从上面的函数表可以看到还有其它很多函数,比如_start函数。实际上程序真正的入口并不是main函数,我们以下面命令对hello.c代码进行编译:
$ gcc hello.c -nostdlib
/usr/bin/ld: warning: cannot find entrysymbol _ defaulting to 0144
-nostdlib命令是指不链接标准库,报错说找不到entry symbol _start,这里是说找不到入口符号_start,也就是说程序的真正入口是_start函数。
实际上main函数只是用户代码的入口,它会由系统库去调用,在main函数之前,系统库会做一些初始化工作,比如分配全局变量的内存,初始化堆、线程等,当main函数执行完后,会通过exit()函数做一些清理工作,用户可以自己实现_start函数:
/* hello_start.c */
#include &stdio.h&
#include &stdlib.h&
_start(void)
printf("hello world!\n");
执行如下编译命令并运行:
$ gcc hello_start.c -nostartfiles -ohello_start
$ ./hello_start
hello world!
这里的-nostartfiles的功能是Do notuse the standard system startup files when linking,也就是不使用标准的startupfiles,但是还是会链接系统库,所以程序还是可以执行的。同样我们查看符号表:
$ readelf -s hello_start | grep FUNC
UND puts@@GLIBC_2.2.5
UND exit@@GLIBC_2.2.5
现在就只剩下三个函数了,并且都是我们自己实现的,其中printf由于只有一个参数会被编译器优化为puts函数,在编译时加-fno-builtin选项可以关掉优化。
如果我们在_start函数中去掉exit(0)语句,程序执行会出core,这是因为_start函数执行完程序就结束了,而我们自己实现的_start里面没有调用exit()去清理内存。
好不容易去掉了main函数,这时又发现必须得有一个_start函数,是不是让人很烦,其实_start函数只是一个默认入口,我们是可以指定入口的
/* hello_nomain.c */
#include &stdio.h&
#include &stdlib.h&
int nomain()
printf("helloworld!\n");
采用如下命令编译
$ gcc hello_nomain.c -nostartfiles -enomain -o hello_nomain
其中-e选项可以指定程序入口符号,查看符号表如下:
$ readelf -s hello_nomain | grep FUNC
UNDputs@@GLIBC_2.2.5
UNDexit@@GLIBC_2.2.5
对比hello_start的符号表发现只是将_start换成了nomain。
到这里我们就很清楚了,程序默认的入口是标准库里的_start函数,它会做一些初始化工作,调用用户的main函数,最后再做一些清理工作,我们可以自己写_start函数来覆盖标准库里的_start,甚至可以自己指定程序的入口。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长![原创]第一章:1.1、寻找main函数入口
逆向的第一步是什么?这要问你学习C语言的第一步是什么,很自然的,逆向的第一步当然也是大名鼎鼎“HelloWorld!”了。但是也不要因此就误认为这一节会很简单,如果你是第一次接触逆向的话,那么这一节还是有些难度的。
& && &好的,让我们先写一个世界上最出名的程序:
int _tmain(int argc, _TCHAR* argv[])
& & printf("Hello World!\r\n");
& & return 0;
& && &不错!很好的开始!然后用VS2008以Debug方式编译下,再用OllyDbg打开看看:
&JMP Test_0.
0041107D&&JMP Test_0.00412CC0
&&JMP &JMP.&MSVCR90D._lock&
&&JMP &JMP.&KERNEL32.GetProcAddress&
0041108C&&JMP Test_0.
&&JMP Test_0.
&&JMP &JMP.&MSVCR90D.?terminate@@YAXXZ&
0041109B&&JMP &JMP.&MSVCR90D._exit&
&&JMP &JMP.&KERNEL32.GetCurrentThreadId&
&&JMP &JMP.&MSVCR90D._initterm&
& && &看看我们的程序停在了什么鬼地方,如果各位初学读者试图从这里就开始分析的话那真的很恐怖,相信30分钟内你的自信心将被打击到零……
& && &我们都知道其实编译器在编译我们的程序前会做很多准备工作,而这些准备工作由于涉及的东西较多且每个由此编译器生成的程序都一样,因此我们不必深究,只需快速且准确的找到main函数即可。
& && &但是这对于初学逆向的朋友来说也是最难的,下面我就教各位读者怎样突破这个障碍。
& && &想要找到main函数,那么我们就要从C语言本身讲起,在刚刚开始学习C语言的时候我们就被不幸的告知,我们的程序中必须要包含一个名字叫做main的函数,不管你多讨厌它都必须如此,后来便成了习惯……
& && &后来查查C99标准,发现“int main(int argc, char *argv[])”与“int main(void)”都是被接受的,然后又查查MSDN,可以清晰看到一句话“The main and main functions can take the following three optional arguments”,也就是告诉了我们main函数其实是有3个参数的,其后面的例子更是证明了这句话确实是微软写上去的:
& & main( int argc, char *argv[ ], char *envp[ ] )
& && &嗯,他们又在标准上较劲了,但是考虑到我们大部分程序都是用vs编译的(而且Borland的C++的参数也是如此),因此我们还是做墙头草,随大流吧……
& && &到这里有的读者可能会感到疑惑,如果我们使用的是符合C99标准的main函数呢?例如我们源码的main函数不就是两个参数吗。但是在这里我要很负责的告诉大家,不管我们代码中实际使用了几个参数,在程序被编译时其main函数肯定是三个参数的,因为这取决于Windows系统的机制。
& && &因此现在已经为我们识别main函数提供了很好的特征,既有三个参数,且前两个参数为地址量的call就应该是我们的main函数了。除此之外,我们通过MSDN可知应用程序会随着main函数结束而退出,这又给了我们第二个有力的特征,既main函数很定是在程序退出代码附近的(而且目前的主流调试、反汇编工具都可以正确识别出退出函数exit)。
& && &有了这些特征,我们再想找到main函数就不难了,目前我为大家提供三种方法:
1.1.1、字符串搜索法
& && &安装完各个版本的C++编译器后,逐个写Hello World,然后用OllyDbg的搜索字符串功能搜索这个字符串,最后逐步回溯即可,下面我为大家演示一下我做的步骤。
& && &用OllyDbg打开目标文件后,先记住程序默认停在哪里,然后在CPU窗格点击右键,依次选择【超级字符串参考】&【查找ASCII字符】,选择我们的“Hello World”后双击即可到main函数中,代码如下:
&&PUSH EBP& && && && && && && && && && &&&; 函数入口
&&MOV EBP, ESP
&&SUB ESP, 0C0
&&PUSH EBX
004113AA&&PUSH ESI
004113AB&&PUSH EDI
004113AC&&LEA EDI, DWORD PTR SS:[EBP-C0]
&&MOV ECX, 30
&&MOV EAX, CCCCCCCC
004113BC&&REP STOSD
004113BE&&MOV ESI, ESP
&&PUSH Test_0.0041573C& && && && && && && &; /Hello World!\r\n
&&CALL DWORD PTR DS:[&&MSVCR90D.printf&]& &; \printf
004113CB&&ADD ESP, 4
004113CE&&CMP ESI, ESP
&&CALL Test_0.
&&XOR EAX, EAX
004113DA&&ADD ESP, 0C0
&&CMP EBP, ESP
&&CALL Test_0.
&&MOV ESP, EBP
004113EA&&RETN
& && &我们单击选择函数入口后,可以看到CPU窗格下面的信息窗格中显示如下信息:
跳转来自 0041100F
& && &我们单击选择此信息后,点击鼠标右键,并选择【转到 JMP 来自0041100F】后即可来到上层调用函数(以后我们将之称为“返回到调用”):
0041100A&&JMP &JMP.&KERNEL32.DebugBreak&
0041100F&&JMP Test_0.& && && && && && && &&&; 我们停到这里
&&JMP Test_0.
& && &遇到这种情况直接在返回到调用,此时来到真正调用main函数的地方:
0041195F&&MOV EAX, DWORD PTR DS:[417148]
&&PUSH EAX
&&MOV ECX, DWORD PTR DS:[41714C]
0041196B&&PUSH ECX
0041196C&&MOV EDX, DWORD PTR DS:[417144]
&&PUSH EDX
&&CALL Test_0.0041100F& && && && && && && &; 我们停到这里
&&ADD ESP, 0C
0041197B&&MOV DWORD PTR DS:[41715C], EAX
&&CMP DWORD PTR DS:[417150], 0
&&JNZ SHORT Test_0.
&&MOV EAX, DWORD PTR DS:[41715C]
0041198E&&PUSH EAX& && && && && && && && && && && &; /status =& 0
0041198F&&CALL DWORD PTR DS:[&&MSVCR90D.exit&]& &&&; \exit
& && &通过上面的代码我们便看到了main函数的典型特征,临近exit,且有三个参数。接下来我们要做的就是不断地重复上面的步骤,一直到找到程序入口点为止。
& && &最后你要做的就是针对不同的版本不同城上的编译器重复上面的步骤,直到收集到你认为足够丰富的信息后结束,从此你就再也不用怕为找不到main函数而苦恼了。
1.1.2、栈回溯法
& && &栈回溯的方法是先找到main函数中的那个“HelloWorld”,下断点并按【F9】键运行后查看堆栈情况,我这里的堆栈情况如下:
0012FE9C& &7C930208&&ntdll.7C930208& && & ; 我们停在这里
0012FEA0& &FFFFFFFF
0012FEA4& &7FFDE000
0012FEA8& &CCCCCCCC
& & ……& &……
0012FF64& &CCCCCCCC
0012FF68&&/0012FFB8
0012FF6C&&|&&返回到 Test_0. 来自 Test_0.0041100F
0012FF70&&|
0012FF74&&|003D2C60
0012FF78&&|003D2D40
0012FF7C&&|0A641DBC
0012FF80&&|7C930208&&ntdll.7C930208
0012FF84&&|FFFFFFFF
0012FF88&&|7FFDE000
0012FF8C&&|00369E99
0012FF90&&|
0012FF94&&|
0012FF98&&|&&ASCII "Actx "
0012FF9C&&|
0012FFA0&&|0012FF7C
0012FFA4&&|
0012FFA8&&|0012FFE0&&指向下一个 SEH 记录的指针
0012FFAC&&|0041107D&&SE处理程序
0012FFB0&&|0A3788D4
0012FFB4&&|
0012FFB8&&]0012FFC0
0012FFBC&&|004117BF&&返回到 Test_0.004117BF 来自 Test_0.
0012FFC0&&\0012FFF0
0012FFC4& &7C817077&&返回到 kernel32.7C817077
& && &对于这些信息我们只需要关注注释前面有“返回到”三个字的,离我们最近是:
0012FF6C&&|&&返回到 Test_0. 来自 Test_0.0041100F
& && &鼠标单击选择该项后,按【Enter】键即可来到返回地址处:
0041195F& &.&&A1 & &MOV EAX, DWORD PTR DS:[417148]
& &.&&50& && && && &PUSH EAX
& &.&&8B0D 4C714100 MOV ECX, DWORD PTR DS:[41714C]
0041196B& &.&&51& && && && &PUSH ECX
0041196C& &.&&8B15
MOV EDX, DWORD PTR DS:[417144]
& &.&&52& && && && &PUSH EDX
& &.&&E8 97F6FFFF& &CALL Test_0.0041100F
& &.&&83C4 0C& && & ADD ESP, 0C& && && && && && && && && && &; 我们停在这里
0041197B& &.&&A3 5C714100& &MOV DWORD PTR DS:[41715C], EAX
& &.&&833D &CMP DWORD PTR DS:[417150], 0
& &.&&75 0C& && && &JNZ SHORT Test_0.
& &.&&A1 5C714100& &MOV EAX, DWORD PTR DS:[41715C]
0041198E& &.&&50& && && && &PUSH EAX& && && && && && && && && && && &; /status =& 0
0041198F& &.&&FF15
CALL DWORD PTR DS:[&&MSVCR90D.exit&]& &&&; \exit
& && &此时我们又来到了这个熟悉的地方,接下来的事情就要各位读者自己发挥了(重复上面的步骤)。1.1.3、逐步分析法
& && &以上讲的两种方法都是在学习与知识储备时用的,不可能收到什么实战效果。假如我们现在碰到了一个现在就需要我们分析的软件,而且它的编译环境我们以前没碰到过,这就要求我们纯手工分析并找到main函数了。
& && &之所以将之称为逐步分析法,是因为我们不需要阅读它代码的具体含义,而是只需要以JMP与CALL为单位逐个跟进,从而根据main函数的特征判定main函数的所在位置。
& && &其实这种方法有点类似于文件搜索,先搜索根目录、在逐层加深搜索其子目录,直到找到我们需要的东西。
& && &那我们的程序为例,我们的OEP处就是一个JMP,因此其“根目录”也就是第一层代码里是不可能有我们的main函数了,当我们跟进这个JMP后会发现如下代码:
& && \8BFF& && && & MOV EDI, EDI
&&/.&&55& && && && &PUSH EBP
&&|.&&8BEC& && && & MOV EBP, ESP
&&|.&&E8 96F8FFFF& &CALL Test_0.
004117BA&&|.&&E8 & &CALL Test_0.
004117BF&&|.&&5D& && && && &POP EBP
&&\.&&C3& && && && &RETN
& && &我们发现第二层代码里也没有我们的main函数,但是有两个CALL。因此我们跟进第一个CALL中,为了节省篇幅,我在这里就不贴出代码了,我在这里并没有发现main函数,但是发现了数个JMP与CALL。不过需要注意的是,我们一定要注意采用逐层搜索的思想,因此这里的CALL与JMP就不要再继续跟下去了,我们现在要住的是返回上一层,看看第二个CALL里是什么:
&&MOV EDI, EDI
&&PUSH EBP
&&MOV EBP, ESP
& & ……&&……
&&CALL Test_0.004110FF
& & ……&&……
&&CALL DWORD PTR DS:[&&KERNEL32.Interlocke&;&&kernel32.InterlockedCompareExchange
& & ……&&……
0041184E&&JMP SHORT Test_0.0041185D
&&PUSH 3E8& && && && && && && && && && && &; /Timeout = 1000. ms
&&CALL DWORD PTR DS:[&&KERNEL32.Sleep&]& & ; \Sleep
0041185B&&JMP SHORT Test_0.
& & ……&&……
004118EB&&PUSH Test_0.& && && && && && && &;&&_
&&PUSH 1F4
&&PUSH Test_0.& && && && && && && &;&&f
004118FC&&PUSH 2
004118FE&&CALL DWORD PTR DS:[&&MSVCR90D._CrtDbgRep&;&&MSVCR90D._CrtDbgReportW
&&ADD ESP, 14
& & ……&&……
&&PUSH 0& && && && && && && && && && && &&&; /NewValue = 0
&&PUSH Test_0.0041756C& && && && && && && &; |pTarget = Test_0.0041756C
0041191A&&CALL DWORD PTR DS:[&&KERNEL32.Interlocke&; \InterlockedExchange
& & ……&&……
&&PUSH Test_0.
0041192E&&CALL Test_0.
&&ADD ESP, 4
& & ……&&……
0041193A&&PUSH 0
0041193C&&PUSH 2
0041193E&&PUSH 0
&&CALL DWORD PTR DS:[417590]& && && && && & ; 注意这里,虽然这个CALL也有三个参数,但是仔细分析一下我们就会发现
& && && && && && && && && && && && && && &&&; 这并不是main函数,因为main函数的后两个参数是指针,这里的0与2显然
& && && && && && && && && && && && && && &&&; 不符合要求。其次他也并非是临近exit的。
&&CALL DWORD PTR DS:[&&MSVCR90D._CrtSetChe&;&&MSVCR90D._CrtSetCheckCount
& & ……&&……
0041195F&&MOV EAX, DWORD PTR DS:[417148]
&&PUSH EAX
&&MOV ECX, DWORD PTR DS:[41714C]
0041196B&&PUSH ECX
0041196C&&MOV EDX, DWORD PTR DS:[417144]
&&PUSH EDX
&&CALL Test_0.0041100F& && && && && && && &; 终于来到我们熟悉的main函数里了!
&&ADD ESP, 0C
& & ……&&……
0041198E&&PUSH EAX& && && && && && && && && && && &; /status =& 0
0041198F&&CALL DWORD PTR DS:[&&MSVCR90D.exit&]& &&&; \exit
& & ……&&……
0041199E&&CALL DWORD PTR DS:[&&MSVCR90D._cexit&]& &;&&MSVCR90D._cexit
& & ……&&……
004119AB&&JMP SHORT Test_0.004119FF
& & ……&&……
&&MOV ECX, DWORD PTR SS:[EBP-14]
004119BA&&PUSH ECX
004119BB&&MOV EDX, DWORD PTR SS:[EBP-28]
004119BE&&PUSH EDX
004119BF&&CALL Test_0.
&&ADD ESP, 8
& && &看到这里各位读者是不是感觉逆向很简单,但也充满挑战?如果你掌握了以上三种方法,那么恭喜你,你已经成功的走出了第一步,这很有纪念意义。
支付方式:
最新回复 (78)
第一次坐沙发,哈哈。
刚试过了,堆栈法以前没用过。
期待其它文章。
呵呵,看完了,就是记不住啊
hha 不错啦 楼主很是实在啊 很有用的东西啦
不错不错 LZ 辛苦了!
mark一下。。
可以慢慢看了
,& &楼主的治学很严谨,看过楼主的不少好文章。支持一下,再接再厉,哈哈。
很好,楼主开始了一个新方向,将逆向系统化,条理化。
希望能借这位朋友吉言,可以让这套教程有些意义。-------------------------------------------------------
本教程是边写作,边发表,因此各位如果觉得在整体结构上哪里需要调整,或有什么好的建议,还望各位不惜赐教,让我们在交流中提高自己,服务大家。
通俗易懂 辛苦
如果能先科普下程序在执行main函数前系统都做了些啥,那就更perfect了
看来逆向的门槛将会越来越低了
如果都能高质量的完成,也算是很好很好的一件事了,论坛上这样能让我们新手看的文章,现在不是很多。谢谢LZ。
找main的主要方法是
在开始的那段中找关闭进程的API调用,exit/ exitprocess/ TerminateProcess之类。在其前的调用通常是 main
还有找DLL的入口,loadlibrary调用的lpkloaddll在调用dll入口之前是
call ebx这样调用DLL入口的,所以OD挂个DLL直接执行到此DLL main的返回位置,retn之后就得到了本机上 中loadlibrary调用&&dll main的关键点,因为user32.dll总是最早载入,本机上可以是一直用。
我个人觉得,DEBUG和RELEASE,如果从一开始就放在一起对比(工作量就大了)会比较好。
当然分析DEBUG再加上PDB是件相对比较容易的事情。
第一次分析main之前的动作,可以多次进入CALL,根据OD提示的API参考MSDN,也就大概知道编译器在main之前所做的工作了。
如果要往下深入,我想分析RELEASE是必然的,如果从一开始就熟悉编译器的优化方式,对以后的分析能够起到比较好的作用。
我也是初学中的初学,一点愚见,如果有什么问题,请谅解。
回复13楼的XPoy:
& && &非常感谢你提出的建议,其实我也一直在想,本篇文章作为开篇,怎样写会比较好一些,应不应该带上DLL入口相关的内容。但是后来自己仔细论证了一下,我感觉第一篇文章作为整个教程的一个基调,切不可贪多。一定要做到有目的的写作,也就是说我的教程究竟想教会别人做什么。
& &&&我感觉,我的教程所面向的对象仍然是以基础薄弱,刚入门的读者为主,因此我要做的事情仅仅是让他们了解最为重要的、最为迫切的知识。修剪枝蔓才能确立主干,既然我们的立足点是逆向工程,那么就应该尽量减少其他附加知识的,以降低阅读门槛。
回复14楼的yasm:
& &&&首先非常感谢你的反馈,你说的观点也没什么问题,对于Release版,我想在第一篇文章就出现会吓到很多读者,我在后面的教程中会逐步引入的。
学习了,终于看到通俗易懂的教程了
谢谢楼主,可以系统的深层次的学习一下了
LZ 文章写的不错 !值得期待
楼主很强大!!!!!
严重期待,希望不要等太久...学习ing
hha 不错啦 楼主很是实在啊 很有用的东西啦
本教程计划2-3天跟新一篇(事情很多,否则就天天更新了),估计最多不会超过3个月就会更新完毕的。
如果各位初学你想的朋友能从今天开始跟着本教程一直学下去,三个月后相信你会脱胎换骨的。
太好了,学习!!!!!!
谢谢分享~~ 我新手对我很有帮助
学习了,哈哈~
我等新手,受益匪浅啊,谢谢楼主!
顶楼主。先顶后看。
不错不错 LZ 幸苦了!
看完了,感觉非常好。
以前总是感觉一进入ollaydbg,找不着头脑,终于有点头绪了。
谢谢楼主。
我们单击选择函数入口后,可以看到CPU窗格下面的信息窗格中显示如下信息:
跳转来自 0041100F
& && &我们单击选择此信息后,点击鼠标右键,并选择【转到 JMP 来自0041100F】后即可来到上层调用函数(以后我们将之称为“返回到调用”):
=============================================
我有一个非常白痴的问题,
就是我用VC6的时候可以在信息框格中看见这个信息,
可是我用VC2008 VC2010的时候却没有这个返还信息,
这是什么原因,
而且这个问题不仅仅是这个程序,
我以前调试完美这个游戏的时候,那个做视频教程的都有这个返还的提示,
可是就是我的没有,
我百度 谷歌都没解啊。
我本文描述的操作都是在OllyDbg里
抱歉,我没有说清楚,就是我用OD调试VC6编译出来的东西,会出现“跳转来之什么什么地方”
而用OD调试VS2008,VS2010的时候就没有这个提示,如果没有这个提示是什么原因,
或者有什么解决方案。
上传的附件:
(58.45kb,382次下载)
(60.97kb,383次下载)
这个问题我没碰到过,你可以检查一下你的【选项】&【调试选项】&【CPU】,看看此选项卡下面的所有复选框是否为选中状态,如果不是将其全部勾选上。
不错,分析得很好,学习了!
我也没有显示,后来通过以下方法达到类似的效果。
在main函数中的某一点,增加一个断点,运行时,函数停在断点处,[查看]--》[调用堆栈],打开堆栈窗口(快捷键Alt+K),在调用栈可以看到所有的调用过程,如果不对的话,在调用栈窗口,点右键,有一个线程按钮,进去选择不同的线程。
然后选择某个函数,右键,就有[显示调用],[显示过程]等命令。
ps:我看了我的调试设置,cpu中的[显示跳转路径]也check上了,但不知道为什么没有显示出来。
难道和版本有关?这就不得而知了,我的版本是ollyice v1.1 汉化第二版 汉化:cao_cong
楼主写的很好.赞一个
谢谢了,还是不行,我真的怀疑是版本的问题,把
看帖留名,看过楼主的opcode文章,最近才注意到出专题了,支持!
好东东,感谢楼主,学习中。。
顶一个,支持楼主!
1、首先感谢作者的辛勤劳作。
2、希望看雪有更多像作者这样的人,为大家贡献。
我不得不说几句,其实LZ现在做的这些东西,网上已经很多资料了,比如《加密解密3》《夜读天书》、《黑客反汇编揭秘》、《IDA权威指南》还有论坛很多前人的杰作。希望作者结合这些人的精华,把文章写得更好。
我的建议:
1、以后再写文章的时候,比如类似这样的话“但是在这里我要很负责的告诉大家,”就不要加进来了,因为作为一篇学术论文(或许你不是这么认为),是不需要这些的,你应该拿出实据来说明。你可以引用C99的原话,“It shall be defined with a return type of int and with no parameters:......or in some other implementation-defined manner.”C99在这里只是“Shall be”,不是“must be”,所以给编译器的厂家很大空间,并不违背了C99的标准。
2、尽可能结合更多编译器,来说明C/C++的本质。
非常感谢 叁毛 对本文提出的建议,并非常高兴看到有人认可我的劳动成果。
本回复对事不对人,心理承受能力差者请过滤:
接受的建议:
第一条建议我接受,虽然正像你说的那样我并没有把自己的文章当成学术论文来看待,但是正规一点还是好的。
第二条建议我接受50%,首先编译原理是一样的,而VC与其他编译器生成代码上的大体上的异同个人感觉只需几百字即可说明白,而且网上也有类似资料。
& && && && && && && &我接受的是你提出自己建议的初衷,希望文中的例子有对比,行文有起伏,这些我以后会着重考虑。
我的想法 ^_^ :
首先说我写的东西,我本人是一个非常反对重复发明轮子的人,当我认为自己有能力查缺补漏,并完善某个知识点时,我才会去做这件事情。晚辈斗胆在此列出 叁毛 兄提出的哪些书籍,并分别就反汇编这块说说自己的看法,不对之处还望指正。
《加密解密3》:很明显第四章的主要目的不是教会读者分析代码,而是纵观结构与逻辑(例如没有细节、没有C++类相关内容)。
《天书夜读》:这个不多作评论,笔者仅仅用了十几页来描述逆向,很显然只是想给读者建立一个概念而已。
《黑客反汇编揭秘》:简单的说一下吧,分支结构那里,那本书有关于二叉树相关的例子就列出数种情况,而我的文章中只给了一种情况,只为更利于读者读懂。
《IDA权威指南》:本书笔者刚好读完第八章,笔者纵观整书目录只发现第8.7节与第20章描述的内容只算与逆向勉强相关。
& & 因此我也不得不说,不知道这些书 叁毛 兄是怎么理解的,其中两本个人认为与逆向的关系简直是微乎其微。
& & 而单单就《加密解密3》与《黑客反汇编揭秘》这两本书来说,一个是为了破解打基础(请看那部书后面的章节),一个是面向专业的逆向人员。
& & 但我的文章是面向对于逆向工程有兴趣的读者写的,他们并不专业,他们的知识很不系统,因此我的文章的目的就是帮他们穿针引线,就是让他们以后能看懂类似于《黑客反汇编揭秘》这种书。
& & 当然,相信 叁毛 兄是抱着一颗赤诚之心对本文提出建议的,因此特将 叁毛 ID列于本系列教程之首页,以表最诚挚的感谢!!
楼主好人 学习ing
我的小小的建议被你采纳,我觉得是本人最大的荣幸。
可能跟我水平有关系吧,对这几本书的理解跟你的理解不一样。
不管如何,LZ能否无偿地将这些总结,并跟大家分享的这种精神是可贵的。我这里以前收集了一些资料,希望对你有用。
上传的附件:
(1.91MB,38次下载)
(1.91MB,89次下载)
(1.91MB,75次下载)
(0.98MB,62次下载)
感谢 叁毛 提供的资料~~
非常感谢LZ做一些让初学者易于接受的教程。很不错。收了。
学习中,加油
我们单击选择函数入口后,可以看到CPU窗格下面的信息窗格中显示如下信息:
跳转来自 0041100F
& && &我们单击选择此信息后,点击鼠标右键,并选择【转到 JMP 来自0041100F】后即可来到上层调用函数(以后我们将之称为“返回到调用”):
按教程操作到此,找不到”跳转来自 XXXXXXXX“,无法进入上层调用函数啊?请指点一下
[QUOTE=817384]我也没有显示,后来通过以下方法达到类似的效果。
在main函数中的某一点,增加一个断点,运行时,函数停在断点处,[查看]--》[调用堆栈],打开堆栈窗口(快捷键Alt+K),在调用栈可以看到所有的调用过程,如果不对的话,在调用栈窗口,点右键,有一个线程按钮,进去选择不同的线程。
然后选择某个函数...[/QUOTE]
谢谢,这个方法还是可以实现的!
1.请先关注公众号。
2.点击菜单"更多"。
3.选择获取下载码。

我要回帖

更多关于 c main函数参数 的文章

 

随机推荐