Esempio n. 1
0
 private void ApplyCode(IEnumerable <DataPatcherUnitTask> instructionTasks)
 {
     using (var executableConnection = new ExecutableConnection(_executableFilePath))
     {
         foreach (var instructionTask in instructionTasks)
         {
             var realPosition = InstructionHelper.CalculateRealPositionFromVirtualPosition(instructionTask.VirtualPosition);
             Debug.WriteLine($"Executing task {instructionTask.TaskId:D2} at location {instructionTask.VirtualPosition:X8} ({realPosition:X8})");
             executableConnection.WriteByteArray(realPosition, instructionTask.Instructions);
         }
     }
 }
Esempio n. 2
0
        /// <summary>
        /// Applies the changes to enter the track editor in the race screen.
        /// This method is only intended for use with gpw.exe v1.01b.
        /// This method requires the Jump Bypass Patcher to be applied beforehand.
        /// Do not invoke this method more than once on the same file.
        /// </summary>
        public void Apply()
        {
            const int replacementWndProcLocation     = 0x44F2B0;
            var       replacementWndProcInstructions = new byte[]
            {
                0xE8, 0x94, 0x59, 0x02, 0x00
            };

            const int newTrackEditorToggleLocation     = 0x00474C49;
            var       newTrackEditorToggleInstructions = new byte[]
            {
                0x55,
                0x8B, 0xEC,
                0x53,
                0x56,
                0x57,
                0x83, 0x3D, 0xDC, 0x31, 0x5F, 0x01, 0x00,
                0x0F, 0x85, 0x0F, 0x00, 0x00, 0x00,
                0xC7, 0x05, 0xDC, 0x31, 0x5F, 0x01, 0x01, 0x00, 0x00, 0x00,
                0xE9, 0x0A, 0x00, 0x00, 0x00,
                0xC7, 0x05, 0xDC, 0x31, 0x5F, 0x01, 0x00, 0x00, 0x00, 0x00,
                0x5F,
                0x5E,
                0x5B,
                0xC9,
                0xC3
            };

            // Open file and write
            using (var executableConnection = new ExecutableConnection(_executableFilePath))
            {
                // Replace code in WndProc to respond to WM_KEYDOWN for VK_F8 (0x77)
                // Overwrites existing functionality to reduce race speed with the F8 key
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(replacementWndProcLocation), replacementWndProcInstructions);

                // Apply new function to toggle track editor flag value
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(newTrackEditorToggleLocation), newTrackEditorToggleInstructions);
            }
        }
Esempio n. 3
0
        private static void ClearExistingBytes(ExecutableConnection executableConnection, int address, int length)
        {
            // Create byte array
            var clearByteArray = new byte[length];

            for (var i = 0; i < clearByteArray.Length; i++)
            {
                clearByteArray[i] = 0xCC; // Fill array with int3 opcode
            }

            // Write byte array
            executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(address), clearByteArray);
        }
Esempio n. 4
0
        public void Apply()
        {
            var firstByte = 0x0047141F;
            var lastByte  = 0x00478F18;

            // Open file and write
            using (var executableConnection = new ExecutableConnection(_executableFilePath))
            {
                // Create byte array of NOPs
                var nopInstructions = new byte[lastByte + 1 - firstByte];
                for (var i = 0; i < nopInstructions.Length; i++)
                {
                    nopInstructions[i] = 0x90;
                }

                // Write byte array of NOPs
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(firstByte), nopInstructions);

                // Get the data-laden instructions
                var teamInstructions      = TeamData.GetInstructions();
                var personnelInstructions = PersonnelData.GetInstructions();
                var driverInstructions    = DriverData.GetInstructions();

                // Write instructions
                var writePosition = InstructionHelper.CalculateRealPositionFromVirtualPosition(firstByte);
                executableConnection.WriteByteArray(writePosition, teamInstructions);
                writePosition += teamInstructions.Length;
                executableConnection.WriteByteArray(writePosition, personnelInstructions);
                writePosition += personnelInstructions.Length;
                executableConnection.WriteByteArray(writePosition, driverInstructions);

                // Apply track changes
                firstByte = 0x005031C6;
                lastByte  = 0x00503EE5;

                // Create byte array of NOPs
                var nopTrackInstructions = new byte[lastByte + 1 - firstByte];
                for (var i = 0; i < nopTrackInstructions.Length; i++)
                {
                    nopTrackInstructions[i] = 0x90;
                }

                // Write byte array of NOPs
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(firstByte), nopTrackInstructions);

                // Get the data-laden instructions
                var trackInstructions = TrackData.GetInstructions();
                writePosition = InstructionHelper.CalculateRealPositionFromVirtualPosition(firstByte);
                executableConnection.WriteByteArray(writePosition, trackInstructions);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Applys the bypass on all detected references to jump functions.
        /// This method is only intended for use with gpw.exe v1.01b.
        /// Do not invoke this method more than once on the same file.
        /// </summary>
        public void Apply()
        {
            // Stats counters
            var textBypassStatsCounter     = 0;
            var text68BypassStatsCounter   = 0;
            var textE8BypassStatsCounter   = 0;
            var textC704BypassStatsCounter = 0;
            var textC705BypassStatsCounter = 0;
            var textC745BypassStatsCounter = 0;
            var textC784BypassStatsCounter = 0;
            var rdataBypassStatsCounter    = 0;
            var dataBypassStatsCounter     = 0;
            var totalBypassesStatsCounter  = 0;

            // Define range where jump functions are located
            const int jumpFunctionFirstReference = 0x00401000; // 0x00000400
            const int jumpFunctionLastReference  = 0x0040452A; // 0x0000392A
            const int jumpFunctionReferenceStep  = 0x00000005; // Bytes

            // Define range of .text section
            const int textSectionFirstByte = 0x00407A60;
            const int textSectionLastByte  = 0x00689FFF;

            // Define range of .rdata section
            const int rdataSectionFirstByte = 0x015ED000;
            const int rdataSectionLastByte  = 0x015EF7CF;

            // Define range of .data section
            const int dataSectionFirstByte = 0x015F0000;
            const int dataSectionLastByte  = 0x0160CBFF;

            // Create collection to hold details of each jump function
            var jumpFunctions = new Collection <JumpFunction>();

            // Populate collection with the positions of the jump functions
            for (var i = jumpFunctionFirstReference; i <= jumpFunctionLastReference; i += jumpFunctionReferenceStep)
            {
                jumpFunctions.Add(new JumpFunction()
                {
                    Position = i
                });
            }

            // Collection of changes to make after read
            var jumpBypasses = new Collection <JumpFunction>();

            // Open file and read
            using (var executableConnection = new ExecutableConnection(_executableFilePath))
            {
                // Populate collection with the referenced function inside each jump function
                foreach (var item in jumpFunctions)
                {
                    var instructions = new byte[4];

                    instructions = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(item.Position) + 1, instructions.Length);

                    item.Function = item.Position + 5 + BitConverter.ToInt32(instructions, 0);
                }

                // Analyse .text section for opcodes with references to jump functions
                for (var i = textSectionFirstByte; i <= textSectionLastByte - 4; i++)
                {
                    // Read two bytes
                    var opcodeCheck = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i), 2);

                    // Check for matching opcodes
                    if (opcodeCheck[0] == 0x68)
                    {
                        // Read last four bytes of five byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 1), 4);
                        var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 1, Function = item.Function
                                });
                                textBypassStatsCounter++;
                                text68BypassStatsCounter++;
                            }
                        }
                    }
                    else if (opcodeCheck[0] == 0xE8)
                    {
                        // Read last four bytes of five byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 1), 4);
                        var referenceCheckValue = i + 5 + BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Calc relative function position for opcode xE8
                                var functionOffset = BitConverter.GetBytes(item.Function - (i + 5));

                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 1, Function = BitConverter.ToInt32(functionOffset, 0)
                                });
                                textBypassStatsCounter++;
                                textE8BypassStatsCounter++;
                            }
                        }
                    }
                    else if ((opcodeCheck[0] == 0xC7) && (opcodeCheck[1] == 0x04))
                    {
                        // Read last four bytes of eleven byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 7), 4);
                        var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 7, Function = item.Function
                                });
                                textBypassStatsCounter++;
                                textC704BypassStatsCounter++;
                            }
                        }
                    }
                    else if ((opcodeCheck[0] == 0xC7) && (opcodeCheck[1] == 0x05))
                    {
                        // Read last four bytes of ten byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 6), 4);
                        var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 6, Function = item.Function
                                });
                                textBypassStatsCounter++;
                                textC705BypassStatsCounter++;
                            }
                        }
                    }
                    else if ((opcodeCheck[0] == 0xC7) && (opcodeCheck[1] == 0x45))
                    {
                        // Read last four bytes of seven byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 3), 4);
                        var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 3, Function = item.Function
                                });
                                textBypassStatsCounter++;
                                textC745BypassStatsCounter++;
                            }
                        }
                    }
                    else if ((opcodeCheck[0] == 0xC7) && (opcodeCheck[1] == 0x84))
                    {
                        // Read last four bytes of eleven byte signature
                        var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i + 7), 4);
                        var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                        // Do bytes match a reference to a jump function in the collection
                        foreach (var item in jumpFunctions)
                        {
                            if (referenceCheckValue == item.Position)
                            {
                                // Add bypass to collection
                                jumpBypasses.Add(new JumpFunction()
                                {
                                    Position = i + 7, Function = item.Function
                                });
                                textBypassStatsCounter++;
                                textC784BypassStatsCounter++;
                            }
                        }
                    }
                }

                // Analyse .rdata section for references to jump functions
                for (var i = rdataSectionFirstByte; i <= rdataSectionLastByte - 3; i++)
                {
                    // Read four bytes
                    var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i), 4);
                    var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                    // Do bytes match a reference to a jump function in the collection
                    foreach (var item in jumpFunctions)
                    {
                        if (referenceCheckValue == item.Position)
                        {
                            // Add bypass to collection
                            jumpBypasses.Add(new JumpFunction()
                            {
                                Position = i, Function = item.Function
                            });
                            rdataBypassStatsCounter++;
                        }
                    }
                }

                // Analyse .data section for references to jump functions
                for (var i = dataSectionFirstByte; i <= dataSectionLastByte - 3; i++)
                {
                    // Read four bytes
                    var referenceCheckBytes = executableConnection.ReadByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(i), 4);
                    var referenceCheckValue = BitConverter.ToInt32(referenceCheckBytes, 0);

                    // Do bytes match a reference to a jump function in the collection
                    foreach (var item in jumpFunctions)
                    {
                        if (referenceCheckValue == item.Position)
                        {
                            // Add bypass to collection
                            jumpBypasses.Add(new JumpFunction()
                            {
                                Position = i, Function = item.Function
                            });
                            dataBypassStatsCounter++;
                        }
                    }
                }
            }

            // Open file and write
            using (var executableConnection = new ExecutableConnection(_executableFilePath))
            {
                // Apply bypasses
                foreach (var item in jumpBypasses)
                {
                    // Write four bytes
                    executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(item.Position), BitConverter.GetBytes(item.Function));
                    totalBypassesStatsCounter++;
                }

                // Remove redundant jump functions
                var clearByteArray = new byte[0x352F];
                for (var i = 0; i < clearByteArray.Length; i++)
                {
                    clearByteArray[i] = 0xCC; // Fill array with int3 opcode
                }
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(jumpFunctionFirstReference), clearByteArray);
            }

            Debug.WriteLine(" .text section bypass count: " + textBypassStatsCounter);
            Debug.WriteLine("   .text opCode    68 count: " + text68BypassStatsCounter);
            Debug.WriteLine("   .text opCode    E8 count: " + textE8BypassStatsCounter);
            Debug.WriteLine("   .text opCode C7 04 count: " + textC704BypassStatsCounter);
            Debug.WriteLine("   .text opCode C7 05 count: " + textC705BypassStatsCounter);
            Debug.WriteLine("   .text opCode C7 45 count: " + textC745BypassStatsCounter);
            Debug.WriteLine("   .text opCode C7 84 count: " + textC784BypassStatsCounter);
            Debug.WriteLine(".rdata section bypass count: " + rdataBypassStatsCounter);
            Debug.WriteLine(" .data section bypass count: " + dataBypassStatsCounter);
            Debug.WriteLine("----------------------------");
            Debug.WriteLine("             Total bypasses: " + totalBypassesStatsCounter);
        }
Esempio n. 6
0
        /// <summary>
        /// Applys the new switch idiom to all predefined target addresses within.
        /// This method is only intended for use with gpw.exe v1.01b.
        /// Do not invoke this method more than once on the same file.
        /// </summary>
        public void Apply()
        {
            // New instruction template, 0x00 will be overwritten by jump/indirect table locations
            var newInstructions = new byte[] {
                0x33, 0xD2, 0x8A, 0x90, 0x00, 0x00, 0x00, 0x00, 0xFF,
                0x24, 0x95, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90
            };

            // File location of each switch idiom to modify
            var switchIdiomLocation = new long[] {
                0x00021A9C, 0x0002EFE2, 0x00034FC5, 0x000387AE, 0x0003AD71, 0x00044C99,
                0x0004EFD4, 0x0004FEDF, 0x0005F0EB, 0x00078996, 0x0008E2F7, 0x0008E850,
                0x0008EE9E, 0x00093B02, 0x0009507E, 0x0009ED62, 0x000A5F9E, 0x001130A4,
                0x001131B2, 0x00116871, 0x001170FA, 0x00117198, 0x001180EF, 0x0011C88A,
                0x00120DB0, 0x00120E1E, 0x0012DE99, 0x00130D87, 0x00138A8E, 0x00139B64,
                0x0013F6D1, 0x0013FE4B, 0x0013FF79, 0x001499BF, 0x00149A59, 0x0014A727,
                0x0014A7F6, 0x0014B09F, 0x0014B5EF, 0x0014BC5D, 0x0014BCEE, 0x00154C42,
                0x00154F47, 0x00155176, 0x0015533B, 0x001556C7, 0x0015BC0B, 0x0015DC59,
                0x0015DE57, 0x0015DF24, 0x0015DFA2, 0x0015E1C0, 0x001675AA, 0x0017344E,
                0x0018BF05, 0x0019012B, 0x001A1AEE, 0x001A1D23, 0x001A1DCC, 0x001A2F62,
                0x001AE631, 0x001B4CAE, 0x001B50EC, 0x001D4F47, 0x001DCC1B, 0x001DE2A5,
                0x001E30E6, 0x001F4FE5, 0x001F51EF, 0x001F74D8, 0x00203BFE, 0x0021862C,
                0x00218788, 0x00218977, 0x0021BFB7, 0x0021C26A, 0x0025FF17, 0x0026028F,
                0x0026082B, 0x002622D7, 0x002638F9, 0x002739E7, 0x00276F01, 0x00276FE8,
                0x00278CB2, 0x00278D53, 0x00278FDD, 0x002790F0
            };

            // For every switch idiom (represented by the target address)
            for (var i = 0; i < switchIdiomLocation.Length; i++)
            {
                var instructions = new byte[18];

                // Get current instructions at target address
                using (var executableConnection = new ExecutableConnection(_executableFilePath))
                {
                    instructions = executableConnection.ReadByteArray(switchIdiomLocation[i], instructions.Length);
                }

                // Extract table location values
                var indirectTableLocation = new byte[4];
                var jumpTableLocation     = new byte[4];

                // Indirect table location is represented in little endian byte order
                for (var j = 0; j < indirectTableLocation.Length; j++)
                {
                    // Read four bytes
                    indirectTableLocation[j] = instructions[j + 2];
                }

                // Jump table location is represented in little endian byte order
                for (var j = 0; j < jumpTableLocation.Length; j++)
                {
                    // Read four bytes
                    jumpTableLocation[j] = instructions[j + 14];
                }

                // Create copy of new instructions by overwriting current instructions
                Array.Copy(newInstructions, instructions, instructions.Length);

                // Modify with new instructions
                for (var j = 4; j < 8; j++)
                {
                    // Write four bytes
                    instructions[j] = indirectTableLocation[j - 4];
                }
                for (var j = 11; j < 15; j++)
                {
                    // Write four bytes
                    instructions[j] = jumpTableLocation[j - 11];
                }

                using (var executableConnection = new ExecutableConnection(_executableFilePath))
                {
                    // Write new instructions to target address
                    executableConnection.WriteByteArray(switchIdiomLocation[i], instructions);
                }
            }
        }
Esempio n. 7
0
        /// <summary>
        /// Applys the redirect on all calls to GlobalUnlock where the return value is tested.
        /// This method is only intended for use with gpw.exe v1.01b.
        /// This method requires the Jump Bypass Patcher to be applied beforehand.
        /// Do not invoke this method more than once on the same file.
        /// </summary>
        public void Apply()
        {
            // New function to call GlobalUnlock on behalf of original calls
            var newGlobalUnlockInstructions = new byte[] {
                0x55,                               // push    ebp
                0x8B, 0xEC,                         // mov     ebp, esp
                0x53,                               // push    ebx
                0x56,                               // push    esi
                0x57,                               // push    edi

                // loc_call
                0x8B, 0x45, 0x08,                   // mov     eax, [ebp + arg_0]
                0x50,                               // push    eax
                0xFF, 0x15, 0xAC, 0xD5, 0x60, 0x01, // call    GlobalUnlock

                // if result greater than 1, go to loc_call
                0x83, 0xF8, 0x01,                   // cmp     eax, 1
                0x0F, 0x8F, 0xED, 0xFF, 0xFF, 0xFF, // jg      loc_call

                // if result is 0, jump to loc_continue
                0x85, 0xC0,                         // test    eax, eax
                0x0F, 0x84, 0x0C, 0x00, 0x00, 0x00, // jz      loc_continue

                // therefore result must be 1
                // call once more for safety (and ignore if still returns 1)
                0x8B, 0x45, 0x08,                   // mov     eax, [ebp + arg_0]
                0x50,                               // push    eax
                0xFF, 0x15, 0xAC, 0xD5, 0x60, 0x01, // call    GlobalUnlock

                // ensure eax is set to 0
                0x33, 0xC0,                         // xor     eax, eax

                // loc_contine
                0x5F,                               // pop     edi
                0x5E,                               // pop     esi
                0x5B,                               // pop     ebx
                0xC9,                               // leave
                0xC3                                // retn
            };

            // File location to insert new function
            var newGlobalUnlockLocation = 0x00474C17;

            // New redirect instruction template, 0x00 will be overwritten by relative function location
            var newRedirectInstructions = new byte[] {
                0x90, 0xE8, 0x00, 0x00, 0x00, 0x00
            };

            // File location of each global unlock call to modify
            var globalUnlockLocation = new long[]
            {
                0x00413322, 0x0041362A, 0x004138A1, 0x00413AB3, 0x00413FB3, 0x00414031, 0x00414219, 0x0041424B,
                0x004142CC, 0x004144B0, 0x0041459F, 0x0041462E, 0x004146A0, 0x0041487B, 0x00414A5E, 0x00414A9A,
                0x00414AE2, 0x0043B9A5, 0x005C5525
            };

            // Open file and write
            using (var executableConnection = new ExecutableConnection(_executableFilePath))
            {
                // Apply new function to call GlobalUnlock on behalf of original calls
                executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(newGlobalUnlockLocation), newGlobalUnlockInstructions);

                // Apply redirects
                foreach (var item in globalUnlockLocation)
                {
                    var redirectInstructions     = new byte[6];
                    var relativeFunctionLocation = BitConverter.GetBytes(newGlobalUnlockLocation - (item + 6));

                    // Create copy of new instructions by overwriting current instructions
                    Array.Copy(newRedirectInstructions, redirectInstructions, redirectInstructions.Length);

                    // Modify with new instructions
                    for (var i = 2; i < 6; i++)
                    {
                        // Write first four bytes of eight into the last four bytes of six
                        redirectInstructions[i] = relativeFunctionLocation[i - 2];
                    }

                    // Write six bytes
                    executableConnection.WriteByteArray(InstructionHelper.CalculateRealPositionFromVirtualPosition(item), redirectInstructions);
                }
            }
        }
Esempio n. 8
0
        private static void WriteNewBytes(ExecutableConnection executableConnection, int address, byte[] instructions)
        {
            var realAddress = InstructionHelper.CalculateRealPositionFromVirtualPosition(address);

            executableConnection.WriteByteArray(realAddress, instructions);
        }