Skip to content

Instantly share code, notes, and snippets.

@zorgiepoo
Last active February 11, 2018 16:12
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zorgiepoo/5f769893ad2a7f58a747 to your computer and use it in GitHub Desktop.
Save zorgiepoo/5f769893ad2a7f58a747 to your computer and use it in GitHub Desktop.
Game speed hack prototype; still WIP
#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
SPEED_MULTIPLIER = 2.0
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
#_gInitializedMachAbsoluteTime
vm.writeUInt64(self.initializedAddress, 0x0)
#_gSpeedMultiplier
vm.writeDouble(self.speedMultiplierAddress, SPEED_MULTIPLIER)
#_gMachAbsoluteBaseTime
vm.writeUInt64(baseTimeAddress, 0)
#_gMachAbsoluteLastTime
vm.writeUInt64(lastTimeAddress, 0)
#_gMachAbsoluteStartTime
vm.writeBytes(startTimeAddress, b'\x00' * 0x10)
#L1
vm.writeUInt64(label1Address, 0x4530000043300000)
vm.writeUInt64(label1Address + 0x8, 0x0)
#L2
vm.writeUInt64(label2Address, 0x4330000000000000)
vm.writeBytes(label2Address + 0x8, b'\x00' * 6 + b'\x30\x45')
#L3
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",
"J2:",
"mov rax, [rbp-0x10]",
"mov [rbp-0x18], rax",
"J3:",
"mov rax, [rbp-0x18]",
"mov rcx, qword %d" % (startTimeAddress),
"mov [rcx], rax",
"mov rax, [rcx]",
"mov [rbp-0x8], rax",
"jmp J4",
"J1:",
"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",
"J4:",
"mov rax, qword %d" % (lastTimeAddress),
"mov rcx, [rbp-0x8]",
"mov [rax], rcx",
"mov rax, [rbp-0x8]",
"add rsp, 0x20",
"pop rbp",
"ret"
])
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))
vm.pause()
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)
vm.unpause()
def finish(self):
vm.pause()
vm.writeUInt8(self.initializedAddress, 0x0)
vm.writeDouble(self.speedMultiplierAddress, 1.0)
vm.unpause()
@skerit
Copy link

skerit commented Feb 11, 2018

How exactly do you tell it which process to target?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment