快捷搜索:

.Net内存程序集的DUMP(ProFile篇)

小序

在 DOTNET加解密历程中,我们常常会碰着从内存中转贮.NET法度榜样集的场景。会常常应用那些神奇的DUMP对象,分外是阐发整体加解密保护的法度榜样集时,感到很爽,当然这是由于它的保护很弱。于是我们想懂得和进修若何完成类似的功能。本文将简单地先容Profiling API的一些观点并经由过程它完成相同的事情。

Profiling API简介

.net为了赞助开拓职员进行利用法度榜样的内存、垃圾收受接收、线程、客栈、法度榜样集、类、措施、机能等低层阐发,供给了我们应用Profiling API编写阐发器或代码探查器的机制,它是CLR的一部分。要求探查器必须被编写为COM办事器并实现IcorProfilerCallback2接口[.net2.0情况]或IcorProfilerCallback[.net1.0情况]接口,这个COM办事器将作为被监视进程的一部分运行并在事故发生时接管看护。

那么怎么启动它?平日我们会写一个Loder也可以手动完成,要做以下几点事情:

1.注册你的COM,可以经由过程敕令行:regsvr32 XXX.dll实现。

2.设置情况变量COR_PROFILER为此COM的GUID,奉告.net由它来完成阐发探查事情,可经由过程敕令行:SET COR_PROFILER={xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}完成。留意CLR仅能经由过程此变量装一个阐发器。

3.设置情况变量COR_ENABLE_PROFILING为1,奉告.net启动Profiling功能,可经由过程敕令行:SET COR_ENABLE_PROFILING=1完成。

4.启动要监视的进程。

下面我们来看看IcorProfilerCallback接口中我们关注的几个事故,及要在此中完成的响应事情:

interface ICorProfilerCallback : IUnknown

{

HRESULT Initialize( [in] IUnknown*pICorProfilerInfoUnk);

// 初始化代码探查器

//其它略。。。

HRESULT ModuleLoadFinished([in] ModuleID moduleId,[in] HRESULT hrStatus);

// 模块加载完成时,可履行模块的代码已完备地出现在内存中,此时我们转贮代码

//其它略。。。

}

别的:请把稳.net的版本,并实现响应接口,否则不会如你期望的那样运行。

更多的内容请参考下面几篇文章及MSDN赞助文档:

应用 .NET Profiler API 反省并优化法度榜样的内存应用

.NET Framework 2.0 中,没有任何代码能够回避 Profiling API 的阐发

.NET Framework Profiling API 迅速重写 MSIL 代码

代码实现及阐明

首先,我们必要完成一个基础的COM办事器并实现IcorProfilerCallback2接口,好在这步头疼的事情可以经由过程改动CLR Profiler for the .NET Framework 2.0源文件来完成。代码实现的主要事情如下:

1.该源文件的Profiler.cpp完成了一个进程内COM办事器的所有内容,我们只必要改变一下GUID以免和原com冲突就可以了。

2.去掉落ProfilerCallback.h和ProfilerCallback.cpp文件中不需要的部分以前进运行速率。假如你不感觉它太慢了的话,可以什么都不改。而我让它变成了一个实了现IcorProfilerCallback2接口的空壳。

3.在Initialize措施中设置我们关心的事故掩码,针对源文件现实,则是在由Initialize措施调用的GetEventMask()措施中。我们只关心ASSEMBLY_LOADS和MODULE_LOADS系列事故。以是:

m_dwEventMask = (DWORD) COR_PRF_MONITOR_MODULE_LOADS | (DWORD) COR_PRF_MONITOR_ASSEMBLY_LOADS;

4.在ModuleLoadFinished措施中完成我们的转贮。

5.在原C#编写的loder中(Launcher.exe),增添注册和反注册我们的COM的功能。

关键代码阐明:

转贮的关键是获得模块加载基址,名称则关系不太大年夜,这两者都可以在ModuleLoadFinished措施中经由过程IcorProfilerInfo及IMetaDataImport接口完成。

ICorProfilerInfo::GetModuleInfo

获取有关指定模块的信息。

ICorProfilerInfo::GetModuleMetaData

获取映射到指定模块的元数据接口实例。

IMetaDataImport::GetScopeProps

获取当前元数据范围内的法度榜样集或模块的名称和版本标识符。

更具体的阐明请拜见MSDN赞助文件或Profiler的Doc文档。

详细代码如下:

HRESULT CProfilerCallback::ModuleLoadFinished(ModuleID moduleId, HRESULT hrStatus)

{

HRESULT hr=m_pICorProfilerInfo->GetModuleInfo (

moduleId, (LPCBYTE *)&pBaseLoadAddress,

2048, &size, name,

&assemblyId);

__try {

// let's determine the module name from metadata

hr = m_pICorProfilerInfo->GetModuleMetaData(moduleId, 0, IID_IMetaDataImport, (IUnknown**) &pImport);

if (SUCCEEDED(hr)) {

GUIDmvid;

ULONGnameLen = 0;

hr = pImport->GetScopeProps(moduleName, 2048, &nameLen, &mvid);

}

在获得了模块基址及名称的环境下,要做的便是根据PE布局写文件了,代码流程如下:

1.模块基址指向DOS头,基址e_lfanew指向NT头。FileHeader.NumberOfSections是节数也是节表项的数。NT头布局 1指向节表。节数量知道了,也就知道了节表的尾部地址。好从模块肇端地址不停到此,先写入文件。

2.节表尾到第一个节区开始处添补零。

3.内存中第一个节区的位置起,每次一字节,向文件中写入节数据,每个节数据大年夜小为section->SizeOfRawData

下面为详细代码:

PIMAGE_NT_HEADERS pNTHeader = NULL;

// only dump executable images

__try {

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbImageBase;

if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {

return false;

}

pNTHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeaderpDosHeader->e_lfanew);

if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {

return false;

}

} __except(EXCEPTION_EXECUTE_HANDLER) {

return false;

}

。。。

int numSections = pNTHeader->FileHeader.NumberOfSections;

PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER)(pNTHeader1);

PBYTE pLastSectionEnd = (PBYTE)(sectionnumSections);

。。。

int headerLen = pLastSectionEnd-pbImageBase;

int numwritten = fwrite( pbImageBase, 1, headerLen, stream );

。。。

char zero = 0;

for (int i=headerLen; iPointerToRawData; i) {

fwrite( &zero, 1, 1, stream );

}

。。。

if (isMapped)

{

buf = pbImageBasesection->VirtualAddress;

} else {

buf = pbImageBasesection->PointerToRawData; //第一个节区的指针}

numwritten = fwrite(buf, 1, section->SizeOfRawData, stream);

请拜见随文档供给的代码及项目文件。

运行环境

这里仍然应用上篇文章中《{samartassembly}4.1.39阐发(加解密)》供给的样例代码(包括原始无压缩/{sa}法度榜样集打包/{sa}整体压缩)进行测试。被精简了的COM运行速率令人知足,每个可履行模块精确转贮成功。但转贮完成的exe并不能直接运行,经比较发明NT头中的AddressOfEntryPoint所指向的RVA差错,这对DLL并没有影响。

修正:

比较原履行法度榜样,改动RVA值。

用ILASM/ILDASM对dump出的exe进行从新编译。

结语

使用Profiling API可以完成很多事情,微软的样列、文档及MSDN供给了较富厚的内容。而在.net解密方面这已是一种迂腐的技巧,但并不阴碍我们进修和在需要时应用它。假如有光阴我们会继承Hook mscoree.dll及Hook mscorjit.dll的旅程。文章中所引用的常识、代码、以致文档风格整个进修和滥觞于互联网,在此向所有具有常识共享精神的网友们表示谢意!

(附代码及工程文件)

作者:RZH网名:看雪_grassdrago

转自:http://www.cnblogs.com/northstarlight/archive/2010/05/25/1743268.html

您可能还会对下面的文章感兴趣: