3.3 main函数的识别

有了3.2节的知识作铺垫,识别VS2019正常编译程序的main函数就非常简单了。

识别main函数的原理如同识别一个人。要对一个人进行识别,首先是观察此人外观,找出他身体和面貌上的特征。然后将这些特征与自己认识的人的特征相匹配,从而判断此人身份。那么,C++下的main函数都有哪些特征呢?从代码清单3-1中可以总结出main函数有如下特征。

  • 有3个参数,分别是命令行参数个数、命令行参数信息和环境变量信息,而且main函数是启动函数中唯一具有3个参数的函数。同理,WinMain也是启动函数中唯一具有4个参数的函数。
  • main函数返回后需要调用exit函数,结束程序根据main函数调用的特征,找到入口代码第一次调用exit函数处,离exit最近的且有3个参数的函数通常就是main函数。

x64dbg在加载程序时直接暂停在应用程序的入口处,而不会直接定位到main函数处,需要分析者手动查找定位。通过main函数的特性查找到所在的位置,如代码清单3-4所示。

代码清单3-4 x64dbg反汇编信息

0040135F |  | mov eax,dword ptr ds:[<&__initenv>]
00401364 |  | mov edx,dword ptr ds:[405010]
0040136A |  | mov dword ptr ds:[eax],edx
0040136C |  | mov eax,dword ptr ds:[405010]
00401371 |  | mov dword ptr ss:[esp+8],eax      ;参数3 env入栈
00401375 |  | mov eax,dword ptr ds:[405014]
0040137A |  | mov dword ptr ss:[esp+4],eax      ;参数2 argv入栈
0040137E |  | mov eax,dword ptr ds:[405018]
00401383 |  | mov dword ptr ss:[esp],eax        ;参数1 argc入栈
00401386 |  | call x86_gcc.401510               ;main函数
0040138B |  | mov ecx,dword ptr ds:[405008]
00401391 |  | mov dword ptr ds:[40500C],eax
00401396 |  | test ecx,ecx
00401398 |  | je x86_gcc.40146C
0040139E |  | mov edx,dword ptr ds:[405004]
004013A4 |  | test edx,edx
004013A6 |  | jne x86_gcc.4013B2
004013A8 |  | call <JMP.&_cexit>                ;第一次调用exit函数

识别出代码清单3-4中的exit()函数后,对应前面讨论的main函数特性继续向上寻找。为了准确识别main函数,可以考察传递参数的个数,如果具有3个参数,便是main函数的调用,双击即可进入main函数的实现中。

IDA下的main函数识别更为简便,它会直接分析出main函数所在的位置并显示出来。如果IDA无法识别,可根据前面讨论的main函数特性,利用exit函数定位,如果IDA无法识别出exit函数,就需要加载sig文件重新识别,如图3-2所示。

图3-2 IDA分析查找启动函数