VS2013编译以下代码:
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
int test = 1;
test = 2;
return 0;
}
使用CFF Explorer或者PEID打开编译后生成的exe文件,查看imageBase和OEP,如下所示:
OEP: 0x00011073
ImageBase:0x00400000
所以该程序的启动点在:0x00411073
该程序的区段信息如下所示:
将其载入OD,其载入地址并非00400000,所以载入OD后,alt+e,先看看主模块的载入地址:
一般进程运行时都是先载入该exe文件,所以exe总能以其imagebase载入,但这里不知这个是为什么,这里可以不用管这个,直接以其载入地址0x00390000进行后面的分析,但是鄙人有强迫症,总想搞清楚这是为什么,所以先来看下为什么不能以imagebase载入。
联想到安全机制ASLR,难道是这个原因?多次载入,果然发现多次载入时主模块的载入地址每次都不同。。。
隐约记得VC6.0是不支持ASLR的,所以下面用VC6.0重新编译以下代码
#include "stdafx.h"
int main(int argc, char* argv[])
{
printf("Hello World!
");
return 0;
}
果然,每次载入地址都是00400000,那么VS2013的ASLR是不是也可以关闭呢?记得好像哪里有说过可以关闭,找一找。。。
强大的网友:
---------------------------------------------------------------------------------------------------
通过加载程序时候不再使用固定的基址加载,从而干扰shellcode定位的一种保护机制
windows Vista出现后,ASLR 才真正开始发挥作用
VS2005 添加 /dynmicbase 就可以支持 ASLR
VS2008 linker->Randomized Base Address 设置
包含 映像随机化,堆栈随机化,PEB+TEB随机化
---------------------------------------------------------------------------------------------------
果然,将其关闭,再次测试
备注:关闭后,少了一个区段reloc,这个此处不分析为什么
搞定,此处只是为了便于多次分析,所以关闭ASLR,正常情况,为了安全还是要将ASLR打开的
下面从基地址(0x00400000)开始分析,如下所示:
我们知道PE文件在内库中是以1000h(一页)对其,所以先看该页:
。。。。。。
。。。。。。
end
这部分数据全是PE文件的结构数据,各种头部,区段数据等,和本主题没关系,继续向下第二页:
0x00401000,PEID和OD都可以知道这部分数据是.textbbs,
0x00401000---0x00402000这一页内存,结果全是0,《程序员的自我修养》中说“.bbs”段存放的是为初始化的全局变量和局部静态变量,这里没有用到未初始化的全局变量和局部静态变量,所以全是0.
下一个区段:.text(0x00411000)
段在OEP,看OEP的代码
这个是OD自动断下来的第一条指令,查看堆栈:
去0x75DF3388看看
在此处3388处下F2断点,Ctrl+F2重启进程,在此处断下来,发现堆栈中没有任何信息,继续向前走,我们在0x75DF3378和0x75DF337A处下断点,重启进程
先看从3385~338B
push 主线程参数;
call 主线程函数;(通过OEP做了一次跳板)
push 主线程返回值;
call ntdll.RtlExitUserThread
其源代码应是:RtlExitUserThread((OEP[jmp到主线程入口main])(参数));
通过Windbg调试发现jnz kernel32.75DF6202处其实是:kernel32!RegSetValueExW+0x655 (75df6202),其功能自行百度,此处具体做了什么先不分析了。。。好复杂。。。
此时的堆栈信息如下:
此时第一条堆栈信息是当前函数的名字:即0x75DF3378是kernel32.BaseThreadInitThunk函数
在这四条堆栈信息中函数过程处下断点,Crtl+F2
然而结果如下:
前面的位置断不下来,所以从最后一条堆栈信息查看,结果如下:
原来此处是ntdll的入口,应该是类似于dllmain的,继续看第二条堆栈信息:
表示不认识,第一条:
依旧不认识。
但是调试发现0x75DF3378会被调用两次,第一次其实并未走到OEP,第二次才会call oep,所以此处再调试一次,等第二次调用0x75DF3378时,堆栈信息如下:
我们去0x77E49F40处看看:
初始化了异常处理链SEH,最终调用BaseThreadInitThunk,但是谁调用了0x77E49F2A?堆栈是空的?jmp?
上IDA?
结果发现ntdll不是以imagebase载入的,所以需要换算:77E49F2A-77E10000=39F2A
ntdll的imagebase:
所以7DE70000+39F2A=7DEA9F2A就是IDA中77E49F2A的位置。
结果如下:
所以,创建主线程的入口应该是ntdll!RtlUserThreadStart,至于其具体做了什么,可以在IDA中详细查看,
这里再不分析了。
感觉好像丢了什么,算了,就这样吧,睡了。
另附:Windows核心编程(第5版)153页的以下代码:
Void RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam)
{
__try{
ExitThread((pfnStartAddr)(pvParam));
}
__except(UnhandledExceptionFilter(GetExceptionInformation())){
ExitProcess(GetExceptionCode());
//NOTE: We Never get here.
}
}