0x01 定义
main是C/C++的标准入口函数名,程序执行总是从main函数开始,如果有有其他函数,则完成对其他函数的调用后再返回到主函数,最后由main函数结束整个程序,其他函数是不能调用main函数的。在执行程序时,由系统先行调用main函数。
WinMain是windows API窗体程序的入口函数,该函数的功能是被系统调用,作为一个32位应用程序的入口点。WinMain函数应初始化应用程序,显示主窗口,进入一个消息接收一发送循环,这个循环是应用程序执行的其余部分的顶级控制结构。
0x02 区别
二者在同一环境下的不同区别:
Windows支持两种类型的应用程序:
一种是基于控制台用户界面的应用程序(Console User Interface,简称CUI),
另一种是基于图形用户界面的程序(Graphic User Interface,简称GUI)。
0x03 函数详解
3.1 main函数
在 Microsoft C 中,程序启动时调用的函数称为 main。 没有针对 main 声明的原型,可以用零个、两个或三个参数对其进行定义:
int main( void )
int main( int argc, char *argv[] )
int main( int argc, char *argv[], char *envp[] )
其中三个参数的解释如下:
argc 表示有多少个命令行参数,第一个就是执行程序名,所以argc最少为1。
argv 是具体程序运行的地址与相搭配的参数。
envp 是系统的环境变量,很少有介绍的。“名称=值”的形式,以NULL结束。
3.2 winmain函数
WinMain是用于应用程序入口点的常规名称,他的句法如下:
int __clrcall WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nShowCmd
);
其中四个参数的解释如下:
hInstance 类型:HINSTANCE,应用程序当前实例的句柄,也就是他所处的基地址。
hPrevInstance 类型:HINSTANCE,应用程序先前实例的句柄。此参数始终为NULL。
lpCmdLine 类型:LPSTR,应用程序的命令行,不包括程序名称。
nShowCmd 待定
0x04 实验查找
这里用两个程序分别进行查询main函数与winmain函数:
-
Fport.exe --> main函数
-
360zipInst.exe --> winmain函数
单独用IDA与OD进行查找:不过还是IDA方便理解一些
4.1 IDA查找main函数
ida打开之后,直接找到start:
反汇编查看伪代码:这里可以看到它是以v7返回值退出的,着重看v7,大部分程序都是以主函数退出:
这里的sub_407D10函数其实就是main函数:
v7 = sub_407D10(dword_41BFC8, dword_41BFCC);
双击步入函数,里面存在相应的命令行显示字符串:
整个的函数逻辑也会存在其中,我们再拿OD调试一下
4.2 OD查找main函数
F8单步运行到这里,观察OD的栈面板窗口,程序运行到 main 函数时,经常会伴随着栈的活动:
这三个数值,其实就是上面所介绍到的main函数的3个参数,来一一对应一下:
Arg1 对应的就是 argc ,表示有多少个命令行参数,第一个就是执行程序名,所以argc最少为1 这里的数值也是 = 00000001
选中 Arg2 ,追踪一下数据:
可以看到数据面板窗口其中就存在着一个物理地址,而这个地址正好对应了 main函数中的 argv ,由于我们运行时,没有搭配任何参数,所以参数为空。
同样的方法,再来看一下Arg3,这里对应的就是相应的系统环境了:
三个参数的数值都可以对应上main函数的参数,那么这里的 Fport.00407D10 也就是整个程序的主入口了
为了进一步证明,F2下个断点,F9运行到断点处,F7进到函数里面:
相应的命令行字符串直接在旁边显示了出来,找对地方了!
4.3 IDA查找winmain函数
这里的操作与上面打开IDA的操作是类似的,直接找到start,进入其中,开启反汇编,查看伪代码:
这里同样是着重看一下,程序是以哪个变量退出的:
很轻松就可以看到这里的变量是v5,后面紧跟着的就是winmain函数,进到函数里面看一下是不是主函数:
很明显,这里的参数值,就是我们先前介绍的 winmain 的句法参数!
4.4 OD查找winmain函数
这里与上面查找main函数的方法亦是同理,F8单步运行到这里,观察OD的栈面板窗口,程序运行到 main 函数时,经常会伴随着栈的活动:
可以看到,在临近exit时,上面出现了四个参数,其实这里的 Arg1 是上面所介绍的 hInstance ,应用程序当前实例的句柄,也就是他所处的基地址,在OD里面可以用 ALT+E
查看程序的基地址:
这里的基地址正好 = 00400000
再来看 Arg2 = 0 ,也对应了上面介绍的始终为 NULL 的 hPrevInstance
同理追踪 Arg3 :
找到第三个参数:lpCmdLine 应用程序的命令行格式:
第四个参数无实意,为了证明此函数为 winmain函数,同样的方法进到函数里面,查看其内容:
成功进入到程序主函数内!
0x05 参考链接
https://www.cnblogs.com/shucome/p/7572142.html
https://blog.csdn.net/m0_37925202/article/details/78944329
https://docs.microsoft.com/zh-cn/windows/win32/api/winbase/nf-winbase-winmain
https://docs.microsoft.com/zh-cn/cpp/c-language/arguments-to-main?view=vs-2019