/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if field to load is a floating value or the field to load /// is not of size 4 or 8 bytes. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); aScannerState.CurrentStackFrame.Stack.Pop(); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException // 2. Load array length string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); //2. Load array length // - Pop array ref result.AppendLine("pop dword ecx"); // - Load length from array ref GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [ecx+{0}]", lengthOffset)); // - Push array length result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Get the ID (i.e. ASM label) of the method to load a pointer to string methodID = aScannerState.GetMethodID(anILOpInfo.MethodToCall); //If we want to load the pointer at a specified IL op number: if(anILOpInfo.LoadAtILOffset != int.MaxValue) { //Append the IL sub-label to the ID methodID += ".IL_" + anILOpInfo.LoadAtILOffset + "_0"; //Note: This is used by try/catch/finally blocks for pushing pointers // to catch/finally handlers and filters } //Push the pointer to the function result.AppendLine(string.Format("push dword {0}", methodID)); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if divide operands are floating point numbers or if attempting to divide 64-bit numbers. /// </exception> /// <exception cref="System.InvalidOperationException"> /// Thrown if either operand is < 4 bytes long. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); StackItem testItem = aScannerState.CurrentStackFrame.Stack.Pop(); if (testItem.isFloat) { //TODO - Support floats throw new NotSupportedException("Switch for floats no supported!"); } else if (testItem.sizeOnStackInBytes != 4) { //TODO - Support other sizes throw new NotSupportedException("Switch for non-int32s not supported!"); } result.AppendLine("pop dword eax"); for (int i = 0; i < anILOpInfo.ValueBytes.Length / 4; i++) { int branchPos = anILOpInfo.Position + 4 + Utils.ReadInt32(anILOpInfo.ValueBytes, i * 4); branchPos += anILOpInfo.ValueBytes.Length; branchPos += 1; result.AppendLine("cmp eax, " + i); string jumpToLabel = string.Format("{0}.IL_{1}_0", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), branchPos); result.AppendLine("je " + jumpToLabel); } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); StackItem theItem = aScannerState.CurrentStackFrame.Stack.Pop(); if (theItem.isNewGCObject) { //Decrement ref count //Get the ID of method to call as it will be labelled in the output ASM. string methodID = aScannerState.GetMethodID(aScannerState.DecrementRefCountMethod); //Append the actual call result.AppendLine(string.Format("call {0}", methodID)); } result.AppendLine(string.Format("add esp, {0}", theItem.sizeOnStackInBytes)); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if any argument or the return value is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); MethodBase methodToCall = anILOpInfo.MethodToCall; //The method to call is a method base //A method base can be either a method info i.e. a normal method //or a constructor method. The two types are treated separately. if(methodToCall is MethodInfo) { //Need to do callvirt related stuff to load address of method to call // - Check for invoke of a delegate - if so, treat rather differently from normal callvirt string call_Label = string.Format("{0}.IL_{1}_Call", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); if (typeof(Delegate).IsAssignableFrom(((MethodInfo)methodToCall).DeclaringType)) { //Callvirt to delegate method // - We only support calls to Invoke at the moment if (methodToCall.Name != "Invoke") { throw new NotSupportedException("Callvirt to Delegate method not supported! Method name: " + methodToCall.Name); } int bytesForAllParams = ((MethodInfo)methodToCall).GetParameters().Select(x => Utils.GetNumBytesForType(x.ParameterType)).Sum(); // - Move into eax address of function to call from stack - delegate reference is function pointer //All the parameters for the method that was called List<Type> allParams = ((MethodInfo)methodToCall).GetParameters().Select(x => x.ParameterType).ToList(); int bytesForParams = allParams.Select(x => Utils.GetNumBytesForType(x)).Sum(); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", bytesForParams, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword eax, [esp+{0}]", bytesForParams)); //Allocate space on the stack for the return value as necessary Type retType = ((MethodInfo)methodToCall).ReturnType; StackItem returnItem = new StackItem() { isFloat = Utils.IsFloat(retType), sizeOnStackInBytes = Utils.GetNumBytesForType(retType), isGCManaged = Utils.IsGCManaged(retType) }; //We do not push the return value onto the stack unless it has size > 0 //We do not push the return value onto our stack at this point - it is pushed after the call is done if (returnItem.sizeOnStackInBytes != 0) { if (returnItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Cannot handle float return values!"); } else if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword 0"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); } else { throw new NotSupportedException("Invalid return stack operand size!"); } } //Append the actual call result.AppendLine("call eax"); //After a call, we need to remove the return value and parameters from the stack //This is most easily done by just adding the total number of bytes for params and //return value to the stack pointer (ESP register). //Stores the number of bytes to add // - Initially at least 4 for the delegate (method) ref/pointer int bytesToAdd = 4; //Go through all params that must be removed foreach (Type aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam); } //If there is a return value on the stack if (returnItem.sizeOnStackInBytes != 0) { //We need to store the return value then pop all the params //We now push the return value onto our stack as, //after all is said and done below, it will be the //top item on the stack aScannerState.CurrentStackFrame.Stack.Push(returnItem); //SUPPORT - floats (with above) //Pop the return value into the eax register //We will push it back on after params are skipped over. if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("pop dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); } } //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); //If necessary, push the return value onto the stack. if (returnItem.sizeOnStackInBytes != 0) { //SUPPORT - floats (with above) //The return value was stored in eax //So push it back onto the stack if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); } } } else { //Normal callvirt // - Get object ref from loaded args // - Check object ref not null // - Get type table entry from object ref // - Get method table from type table entry // - Scan method table for the method we want // - If found, load method address // - Else, check for parent type method table // - If no parent type method table, throw exception // - Else, scan parent type method table string methodIDValueWanted = aScannerState.GetMethodIDValue((MethodInfo)methodToCall); string loopTableEntries_Label = string.Format("{0}.IL_{1}_LoopMethodTable", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notEqual_Label = string.Format("{0}.IL_{1}_NotEqual", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string endOfTable_Label = string.Format("{0}.IL_{1}_EndOfTable", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notFound_Label = string.Format("{0}.IL_{1}_NotFound", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notNull_Label = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type declaringDBType = DebugDatabase.GetType(aScannerState.GetTypeID(methodToCall.DeclaringType)); //Get object ref int bytesForAllParams = ((MethodInfo)methodToCall).GetParameters().Select(x => Utils.GetNumBytesForType(x.ParameterType)).Sum(); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", bytesForAllParams, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword eax, [esp+{0}]", bytesForAllParams)); //Check object ref result.AppendLine("cmp eax, 0"); result.AppendLine(string.Format("jnz {0}", notNull_Label)); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); result.AppendLine(notNull_Label + ":"); //Get type ref int typeOffset = aScannerState.GetFieldOffset(declaringDBType, "_Type"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", typeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", typeOffset)); //Get method table ref int methodTablePtrOffset = aScannerState.GetTypeFieldOffset("MethodTablePtr"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", methodTablePtrOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", methodTablePtrOffset)); //Loop through entries result.AppendLine(loopTableEntries_Label + ":"); //Load ID Val for current entry GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [eax]"); //Compare to wanted ID value result.AppendLine("cmp ebx, " + methodIDValueWanted); //If equal, load method address into eax result.AppendLine("jne " + notEqual_Label); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [eax+4]"); result.AppendLine("jmp " + call_Label); result.AppendLine(notEqual_Label + ":"); //Else, compare to 0 to check for end of table result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + endOfTable_Label); //Not 0? Move to next entry then loop again result.AppendLine("add eax, 8"); result.AppendLine("jmp " + loopTableEntries_Label); result.AppendLine(endOfTable_Label + ":"); //Compare address value to 0 //If not zero, there is a parent method table to check GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [eax+4]"); result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + notFound_Label); //Load parent method table and loop result.AppendLine("mov eax, ebx"); result.AppendLine("jmp " + loopTableEntries_Label); result.AppendLine(notFound_Label + ":"); //Throw exception! result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(call_Label + ":"); //Allocate space on the stack for the return value as necessary Type retType = ((MethodInfo)methodToCall).ReturnType; StackItem returnItem = new StackItem() { isFloat = Utils.IsFloat(retType), sizeOnStackInBytes = Utils.GetNumBytesForType(retType), isGCManaged = Utils.IsGCManaged(retType) }; //We do not push the return value onto the stack unless it has size > 0 //We do not push the return value onto our stack at this point - it is pushed after the call is done if (returnItem.sizeOnStackInBytes != 0) { if (returnItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Cannot handle float return values!"); } else if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword 0"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); } else { throw new NotSupportedException("Invalid return stack operand size!"); } } //Append the actual call result.AppendLine("call eax"); //After a call, we need to remove the return value and parameters from the stack //This is most easily done by just adding the total number of bytes for params and //return value to the stack pointer (ESP register). //Stores the number of bytes to add int bytesToAdd = 0; //All the parameters for the method that was called List<Type> allParams = ((MethodInfo)methodToCall).GetParameters().Select(x => x.ParameterType).ToList(); //Go through each one if (!methodToCall.IsStatic) { allParams.Insert(0, methodToCall.DeclaringType); } foreach (Type aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam); } //If the number of bytes to add to skip over params is > 0 if (bytesToAdd > 0) { //If there is a return value on the stack if (returnItem.sizeOnStackInBytes != 0) { //We need to store the return value then pop all the params //We now push the return value onto our stack as, //after all is said and done below, it will be the //top item on the stack aScannerState.CurrentStackFrame.Stack.Push(returnItem); //SUPPORT - floats (with above) //Pop the return value into the eax register //We will push it back on after params are skipped over. if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("pop dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); } } //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); //If necessary, push the return value onto the stack. if (returnItem.sizeOnStackInBytes != 0) { //SUPPORT - floats (with above) //The return value was stored in eax //So push it back onto the stack if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); } } } //No params to skip over but we might still need to store return value else if (returnItem.sizeOnStackInBytes != 0) { //The return value will be the top item on the stack. //So all we need to do is push the return item onto our stack. aScannerState.CurrentStackFrame.Stack.Push(returnItem); } } } else if(methodToCall is ConstructorInfo) { throw new NotSupportedException("How the hell are we getting callvirts to constructor methods?!"); } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if constant is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); Type elementType = null; bool pushValue = true; int sizeToPush = 4; bool signExtend = true; bool isFloat = false; switch ((OpCodes)anILOpInfo.opCode.Value) { case OpCodes.Ldelem: { signExtend = false; //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Ldelema: { signExtend = false; //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Ldelem_R4: case OpCodes.Ldelem_R8: //TODO - Add more LdElem op variants support throw new NotSupportedException("Ldelem op variant not supported yet!"); case OpCodes.Ldelem_I1: sizeToPush = 1; elementType = typeof(sbyte); break; case OpCodes.Ldelem_I2: sizeToPush = 2; elementType = typeof(Int16); break; case OpCodes.Ldelem_U1: sizeToPush = 1; signExtend = false; elementType = typeof(byte); break; case OpCodes.Ldelem_U2: sizeToPush = 2; signExtend = false; elementType = typeof(UInt16); break; case OpCodes.Ldelem_Ref: signExtend = false; elementType = null; break; case OpCodes.Ldelem_U4: signExtend = false; elementType = typeof(UInt32); break; case OpCodes.Ldelem_I4: elementType = typeof(Int32); break; case OpCodes.Ldelem_I8: elementType = typeof(Int64); break; } if (isFloat) { //TODO - Support floats throw new NotSupportedException("LdElem for floats not supported yet!"); } //Get element from array and push the value onto the stack // (or for LdElemA push the address of the value) //This involves: // 1. Check array reference is not null // - If it is, throw NullReferenceException // 2. Check array element type is correct // - If not, throw ArrayTypeMismatchException // 3. Check index to get is > -1 and < array length // - If not, throw IndexOutOfRangeException // 4. Calculate address of element // 5. Push the element onto the stack //Stack setup upon entering this op: (top-most downwards) // 0. Index of element to get as Int32 (dword) // 1. Array object reference as address (dword) string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_Load_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp+4]"); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); // 2. Check array element type is correct // 2.1. Move element type ref into eax // 2.2. Move element type ref from array object into ebx // 2.3. Compare eax to ebx // 2.4. If the same, jump to continue execution further down // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //string ContinueExecutionLabel2 = ContinueExecutionLabelBase + "2"; //// 2.1. Move element type ref into eax int elemTypeOffset = aScannerState.GetFieldOffset(arrayDBType, "elemType"); //if (elementType != null) //{ // result.AppendLine(string.Format("mov eax, {0}", aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)))); // // 2.2. Move element type ref from array object into ebx // // - Calculate the offset of the field from the start of the array object // // - Move array ref into ebx //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", 4); // result.AppendLine("mov ebx, [esp+4]"); // // - Move elemType ref ([ebx+offset]) into ebx // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "ebx", elemTypeOffset); // result.AppendLine(string.Format("mov ebx, [ebx+{0}]", elemTypeOffset)); // // 2.3. Compare eax to ebx // result.AppendLine("cmp eax, ebx"); // // 2.4. If the same, jump to continue execution further down // result.AppendLine("je " + ContinueExecutionLabel2); // // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException // result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowArrayTypeMismatchExceptionMethod))); // result.AppendLine(ContinueExecutionLabel2 + ":"); //} // 3. Check index to get is > -1 and < array length // 3.1. Move index into eax // 3.2. Move array length into ebx // 3.2. Compare eax to 0 // 3.3. Jump if greater than to next test condition (3.5) // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException // 3.5. Compare eax to ebx // 3.6. Jump if less than to continue execution further down // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException string ContinueExecutionLabel3_1 = ContinueExecutionLabelBase + "3_1"; string ContinueExecutionLabel3_2 = ContinueExecutionLabelBase + "3_2"; // 3.1. Move index into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 3.2. Move array length into ecx // - Calculate the offset of the field from the start of the array object int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // - Move array ref into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [esp+4]"); // - Move length value ([ebx+offset]) into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [ebx+{0}]", lengthOffset)); // 3.2. Compare eax to 0 result.AppendLine("cmp eax, 0"); // 3.3. Jump if greater than to next test condition (3.5) result.AppendLine("jge " + ContinueExecutionLabel3_1); // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_1 + ":"); // 3.5. Compare eax to ebx result.AppendLine("cmp eax, ebx"); // 3.6. Jump if less than to continue execution further down result.AppendLine("jl " + ContinueExecutionLabel3_2); // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_2 + ":"); // 4. Calculate address of element // 4.1. Pop index into ebx // 4.2. Pop array ref into eax // 4.3. Move element type ref (from array ref) into eax // 4.4. Move IsValueType (from element ref type) into ecx // 4.5. If IsValueType, continue to 4.6., else goto 4.8. // 4.6. Move Size (from element type ref) into eax // 4.7. Skip over 4.8. // 4.8. Move StackSize (from element type ref) into eax // 4.9. Mulitply eax by ebx (index by element size) // 4.10. Move array ref into ebx // 4.11. Add enough to go past Kernel.FOS_System.Array fields // 4.12. Add eax and ebx (array ref + fields + (index * element size)) string ContinueExecutionLabel4_1 = ContinueExecutionLabelBase + "4_1"; string ContinueExecutionLabel4_2 = ContinueExecutionLabelBase + "4_2"; // 4.1. Pop index into ebx result.AppendLine("pop ebx"); // 4.2. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 4.3. Move element type ref (from array ref) into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", elemTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", elemTypeOffset)); // 4.4. Move IsValueType (from element ref type) into ecx int isValueTypeOffset = aScannerState.GetTypeFieldOffset("IsValueType"); result.AppendLine("mov ecx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", isValueTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte cl, [eax+{0}]", isValueTypeOffset)); // 4.5. If IsValueType, continue to 4.6., else goto 4.8. result.AppendLine("cmp ecx, 0"); result.AppendLine("jz " + ContinueExecutionLabel4_1); // 4.6. Move Size (from element type ref) into eax int sizeOffset = aScannerState.GetTypeFieldOffset("Size"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", sizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", sizeOffset)); // 4.7. Skip over 4.8. result.AppendLine("jmp " + ContinueExecutionLabel4_2); // 4.8. Move StackSize (from element type ref) into eax result.AppendLine(ContinueExecutionLabel4_1 + ":"); int stackSizeOffset = aScannerState.GetTypeFieldOffset("StackSize"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", stackSizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", stackSizeOffset)); // 4.9. Mulitply eax by ebx (index by element size) result.AppendLine(ContinueExecutionLabel4_2 + ":"); result.AppendLine("mul ebx"); // 4.10. Pop array ref into ebx result.AppendLine("pop ebx"); // 4.11. Add enough to go past Kernel.FOS_System.Array fields int allFieldsOffset = 0; #region Offset calculation { //Get the child links of the type (i.e. the fields of the type) List<DB_ComplexTypeLink> allChildLinks = arrayDBType.ChildTypes.ToList(); //Calculate the offset //We use StackBytesSize since fields that are reference types are only stored as a pointer allFieldsOffset = allChildLinks.Sum(x => x.ChildType.IsValueType ? x.ChildType.BytesSize : x.ChildType.StackBytesSize); } #endregion result.AppendLine(string.Format("add ebx, {0}", allFieldsOffset)); // 4.12. Add eax and ebx (array ref + fields + (index * element size)) result.AppendLine("add eax, ebx"); // 5. Push the element onto the stack // 5.1. Push value at [eax] (except for LdElemA op in which case just push address) if (pushValue) { switch (sizeToPush) { case 1: result.AppendLine("mov dword ebx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov byte bl, [eax]"); if (signExtend) { throw new NotSupportedException("Sign extend byte to 4 bytes in LdElem not supported!"); } break; case 2: result.AppendLine("mov dword ebx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word bx, [eax]"); if (signExtend) { result.AppendLine("cwde"); } break; case 4: GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword ebx, [eax]"); break; case 8: GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word ebx, [eax]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word ecx, [eax+4]"); break; } if (sizeToPush == 8) { result.AppendLine("push ecx"); } result.AppendLine("push ebx"); } else { result.AppendLine("push eax"); } // 5.2. Pop index and array ref from our stack aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); // 5.3. Push element onto our stack aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { sizeOnStackInBytes = sizeToPush > 4 ? 8 : 4, isFloat = isFloat, isNewGCObject = false, isGCManaged = pushValue ? Utils.IsGCManaged(elementType) : false }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); MethodBase constructorMethod = anILOpInfo.MethodToCall; Type objectType = constructorMethod.DeclaringType; //New obj must: // - Ignore for creation of Delegates // - Allocate memory on the heap for the object // - If no memory is left, throw a panic attack because we're out of memory... // - Call the specified constructor if (typeof(Delegate).IsAssignableFrom(objectType)) { result.AppendLine("; Ignore newobj calls for Delegates"); //Still need to: // - Remove the "object" param but preserve the "function pointer" GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword eax, [esp]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [esp+4], eax"); result.AppendLine("add esp, 4"); return result.ToString().Trim(); } //The label to jump to if allocated memory isn't null //i.e. not out of memory. string NotNullLabel = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Attempt to allocate memory on the heap for the new object //This involves: // - Pushing the type reference onto the stack // - Calling GC NewObj method // - Check the pointer == 0, if so, out of memory //Push type reference string typeIdStr = aScannerState.GetTypeIdString(aScannerState.GetTypeID(objectType)); result.AppendLine(string.Format("push dword {0}", typeIdStr)); //Push a dword for return value (i.e. new object pointer) result.AppendLine("push dword 0"); //Get the GC.NewObj method ID (i.e. ASM label) string methodLabel = aScannerState.GetMethodID(aScannerState.NewObjMethod); //Call GC.NewObj result.AppendLine(string.Format("call {0}", methodLabel)); //Pop the return value (i.e. new object pointer) result.AppendLine("pop dword eax"); //Remove arg 0 from stack result.AppendLine("add esp, 4"); //Check if pointer == 0? result.AppendLine("cmp eax, 0"); //If it isn't 0, not out of memory so continue execution result.AppendLine(string.Format("jnz {0}", NotNullLabel)); //If we are out of memory, we have a massive problem //Because it means we don't have space to create a new exception object //So ultimately we just have to throw a kernel panic //Throw a panic attack... ( :/ ) by calling kernel Halt(uint lastAddress) //result.AppendLine("call GetEIP"); //result.AppendLine("push dword esp"); //result.AppendLine("push dword ebp"); //result.AppendLine("pushad"); //result.AppendLine("mov dword eax, 0xDEADBEEF"); //result.AppendLine("mov dword ebx, 0x1"); //result.AppendLine("mov dword ecx, 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_GC_Enabled], 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_Heap_PreventAllocation], 0"); //result.AppendLine("jmp method_System_Void_RETEND_Kernel_PreReqs_DECLEND_PageFaultDetection_NAMEEND___Fail"); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); //Insert the not null label result.AppendLine(NotNullLabel + ":"); //Call the specified constructor //This involves: // - Push empty dword onto stack // - Move all args down by one dword // - Move object reference into dword as first arg // - Call constructor result.AppendLine("push dword 0"); int sizeOfArgs = 0; ParameterInfo[] allParams = constructorMethod.GetParameters(); foreach(ParameterInfo aParam in allParams) { sizeOfArgs += Utils.GetNumBytesForType(aParam.ParameterType); aScannerState.CurrentStackFrame.Stack.Pop(); } result.AppendLine("mov dword ebx, esp"); if (sizeOfArgs > 0) { if (sizeOfArgs % 4 != 0) { throw new InvalidOperationException("sizeOfArgs not exact multiple of 4!"); } result.AppendLine(string.Format("mov dword ecx, {0}", sizeOfArgs / 4)); string ShiftArgsLoopLabel = string.Format("{0}.IL_{1}_ShiftArgsLoop", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); result.AppendLine(ShiftArgsLoopLabel + ":"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword edx, [ebx+4]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [ebx], edx"); result.AppendLine("add ebx, 4"); result.AppendLine(string.Format("loop {0}", ShiftArgsLoopLabel)); } GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [ebx], eax"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(constructorMethod))); //Only remove args from stack - we want the object pointer to remain on the stack result.AppendLine(string.Format("add esp, {0}", sizeOfArgs)); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isNewGCObject = true, isGCManaged = true }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type Type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); //New array must: // - Allocate memory on the heap for the object // - If no memory is left, throw a panic attack because we're out of memory... // - Call the specified constructor //The label to jump to if allocated memory isn't null //i.e. not out of memory. string NotNullLabel = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Attempt to allocate memory on the heap for the new array //This involves: // - (Number of elements is already on the stack) // - Pushing the element type reference onto the stack // - Calling GC NewArr method // - Check the pointer == 0, if so, out of memory //Push type reference string typeIdStr = aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)); result.AppendLine(string.Format("push dword {0}", typeIdStr)); //Push a dword for return value (i.e. new array pointer) result.AppendLine("push dword 0"); //Get the GC.NewArr method ID (i.e. ASM label) string methodLabel = aScannerState.GetMethodID(aScannerState.NewArrMethod); //Call GC.NewArr result.AppendLine(string.Format("call {0}", methodLabel)); //Pop the return value (i.e. new array pointer) result.AppendLine("pop dword eax"); //Remove args from stack result.AppendLine("add esp, 8"); //Check if pointer == 0? result.AppendLine("cmp eax, 0"); //If it isn't 0, not out of memory so continue execution result.AppendLine(string.Format("jnz {0}", NotNullLabel)); //If we are out of memory, we have a massive problem //Because it means we don't have space to create a new exception object //So ultimately we just have to throw a kernel panic //Throw a panic attack... ( :/ ) by calling kernel Halt(uint lastAddress) //result.AppendLine("call GetEIP"); //result.AppendLine("push dword esp"); //result.AppendLine("push dword ebp"); //result.AppendLine("pushad"); //result.AppendLine("mov dword eax, 0xDEADBEEF"); //result.AppendLine("mov dword ebx, 0x2"); //result.AppendLine("mov dword ecx, 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_GC_Enabled], 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_Heap_PreventAllocation], 0"); //result.AppendLine("jmp method_System_Void_RETEND_Kernel_PreReqs_DECLEND_PageFaultDetection_NAMEEND___Fail"); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); //Insert the not null label result.AppendLine(NotNullLabel + ":"); //Push new array pointer result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isNewGCObject = true, isGCManaged = true }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if either or both values to shift left are floating point values or /// if the values are 8 bytes in size. /// </exception> /// <exception cref="System.InvalidOperationException"> /// Thrown if either or both values to multiply are not 4 or 8 bytes /// in size or if the values are of different size. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Pop in reverse order to push StackItem itemB = aScannerState.CurrentStackFrame.Stack.Pop(); StackItem itemA = aScannerState.CurrentStackFrame.Stack.Pop(); if (itemB.sizeOnStackInBytes < 4 || itemA.sizeOnStackInBytes < 4) { throw new InvalidOperationException("Invalid stack operand sizes!"); } else if (itemB.isFloat || itemA.isFloat) { //SUPPORT - floats throw new NotSupportedException("Shift right on floats is unsupported!"); } else { string op = (OpCodes)anILOpInfo.opCode.Value == OpCodes.Shr_Un ? "shr" : "sar"; if (itemA.sizeOnStackInBytes == 4 && itemB.sizeOnStackInBytes == 4) { //Pop item B result.AppendLine("pop dword ecx"); //Pop item A result.AppendLine("pop dword eax"); result.AppendLine(op + " eax, cl"); result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); } else if ((itemA.sizeOnStackInBytes == 4 && itemB.sizeOnStackInBytes == 8)) { throw new InvalidOperationException("Invalid stack operand sizes! 4,8 not supported."); } else if ((itemA.sizeOnStackInBytes == 8 && itemB.sizeOnStackInBytes == 4)) { if ((OpCodes)anILOpInfo.opCode.Value == OpCodes.Shr_Un) { string shiftMoreThan32LabelName = string.Format("{0}.IL_{1}_Shift64", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string endLabelName = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Pop item B result.AppendLine("pop dword ecx"); //Pop item A (8 bytes) result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); //Check shift size result.AppendLine("cmp ecx, 32"); result.AppendLine("jae " + shiftMoreThan32LabelName); //Shr (< 32) result.AppendLine("shrd eax, edx, cl"); result.AppendLine("shr edx, cl"); result.AppendLine("jmp " + endLabelName); //Shr (>= 32) result.AppendLine(shiftMoreThan32LabelName + ":"); result.AppendLine("mov eax, edx"); result.AppendLine("mov edx, 0"); result.AppendLine("sub ecx, 32"); result.AppendLine("shr eax, cl"); //Push result result.AppendLine(endLabelName + ":"); result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 8, isGCManaged = false }); } else { string shiftMoreThan32LabelName = string.Format("{0}.IL_{1}_Shift64", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string endLabelName = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Pop item B result.AppendLine("pop dword ecx"); //Pop item A (8 bytes) result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); //Check shift size result.AppendLine("cmp ecx, 32"); result.AppendLine("jae " + shiftMoreThan32LabelName); //Shr (< 32) result.AppendLine("shrd eax, edx, cl"); result.AppendLine("sar edx, cl"); result.AppendLine("jmp " + endLabelName); //Shr (>= 32) result.AppendLine(shiftMoreThan32LabelName + ":"); result.AppendLine("mov eax, edx"); // (Preserve sign bit) result.AppendLine("sar edx, 32"); result.AppendLine("sub ecx, 32"); result.AppendLine("sar eax, cl"); //Push result result.AppendLine(endLabelName + ":"); result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 8, isGCManaged = false }); } } else if (itemA.sizeOnStackInBytes == 8 && itemB.sizeOnStackInBytes == 8) { //Note: Shifting by more than 64 bits is pointless since the value will be annihilated entirely. // "64" fits well within the low 32-bits // So for this op, we do the same as the 8-4 byte version but discard the top four bytes // of the second operand // Except we must check the high bytes for non-zero value. If they are non-zero, we simply // push a result of zero. if ((OpCodes)anILOpInfo.opCode.Value == OpCodes.Shr_Un) { string zeroLabelName = string.Format("{0}.IL_{1}_Zero", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string shiftMoreThan32LabelName = string.Format("{0}.IL_{1}_Shift64", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string end1LabelName = string.Format("{0}.IL_{1}_End1", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string end2LabelName = string.Format("{0}.IL_{1}_End2", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Pop item B result.AppendLine("pop dword ecx"); result.AppendLine("pop dword ebx"); //Pop item A (8 bytes) result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); //Check high 4 bytes of second param result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + zeroLabelName); result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); result.AppendLine("jmp " + end2LabelName); result.AppendLine(zeroLabelName + ":"); //Check shift size result.AppendLine("cmp ecx, 32"); result.AppendLine("jae " + shiftMoreThan32LabelName); //Shr (< 32) result.AppendLine("shrd eax, edx, cl"); result.AppendLine("shr edx, cl"); result.AppendLine("jmp " + end1LabelName); //Shr (>= 32) result.AppendLine(shiftMoreThan32LabelName + ":"); result.AppendLine("mov eax, edx"); result.AppendLine("mov edx, 0"); result.AppendLine("sub ecx, 32"); result.AppendLine("shr eax, cl"); //Push result result.AppendLine(end1LabelName + ":"); result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); result.AppendLine(end2LabelName + ":"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 8, isGCManaged = false }); } else { string zeroLabelName = string.Format("{0}.IL_{1}_Zero", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string shiftMoreThan32LabelName = string.Format("{0}.IL_{1}_Shift64", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string end1LabelName = string.Format("{0}.IL_{1}_End1", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string end2LabelName = string.Format("{0}.IL_{1}_End2", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Pop item B result.AppendLine("pop dword ecx"); result.AppendLine("pop dword ebx"); //Pop item A (8 bytes) result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); //Check high 4 bytes of second param result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + zeroLabelName); // (Preserve sign bit) result.AppendLine("sar edx, 32"); result.AppendLine("push dword edx"); result.AppendLine("push dword edx"); result.AppendLine("jmp " + end2LabelName); result.AppendLine(zeroLabelName + ":"); //Check shift size result.AppendLine("cmp ecx, 32"); result.AppendLine("jae " + shiftMoreThan32LabelName); //Shr (< 32) result.AppendLine("shrd eax, edx, cl"); result.AppendLine("sar edx, cl"); result.AppendLine("jmp " + end1LabelName); //Shr (>= 32) result.AppendLine(shiftMoreThan32LabelName + ":"); result.AppendLine("mov eax, edx"); // (Preserve sign bit) result.AppendLine("sar edx, 32"); result.AppendLine("sub ecx, 32"); result.AppendLine("sar eax, cl"); //Push result result.AppendLine(end1LabelName + ":"); result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); result.AppendLine(end2LabelName + ":"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 8, isGCManaged = false }); } } } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if constant is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); Type elementType = null; //bool pushValue = true; int sizeToPop = 4; bool isFloat = false; switch ((OpCodes)anILOpInfo.opCode.Value) { case OpCodes.Stelem: { //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Stelem_R4: case OpCodes.Stelem_R8: //TODO - Add more StElem op variants support throw new NotSupportedException("Stelem op variant not supported yet!"); case OpCodes.Stelem_I1: sizeToPop = 1; elementType = typeof(sbyte); break; case OpCodes.Stelem_I2: sizeToPop = 2; elementType = typeof(Int16); break; case OpCodes.Stelem_Ref: elementType = null; break; case OpCodes.Stelem_I4: elementType = typeof(Int32); break; case OpCodes.Stelem_I8: elementType = typeof(Int64); break; } if (isFloat) { //TODO - Support floats throw new NotSupportedException("StElem for floats not supported yet!"); } //Get element from array and push the value onto the stack // (or for LdElemA push the address of the value) //This involves: // 1. Check array reference is not null // - If it is, throw NullReferenceException // 2. Check array element type is correct // - If not, throw ArrayTypeMismatchException // 3. Check index to get is > -1 and < array length // - If not, throw IndexOutOfRangeException // 4. Calculate address of element // 5. Pop the value from the stack into the element //Stack setup upon entering this op: (top-most downwards) // 0. Value to store (dword or 2 dwords) // 1. Index of element to get as Int32 (dword) // 2. Array object reference as address (dword) string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_Store_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); // 2. Check array element type is correct // 2.1. Move element type ref into eax // 2.2. Move element type ref from array object into ebx // 2.3. Compare eax to ebx // 2.4. If the same, jump to continue execution further down // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //string ContinueExecutionLabel2 = ContinueExecutionLabelBase + "2"; //// 2.1. Move element type ref into eax //if (elementType != null) //{ // result.AppendLine(string.Format("mov eax, {0}", aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)))); //} //else //{ // //Should be the same for all classes since they are (indirectly) derived from ObjectWithType // int typeOffset = aScannerState.GetFieldOffset(arrayDBType, "_Type"); // // - Move value (which is a ref) into eax // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", 0); // result.AppendLine("mov eax, [esp]"); // // - Move value type ref (from value (ref)) into eax // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "eax", typeOffset); // result.AppendLine(string.Format("mov eax, [eax+{0}]", typeOffset)); //} //// 2.2. Move element type ref from array object into ebx //// - Move array ref into ebx //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8); //result.AppendLine(string.Format("mov ebx, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); //// - Move elemType ref ([ebx+offset]) into ebx int elemTypeOffset = aScannerState.GetFieldOffset(arrayDBType, "elemType"); //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "ebx", elemTypeOffset); //result.AppendLine(string.Format("mov ebx, [ebx+{0}]", elemTypeOffset)); //// 2.3. Compare eax to ebx //result.AppendLine("cmp eax, ebx"); //// 2.4. If the same, jump to continue execution further down //result.AppendLine("je " + ContinueExecutionLabel2); //// 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowArrayTypeMismatchExceptionMethod))); //result.AppendLine(ContinueExecutionLabel2 + ":"); // 3. Check index to get is > -1 and < array length // 3.1. Move index into eax // 3.2. Move array length into ebx // 3.2. Compare eax to 0 // 3.3. Jump if greater than to next test condition (3.5) // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException // 3.5. Compare eax to ebx // 3.6. Jump if less than to continue execution further down // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException string ContinueExecutionLabel3_1 = ContinueExecutionLabelBase + "3_1"; string ContinueExecutionLabel3_2 = ContinueExecutionLabelBase + "3_2"; // 3.1. Move index into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 8 : 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [esp+{0}]", sizeToPop == 8 ? 8 : 4)); // 3.2. Move array length into ecx // - Calculate the offset of the field from the start of the array object int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // - Move array ref into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); // - Move length value ([ebx+offset]) into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [ebx+{0}]", lengthOffset)); // 3.2. Compare eax to 0 result.AppendLine("cmp eax, 0"); // 3.3. Jump if greater than to next test condition (3.5) result.AppendLine("jge " + ContinueExecutionLabel3_1); // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_1 + ":"); // 3.5. Compare eax to ebx result.AppendLine("cmp eax, ebx"); // 3.6. Jump if less than to continue execution further down result.AppendLine("jl " + ContinueExecutionLabel3_2); // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_2 + ":"); // 4. Calculate address of element // 4.0. Pop value into ecx:edx // 4.1. Pop index into ebx // 4.2. Pop array ref into eax // 4.3. Move element type ref (from array ref) into eax // 4.4. Push eax // 4.5. Move IsValueType (from element ref type) into eax // 4.6. If IsValueType, continue to 4.6., else goto 4.9. // 4.7. Pop eax // 4.8. Move Size (from element type ref) into eax // 4.9. Skip over 4.9. and 4.10. // 4.10. Pop eax // 4.11. Move StackSize (from element type ref) into eax // 4.12. Mulitply eax by ebx (index by element size) // 4.13. Move array ref into ebx // 4.14. Add enough to go past Kernel.FOS_System.Array fields // 4.15. Add eax and ebx (array ref + fields + (index * element size)) string ContinueExecutionLabel4_1 = ContinueExecutionLabelBase + "4_1"; string ContinueExecutionLabel4_2 = ContinueExecutionLabelBase + "4_2"; // 4.0. Pop value into ecx:edx result.AppendLine("pop ecx"); if (sizeToPop == 8) { result.AppendLine("pop edx"); } // 4.1. Pop index into ebx result.AppendLine("pop ebx"); // 4.2. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 4.3. Move element type ref (from array ref) into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", elemTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", elemTypeOffset)); // 4.4. Push eax result.AppendLine("push eax"); // 4.5. Move IsValueType (from element ref type) into eax int isValueTypeOffset = aScannerState.GetTypeFieldOffset("IsValueType"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", isValueTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte al, [eax+{0}]", isValueTypeOffset)); // Zero-out the rest of eax result.AppendLine("and eax, 1"); // 4.6. If IsValueType, continue to 4.7., else goto 4.9. result.AppendLine("cmp eax, 0"); result.AppendLine("jz " + ContinueExecutionLabel4_1); // 4.7. Pop eax result.AppendLine("pop eax"); // 4.8. Move Size (from element type ref) into eax int sizeOffset = aScannerState.GetTypeFieldOffset("Size"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", sizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", sizeOffset)); // 4.9. Skip over 4.9. and 4.10. result.AppendLine("jmp " + ContinueExecutionLabel4_2); // 4.10. Pop eax result.AppendLine(ContinueExecutionLabel4_1 + ":"); result.AppendLine("pop eax"); // 4.11. Move StackSize (from element type ref) into eax int stackSizeOffset = aScannerState.GetTypeFieldOffset("StackSize"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", stackSizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", stackSizeOffset)); // 4.12. Mulitply eax by ebx (index by element size) result.AppendLine(ContinueExecutionLabel4_2 + ":"); result.AppendLine("mul ebx"); // 4.13. Pop array ref into ebx result.AppendLine("pop ebx"); // 4.14. Add enough to go past Kernel.FOS_System.Array fields int allFieldsOffset = 0; #region Offset calculation { //Get the child links of the type (i.e. the fields of the type) List<DB_ComplexTypeLink> allChildLinks = arrayDBType.ChildTypes.ToList(); //Calculate the offset //We use StackBytesSize since fields that are reference types are only stored as a pointer allFieldsOffset = allChildLinks.Sum(x => x.ChildType.IsValueType ? x.ChildType.BytesSize : x.ChildType.StackBytesSize); } #endregion result.AppendLine(string.Format("add ebx, {0}", allFieldsOffset)); // 4.15. Add eax and ebx (array ref + fields + (index * element size)) result.AppendLine("add eax, ebx"); // 5. Pop the element from the stack to array // 5.1. Move value in edx:ecx to [eax] if (sizeToPop == 8) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax], ecx"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax+4], edx"); } else if(sizeToPop == 4) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax], ecx"); } else if (sizeToPop == 2) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word [eax], cx"); } else if (sizeToPop == 1) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov byte [eax], cl"); } // 5.2. Pop index, array ref and value from our stack aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if branch test operand value is a floating point value. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //This will store the offset from the current next op's position //to the IL op to jump to. int ILOffset = 0; //This will store the jump operation - by default, the basic jump op string jumpOp = "jmp"; string inverseJumpOp = ""; bool isNegativeTest = false; //The value for the jump op to test against - currently always "0" since on jnz and jz ops are used. string testVal = "0"; switch ((OpCodes)anILOpInfo.opCode.Value) { case OpCodes.Br: //Load the IL offset as signed Int 32 from the value bytes. ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); break; case OpCodes.Br_S: //Load the IL offset as signed Int 8 from the value bytes. ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; break; case OpCodes.Brtrue: //See above. ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-true means we want to do the jump if the operand is not equal to 0 //i.e. not false //Set the jump op to jnz - Jump if Not Zero jumpOp = "jnz"; break; case OpCodes.Brtrue_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jnz"; break; case OpCodes.Brfalse: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-true means we want to do the jump if the operand is equal to 0 //i.e. is false //Set the jump op to jz - Jump if Zero jumpOp = "jz"; break; case OpCodes.Brfalse_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jz"; break; case OpCodes.Beq: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-equal means we want to do the jump if the operand is equal to the other operand //i.e. A == B //Set the jump op to je - Jump if equal jumpOp = "je"; inverseJumpOp = "jne"; break; case OpCodes.Beq_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "je"; inverseJumpOp = "jne"; break; case OpCodes.Bne_Un: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-equal means we want to do the jump if the operand is equal to the other operand //i.e. A == B //Set the jump op to je - Jump if not equal jumpOp = "jne"; isNegativeTest = true; break; case OpCodes.Bne_Un_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jne"; isNegativeTest = true; break; case OpCodes.Bge: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-greater-than-or-equal means we want to do the jump if the operand is greater than equal to //the other operand //i.e. A >= B //Set the jump op to jge - Jump if greater than or equal jumpOp = "jge"; inverseJumpOp = "jl"; break; case OpCodes.Bge_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jge"; inverseJumpOp = "jl"; break; case OpCodes.Bge_Un: //See above : This is unsigned variant ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //See above jumpOp = "jae"; inverseJumpOp = "jb"; break; case OpCodes.Bge_Un_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jae"; inverseJumpOp = "jb"; break; case OpCodes.Ble: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-less-than-or-equal means we want to do the jump if the operand is less than equal to //the other operand //i.e. A <= B //Set the jump op to jle - Jump if less than or equal jumpOp = "jle"; inverseJumpOp = "jg"; break; case OpCodes.Ble_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jle"; inverseJumpOp = "jg"; break; case OpCodes.Ble_Un: //See above : This is unsigned variant ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //See above jumpOp = "jbe"; inverseJumpOp = "ja"; break; case OpCodes.Ble_Un_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jbe"; inverseJumpOp = "ja"; break; case OpCodes.Blt: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-less-than means we want to do the jump if the operand is less than //the other operand //i.e. A < B //Set the jump op to jl - Jump if less than jumpOp = "jl"; isNegativeTest = true; break; case OpCodes.Blt_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jl"; isNegativeTest = true; break; case OpCodes.Blt_Un: //See above : This is unsigned variant ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //See above jumpOp = "jb"; isNegativeTest = true; break; case OpCodes.Blt_Un_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jb"; isNegativeTest = true; break; case OpCodes.Bgt: //See above ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Branch-if-greater-than means we want to do the jump if the operand is greater than //the other operand //i.e. A > B //Set the jump op to jg - Jump if greater than jumpOp = "jg"; isNegativeTest = true; break; case OpCodes.Bgt_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "jg"; isNegativeTest = true; break; case OpCodes.Bgt_Un: //See above : This is unsigned variant ILOffset = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //See above jumpOp = "ja"; isNegativeTest = true; break; case OpCodes.Bgt_Un_S: //See above ILOffset = (int)(sbyte)anILOpInfo.ValueBytes[0]; //See above jumpOp = "ja"; isNegativeTest = true; break; } if (ILOffset == 0) { //Err..why bother jumping at all if the offset is 0? result.AppendLine("; No jump insert - pointless 0 distance jump"); //DEBUG INFO } else { //Get the IL number of the next op int startILNum = anILOpInfo.NextPosition; //Add the offset to get the IL op num to jump to int ILNumToGoTo = startILNum + ILOffset; ////Find and mark the IL op to jump to as requiring a label. //(from ops in aScannerState.CurrentILChunk.ILOpInfos // where (ops.Position == ILNumToGoTo) // select ops).First().ASMInsertLabel = true; //Pre-work out the asm label for jumping to string jumpToLabel = string.Format("{0}.IL_{1}_0", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), ILNumToGoTo); //Pre-work out the asm label for jumping to if the condition is not met. string endLabelName = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //If the jump op is not a straightforward jump i.e. has one or more conditions if (jumpOp == "jz" || jumpOp == "jnz") { //Pop from our stack the test item to use in the condition StackItem testItem = aScannerState.CurrentStackFrame.Stack.Pop(); if (testItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Branch test based on float not supported!"); } //If the test item is Int64 or UInt64 else if (testItem.sizeOnStackInBytes == 8) { //Compare first 32 bits (low bits) //Then (if necessary) compare second 32 bits (high bits) //Pop the low bits result.AppendLine("pop dword eax"); //Compare the low bits to the test value result.AppendLine(string.Format("cmp eax, {0}", testVal)); //Pre-emptively pop the high bits result.AppendLine("pop dword eax"); //If we are testing for not equal to zero: if (jumpOp == "jnz") { //If the low bits are not zero, do the jump result.AppendLine(string.Format("jnz {0}", jumpToLabel)); //Otherwise, test if the high bits are not 0 //We use "cmp" not "test" as "cmp" uses +/- test val, "test" uses & with test val. result.AppendLine(string.Format("cmp eax, {0}", testVal)); //If the high bits are not zero, do the jump result.AppendLine(string.Format("jnz {0}", jumpToLabel)); } //If we are testing for equal to zero: else if (jumpOp == "jz") { //If the low bits are not zero, jump to the end of these test as condition has not been met result.AppendLine(string.Format("jnz {0}", endLabelName)); //Otherwise, test if the high bits are zero result.AppendLine(string.Format("cmp eax, {0}", testVal)); //If they are, do the jump result.AppendLine(string.Format("jz {0}", jumpToLabel)); //Insert the end label to be jumped to if condition is not met (see above) result.AppendLine(string.Format("{0}:", endLabelName)); } else { //Check that no extra ops have been added in the switch block above! throw new NotSupportedException("Unsupported 64-bit branch op!"); } } //If the test item is Int32 or UInt32 else if (testItem.sizeOnStackInBytes == 4) { //Pop the test item result.AppendLine("pop dword eax"); //Compare the test item to the test value result.AppendLine(string.Format("cmp eax, {0}", testVal)); //Do the specified jump result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); } else { throw new InvalidOperationException("Invalid stack operand sizes!"); } } else if (jumpOp == "je" || jumpOp == "jne" || jumpOp == "jge" || jumpOp == "jae" || jumpOp == "jle" || jumpOp == "jbe" || jumpOp == "jl" || jumpOp == "jb" || jumpOp == "jg" || jumpOp == "ja") { //Pop from our stack the test items to use in the condition StackItem itemB = aScannerState.CurrentStackFrame.Stack.Pop(); StackItem itemA = aScannerState.CurrentStackFrame.Stack.Pop(); if (itemA.isFloat || itemB.isFloat) { //SUPPORT - floats throw new NotSupportedException("Branch test based on float not supported!"); } else if(itemA.sizeOnStackInBytes != itemB.sizeOnStackInBytes) { throw new InvalidOperationException("Branch test operands must be same size!"); } else if (itemA.sizeOnStackInBytes == 8) { //Pop the test item B result.AppendLine("pop dword ebx"); result.AppendLine("pop dword ecx"); //Pop the test item A result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); if(!isNegativeTest) { //Compare test item A high bits to test item B high bits result.AppendLine("cmp edx, ecx"); //If they are not equal, abort the testing result.AppendLine(string.Format("{0} {1}", inverseJumpOp, endLabelName)); //Else the igh bits are equal so test low bits //Compare test item A low bits to test item B low bits result.AppendLine("cmp eax, ebx"); //Do the specified jump result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); //Insert the end label to be jumped to if condition is not met (see above) result.AppendLine(string.Format("{0}:", endLabelName)); } else { //Compare test item A high bits to test item B high bits result.AppendLine("cmp edx, ecx"); //Do the specified jump result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); //Compare test item A low bits to test item B low bits result.AppendLine("cmp eax, ebx"); //Do the specified jump result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); } } else if (itemA.sizeOnStackInBytes == 4) { //Pop the test item B result.AppendLine("pop dword ebx"); //Pop the test item A result.AppendLine("pop dword eax"); //Compare test item A to test item B result.AppendLine("cmp eax, ebx"); //Do the specified jump result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); } else { throw new NotSupportedException("Branch test based on supplied stack item sizes not supported!"); } } else { //Do the straightforward jump... result.AppendLine(string.Format("{0} {1}", jumpOp, jumpToLabel)); } } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); string Label_3 = string.Format("{0}.IL_{1}_Point3", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_False1 = string.Format("{0}.IL_{1}_False1", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_False2 = string.Format("{0}.IL_{1}_False2", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_End = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); // Test if the object provided inherits from the specified class // 1. Pop object ref // 1.1. Test if object ref is null: // 1.1.1 True: Push null and continue // 1.1.2 False: Go to 2 // 2. Load object type // 3. Test if object type == provided type: // 3.1 True: Push object ref and continue // 3.2 False: // 3.2.1. Move to base type // 3.2.2. Test if base type null: // 3.2.2.1 True: Push null and continue // 3.2.2.2 False: Jump back to (3) // 1. Pop object ref result.AppendLine("pop dword eax"); // 1.1. Test if object ref is null: result.AppendLine("cmp eax, 0"); result.AppendLine("jne " + Label_False1); // 1.1.1 True: Push null and continue result.AppendLine("push dword 0"); result.AppendLine("jmp " + Label_End); // 1.1.2 False: Go to 2 result.AppendLine(Label_False1 + ":"); // 2. Load object type GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword ebx, [eax]"); // 3. Test if object type == provided type: int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); Type theType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); string TestTypeId = aScannerState.GetTypeIdString(aScannerState.GetTypeID(theType)); result.AppendLine("mov dword ecx, " + TestTypeId); result.AppendLine(Label_3 + ":"); result.AppendLine("cmp ebx, ecx"); result.AppendLine("jne " + Label_False2); // 3.1 True: Push object ref and continue result.AppendLine("push dword eax"); result.AppendLine("jmp " + Label_End); // 3.2 False: result.AppendLine(Label_False2 + ":"); // 3.2.1. Move to base type int baseTypeOffset = aScannerState.GetTypeFieldOffset("TheBaseType"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", baseTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword ebx, [ebx+{0}]", baseTypeOffset)); // 3.2.2. Test if base type null: result.AppendLine("cmp ebx, 0"); // 3.2.2.2 False: Jump back to (3) result.AppendLine("jne " + Label_3); // 3.2.2.1 True: Push null and continue result.AppendLine("push dword 0"); result.AppendLine(Label_End + ":"); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if compare operands are floating point numbers or if either value is < 4 bytes in length or /// operands are not of the same size. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Pop in reverse order to push StackItem itemB = aScannerState.CurrentStackFrame.Stack.Pop(); StackItem itemA = aScannerState.CurrentStackFrame.Stack.Pop(); bool unsignedComparison = (OpCodes)anILOpInfo.opCode.Value == OpCodes.Clt_Un; string trueLabelName = string.Format("{0}.IL_{1}_True", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string elseLabelName = string.Format("{0}.IL_{1}_Else", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string endLabelName = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); if (itemB.isFloat || itemA.isFloat) { //SUPPORT - floats throw new NotSupportedException("Add floats is unsupported!"); } else if(itemA.sizeOnStackInBytes == 4 && itemB.sizeOnStackInBytes == 4) { //Pop item B result.AppendLine("pop dword ebx"); //Pop item A result.AppendLine("pop dword eax"); //Compare A to B result.AppendLine("cmp eax, ebx"); //If A >= B, jump to Else (not-true case) if (unsignedComparison) { result.AppendLine(string.Format("jae {0}", elseLabelName)); } else { result.AppendLine(string.Format("jge {0}", elseLabelName)); } //Otherwise, A < B, so push true (true=1) result.AppendLine("push dword 1"); //And then jump to the end of this IL op. result.AppendLine(string.Format("jmp {0}", endLabelName)); //Insert the Else label. result.AppendLine(string.Format("{0}:", elseLabelName)); //Else case - Push false (false=0) result.AppendLine("push dword 0"); //Push the result onto our stack aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); } else if (itemA.sizeOnStackInBytes == 8 && itemB.sizeOnStackInBytes == 8) { //Pop item B result.AppendLine("pop dword ebx"); result.AppendLine("pop dword ecx"); //Pop item A result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); //If A high bytes < B high bytes : True //If A high bytes = B high bytes : Check, if A low bytes < B low bytes : True //Else : False //Compare high bytes result.AppendLine("cmp edx, ecx"); //A high bytes < B high bytes? Jump to true case. if (unsignedComparison) { result.AppendLine(string.Format("jb {0}", trueLabelName)); } else { result.AppendLine(string.Format("jl {0}", trueLabelName)); } //A high bytes > B high bytes? Jump to else case. if (unsignedComparison) { result.AppendLine(string.Format("ja {0}", elseLabelName)); } else { result.AppendLine(string.Format("jg {0}", elseLabelName)); } //Otherwise, A high bytes = B high bytes //So compare low bytes result.AppendLine("cmp eax, ebx"); //A low bytes >= B low bytes? Jump to else case. if (unsignedComparison) { result.AppendLine(string.Format("jae {0}", elseLabelName)); } else { result.AppendLine(string.Format("jge {0}", elseLabelName)); } //Otherwise A < B. //Insert True case label result.AppendLine(string.Format("{0}:", trueLabelName)); //True case - Push true (true=1) result.AppendLine("push dword 1"); //And then jump to the end of this IL op. result.AppendLine(string.Format("jmp {0}", endLabelName)); //Insert Else case label result.AppendLine(string.Format("{0}:", elseLabelName)); //Else case - Push false (false=0) result.AppendLine("push dword 0"); //Push the result onto our stack aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, // Yes, this is supposed to be 4 - the value that just got pushed was a // true / false integer sizeOnStackInBytes = 4, isGCManaged = false }); } else { throw new NotSupportedException("Unsupported number of bytes for compare less than!"); } //Always append the end label result.AppendLine(string.Format("{0}:", endLabelName)); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if any argument or the return value is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); MethodBase methodToCall = anILOpInfo.MethodToCall; //The method to call is a method base //A method base can be either a method info i.e. a normal method //or a constructor method. The two types are treated separately. if(methodToCall is MethodInfo) { //Allocate space on the stack for the return value as necessary Type retType = ((MethodInfo)methodToCall).ReturnType; StackItem returnItem = new StackItem() { isFloat = Utils.IsFloat(retType), sizeOnStackInBytes = Utils.GetNumBytesForType(retType), isGCManaged = Utils.IsGCManaged(retType) }; //We do not push the return value onto the stack unless it has size > 0 //We do not push the return value onto our stack at this point - it is pushed after the call is done if (returnItem.sizeOnStackInBytes != 0) { if (returnItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Cannot handle float return values!"); } else if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword 0"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); } else { throw new NotSupportedException("Invalid return stack operand size!"); } } //Get the ID of method to call as it will be labelled in the output ASM. string methodID = aScannerState.GetMethodID(methodToCall); //Append the actual call result.AppendLine(string.Format("call {0}", methodID)); //After a call, we need to remove the return value and parameters from the stack //This is most easily done by just adding the total number of bytes for params and //return value to the stack pointer (ESP register). //Stores the number of bytes to add int bytesToAdd = 0; //All the parameters for the method that was called List<Type> allParams = ((MethodInfo)methodToCall).GetParameters().Select(x => x.ParameterType).ToList(); //Go through each one if (!methodToCall.IsStatic) { allParams.Insert(0, methodToCall.DeclaringType); } foreach (Type aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam); } //If the number of bytes to add to skip over params is > 0 if (bytesToAdd > 0) { //If there is a return value on the stack if (returnItem.sizeOnStackInBytes != 0) { //We need to store the return value then pop all the params //We now push the return value onto our stack as, //after all is said and done below, it will be the //top item on the stack aScannerState.CurrentStackFrame.Stack.Push(returnItem); //SUPPORT - floats (with above) //Pop the return value into the eax register //We will push it back on after params are skipped over. if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("pop dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); } } //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); //If necessary, push the return value onto the stack. if (returnItem.sizeOnStackInBytes != 0) { //SUPPORT - floats (with above) //The return value was stored in eax //So push it back onto the stack if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); } } } //No params to skip over but we might still need to store return value else if (returnItem.sizeOnStackInBytes != 0) { //The return value will be the top item on the stack. //So all we need to do is push the return item onto our stack. aScannerState.CurrentStackFrame.Stack.Push(returnItem); } } else if(methodToCall is ConstructorInfo) { ConstructorInfo aConstructor = (ConstructorInfo)methodToCall; if (aConstructor.IsStatic) { //Static constructors do not have parameters or return values //Get the ID of method to call as it will be labelled in the output ASM. string methodID = aScannerState.GetMethodID(methodToCall); //Append the actual call result.AppendLine(string.Format("call {0}", methodID)); } else { //Get the ID of method to call as it will be labelled in the output ASM. string methodID = aScannerState.GetMethodID(methodToCall); //Append the actual call result.AppendLine(string.Format("call {0}", methodID)); //After a call, we need to remove the parameters from the stack //This is most easily done by just adding the total number of bytes for params //to the stack pointer (ESP register). //Stores the number of bytes to add int bytesToAdd = 0; //All the parameters for the method that was called ParameterInfo[] allParams = methodToCall.GetParameters(); //Go through each one foreach (ParameterInfo aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam.ParameterType); } //Add 4 bytes for the instance ref bytesToAdd += 4; //If the number of bytes to add to skip over params is > 0 if (bytesToAdd > 0) { //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); } } } return result.ToString().Trim(); }