[Reprint] modifying Il at runtime (Step II)

Source: Internet
Author: User

A couple of days back, We tweaked the running il a little bit. Today, let's modify it some more!

We'll insert a method call at the beginning of the body ofMainMethod. The method we'll call is as easy as can be: it is part of the same class, is static and has a void () signature.

The IL used during the modification is still hardcoded. but we'll try to start moving away from that by grouping the metadata, to try and find the method token at runtime. the first step of this is to list all the methods on the current class and print out their names, which we'll see how to do.

You'll need a running copy of The dnprofiler tool to try the Code provided and I recommend that you read my previous blog on the subject if you haven't used the profiler APIs before.

First, let's look at the class we'll play:

Using system;

 

Public class hello
{
Public static void main (string [] PRMS)
{
Console. writeline ("test ");
}

Public static void log (){
Console. writeline ("log! ");
}
}

 

One of the goals is to modifyMainMethod just before it gets jited and insert a callLog. The other goal is to also list all the methods onHelloClass, using the unmanaged Metadata API.

References
The more I look and the more information I find in the "tool developers Guide" that comes with the SDK. it is too bad that it isn't part of msdn and all the documentation files are word.
Here is my PDF copy of\ Frameworksdk \ Tool developers guide \ docs \ metadata unmanaged api.docSDK document: CLR metadata unmanaged API (Tool developers Guide) (1 MB ).
Although I didn't spend much time digging in the samples that come in this Guide, they really seem promising: A profiler, a metadata inspector, a command-line debugger and a couple compilers.

A great set of slides on metadata internals by John Lam. It describes the metadata framework as well as the managed and unmanaged APIs.

Inserting a method call
When you look at a dis-assembled class (with "ildasm/all"), you can see the actual bytes of the Il. You'll notice that contains struinctions (likeLdstrAndCall) Take the form/* XX | (yy) ZZ */. It turns outXxIs the operation and(Yy) ZZIs the operand. from what I understand so far, the operand is always a token that represents a resource (a string, a method ). in a class, all uses of the same resource are referenced using the same token.

By looking atLogMethod in "ildasm/All hello.exe" (see below for a copy of the output), we find the method token that identifies this method. In this case it is06 00 00 02.
As you can probably guess, we'll see that06 00 00 01And06 00 00 03Are also valid method tokens for this class, and they refer to. Ctor(Constructor) andMainMethods.

. Method /X 06000002 */ Public hidebysig static void log () cel managed // Sig: 00 00 01 {// method begins at RVA 0x2068

// Code size 11 (0xb)

. Maxstack 1

Il_0000:/* 72 | (70) 00000b */ldstr "log! "

Il_0005 :/*28| (0a) 000002 */call void [mscorlib/* 23000001 */] system. Console/* 01000003 */: writeline (string)

Il_000a:/* 2a | */RET

} // End of method Hello: Log

 

The first byte (06) means this is a metadata token that represents a method (mdtmethoddef). You'll find the other prefixes in the cortokentype enumeration.

We also notice that28Is the Il codeCall. So what we need to do is insert28 | 06 00 00 02At the beginningMain.
If you modify the code inHello. CSTo callLog, You'll see exactly this sequence in the dis-assembly.

InJitcompilationstartedCode below, you notice how we generate this sequence by usingPackedStructure Containing a byte and a DWORD.
Two things you need to be careful with when you insert il instructions: You need to allocate more space for your il (seeAllocCall) and to updateCodesizeIn the Il method header.

The method call that we insert is pretty simple, as it doesn't require any parameters and doesn't return any value. in a next iterations of this blog, we'll look at more complex method CILS.

Reflection via metadata
In the long run, we want to avoid hardcoding the method token, so we want to have e the available methods at runtime, to figure what method token we shocould use.

Getting an imetadataimport object (Icorprofilerinfo: getmodulemetadataMethod) will enable us to query the metadata for this module.
The documentation for imetadataimport describesEnummethodsAs a way of enumerating all the methods on a class. You can then get details on each method usingGetmethodpropsCall.

The only trick is that the "mdtypedef Cl" parameter onEnummethodsIsn' t very well known ented and wasn' t obvious to me what shoshould be passed in there. it turns out you need to pass in the token for the class you want to look. one way of acquiring this token by callingIcorprofilerinfo: getclassidinfo.

The Code

Hresult cprofilercallback: jitcompilationstarted (uint functionid,
Bool fissafetoblock)
{
Wchar_t wszclass [512];
Wchar_t wszmethod [512];

 

Hresult hR = s_ OK;

Classid = 0;
Moduleid = 0;
Mdtoken tkmethod = 0;
Lpcbyte pmethodheader = NULL;
Ulong imethodsize = 0;

If (getmethodnamefromfunctionid (functionid, wszclass, wszmethod ))
{
Profilerprintf ("jitcompilationstarted: % ls \ n", wszclass, wszmethod );
} Else {
Profilerprintf ("jitcompilationstarted \ n ");
Goto exit;
}
If (wcscmp (wszclass, l "hello ")! = 0 | wcscmp (wszmethod, l "Main ")! = 0 ){
Goto exit;
}

//
// Get the existing Il
//
HR = m_picorprofilerinfo-> getfunctioninfo (functionid, & classid, & moduleid, & tkmethod );
If (failed (HR ))
{Goto exit ;}

HR = m_picorprofilerinfo-> getilfunctionbody (moduleid, tkmethod, & pmethodheader, & imethodsize );
If (failed (HR ))
{Goto exit ;}

//
// Print the existing Il
//
Image_cor_ilmethod * pmethod = (image_cor_ilmethod *) pmethodheader;
Cor_ilmethod_fat * fatimage = (cor_ilmethod_fat *) & pmethod-> fat;

If (! Fatimage-> isfat ()){
Goto exit;
}

Profilerprintf ("\ n ");
Profilerprintf ("Flags: % x \ n", fatimage-> flags );
Profilerprintf ("Size: % x \ n", fatimage-> size );
Profilerprintf ("maxstack: % x \ n", fatimage-> maxstack );
Profilerprintf ("codesize: % x \ n", fatimage-> codesize );
Profilerprintf ("localvarsigtok: % x \ n", fatimage-> localvarsigtok );
Profilerprintil (fatimage-> getcode (), fatimage-> codesize );

 

//
// Get the "log" method token reference
//
Imetadataimport * pmetadataimport = NULL;
HR = m_picorprofilerinfo-> getmodulemetadata (moduleid, ofread, iid_imetadataimport,
(Iunknown **) & pmetadataimport );
If (failed (HR ))
{Goto exit ;}

// Get the typedef token for the class
Mdtoken tkclass = 0;
HR = m_picorprofilerinfo-> getclassidinfo (classid, & moduleid, & tkclass );
If (failed (HR ))
{Goto exit ;}

// Get all methods tokens for the class
Hcorenum ENR = 0; // enumerator
Const int siz = 5; // size of Arrays
Mdtypedef rtoks [siz]; // array to hold returned bodies
Ulong count; // count of tokens returned

HR = pmetadataimport-> enummethods (& ENR, tkclass, rtoks, siz, & COUNT );
If (failed (HR) {goto exit ;}

While (count> 0 ){
For (ulong I = 0; I <count; I ++ ){
Profilerprintf ("Tok: % x \ n", rtoks [I]);

// Get metadata for this method
Mdtypedef mdclasstok;
Wchar_t wszfunctionname [512];
Ulong COUNT = 0;
DWORD dwattr;
Pccor_signature signature;
Ulong signaturelen;
Ulong ulcoderva;
DWORD dwimplflags;
HR = pmetadataimport-> getmethodprops (rtoks [I], & mdclasstok, wszfunctionname, 512, & count,
& Dwattr,
& Signature, & signaturelen, & ulcoderva, & dwimplflags );
If (failed (HR) {goto exit ;}

Fwprintf (m_poutfile, l "function name: % s \ n", wszfunctionname );
}

HR = pmetadataimport-> enummethods (& ENR, tkclass, rtoks, siz, & COUNT );
If (failed (HR) {goto exit ;}
}
Pmetadataimport-> closeenum (ENR );
Pmetadataimport-> release ();

 

//
// Get the Il Allocator
//
Imethodmalloc * pimethodmalloc = NULL;
Image_cor_ilmethod * pnewmethod = NULL;
HR = m_picorprofilerinfo-> getilfunctionbodyallocator (moduleid, & pimethodmalloc );
If (failed (HR ))
{Goto exit ;}

//
// Inserted il code
//
# Include <pshpack1.h>
Struct {
Byte call; DWORD method_token;
} Ilcode;
# Include <poppack. h>

Ilcode. Call = 0x28;
Ilcode. method_token = 0x06000002;

//
// Allocate il space and copy the IL in it
//
Pnewmethod = (image_cor_ilmethod *) pimethodmalloc-> alloc (imethodsize + sizeof (ilcode ));
If (pnewmethod = NULL)
{Goto exit ;}
Cor_ilmethod_fat * newfatimage = (cor_ilmethod_fat *) & pnewmethod-> fat;

//
// Modify Il
//
// Copy the header
Memcpy (byte *) newfatimage, (byte *) fatimage, fatimage-> size * sizeof (DWORD ));

// Add a call to "log"
Memcpy (newfatimage-> getcode (), & ilcode, sizeof (ilcode ));

// Copy the remaining of the Method
Memcpy (newfatimage-> getcode () + sizeof (ilcode ),
Fatimage-> getcode (),
Fatimage-> codesize );

// Update the code size
Newfatimage-> codesize + = sizeof (ilcode );

// Print modified Il
Profilerprintf ("\ n ");
Profilerprintf ("modified flags: % x \ n", newfatimage-> flags );
Profilerprintf ("modified size: % x \ n", newfatimage-> size );
Profilerprintf ("modified maxstack: % x \ n", newfatimage-> maxstack );
Profilerprintf ("modified codesize: % x \ n", newfatimage-> codesize );
Profilerprintf ("modified localvarsigtok: % x \ n", newfatimage-> localvarsigtok );
Profilerprintil (newfatimage-> getcode (), newfatimage-> codesize );

// Push il back in
HR = m_picorprofilerinfo-> setilfunctionbody (moduleid, tkmethod, (lpcbyte) pnewmethod );
If (failed (HR ))
{Goto exit ;}

Pimethodmalloc-> release ();

Exit:
Return hr;
}

Void cprofilercallback: profilerprintil (byte * codebytes, ulong codesize)
{
For (ulong I = 0; I <codesize; I ++ ){
If (codebytes [I]> 0x0f ){
Profilerprintf ("codebytes [% u] = 0x % x; \ n", I, codebytes [I]);
} Else {
Profilerprintf ("codebytes [% u] = 0x0% x; \ n", I, codebytes [I]);
}
}
}

 

If everything worked, calling hello.exe in a console (with the profiler attached) shocould output "log! "Then" test ". Success !!

Here is a copy of the output in dnprofiler. out:

Initialize
Jitcompilationstarted: Hello: Main

 

Flags: 13
Size: 3
Maxstack: 1
Codesize: B
Localvarsigtok: 0
Codebytes [0] = 0x72;
Codebytes [1] = 0x01;
Codebytes [2] = 0x00;
Codebytes [3] = 0x00;
Codebytes [4] = 0x70;
Codebytes [5] = 0x28;
Codebytes [6] = 0x02;
Codebytes [7] = 0x00;
Codebytes [8] = 0x00;
Codebytes [9] = 0x0a;
Codebytes [10] = 0x2a;

Tok: 6000001
Function Name: Main
Tok: 6000002
Function Name: Log
Tok: 6000003
Function Name:. ctor

Modified flags: 13
Modified size: 3
Modified maxstack: 1
Modified codesize:10
Modified localvarsigtok: 0
Codebytes [0] = 0x28;
Codebytes [1] = 0x02;
Codebytes [2] = 0x00;
Codebytes [3] = 0x00;
Codebytes [4] = 0x06;

Codebytes [5] = 0x72;
Codebytes [6] = 0x01;
Codebytes [7] = 0x00;
Codebytes [8] = 0x00;
Codebytes [9] = 0x70;
Codebytes [10] = 0x28;
Codebytes [11] = 0x02;
Codebytes [12] = 0x00;
Codebytes [13] = 0x00;
Codebytes [14] = 0x0a;
Codebytes [15] = 0x2a;
Jitcompilationstarted: Hello: Log
Shutdown

 

You can see the inserted Il as well as the list of three methods available on the class hello.

A little problem
One problem I ran into is that while debugging in vs.net I wocould often get a "no source code is available for this location" warning message and I wouldn't be able to watch variables anymore.
Let me know if you have any idea why this is occuring.
My workaround was to rely on the strings outputted in dnprofiler. out more.

To be continued...
In the next iteration, I'll try to analyze the signatures of the methods that are listed and find the token forLogMethod by recognizing its signature.

Http://blog.monstuff.com/archives/000059.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.