Speed Hacking

Discussion in 'MacOS Game Hacking' started by erfg12, Nov 18, 2016.

  1. erfg12

    erfg12 Administrator Staff Member

    Oct 20, 2016
    Likes Received:
    Trophy Points:
    Blog User:
    Arena Points:
    This script will modify at what speed the game runs at. It was written by Zorgie.

    How do you use it? In Bit Slicer, right click in a blank area and press Add variable > Script. Double click on the "script" text. An XCode editor will open, erase all the text in the editor and replace it with the code below. Close the XCode editor, check the activate checkbox next to your variable to use it.

    Change the variable SPEED_MULTIPLIER to run the program at a set speed. (2.0 is set right now, change it)

    Zorgie states that it may or may not work in some games.

    #Game Speed Hack
    #Increase x86-64 game by 2x by overriding mach_absolute_time
    #May not work on games that call gettimeofday or something else instead
    #May not work on games that don't call a time function at all (these areee badddd)
    #May also not work if the function is referenced in more than one executable image (eg, local library)
    #This is not very robust
    from bitslicer import VirtualMemoryError, DebuggerError
    import vmprot
    class Script(object):
        def __init__(self):
            allocationSize = 4096
            self.address = vm.allocate(allocationSize)
            vm.protect(self.address, allocationSize, vmprot.ALL)
            vm.writeBytes(self.address, b'\x90' * allocationSize)
            offsetToStartCode = 0x70
            machAbsoluteTimeAddress = debug.findSymbol("mach_absolute_time", "/libsystem_kernel.dylib")
            ##this is the following C code we want to create in asm##
            # volatile double gSpeedMultiplier = 2.0;
            # volatile uint8_t gInitializedMachAbsoluteTime;
            # volatile uint64_t gMachAbsoluteBaseTime;
            # volatile uint64_t gMachAbsoluteStartTime;
            # volatile uint64_t gMachAbsoluteLastTime;
            # uint64_t my_mach_absolute_time(void)
            # {
            #     uint64_t result;
            #     uint64_t currentTime = mach_absolute_time();
            #     if (!gInitializedMachAbsoluteTime)
            #     {
            #         gInitializedMachAbsoluteTime = 1;
            #         gMachAbsoluteBaseTime = currentTime;
            #         gMachAbsoluteStartTime = (gMachAbsoluteLastTime != 0) ? gMachAbsoluteLastTime : currentTime;
            #         result = gMachAbsoluteStartTime;
            #     }
            #     else
            #     {
            #         result = (uint64_t)(gMachAbsoluteStartTime + (currentTime - gMachAbsoluteBaseTime) * gSpeedMultiplier);
            #     }
            #     gMachAbsoluteLastTime = result;
            #     return result;
            # }
            self.initializedAddress = self.address
            self.speedMultiplierAddress = self.address + 0x8
            baseTimeAddress = self.address + 0x10
            lastTimeAddress = self.address + 0x18
            startTimeAddress = self.address + 0x20
            label1Address = self.address + 0x30
            label2Address = self.address + 0x40
            label3Address = self.address + 0x50
            vm.writeUInt64(self.initializedAddress, 0x0)
            vm.writeDouble(self.speedMultiplierAddress, SPEED_MULTIPLIER)
            vm.writeUInt64(baseTimeAddress, 0)
            vm.writeUInt64(lastTimeAddress, 0)
            vm.writeBytes(startTimeAddress, b'\x00' * 0x10)
            vm.writeUInt64(label1Address, 0x4530000043300000)
            vm.writeUInt64(label1Address + 0x8, 0x0)
            vm.writeUInt64(label2Address, 0x4330000000000000)
            vm.writeBytes(label2Address + 0x8, b'\x00' * 6 + b'\x30\x45')
            vm.writeUInt64(label3Address, 0x43e0000000000000)
            code = "\n".join([
                "push rbp",
                "mov rbp, rsp",
                "sub rsp, 0x20",
                "mov rax, qword %d" % (machAbsoluteTimeAddress),
                "call rax",
                "mov rcx, qword %d" % (self.initializedAddress),
                "mov [rbp-0x10], rax",
                "mov dl, [rcx]",
                "cmp dl, 0x0",
                "jnz J1",
                "mov rax, qword %d" % (lastTimeAddress),
                "mov rcx, qword %d" % (baseTimeAddress),
                "mov rdx, qword %d" % (self.initializedAddress),
                "mov byte [rdx], 0x1",
                "mov rdx, [rbp-0x10]",
                "mov [rcx], rdx",
                "mov rax, [rax]",
                "cmp rax, 0x0",
                "jz J2",
                "mov rax, qword %d" % (lastTimeAddress),
                "mov rax, [rax]",
                "mov [rbp-0x18], rax",
                "jmp J3",
                "mov rax, [rbp-0x10]",
                "mov [rbp-0x18], rax",
                "mov rax, [rbp-0x18]",
                "mov rcx, qword %d" % (startTimeAddress),
                "mov [rcx], rax",
                "mov rax, [rcx]",
                "mov [rbp-0x8], rax",
                "jmp J4",
                "mov rax, %d" % (startTimeAddress),
                "movq xmm0, [rax]",
                "mov rax, qword %d" % (label1Address),
                "movaps xmm1, oword [rax]",
                "punpckldq xmm0, xmm1",
                "mov rax, qword %d" % (label2Address),
                "movapd xmm2, [rax]",
                "subpd xmm0, xmm2",
                "haddpd xmm0, xmm0",
                "mov rax, [rbp-0x10]",
                "mov rcx, qword %d" % (baseTimeAddress),
                "mov rcx, [rcx]",
                "sub rax, rcx",
                "movd xmm3, rax",
                "punpckldq xmm3, xmm1",
                "subpd xmm3, xmm2",
                "haddpd xmm3, xmm3",
                "mov rax, qword %d" % (self.speedMultiplierAddress),
                "movsd xmm1, [rax]",
                "mulsd xmm3, xmm1",
                "addsd xmm0, xmm3",
                "mov rax, qword %d" % (label3Address),
                "movsd xmm1, [rax]",
                "movaps xmm2, xmm0",
                "subsd xmm2, xmm1",
                "cvttsd2si rax, xmm2",
                "mov rcx, 0x8000000000000000",
                "xor rax, rcx",
                "cvttsd2si rcx, xmm0",
                "ucomisd xmm0, xmm1",
                "cmovb rax, rcx",
                "mov [rbp-0x8], rax",
                "mov rax, qword %d" % (lastTimeAddress),
                "mov rcx, [rbp-0x8]",
                "mov [rax], rcx",
                "mov rax, [rbp-0x8]",
                "add rsp, 0x20",
                "pop rbp",
            data = debug.assemble(code, self.address + offsetToStartCode)
            vm.writeBytes(self.address + offsetToStartCode, data)
            self.stubAddress = debug.findSymbol("DYLD-STUB$$mach_absolute_time")
            # debug.log(hex(self.address))
            # debug.log(hex(self.address + offsetToStartCode))
            # debug.log(hex(self.stubAddress))
            stubSize = 0x6
            vm.protect(self.stubAddress, stubSize, vmprot.ALL)
            vm.writeBytes(self.stubAddress, debug.assemble("jmp %d\nnop" % (self.address + offsetToStartCode), self.stubAddress))
            vm.protect(self.stubAddress, stubSize, vmprot.READ | vmprot.EXECUTE)
        def finish(self):
            vm.writeUInt8(self.initializedAddress, 0x0)
            vm.writeDouble(self.speedMultiplierAddress, 1.0)

Share This Page