/// <summary> /// Estimates the number of input parameters of each funciton. /// </summary> /// <param name="retList">Return instruction list.</param> public static void estimateFunctionParameters(oAsmRetList retList, oEbpArgumentList ebpAddresses, Form parent) { if (functions == null) { functions = new List <oFunction>(); } // Estimate the number of parameters for each function return int count = 0; formProgress progress = new formProgress(parent); progress.Show(parent); progress.setMin(0); progress.setMax(oFunctionMaster.numFunctions / 273); progress.setTitle("Estimating Number Parameter Counts..."); progress.setLabel1("Estimating Function Input Parameter Counts: " + count.ToString() + "0 of " + oFunctionMaster.numFunctions.ToString()); progress.setLabel2(""); // Loop through each function, estimating the number of parameters List <oFunction> invalidFunctions = new List <oFunction>(0); foreach (oFunction function in functions) { // Estimate parameters function.estimateNumParameters(retList, ebpAddresses); // Check the number of parameters is valid if (function.getNumParams() > 50) { // Invalid function invalidFunctions.Add(function); } count++; if (count % 273 == 0) { progress.setLabel1("Estimating Function Input Parameter Counts: " + count.ToString() + "0 of " + oFunctionMaster.numFunctions.ToString()); progress.increment(); } } // Remove the invalid functions foreach (oFunction function in invalidFunctions) { functions.Remove(function); } progress.Dispose(); }
/// <summary> /// This disassmebles all the modules /// </summary> public static void disassembleProcess(Form parent, List <HEAP_INFO> selectedHeaps) { // Create the progress bar formProgress progress = new formProgress(parent); progress.Show(parent); progress.setMin(0); progress.setMax(selectedHeaps.Count); progress.setTitle("Disassembling Modules..."); progress.setLabel1("Generating Memory Map"); progress.setLabel2("Total Discovered Internal Calls: 0"); // Reset the function master oFunctionMaster.reset(); // Create the return instruction list, for parameter count estimation usage later on oAsmRetList returnAddresses = new oAsmRetList(); oEbpArgumentList ebpAddresses = new oEbpArgumentList(); // Process the selected heaps ulong count = 0; HeaderReader headerReader = null; for (int i = 0; i < selectedHeaps.Count; i++) { HEAP_INFO heap = selectedHeaps[i]; // Check if it is a PE Header if (heap.extra.Contains("PE")) { // Process the PE import table headerReader = new HeaderReader(activeProcess, heap.heapAddress); // Set the access rights to this heap so that we can edit the header if (headerReader.importTable.Count > 0) { HEAP_INFO tmpHeapInfo = oMemoryFunctions.LookupAddressInMap(map, headerReader.optHeader.DataDirectory13.VirtualAddress + (uint)heap.heapAddress); if (tmpHeapInfo.heapProtection.Contains("EXECUTE")) { oMemoryFunctions.SetMemoryProtection(activeProcess, (uint)tmpHeapInfo.heapAddress, (uint)tmpHeapInfo.heapLength, MEMORY_PROTECT.PAGE_EXECUTE_READWRITE); } else { oMemoryFunctions.SetMemoryProtection(activeProcess, (uint)tmpHeapInfo.heapAddress, (uint)tmpHeapInfo.heapLength, MEMORY_PROTECT.PAGE_READWRITE); } } // Add all the imported functions foreach (IMPORT_FUNCTION importFunction in headerReader.importTable) { // Lookup the destination address HEAP_INFO destinationHeap = oMemoryFunctions.LookupAddressInMap(map, (uint)importFunction.targetAddress); // Add this function if it is valid if (destinationHeap.heapProtection.Contains("EXECUTE") && destinationHeap.heapLength > 0) { // Valid destination oFunctionMaster.addCall((uint)importFunction.memoryAddress, (uint)importFunction.targetAddress, oFunction.CALL_TYPE.PE_TABLE, importFunction.name); // Disasemble only one function and find return (needed for parametar count estimation) uint length = (uint) (destinationHeap.heapLength - (importFunction.targetAddress - destinationHeap.heapAddress)); if (length > 0x1000) { length = 0x1000; } byte[] block = oMemoryFunctions.ReadMemory(activeProcess, importFunction.targetAddress, length); // Process the function only LengthDecoder.AddFirstRet(block, (int)importFunction.targetAddress, 0, ref returnAddresses); } } // Add all the exported functions if (headerReader.exports.Count > 0) { // Read in this heap HEAP_INFO tmpHeap; byte[] block = null; IEnumerator exportEnum = headerReader.exports.Values.GetEnumerator(); if (exportEnum.MoveNext()) { tmpHeap = oMemoryFunctions.LookupAddressInMap(map, (uint)((export)exportEnum.Current).Address); block = oMemoryFunctions.ReadMemory(activeProcess, tmpHeap.heapAddress, (uint)tmpHeap.heapLength); // We need to add all the exported functions from this library foreach (export exportFunction in headerReader.exports.Values) { if ((exportFunction.Address >= tmpHeap.heapAddress) && (exportFunction.Address <= tmpHeap.heapAddress + tmpHeap.heapLength)) { // Add this call oFunctionMaster.addCall(0, (uint)exportFunction.Address, oFunction.CALL_TYPE.PE_EXPORT, exportFunction.Name); // Disasemble only one function and find return (needed for parameter count estimation) // Process the function only LengthDecoder.AddFirstRet(block, (int)tmpHeap.heapAddress, (int)(exportFunction.Address - tmpHeap.heapAddress), ref returnAddresses); } } } } } // If this contains EXECUTE privileges, instrument this heap even if it is a PE header. // Instrument this heap if it has been selected, but not if it is a PE header region without EXECUTE privileges. if (!heap.extra.Contains("PE") || heap.heapProtection.Contains("EXECUTE")) { // Disassemble this heap as a code region List <jmpInstruction> jmpAddresses = new List <jmpInstruction>(10000); // Let's process this data region if (heap.associatedModule != null) { progress.setLabel1("Analyzing code region for module " + heap.associatedModule.ModuleName + " heap at address 0x" + heap.heapAddress.ToString("X")); } else { progress.setLabel1( "Analyzing code region associated with an unknown module at address 0x" + heap.heapAddress.ToString("X")); } // Read in the heap byte[] block = oMemoryFunctions.ReadMemory(activeProcess, heap.heapAddress, (uint)heap.heapLength); // Process the heap List <SIMPLE_INSTRUCTION> instructions = LengthDecoder.DisassembleBlockCallsOnly(block, (int)heap.heapAddress, ref returnAddresses, ref ebpAddresses, ref jmpAddresses); // Get a list of the return instruction addresses for this heap List <retAddress> retAddresses = returnAddresses.getRets((uint)heap.heapAddress, (uint)(heap.heapAddress + heap.heapLength)); // Generate a list of call source-destination pairs. List <SOURCE_DEST_PAIR> callPairs = new List <SOURCE_DEST_PAIR>(1000); for (int n = 0; n < instructions.Count; n++) { HEAP_INFO destinationHeap = oMemoryFunctions.LookupAddressInMap(map, (uint) instructions[n].jmp_target); if (destinationHeap.heapLength > 0 && destinationHeap.heapAddress == heap.heapAddress && isValidCodePointer(activeProcess, instructions[n].address, (uint)instructions[n].jmp_target, headerReader) && isValidCodeBlock(activeProcess, heap, instructions[n].address, jmpAddresses, retAddresses, 500) && isValidCodeBlock(activeProcess, heap, instructions[n].jmp_target, jmpAddresses, retAddresses, 500)) { // Add this call, it may be filtered out later. callPairs.Add(new SOURCE_DEST_PAIR((uint)instructions[n].address, (uint)instructions[n].jmp_target, oFunction.CALL_TYPE.FIXED_OFFSET, null, "")); } } // ----------------------------------- // PROPER CALLBACK TABLE CONDITION // ----------------------------------- // The start of the callback table is defined as: // 1. 4 dwords in a row that point to within this heap. // 2. Must point to at least 2 different addresses. // 3. All 4 pointers must point to valid code regions. // // The end of the callback table is defined as: // 1. first not heap-internal pointer // 2. or first heap-internal pointer that fails the // valid function test. // bool inCallbackTable = false; for (int n = 0; n < block.Length - 4 * 4; n += 4) { // Check if this could be the start of a callback table. uint value1 = oMemoryFunctions.ByteArrayToUint(block, n); if (inCallbackTable) { // Check is this value should be added onto the callback table if (value1 >= heap.heapAddress && value1 <= heap.heapAddress + heap.heapLength && isValidCodePointer(activeProcess, ((uint)n) + (uint)heap.heapAddress, value1, headerReader) && isValidCodeBlock(activeProcess, heap, value1, jmpAddresses, retAddresses, 500)) { // Another valid callback entry callPairs.Add(new SOURCE_DEST_PAIR(((uint)n) + (uint)heap.heapAddress, value1, oFunction.CALL_TYPE.CALLBACK_TABLE, callPairs[callPairs.Count - 1], "vtable")); } else { // End of callback table. inCallbackTable = false; } } else if (value1 >= heap.heapAddress && value1 <= heap.heapAddress + heap.heapLength) { // Check second pointer uint value2 = oMemoryFunctions.ByteArrayToUint(block, n + 4); if (value2 >= heap.heapAddress && value2 <= heap.heapAddress + heap.heapLength) { // Check third pointer uint value3 = oMemoryFunctions.ByteArrayToUint(block, n + 4 * 2); if (value3 >= heap.heapAddress && value3 <= heap.heapAddress + heap.heapLength) { // Check fourth pointer uint value4 = oMemoryFunctions.ByteArrayToUint(block, n + 4 * 3); if (value4 >= heap.heapAddress && value4 <= heap.heapAddress + heap.heapLength) { // We found four internal pointers in a row. Check that condition #2 is met. if (!((value1 == value2 && value1 == value3) || (value1 == value2 && value1 == value4) || (value2 == value3 && value2 == value4))) { // At least two of the values are different // Check condition #3, all valid code poitners if (isValidCodePointer(activeProcess, ((uint)n) + (uint)heap.heapAddress, value1, headerReader) && isValidCodeBlock(activeProcess, heap, value1, jmpAddresses, retAddresses, 500) && isValidCodePointer(activeProcess, ((uint)n + 4 * 1) + (uint)heap.heapAddress, value2, headerReader) && isValidCodeBlock(activeProcess, heap, value2, jmpAddresses, retAddresses, 500) && isValidCodePointer(activeProcess, ((uint)n + 4 * 2) + (uint)heap.heapAddress, value3, headerReader) && isValidCodeBlock(activeProcess, heap, value3, jmpAddresses, retAddresses, 500) && isValidCodePointer(activeProcess, ((uint)n + 4 * 3) + (uint)heap.heapAddress, value4, headerReader) && isValidCodeBlock(activeProcess, heap, value4, jmpAddresses, retAddresses, 500)) { // All pointers are valid, start of callback table inCallbackTable = true; // Add these function calls callPairs.Add(new SOURCE_DEST_PAIR(((uint)n) + (uint)heap.heapAddress, value1, oFunction.CALL_TYPE.CALLBACK_TABLE, null, "vtable")); callPairs.Add(new SOURCE_DEST_PAIR(((uint)n + 4 * 1) + (uint)heap.heapAddress, value2, oFunction.CALL_TYPE.CALLBACK_TABLE, callPairs[callPairs.Count - 1], "vtable")); callPairs.Add(new SOURCE_DEST_PAIR(((uint)n + 4 * 2) + (uint)heap.heapAddress, value3, oFunction.CALL_TYPE.CALLBACK_TABLE, callPairs[callPairs.Count - 1], "vtable")); callPairs.Add(new SOURCE_DEST_PAIR(((uint)n + 4 * 3) + (uint)heap.heapAddress, value4, oFunction.CALL_TYPE.CALLBACK_TABLE, callPairs[callPairs.Count - 1], "vtable")); // Move on to the next callback n += 4 * 3; // because n+=4 } } } } } } } // ----------------------------------- // PROPER SUBROUTINE ENTER FILTER CONDITION: // ----------------------------------- // Condition Description: // - each subroutine is only called from the start of the subroutine, and no call // can enter the subroutine in the middle of the function. I have yet to see a // function that violates rule even under obfuscation (except rarely the skipping // of hotpatching bytes). // // Condition Implementation: // - only smallest call destination in range (retAddress[i-1], retAddress[i]] is valid // for funcion i. // - a destination of min destionation + const, where 0 <= const <= 2 is allowed to support // some obfuscation methods that skip the hotpatching useless bytes. // // Sort the array by the destination SOURCE_DEST_PAIR.COMPARER_DEST comparerDest = new SOURCE_DEST_PAIR.COMPARER_DEST(); callPairs.Sort(comparerDest); // Loop through each return instruction for (int n = 0; n < retAddresses.Count; n++) { // Find all destination addresses associated with this function // Find the start destination pair index int indexStart = (n > 0 ? callPairs.BinarySearch(new SOURCE_DEST_PAIR(0, retAddresses[n - 1].address, oFunction.CALL_TYPE.FIXED_OFFSET, null, ""), comparerDest) : 0); if (indexStart < 0) { indexStart = ~indexStart; } // Find the end destination pair index int indexEnd = callPairs.BinarySearch(new SOURCE_DEST_PAIR(0, retAddresses[n].address, oFunction.CALL_TYPE.FIXED_OFFSET, null, ""), comparerDest); if (indexEnd < 0) { indexEnd = ~indexEnd - 1; } // If we have more than one call to this procedure, loop through them and maybe filter them out if (indexEnd - indexStart + 1 > 1) { // Find the start of the subroutine uint minDestination = uint.MaxValue; for (int m = indexStart; m <= indexEnd; m++) { if (callPairs[m].dest < minDestination) { minDestination = callPairs[m].dest; } } // Loop through these call destinations to make sure they are not entering in the middle of the subroutine for (int m = indexStart; m <= indexEnd; m++) { if (callPairs[m].dest > minDestination + 2) { // This is entering the subrouting in the middle! Remove this call as invalid. callPairs.RemoveAt(m); m--; indexEnd--; } } } } // Add the calls to the function list) for (int n = 0; n < callPairs.Count; n++) { // Add this function call if (disassemblyMode.recordIntramodular) { oFunctionMaster.addCall(callPairs[n].source, callPairs[n].dest, callPairs[n].type, callPairs[n].name); } } count = count + (ulong)instructions.Count; } progress.setLabel2("Total Discovered Internal Calls: " + count); progress.increment(); } progress.Dispose(); // Estimate number of parameters for each function oFunctionMaster.estimateFunctionParameters(returnAddresses, ebpAddresses, parent); }
/// <summary> /// This function injects the instrumentation code and /// redirects the call instructions and pe tables. /// </summary> public static bool inject(uint bufferSize, uint maxCallCount, List <HEAP_INFO> invalidCallSourceHeaps, Form parent) { if (functions.Count > 0) { // Create the progress bar formProgress progress = new formProgress(parent); progress.Show(); progress.setMin(0); progress.setMax(functions.Count / 457); progress.setTitle("Injecting Function Instrumentation Code..."); progress.setLabel1("Allocating buffer space..."); progress.setLabel2(""); // Create and setup the circular buffer recording buffer for the visualizations. uint visualizationAddress = (uint)oMemoryFunctions.VirtualAllocEx(oProcess.activeProcess.Handle, (IntPtr)0, 20000000 + 1000, (uint)(MEMORY_STATE.COMMIT), (uint)MEMORY_PROTECT.PAGE_READWRITE ); injectedHeapAddress1 = visualizationAddress; injectedHeapSize1 = 20000000 + 1000; oMemoryFunctions.WriteMemoryDword(oProcess.activeProcess, visualizationAddress + 4, 0); oAssemblyGenerator.addCommonAddress(visualizationAddress + 4, "VIS_AD_CIRCULAR_OFFSET"); oAssemblyGenerator.addCommonAddress(bufferSize * 1000000 - 8 - 4, "VIS_CIRCULAR_SIZE"); oAssemblyGenerator.addCommonAddress(visualizationAddress + 16 + 4, "VIS_AD_CIRCULAR_BASE"); oAssemblyGenerator.addCommonAddress(0, "mainRecordFunction"); //MessageBox.Show((visualizationAddress + 16).ToString("X")); // Create the visualization buffer reader reader_visualization = new oCircularBufferReader(visualizationAddress + 16 + 4, bufferSize * 1000000 - 4, visualizationAddress + 4); // Set the global buffer parameters oMemoryFunctions.WriteMemoryDword(oProcess.activeProcess, visualizationAddress, maxCallCount); oAssemblyGenerator.addCommonAddress(visualizationAddress, "MAXCALLS"); // Determine the size per function int sizePerFunc = ((oFunction)functions[0]).generateInstrumentation(10000).Length; // Allocate the memory heap for the funciton code injections uint currentAddress = (uint)oMemoryFunctions.VirtualAllocEx(oProcess.activeProcess.Handle, (IntPtr)0, functions.Count * sizePerFunc + 1000 + 2 * 0x80000, // 1000 + 2*0x80000 bytes for the mainRecordFunction (uint)(MEMORY_STATE.COMMIT), (uint)MEMORY_PROTECT.PAGE_EXECUTE_READWRITE ); injectedHeapAddress2 = currentAddress; injectedHeapSize2 = (uint)(functions.Count * sizePerFunc + 1000 + 2 * 0x80000); if (currentAddress == 0) { oConsole.printMessageShow("ERROR: Could not allocate memory space in target process for code injections."); progress.Dispose(); return(false); } progress.setLabel1("Injecting common base functions."); // Inject the common function oAssemblyGenerator.addCommonAddress(currentAddress, "mainRecordFunction"); uint size = (uint)oFunction.injectCommonCode(currentAddress); // Generate and write the valid read pointer table progress.setLabel1("Generating and injecting the read pointer dereference lookup table."); // Get the address of the table table_addressValidReadTable = UInt32.Parse(oMemoryFunctions.ReverseString(oAssemblyGenerator.evaluateCommonAddress("sLOOKUPVALIDPOINTER", 0, currentAddress)), NumberStyles.HexNumber); updateValidReadPointerTable(); // Generate and write the excluded call source table progress.setLabel1("Generating and injecting the exclusion source lookup table."); // Get the address of the table table_addressInvalidSourceTable = UInt32.Parse(oMemoryFunctions.ReverseString(oAssemblyGenerator.evaluateCommonAddress("sLOOKUPINVALIDSOURCE", 0, currentAddress)), NumberStyles.HexNumber); updateInvalidCallSourceTable(invalidCallSourceHeaps); // Set the size to dereference option dereferenceSizeAddress = UInt32.Parse(oMemoryFunctions.ReverseString(oAssemblyGenerator.evaluateCommonAddress("sSTRINGDEREFERENCESIZE", 0, currentAddress)), NumberStyles.HexNumber); oMemoryFunctions.WriteMemoryDword(oProcess.activeProcess, dereferenceSizeAddress, (uint)Properties.Settings.Default.NumDereferencedBytes); currentAddress += size; // Perform the intecept function code injection int count = 0; progress.setLabel1("Injecting instrumentation functions and redirecting functions: " + count.ToString() + " of " + functions.Count.ToString()); foreach (oFunction function in functions) { // Inject the instrumentation functions uint injectionAddress = currentAddress; size = (uint)function.injectInstrumentation(currentAddress); // Update the function parameters function.addressCount = UInt32.Parse(oMemoryFunctions.ReverseString(oAssemblyGenerator.evaluateCommonAddress("sCOUNT", 0, currentAddress)), NumberStyles.HexNumber); function.addressRecord = UInt32.Parse(oMemoryFunctions.ReverseString(oAssemblyGenerator.evaluateCommonAddress("sSAVEDATA", 0, currentAddress)), NumberStyles.HexNumber); // Redirect the function calls function.redirect(injectionAddress); currentAddress += size; // Update the progress bar count++; if ((count % 457) == 0) { progress.setLabel1("Injecting instrumentation functions and redirecting functions: " + count.ToString() + " of " + functions.Count.ToString()); progress.increment(); } } progress.Dispose(); } return(true); }