-
-
[原创].NET的EXE启动流程跟踪(流水账)
-
发表于:
2012-12-19 12:13
11115
-
无聊写的东西罢了,流水账一样的玩意,使用的是.NET Framework 4.0。
在OD选项中设置中断于系统断点,因为.NET程序一启动,加载mscoree后,mscoree立马执行CorExeMain,是不返回的,所以.NET的EXE文件的EP是永远不会被执行的,关键的东西全在CorExeMain中,所以我们需要在mscoree的任何代码都没执行的时候断下来。
在模块中找到mscoree,在CorExeMain上F2,然后F9运行。
断了,CorExeMain的第一个call一开始我以为是登记SEH,可转身想想,这完全没必要,应该是一个有用的call,所以跟进去了。
跟进去后发现果然不是等级SEH的,继续看,显然这里面的第一个call也不可能是登记SEH的,跟进去,有一个运行时是否加载了的全局变量,现在是NULL,准备加载运行时。
下面有各种登记SEH的call,略过,一路F7,走到从注册表查询运行时,
在进行各种跑飞后,第一个访问的注册表是:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\Policy\,打开后调用RegQueryInfoKeyW,然后是RegEnumKeyExW,然后调用一个call,传入当前exe的版本,如“v4.0”,不断Enum是否当前已经有安装这个版本(就是注册表已经登记),找到这个Key的话,调用RegOpenKeyExW,取得里面的子版本路径,如“30319-30319”,也就是“v4.0.30319”,获取这个完全版本后,调用一个call,传入“mscoreei.dll”和“v4.0.30319”,call内会打开“HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework”,取得字符串值“InstallRoot”,这个一般指向“C:\Windows\Microsoft.NET\Framework\”,然后“C:\Windows\Microsoft.NET\Framework\”+“v4.0.30319”,生成这个目录下面有一个mscoreei,会被载入,这个就是当前.NET运行时的核心库,mscoree到现在任务完成了,保存mscoreei的hModule,调用mscoree的函数的时候,GetProcAddress然后jmp到mscoreei就行了。
上面扯飞了,载入mscoreei后,首先GetProcAddress“RegisterShimImplCallback”和“RegisterShimImplCleanupCallback”这2个东西,然后是“SetShellShimInstance”,如果3个有一个取得失败,FreeLibrary,gameover。
再取得“OnShimDllMainCalled”,然后调用RegisterShimImplCallback,2个参数,2个都是callback,然后会GetProcAddress“_CorExeMain_RetAddr”、“_CorExeMain”、最终调用mscoreei的“_CorExeMain”,无返回。
mscoreei的ExeMain会进行这样一个调用:
/*6A82F576*/ PUSH EAX
/*6A82F577*/ INC EDX
/*6A82F578*/ MOV ECX,mscoreei.6A8264D0
/*6A82F57D*/ MOV DWORD PTR SS:[ESP+14],EDI
/*6A82F581*/ CALL mscoreei.6A82F412
/*6A82F586*/ MOV ESI,EAX
/*6A82F588*/ TEST ESI,ESI
/*6A82F58A*/ JS SHORT mscoreei.6A82F5A5
/*6A82F58C*/ CMP DWORD PTR SS:[ESP+10],EDI
/*6A82F590*/ JE SHORT mscoreei.6A82F5A5
/*6A82F592*/ CALL mscoreei.6A8257E8
/*6A82F597*/ TEST AL,AL
/*6A82F599*/ JNZ mscoreei.6A830642
/*6A82F59F*/ CALL DWORD PTR SS:[ESP+10] -> clr._CorExeMain
/*6A82F5A3*/ MOV ESI,EAX
/*6A82F5A5*/ PUSH ESI
/*6A82F5A6*/ CALL DWORD PTR DS:[<&KERNEL32.ExitProcess>]
/*6A82F5AC*/ INT3
6A82F412这个call会调用mscoreei.CreateInterface,传入2个GUID,返回一个ppv,调用ppv中的2个interface加载clr.dll等等东西后,就Release了。
然后就到CALL DWORD PTR SS:[ESP+10]这个clr的ExeMain了。
clr那货的ExeMain是:
/*651A414E*/ PUSH 14
/*651A4150*/ PUSH clr.651A4180
/*651A4155*/ CALL clr.65051050
/*651A415A*/ XOR EAX,EAX
/*651A415C*/ MOV DWORD PTR SS:[EBP-20],EAX
/*651A415F*/ MOV DWORD PTR SS:[EBP-1C],EAX
/*651A4162*/ MOV DWORD PTR SS:[EBP-4],EAX
/*651A4165*/ CALL clr.651A1ED8
/*651A416A*/ MOV DWORD PTR SS:[EBP-4],-2
/*651A4171*/ XOR EAX,EAX
/*651A4173*/ CALL clr.65051095
/*651A4178*/ RETN
显然忽略2个SEH东东,就是clr.651A1ED8这个玩意了。
跟进去,上截图吧,相信大家不陌生:
下面有一段这样的代码:
/*651A1F67*/ MOV BYTE PTR SS:[EBP-4],4
/*651A1F6B*/ PUSH 0
/*651A1F6D*/ CALL DWORD PTR DS:[<&KERNEL32.GetModuleHandleW>]
/*651A1F73*/ MOV ECX,EAX
/*651A1F75*/ CALL clr.651A1E1E
那个call就是我们.NET程序的真正EP了,程序结束之前这个call都不会返回。返回了就进行一些扫尾工作,进程end~
进去后,是这样的:
/*651A1E1E*/ PUSH 34
/*651A1E20*/ PUSH clr.651A1EB0
/*651A1E25*/ CALL clr.65051050
/*651A1E2A*/ MOV EDI,ECX
/*651A1E2C*/ TEST EDI,EDI
/*651A1E2E*/ JE clr.6522C364
/*651A1E34*/ PUSH ECX
/*651A1E35*/ PUSH ECX
/*651A1E36*/ MOV ECX,clr.651A1EA0
/*651A1E3B*/ CALL clr.65060F11
/*651A1E40*/ MOV DWORD PTR SS:[EBP-28],EDI
/*651A1E43*/ AND DWORD PTR SS:[EBP-20],0
/*651A1E47*/ AND DWORD PTR SS:[EBP-4],0
/*651A1E4B*/ CALL clr.65051CB8
/*651A1E50*/ PUSH EAX
/*651A1E51*/ LEA ECX,DWORD PTR SS:[EBP-44]
/*651A1E54*/ CALL clr.6505116A
/*651A1E59*/ MOV DWORD PTR SS:[EBP-4],1
/*651A1E60*/ MOV ECX,EDI
/*651A1E62*/ CALL clr.651A183B
/*651A1E67*/ AND DWORD PTR SS:[EBP-4],0
/*651A1E6B*/ CALL clr.651C4EE1
/*651A1E70*/ MOV DWORD PTR SS:[EBP-4],-2
/*651A1E77*/ PUSH ECX
/*651A1E78*/ PUSH ECX
/*651A1E79*/ MOV ECX,clr.651A1E90
/*651A1E7E*/ CALL clr.65060F11
/*651A1E83*/ XOR EAX,EAX
/*651A1E85*/ INC EAX
/*651A1E86*/ CALL clr.65051095
/*651A1E8B*/ RETN
关键的call是CALL 651A183B,其他的都是SEH和扫屁股的,跟进去。
F7,好长的call...算了,闭着眼睛Ctrl+F8。。。
停在这里:
PUSH 0
CALL clr.65117550
进去
PUSH EBX
LEA EAX,DWORD PTR SS:[ESP+18]
PUSH EAX
MOV ECX,EDI
CALL clr.65117DDE
跑了那么多,累死了。。。
其实跑到65117DDE的时候,整个进程还是“干净”的,也就是.NET的模块都没载入,仅仅载入了当前的mscoreei和资源ui的dll而已。。。
继续Ctrl+F8。。。
PUSH EAX
LEA ECX,DWORD PTR SS:[EBP-110]
CALL clr.65062DA1
跑到65062DA1的时候我在stack的参数里面发现了老子编译的工程Release文件夹路径。。。狗血。。。
65062DA1这个call上上下下执行了N次,加载了程序使用的.NET的dll文件,然后进入JIT执行,没管了,想的跟可以下断clr.CreateApplicationContext。就这样了,下午还得上课撸撸睡了。
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!