Click here to Skip to main content
15,881,852 members
Articles / Programming Languages / MSIL

ILRewriting for beginners

Rate me:
Please Sign up or sign in to vote.
4.56/5 (15 votes)
27 Sep 2012Ms-PL11 min read 62K   1.4K   46  
Runtime IL-Rewriting can be used to add behavior such as logging to applications, or redirect calls from one API to another. This article and accompanying source code explains how to substitute a method call at runtime.
// ----------------------------------------------------------------------------------------------
// Copyright (c) Mattias H�gstr�m.
// ----------------------------------------------------------------------------------------------
// This source code is subject to terms and conditions of the Microsoft Public License. A 
// copy of the license can be found in the License.html file at the root of this distribution. 
// If you cannot locate the Microsoft Public License, please send an email to 
// dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
// by the terms of the Microsoft Public License.
// ----------------------------------------------------------------------------------------------
// You must not remove this notice, or any other, from this software.
// ----------------------------------------------------------------------------------------------

#include "stdafx.h"
#include <cor.h>
#include "OpCodeParser.h"


ULONG OpCodeParser::ReplaceFunctionCall(BYTE* opCodeBytes, ULONG length, mdMemberRef fromMemberRef, mdMemberRef toMemberRef)
{
   ULONG count = 0;
   ULONG index = 0;
   while (index < length)
   {
      BYTE opCode = opCodeBytes[index];
      if (IsFunctionCall(opCode))
      {
         int addressIndex = index + 1;
         BYTE* address = opCodeBytes + addressIndex;
         mdMemberRef* memberRefAddress = reinterpret_cast<mdMemberRef*>(address);
         mdMemberRef memberRef = *memberRefAddress;

         if (fromMemberRef == memberRef)
         {
            *memberRefAddress = toMemberRef;
            count++;
         }
      }
      ULONG opCodeSize = InstructionSize(opCodeBytes + index);
      index += opCodeSize;
   }
   return count;
}

ULONG OpCodeParser::CountFunctionCalls(const BYTE* opCodeBytes, ULONG length)
{
   ULONG count = 0;
   ULONG index = 0;
   while (index < length)
   {
      BYTE opCode = opCodeBytes[index];
      if (IsFunctionCall(opCode))
         count++;
      ULONG opCodeSize = InstructionSize(opCodeBytes + index);
      index += opCodeSize;
   }
   return count;
}

bool OpCodeParser::IsFunctionCall(BYTE opCode)
{
   switch (opCode)
   {
   case 0x28:
      return true;
   case 0x29:
   default:
      return false;
   }
}

 ULONG OpCodeParser::InstructionSize(const BYTE* opCodeBytes)
 {
    const int tokenSize = sizeof(mdToken);
    const int int8Size = 1;
    const int int32Size = 4;
    const int int64Size = 8;
    const int float32Size = 4;
    const int float64Size = 8;



    BYTE opCode = *opCodeBytes;
    //__debugbreak();
    if ((0x00 <= opCode) && (opCode <= 0x0d))
       return 1;
    if ((0x0e <= opCode) && (opCode <= 0x13))
       return 1 + int8Size;
    if ((0x14 <= opCode) && (opCode <= 0x1e))
       return 1;
    if ((0x2b <= opCode) && (opCode <= 0x37))
       return 1 + int8Size;
    if ((0x38 <= opCode) && (opCode <= 0x44))
       return 1 + int32Size;
    if ((0x46 <= opCode) && (opCode <= 0x6e))
       return 1;
    if ((0x6f <= opCode) && (opCode <= 0x75))
       return 1 + tokenSize;
    if ((0x7b <= opCode) && (opCode <= 0x81))
       return 1 + tokenSize;
    if ((0x82 <= opCode) && (opCode <= 0x8b))
       return 1;
    if ((0x90 <= opCode) && (opCode <= 0xa2))
       return 1;
    if ((0xb3 <= opCode) && (opCode <= 0xba))
       return 1;
    if ((0xd1 <= opCode) && (opCode <= 0xdc))
       return 1;

    switch (opCode)
    {
    case 0x1f:
       return 1 + int8Size;
    case 0x20:
       return 1 + int32Size;
    case 0x21:
       return 1 + int64Size;
    case 0x22:
       return 1 + float32Size;
    case 0x23:
       return 1 + float64Size;
    case 0x25:
    case 0x26:
       return 1;
    case 0x27:
    case 0x28:
    case 0x29:
       return 1 + tokenSize;
    case 0x2a:
       return 1;
    case 0x45:
       {
          BYTE length = opCodeBytes[1];
          return 1 + 1+ length;
       }
    case 0x76:
    case 0x7a:
       return 1;
    case 0x79:
    case 0x8c:
    case 0x8d:
       return 1 + tokenSize;
    case 0x8e:
       return 1;
    case 0x8f:
       return 1 + tokenSize;
    case 0xa3:
    case 0xa4:
    case 0xa5:
       return 1 + tokenSize;
    case 0xc2:
    case 0xc6:
    case 0xd0:
       return 1+ tokenSize;
    case 0xc3:
       return 1;
    case 0xdd:
       return 1 + int32Size;
    case 0xde:
       return 1 + int8Size;
    case 0xdf:
    case 0xe0:
       return 1;
    }

    if (opCode == 0xfe)
    {
       BYTE extOpCode = opCodeBytes[1];

       if ((0x00 <= extOpCode) && (extOpCode <= 0x05))
          return 1;
       if ((0x09 <= extOpCode) && (extOpCode <= 0x0e))
          return 1 + int32Size;
       switch (extOpCode)
       {
       case 0x06:
       case 0x07:
          return 1 + tokenSize;
       case 0x0f:
       case 0x11:
          return 1;
       case 0x12:
          return 1 + int8Size;
       case 0x13:
       case 0x14:
          return 1;
       case 0x15:
       case 0x16:
          return 1 + tokenSize;
       case 0x17:
       case 0x18:
       case 0x1a:
          return 1;
       case 0x1c:
          return 1 + tokenSize;
       case 0x1d:
       case 0x1e:
          return 1;
       }
    }    
    __debugbreak();
    throw "Unrecognized opcode";
 }

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Architect Visma Software AB
Sweden Sweden
Mattias works at Visma, a leading Nordic ERP solution provider. He has good knowledge in C++/.Net development, test tool development, and debugging. His great passion is memory dump analysis. He likes giving talks and courses.

Comments and Discussions