Code:
Public Function GetRegisterOnce(ByVal sourceLoc As IntPtr, ByVal register As CpuRegister) As IntPtr
Dim _codeCaveStartLoc As IntPtr = Malloc(200) 'in wow.exe ram, used to store (then run) bytecode. todo: auto-sized
Dim _codeCaveCode(0) As Byte ' stores bytecode to inject into process
Dim _origAsmLoc As IntPtr = _codeCaveStartLoc.ToInt32 + 100 'executable's orig. code copied here (8 bytes)
Dim _rtnValueLoc As IntPtr = _codeCaveStartLoc.ToInt32 + 175 'where our asm code copies the register to
Dim _origProcessByteCode() As Byte = ReadBytes(sourceLoc, 8) 'orig. wow.exe code (must be restored by codecave code)
WriteBytes(_origAsmLoc, _origProcessByteCode)
'Create codeCave byte code (copy,cleanup,return)
'copy register to our dump location
Dim registerAsString As String = System.Enum.GetName(GetType(CpuRegister), register) 'awk
Dim sb As New System.Text.StringBuilder
With sb
'save registers (that I use) to avoid corrupting stack
.AppendLine("push eax")
.AppendLine("push ebx")
.AppendLine("push edx")
'copy our rtnValueLoc to a register
.AppendLine("mov eax, " & _rtnValueLoc.ToInt32.ToString)
'copy register into the [value] at rtnValueLoc
.AppendLine("mov [eax], " & registerAsString) 'the magic happens right here
'clean-up
.AppendLine("mov eax, " & sourceLoc.ToInt32.ToString)
.AppendLine("mov ebx, " & _origAsmLoc.ToInt32.ToString)
.AppendLine("mov edx, [ebx]") 'copy 4 bytes from _origAsmLoc to exe source_loc
.AppendLine("mov [eax], edx") ' ie. unpatch using exe's orig source code
.AppendLine("add ebx, 4")
.AppendLine("add eax, 4")
.AppendLine("mov edx, [ebx]") 'copy next/last 4 bytes back into wow.exe
.AppendLine("mov [eax], edx") '
.AppendLine("pop edx") 'pop order is important. duh.
.AppendLine("pop ebx")
.AppendLine("pop eax")
End With
Dim _individualAsmStrings() As String = Split(sb.ToString, Environment.NewLine)
If _individualAsmStrings(_individualAsmStrings.Length - 1) = "" Then ReDim Preserve _individualAsmStrings(_individualAsmStrings.Length - 2) 'chop off empty string: last call should use ASM.Append() Not AppendLine() ? works.
Dim _cdc() As Byte 'codecave bytecode, except 5 bytes for a JMP command.
_cdc = GetByteCode(_individualAsmStrings)
Dim _jmpAsm(4) As Byte ' hand crafted JMP command : instead of using managed_fasm.assemble("JMP address")..Educational.
_jmpAsm(0) = &HE9 'opcode for Relative jump
ReDim _codeCaveCode(_cdc.Length - 1 + 5) '5 = _jmpAsm.Length in bytes, 'E9' + 4 byte ram address
Dim _bts() As Byte = BitConverter.GetBytes(sourceLoc.ToInt32 - _codeCaveStartLoc.ToInt32 - _codeCaveCode.Length) 'address where codecave jmps back to. (ie. sourceLoc, but relative* because I use 'E9' for JMP not 'FF') !!!
_bts.Reverse() 'endianness of bytecode
Array.Copy(_bts, 0, _jmpAsm, 1, 4) 'copy the 4 byte address into the asm jmp command
Array.Copy(_cdc, _codeCaveCode, _cdc.Length) 'copy the codecavecode(w/o jmp) to array
Array.Copy(_jmpAsm, 0, _codeCaveCode, _codeCaveCode.Length - 5, 5) 'copy jmp command to end of array
WriteBytes(_codeCaveStartLoc, _codeCaveCode) 'write the bytecode (100% complete now) into process's ram.
''Write Jmp command to sourceLoc. ie. WILL make it re-route
_bts = BitConverter.GetBytes(_codeCaveStartLoc.ToInt32 - (sourceLoc.ToInt32 + 5)) 'address of beginning of codecave. +5 because in asm, JMP is relative* to the NEXT instruction. Jmp is 5 bytes long.
_bts.Reverse() 'indianness of byte code
Array.Copy(_bts, 0, _jmpAsm, 1, 4) '_jmpAsm is now complete, again.
Dim scanStartTime As DateTime = Date.Now
Dim scanTimeLapse As TimeSpan
'enable ram to be written to
Dim _origAccessRights As UInt32 = 0
Dim _mbi As MEMORY_BASIC_INFORMATION
VirtualQueryEx(_targetProcessHandle, sourceLoc, _mbi, _mbiSize)
If Not (_mbi.Protect And MemoryAllocationProtectionType.PAGE_EXECUTE_READWRITE) Then
'change rights
If Not VirtualProtectEx(_targetProcessHandle, _mbi.BaseAddress, _mbi.RegionSize, MemoryAllocationProtectionType.PAGE_EXECUTE_READWRITE, _origAccessRights) Then
modPublic.DoOutput("CodeCave::CopyRegisterOnce::VirtualProtectEx fail 0x" & sourceLoc.ToString("X"))
Return IntPtr.Zero 'can't write to this memory?
End If
End If
'if we get here, orig perms. were exec_r_w or VirtualProtect worked
WriteBytes(sourceLoc, _jmpAsm) ' .Patch()
'modPublic.DoOutput("scanning for rtn value...")
Dim _origJumpLocCode As UInt64 = BitConverter.ToUInt64(_origProcessByteCode, 0) 'because orig asm is only 8 bytes i used int64. should use byte array.
Do Until ReadUInt64(sourceLoc) = _origJumpLocCode 'do until the codecave unpatches itsself!(ie. orig code has been restored) nifty.
scanTimeLapse = Date.Now.Subtract(scanStartTime)
If scanTimeLapse.TotalSeconds > 10 Then
'we need to manually put the source Bcode back
WriteBytes(sourceLoc, _origProcessByteCode)
If _origAccessRights <> 0 Then
'we changed rights, change them back.
VirtualProtectEx(_targetProcessHandle, _mbi.BaseAddress, _mbi.RegionSize, _origAccessRights, New UInt32)
End If
modPublic.DoOutput("Time limit exceeded. 10s")
Return IntPtr.Zero ' wow.exe code never got called. or other asm/codecavecode error?...
End If
Loop
'if we get here, success!
Dim _rtnPtr As IntPtr = ReadIntPtr(_rtnValueLoc) ' == register value !
'modPublic.DoOutput("success: val=" & _rtnPtr.ToString("x"))
'free mem allocated for codecave inside wow
VirtualFreeEx(_targetProcessHandle, _codeCaveStartLoc, 200, MemoryAllocationState.Decommit)
'restore rights?
If _origAccessRights <> 0 Then
VirtualProtectEx(_targetProcessHandle, _mbi.BaseAddress, _mbi.RegionSize, _origAccessRights, New Int32)
End If
Return _rtnPtr
End Function