Exemple #1
0
        public bool fillOutHeapsStack(oSingleData call)
        {
            // Check to see if this is already in the heaps list
            HEAP_INFO heap = oMemoryFunctions.LookupAddressInMap(oProcess.map, call.esp);

            if (selectedHeaps.Contains(heap.heapAddress))
            {
                return(false);
            }
            selectedHeaps.Add(heap.heapAddress, heap);
            return(false);
        }
Exemple #2
0
        private static bool isValidCodeBlock(Process process, HEAP_INFO heap, uint address, List <jmpInstruction> jmps, List <retAddress> rets, uint maxLength)
        {
            // Convert this block to a linear block
            byte[] block = getLinearCodeToRet(process, heap, address, jmps, rets, maxLength);

            // Check if this is a valid code block
            //byte[] block = oMemoryFunctions.readMemory(process, address, addressEnd - address);
            int warnings = LengthDecoder.CountWarnings(block);

            if (warnings > 0)
            {
                return(false);
            }

            return(true);
        }
Exemple #3
0
        /// <summary>
        /// Returns the row and column text for a listgridview of functions.
        /// </summary>
        /// <param name="row">Cell row</param>
        /// <param name="column">Cell column</param>
        /// <returns></returns>
        public string getFunctionListCell(int row, int column, oVisPlayBar playBar)
        {
            // Get this row and column
            if (row < functions.Count && row >= 0 && functions[row] != null)
            {
                switch (column)
                {
                case 0:
                    // Get the address description for this function
                    HEAP_INFO heap = oMemoryFunctions.LookupAddressInMap(oProcess.map, functions[row].address);

                    string name;

                    // Generate the string representation of this address
                    if (heap.associatedModule != null)
                    {
                        name = heap.associatedModule.ModuleName + " + 0x" +
                               (functions[row].address - (uint)heap.associatedModule.BaseAddress).ToString("X");
                    }
                    else
                    {
                        name = "0x" + functions[row].address.ToString("X");
                    }

                    // Add the known name on a second line
                    if (functions[row].name != "")
                    {
                        name = functions[row].name + " - " + name;
                    }

                    if (functions[row].disabled)
                    {
                        name = "DISABLED " + name;
                    }

                    return(name);

                case 1:
                    // Address
                    return(functions[row].address.ToString("X"));

                case 2:
                    // Call count
                    if (dataVis != null)
                    {
                        // Lets count the number of calls
                        int tmpCount =
                            dataVis.getCallCount(functions[row].address);

                        // If we are near the maximum number of calls, lets add a + sign. This is because this function
                        // stopped recorded further calls for performance reasons. (eg. a function being called millions
                        // of times a second uses up a lot of diskspace to record!)
                        string totalCount = tmpCount < Properties.Settings.Default.MaxRecordedCalls
                                                    ? tmpCount.ToString()
                                                    : tmpCount.ToString() + @"+";

                        // Get the selected region call count
                        string selectedRegionCount;
                        if (playBar != null)
                        {
                            selectedRegionCount = playBar.countCallSelected(functions[row].address).ToString();
                        }
                        else
                        {
                            selectedRegionCount = "";
                        }

                        // Return the two results
                        return(String.Concat(totalCount, "/", selectedRegionCount));
                    }
                    return("no data");

                case 3:
                    // Number of arguments and calling convention
                    return(String.Concat(functions[row].getNumParams().ToString(), " args"));

                default:
                    return("?");
                }
            }
            return("?");
        }
Exemple #4
0
        private static byte[] getLinearCodeToRet(Process process, HEAP_INFO heap, uint address, List <jmpInstruction> jmps_in, List <retAddress> rets, uint maxLength)
        {
            // This gets the block of data starting at address, following all force jumps, and ending at the first return.

            byte[] result = new byte[0];

            bool cloned = false;
            List <jmpInstruction> jmps = jmps_in;
            uint curAddress            = address;

            retAddress.COMPARER_ADDRESS retComparer = new retAddress.COMPARER_ADDRESS();
            jmpInstructionComparer      jmpComparer = new jmpInstructionComparer();

            while (curAddress >= heap.heapAddress && curAddress < heap.heapAddress + heap.heapLength)
            {
                // Find the next return or jump - whichever comes first.

                // Find the next return
                int retIndex = rets.BinarySearch(new retAddress(curAddress, 0), retComparer);
                if (retIndex < 0)
                {
                    retIndex = ~retIndex;
                }
                uint nextRet = uint.MaxValue;
                if (retIndex < rets.Count && retIndex >= 0)
                {
                    nextRet = rets[retIndex].address;
                }

                // Find the next jump
                int jmpIndex = jmps.BinarySearch(new jmpInstruction(curAddress, 0), jmpComparer);
                if (jmpIndex < 0)
                {
                    jmpIndex = ~jmpIndex;
                }
                uint nextJmp = uint.MaxValue;
                if (jmpIndex < jmps.Count && jmpIndex >= 0)
                {
                    nextJmp = jmps[jmpIndex].address;
                }

                // Make sure we found either a next jump or return
                if (nextJmp != uint.MaxValue || nextRet != uint.MaxValue)
                {
                    // Add this region to the result
                    uint blockEnd  = (nextJmp > nextRet ? nextRet : nextJmp) - 1;
                    uint newLength = (uint)result.Length + (blockEnd - curAddress + 1);
                    if (newLength > maxLength)
                    {
                        newLength = maxLength;
                    }
                    byte[] newResult = new byte[newLength];
                    Array.ConstrainedCopy(result, 0, newResult, 0, result.Length);
                    byte[] tmp = oMemoryFunctions.ReadMemory(process, curAddress, (uint)(newResult.Length - result.Length));
                    Array.ConstrainedCopy(tmp, 0, newResult, result.Length, tmp.Length);
                    result = newResult;

                    // Check if we have gathered enough bytes
                    if (result.Length >= maxLength)
                    {
                        return(result);
                    }

                    // If this is a return we are done, jmp we need to update the current address
                    if (nextJmp > nextRet)
                    {
                        // We hit the return statement next. We are done.
                        return(result);
                    }
                    else
                    {
                        // Update the current address to the jmp target
                        curAddress = jmps[jmpIndex].destination;

                        // Delete this jmp so that we do not follow it more than once
                        if (!cloned)
                        {
                            jmps   = new List <jmpInstruction>(jmps_in);
                            cloned = true;
                        }
                        jmps.RemoveAt(jmpIndex);
                    }
                }
                else
                {
                    // No return or jump coming up, we are done.
                    return(result);
                }
            }

            // We jumped out of this heap. Stop analysis.
            return(result);
        }
Exemple #5
0
        /// <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);
        }
        private string replaceConstants(string code)
        {
            // This replaces all the # # constants with their appropriate values.
            // List of constants to replace iin this code:
            // #MODULENAME#       - Module name containing the function
            // #FUNCTIONOFFSET#   - Offset of the function
            // #FUNCTIONNAME#     - Name of the function. eg. kernel32_4a029
            // #NUMSTACKARGS#     - Number of arguments passed on the stack
            // #MAINMODULENAME#   - The name of the main module of the process
            // #CALLCODE#         - The binary responsible for setting up the stack and registers in the other process.
            // #PROCESSNAME#      - The name of the process
            //
            // List of constants that the C#, delphi, vb, etc is expected to replace in the built ASM string, #CALLCODE#:
            // #FUNCTION#         - This is the address of the function. It varies according to the loading module base.
            // #_ECX__#           - With arugment value
            // #_EDX__#           - With arugment value
            // #_EAX__#           - With arugment value
            // #ARGN01#           - With arugment value
            // #ARGN02#           - With arugment value
            // #ARGN03#           - With arugment value
            // #ARGN04#           - With arugment value
            // ...


            // Initialize the result
            string    result = code;
            HEAP_INFO heap   = oMemoryFunctions.LookupAddressInMap(oProcess.map, function.address);

            if (heap.heapAddress != 0)
            {
                // Replace the process name
                result = result.Replace("#PROCESSNAME#", oProcess.activeProcess.ProcessName);

                // Replace the module name
                result = result.Replace("#MODULENAME#", heap.associatedModule.ModuleName);

                // Replace the function offset
                if (heap.associatedModule != null)
                {
                    result = result.Replace("#FUNCTIONOFFSET#", "0x" + (function.address - (uint)heap.associatedModule.BaseAddress).ToString("X"));
                }
                else
                {
                    result = result.Replace("#FUNCTIONOFFSET#", "0x" + (function.address - heap.heapAddress + 0x1000).ToString("X"));
                }

                // Replace the function name
                string name = "";
                if (heap.associatedModule != null)
                {
                    name = heap.associatedModule.ModuleName + "_" + (function.address - (uint)heap.associatedModule.BaseAddress).ToString("X");
                }
                else
                {
                    name = heap.associatedModule.ModuleName + "_" + (function.address - heap.heapAddress + 0x1000).ToString("X");
                }

                name = name.Replace(".", "_");
                for (int i = 0; i < name.Length; i++)
                {
                    if ((name[i] < 48 || name[i] > 57) && (name[i] < 65 || name[i] > 90) && (name[i] < 97 || name[i] > 122) && name[i] != 95)
                    {
                        // Invalid character for a function name, remove it
                        name = name.Remove(i, 1);
                        i--;
                    }
                }
                // Cannot be 0 length and cannot start with a number
                if (name.Length <= 0 || (name[0] >= 48 && name[0] <= 57))
                {
                    name = "f_" + name;
                }
                result = result.Replace("#FUNCTIONNAME#", name);

                // Replace the number of stack arguments
                result = result.Replace("#NUMSTACKARGS#", function.getNumParams().ToString());

                // Generate and replace the call code
                List <string> stackArgNames = new List <string>((int)function.getNumParams());
                for (int i = 1; i <= function.getNumParams(); i++)
                {
                    string argNum = i.ToString();
                    while (argNum.Length < 2)
                    {
                        argNum = "0" + argNum;
                    }
                    stackArgNames.Add("#ARGN" + argNum + "#");
                }
                string asmCode = oAssemblyGenerator.processLabels(oAssemblyGenerator.replaceCommands(
                                                                      function.generateThreadCallCode("#_ECX__#", "#_EDX__#", "#_EAX__#", stackArgNames).Replace("\n", " ").Replace("\r", "")));
                asmCode = oAssemblyGenerator.buildInjectionStringOnly(0, asmCode.Replace("<destination>", "#FUNCTION#"), oProcess.activeProcess,
                                                                      function.address, 0);
                result = result.Replace("#CALLCODE#", asmCode);
            }

            return(result);
        }