Page 1 of 2 12 LastLast
Results 1 to 15 of 19

  Click here to go to the first staff post in this thread.   Thread: Runtime QVM Modification

  1. #1
    Join Date
    Jul 2006
    Location
    Austin, Texas
    Posts
    450
    Thanks
    92
    Thanked 30 Times in 17 Posts
    Rep Power
    108

    Runtime QVM Modification

    Recently c0re made a post ( http://forum.gamedeception.net/showthread.php?t=18794 ) that mentioned getting the address of a function in the QVM *after* it's been compiled to native code. I feel I should explain more about what this is and what it can do.

    The Quake VM allows a developer to compile a module to a bytecode form and gain two benefits at a minimal cost of speed. These benefits are security and cross compatibility. Any platform that has an implementation of the Quake VM can run a module compiled to QVM code. QVM code is "more secure" because the QVM code can't do anything that the underlying VM can't(unless you find a vulnerability in the VM itself or the syscalls that are implemented in the VM give you access to do so).

    There are a few different ways that QVMs are used in Q3 based games but I am going to focus on games that compile the QVM code to native code. That is, it converts the QVM instructions to analogous instructions or instruction sequences of the underlying architecture. In my case, it converts QVM bytecode to PowerPC machine code.

    When the game wants to load a module, it calls VM_Create. VM_Create then checks a certain parameter and if the game uses DLLs, it'll load the DLL that was requested. However, if the game uses QVMs it'll call VM_LoadQVM. VM_LoadQVM loads the QVM into memory and does some sanity checks. If everything checks out, it initializes a vm_t struct. This vm_t struct has various information about the VM such as the syscall handler(syscall hooking is as simple as saying vm->systemCalls = &my_syscalls).

    Here's the interesting bit. Once the QVM is loaded into memory, depending on how the game uses QVMs(interpreted or compiled, we're talking about compiling here) it'll compile the QVM to native machine code. It does this by going through every instructing in the QVM and creating analogous instruction sequences. If we are able to modify the QVM code *after* it's loaded into memory, but *before* it's compiled to native code, we can essentially change the QVM itself.

    The easiest way to do this is to hook VM_LoadQVM, call the original, but before returning do something like this....

    Code:
    vmHeader_t *hook_VM_LoadQVM( vm_t *vm, qboolean alloc ) {
        vmHeader_t *ret = orig_VM_LoadQVM(vm, alloc);
    
        if(!strcmp(vm->name, "cgame") {
            vm->instructionPointers[0x0FF5E7] = 0x00; //0x00 is the opcode of whatever you want the instruction to be
        }
    
        return ret;
    }
    instructionPointers is a member of the vm_t struct that is an array of bytes, each representing a QVM opcode. 0x0FF5E7 is the number of the instruction(NOT the offset from the start of the code segment) in the QVM to be modified, and the element at instructionPointers[0xFF5E7] is the opcode of the 0x0FF5E7th instruction in the QVM. I don't know of any standard terminology to be applied here, but the main idea is to note that code addresses in the VM are not represented using byte offsets, they are represented using instruction numbers.

    Now once the QVM is compiled to native machine code, it compiles a completely different sequence of instructions. You've effectively changed the VM code.

    This can be used for ton of things. I'm currently using it for no recoil in Urban Terror. c0re used it to find the address of a function after it's been compiled which would allow for native hooking instead of doing everything in the syscall handler. If you did a bit of work you might be able to implement completely QVM based function hooking.

    I plan on doing more research and releasing a complete paper on the Quake 3 VM, as well as an example(UrT no recoil) utilizing this method.

    It is 6AM here and I may sound like an idiot at the moment. Please excuse me. :-)

    Credits:
    q3 sdk
    c0re, I believe he said he found this or something similar though we did discover it independently

    Shouts: c0re, chaplja, kingOrgy, Ecc, #nixcoders, #game-deception, Naomi

    Happy hacking,
    Macpunk
    Last edited by Macpunk; 01-04-2010 at 04:51 PM.

  2. The Following 2 Users Say Thank You to Macpunk For This Useful Post:

    kingorgy96 (10-30-2010), Twice (07-26-2010)

  3. #2
    Join Date
    Jul 2006
    Location
    Austin, Texas
    Posts
    450
    Thanks
    92
    Thanked 30 Times in 17 Posts
    Rep Power
    108

    Re: Runtime QVM Modification

    I wasn't really clear on how c0re managed to find the offset of a function with this. Basically, he made the first instruction of the function a BREAK instruction. When a QVM BREAK instruction is compiled, it basically becomes " *(int *)0 = 0;" which would clearly crash the program. Wherever the program crashes, that's the offset of the function in machine code. Handy trick.

    --Macpunk

  4. #3
    Join Date
    Sep 2006
    Posts
    849
    Thanks
    71
    Thanked 64 Times in 46 Posts
    Rep Power
    126

    Re: Runtime QVM Modification

    nice idea!

    looking forward to your paper
    - "Double the gun, double the fun!"

  5. #4
    Join Date
    Jun 2004
    Posts
    650
    Thanks
    42
    Thanked 7 Times in 6 Posts
    Rep Power
    125

    Thumbs up Re: Runtime QVM Modification

    Don't expose our secrets. jk. Very good detailed read. Thread should be a sticky. We should make a collaboration of stuff once you get started on making that paper. So hurry up so I can steal your urban terror norecoil.

  6. #5
    Join Date
    Aug 2004
    Posts
    895
    Thanks
    6
    Thanked 36 Times in 9 Posts
    Rep Power
    125

    Re: Runtime QVM Modification

    Quote Originally Posted by c0re View Post
    Don't expose our secrets. jk. Very good detailed read. Thread should be a sticky. We should make a collaboration of stuff once you get started on making that paper. So hurry up so I can steal your urban terror norecoil.
    I'll join you guys on *NIX ;D

  7. #6
    Join Date
    Aug 2008
    Location
    Germany
    Posts
    109
    Thanks
    20
    Thanked 132 Times in 33 Posts
    Rep Power
    79

    Re: Runtime QVM Modification

    Sounds nice...
    Let's assume I wanna hook CG_EntityEvent in the quake3 clone OpenArena.
    The first bad thing is, that the instructionPointers of the cgame-vm aren't set after VM_LoadQVM gets called.
    Second, how do you get the Instruction-Number of the CG_EntityEvent-Entrypoint? I may have the Code-Offset after disassembling the .qvm-File, but that's all.

    Hoping for an answer.

  8. #7
    Join Date
    Jul 2006
    Location
    Austin, Texas
    Posts
    450
    Thanks
    92
    Thanked 30 Times in 17 Posts
    Rep Power
    108

    Re: Runtime QVM Modification

    Are you 100% positive instructionPointers isn't initialized? Make sure, because it should be. I don't have the time to check atm.

    Getting the instruction number of a given instruction is as easy as looking at the disassembly in your favorite disassembler. I know QVMDisas.py(shameless plug) shows this, and I can't remember if any others do. I'm thinking of writing a GUI version of QVMDisas, maybe in Go for shits and giggles.

    Good luck Twice,
    Macpunk

  9. #8
    Join Date
    Aug 2008
    Location
    Germany
    Posts
    109
    Thanks
    20
    Thanked 132 Times in 33 Posts
    Rep Power
    79

    Re: Runtime QVM Modification

    Yes, the vmHeader_t struct is filled with valid infos, but vm->instructionPointers == NULL.
    The earliest point it's accessible to me is in VM_Create.

    Your QVMDisas keeps telling me my qvm-File hasn't got the right size. I'm using ascii's QvmKanker, which doesn't let me know the instruction number.

  10. #9
    Join Date
    Jun 2004
    Posts
    650
    Thanks
    42
    Thanked 7 Times in 6 Posts
    Rep Power
    125

    Re: Runtime QVM Modification

    I wrote a tool for it as well because his qvmdisas was giving me the same similar problems. I'll take a peak and dig through some of my stuff. Where did you download the game at?

  11. #10
    Join Date
    Aug 2008
    Location
    Germany
    Posts
    109
    Thanks
    20
    Thanked 132 Times in 33 Posts
    Rep Power
    79

    Re: Runtime QVM Modification

    Its Homepage:
    http://openarena.ws/smfnews.php

    Download Version 0.8.1
    Download Patch 0.8.5

    EDIT\\ After some research I found out, that the VM gets compiled in VM_Create, hence the instruction pointers are set in there, too. So, wouldn't it be easier to do this stuff in VM_Compile rather than in VM_LoadQVM?

  12. #11
    Join Date
    Aug 2008
    Location
    Germany
    Posts
    109
    Thanks
    20
    Thanked 132 Times in 33 Posts
    Rep Power
    79

    Re: Runtime QVM Modification

    I found the address of CG_EntityEvent in native code, now.
    Is there anything to care of when hooking vm-functions, or does it work the normal way? My hook is working since the game doesn't crash, but I'm still having trouble obtaining the args.

  13. #12
    Join Date
    Aug 2008
    Location
    Germany
    Posts
    109
    Thanks
    20
    Thanked 132 Times in 33 Posts
    Rep Power
    79

    Re: Runtime QVM Modification

    It works, now. Not the cool way, but at least it works. For everyone, who is interested:

    - Disassemble the cgame.qvm with a disassembler, that shows the instructionNumber. I have slightly modified ascii's qvmkanker.
    - After VM_Compile create a detour at the address, which vm->instructionNumber[ yoursHere ] contains.
    - The function-type should be no return value and no parameter.
    - The first thing you do in the hooked function is saving the ESI-Register.
    - Then use my crappy function to obtain the arguments:
    Code:
    // I haven't figured out, what baseAddr actually is, yet (might be some kinda stack).
    //	Look at the function in native code and you will see
    //	a big number at the beginning lines. That's it.
    void* qvmGetArg( int baseAddr, int argIndex, int reg_esi ) // First arg has argIndex == 0
    {
    	void* result = NULL;
    
    	argIndex *= 4;
    	argIndex += 8;
    
    	__asm
    	{
    		mov		eax, 0
    
    		add		eax, [ reg_esi ]
    		add		eax, [ argIndex ]
    		lea		eax, dword ptr [ eax ]
    		add		eax, [ baseAddr ]
    		mov		eax, dword ptr [ eax ]
    		add		eax, [ baseAddr ]
    
    		mov		[ result ], eax
    	}
    
    	return result;
    }
    - The original function can be called by doing something like this:
    Code:
    __asm add	esp, 4
    __asm call	origCG_EntityEvent
    Sorry for my 3-Posts-In-A-Row

  14. The Following User Says Thank You to Twice For This Useful Post:

    Macpunk (07-30-2010)

  15. #13
    Join Date
    Jul 2006
    Location
    Austin, Texas
    Posts
    450
    Thanks
    92
    Thanked 30 Times in 17 Posts
    Rep Power
    108

    Re: Runtime QVM Modification

    At first glance, it would appear that Twice is correct. instructionPointers gets initialized in VM_Compile for the host architecture, *after* VM_LoadQVM is called. Unfortunately, I have no fucking clue why my code for UrT worked if the instructionPointers wasn't initialized. LMFAO.

    Congratulations to Twice. I am *very* pleased to see someone using this technique with some rate of success.

    --Macpunk

  16. #14
    Join Date
    Mar 2011
    Location
    Westberlin!
    Posts
    26
    Thanks
    0
    Thanked 5 Times in 1 Post
    Rep Power
    27

    Re: Runtime QVM Modification

    btw at first code
    Code:
    vmHeader_t *hook_VM_LoadQVM( vm_t *vm, qboolean alloc ) {
        vmHeader_t *ret = orig_VM_LoadQVM(vm, alloc);
    
        if(!strcmp(vm->name, "cgame") {
            vm->instructionPointers[0x0FF5E7] = 0x00; //0x00 is the opcode of whatever you want the instruction to be
        }
    
        return ret;
    }
    there is a missing ) at "if(!strcmp(vm->name, "cgame")"
    Urban Terror MOD Developer. (pr0v0ziert@gmx.de)
    Some published Binary Stuff

  17. #15
    Join Date
    Nov 2007
    Location
    Kangarooland
    Posts
    483
    Thanks
    23
    Thanked 276 Times in 91 Posts
    Rep Power
    5

    Re: Runtime QVM Modification

    Resurrecting a year old thread for a bracket, nice job.
    Patrick: we talked for 6 hours today
    Tamimego: yarp
    Patrick: exactly 6 hours
    Patrick: we are like girls
    Tamimego: no
    Tamimego: we are like programmers

  18. The Following 2 Users Say Thank You to Tamimego For This Useful Post:

    c0re (09-05-2011), kynox (09-05-2011)

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. BF2 Viewangle modification
    By P47R!CK in forum Frostbite Engine
    Replies: 16
    Last Post: 02-18-2009, 05:21 AM
  2. Hooking OGL runtime
    By ReVeR in forum Beginner
    Replies: 1
    Last Post: 08-17-2005, 12:48 PM
  3. Replies: 2
    Last Post: 06-12-2004, 08:41 PM

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •