Results 1 to 7 of 7
  1. #1
    why06's Avatar
    Join Date
    Jul 2009
    Gender
    male
    Location
    IBM
    Posts
    4,304
    Reputation
    170
    Thanks
    2,203
    My Mood
    Flirty

    The Ancient Art of Trainer Making

    Leeched. I take no credit for the following. My personal anotattions will be marked in read, and are only there to provide clarity:
    The Ancient Art of Trainer Making
    By: A Namless Stranger (the author chose to remain anonymous)

    Introduction

    This essay covers the concept of trainers in Windows9x environments in FAQ style.
    If the reader needs a ready made trainer 'skeleton' this is definitely the wrong essay,
    but the author covers all topics the reader needs to know for coding his/her own trainer.
    This way the reader will have the possibility to be proud of his/her own work and he/she won't have to lie about the credits.

    Tools required

    SoftIce (:this is a little dated since SoftIce is discontinued, but any debugger should do fine)
    GameHack (:a memory searcher)
    C compiler
    The reader doesn't need any special drink and/or music. The author won't impose his/her preferences on the reader.

    Target's URL/FTP

    Trainer targets are usually games which can be found at any game store or any major distro site.
    The reader might have already one or two games at home. The author mostly used Expendable in the examples.

    Program History


    A very brief history of trainers:

    Once upon a time cracker groups didn't only crack. They coded their own intros, demos, trainers...
    As long as there were such cracker groups trainers were coded.
    Later the cracker groups stopped coding and specialized in changing jne/je to nop.
    A few individuals conquered the free space and tried to build something like a trainer scene.
    Today the warez groups try to revive the glory of the old days by producing intros, demos and trainers again.

    Essay


    What's a trainer?

    A trainer is a program that allows to manipulate certain values of games such as life points, experience points, ammo, credits, tiberium, gold, armor, .... at run time. In short words - A trainer lets the player cheat.

    What's a 'mega' trainer?

    A mega trainer is a trainer which allows cheating on multiple values.
    It not only freezes life points. It freezes time, ammo, life points, credits, skips levels, adds weapons or whatever.

    What does 'freezing' mean?

    'Freezing' is the effect a player experiences when a trainer stops values from changing.
    This can be accomplished by modifying the game's code in a way that the value won't be accessed anymore or by updating the value in short intervals. Another way to 'freeze' values is to change the code which accesses the value.

    Example:
    If the code that drains your life points looks like this:
    'sub [esi+1C6FCF6],ax' change it to 'mov [esi+1C6FCF6],64'
    and you'll always have 100 LP.

    How does a trainer work?

    In most cases a trainer reacts upon defined key strokes (hotkeys). If the right key is pressed the trainer's 'engine' attemps to patch the memory of the target's process at the address where the desired value/code is located.

    How do I patch the memory of a target's process?

    This is no secret. A lot of material can be found on this topic in various books, magazines, journals...
    The main idea is to utilize the Win32 API function WriteProcessMemory().
    The Win32 API reference sates:


    Code:
    BOOL WriteProcessMemory(
         HANDLE hProcess,               // handle to process whose memory is written to  
         LPVOID lpBaseAddress,          // address to start writing to 
         LPVOID lpBuffer,               // pointer to buffer to write data from
         DWORD nSize,   // number of bytes to write
         LPDWORD lpNumberOfBytesWritten // actual number of bytes written 
    );

    How do I find out all these parameters?


    1. parameter: handle to process whose memory is written to

    A handle to a process can be obtained by calling the functions FindWindow(), GetWindowThreadProcessId() and OpenProcess() like this:


    Code:
    HANDLE hWndTarget = FindWindow( NULL, "Your target's window name");
     DWORD dwProcessId;
     GetWindowThreadProcessId(hWndTarget, &dwProcessId);
     HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    If you're unsure about the target's window name use any of those nice little utilities which list the windows' names.


    Remark:
    If you're using OpenProcess() you should use CloseHandle() too else you will produce a nice little memory leak. Closing a process' handle does NOT cause closing of the associated process!
    For a detailed description of the above functions consult the 'Appendix - Win32 functions used'.

    2. parameter: address to start writing to

    To find the address where you have to patch the target's memory is the trickiest part of it all.
    But there's help. The easiest way to find the right address is to use GameHack 2.0 (www.gamehack.com).
    This little program allows the user to scan a traget's memory for specified values and shows the associated addresses. When the desired value changes the user lets GameHack scan the found addresses for the new value. This way GameHack sorts out the wrong addresses until there are only a few left.
    Get it. Take a look at it. For composing trainers it's a very valuable tool. Of course you can write such a memory scanner on your own and add some advanced pattern scan methods. It's not very difficult if you've coded your own trainer you're already half the way down. Creating the UI is the worst of it all.
    Anyway, use GameHack to find the address where your target stores the desired value.
    Sounds easy, doesn't it? Well, it is, but you may experience some problems relying only on GameHack's addresses. The problem is that you can't take for granted that the desired values are always stored at the same addresses.

    Example:
    You can use addresses found with GameHack to train e.g. Heroes of Might & Magic 3 which works fine in a single scenario or in the first campain but later, in the next campain or in multi player mode the memory layout changes i.e. the addresses of the values change and become useless.

    You see it's necessary to check if your addresses work all the time not only the first five minutes.
    If the address(es) change it's a good idea not to patch the value but the code that accesses the value. The address of the code can be easily located by putting a memory breakpoint on the value's address. (i.e. 'bpm address w' or 'bpr addr1 addr2 w' in SoftIce).

    Example:
    You've found with GameHack that your target stores life points in a WORD at address 0x1C6FCF6.
    Start your target.
    Set a breakpoint 'bpmw 1C6FCF6 w' in softice.
    Resume your target.
    When your life points change SoftIce pops up at a location like this:


    :004657FA 668986F6FCC601 mov word ptr [esi+01C6FCF6], ax

    This line of code changes the value of your life points. To stop the program killing you simply nop it out by writing 7 times 90 (nop) to address 4657FA. If you want to disable your trainer restore the original bytes at address 4657FA.

    Of course there some other ways to locate the right address - scanning for specifc patterns or even dead listing - but I think you got the idea, didn't you?

    3. parameter: pointer to buffer to write data from

    A buffer with data to write from looks like this:


    Code:
     BYTE bNewData[]={0x90,0x90,0x90,0x90,0x90,0x90,0x90};
    Yes, that's really all. Simply create an array holding the bytes you want to write. Pay attention to the Intel byte order!

    4. parameter: number of bytes to write

    Count your bytes or let the function sizeof() count them for you:


    Code:
     DWORD dwNewDataSize = sizeof(bNewData);
    5. parameter: actual number of bytes written

    We don't want to know. Set it to NULL.

    The complete trainer 'engine' should look like this:


    Code:
     BYTE bNewData[]={0x90,0x90,0x90,0x90,0x90,0x90,0x90};
         DWORD dwNewDataSize = sizeof(bNewData);
         HANDLE hWndTarget = FindWindow( NULL, "Expendable");
         DWORD dwProcessId;
         GetWindowThreadProcessId(hWndTarget, &dwProcessId);
         HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
         WriteProcessMemory(hProcess, 0x4657FA, &bNewData, dwNewDataSize, NULL);
         CloseHandle(hProcess);

    Yes, it's that simple. You've already trained Expendable's life points.
    See 'Appendix - a live record' for a brief description of the whole process.


    How do I realize hotkeys?

    Using the Win32 API you can realize hotkeys in various ways.

    1. You can use RegisterHotkey(),
    but you shouldn't 'cause it doesn't work reliable in every case.

    2. You can use SetWindowsHook().
    It works reliable most times but you have to code a DLL to use it for trainer purposes. If another process starts after your trainer wich uses SetWindowsHook() too and doesn't call CallNextHook() your hotkey will be disabled. See the Win32 API reference for details.

    3. You can use GetAsyncKeyState().
    The Win32 API reference states:

    The GetAsyncKeyState function determines whether a key is up or down at the time the function is called, and whether the key was pressed after a previous call to GetAsyncKeyState.

    Code:
    SHORT GetAsyncKeyState(
        int vKey    // virtual-key code
       );
    Parameters:

    vKey - Specifies one of 256 possible virtual-key codes.
    See 'Appendix - All the virtual keys with their values'.

    Return Values:

    If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down....

    To use this function for hotkeys the only thing you have to do is polling. Polling means to let it run in a loop.
    If you call it in a loop like:


    Code:
     while(1)
         {
            if (GetAsyncKeyState(VK_F12)) // if F12 is down or was down since the last call
            {
              train();    // call the trainer engine
            }
         };
    it will consume too much if not all of the cpu time. So it is important to time the 'loop' carefully.
    A timed loop is nothing else than a timer callback function. You can realize it like this:


    Code:
     void CALLBACK PollKeys (HWND hWnd,UINT uMsg,UINT idEvent,DWORD dwTime)
       {
          if (GetAsyncKeyState(VK_F12))    // if F12 is down or was down since the last call
          {
             train();      // call the trainer engine
          }
       }
    Now you need to activate the timer with:

    Code:
    SetTimer(hWnd, 666, 1000, (TIMERPROC) PollKeys);
    Parameters:

    hWnd = your trainer's window handle (HANDLE)
    666 = a unique identifier (UINT)
    1000 = timer interval in ms (UINT)
    PollKeys = address of timer procedure (TIMERPROC)

    Generally it's a good idea to check the game's key layout before choosing your hotkey i.e. don't use F5 if the game uses this for e.g. quicksave.

    Remark:
    The timer callback for the hotkeys consumes cpu time everytime it's called. You shouldn't set the timer interval to very short periods (<75 ms) to avoid slow downs of the running target. Usually a ~1000 ms interval is an acceptable choice.
    See the 'Appendix - Win32 functions used' for detailed information.

    That's all?

    Yes! This is all you need to know for coding trainers. You know how find the right addresses, how to patch memory and how to realize hotkeys. You may need to work out the address finding part a bit but with GameHack on your side you'll soon discover how easy things really are.
    All what's left to do is assemble what you've learned to a working Win32 program and ready is your trainer. You've mastered your way to the olympus of the 'trainer gods'.

    Remark:
    The author tried to make this as short and simple as possible. This is the MINIMUM trainer (i.e. a hotkey and memory patching)! Just a starting point. If you think your trainer won't work this way 'cause you need an in built memory scanner or the use of the debug interface or whatever, good, very good: CODE IT YOURSELF!

    Appendix - a live record


    This is just an EXAMPLE for illustration purposes. If you think that some code locations should be patched another way, good, do so. You should of course add some sanity checking i.e. error handling to your trainer code!



    - Chosen target: Expendable

    - Used GameHack to find the adressses: 04A0134 - time (WORD)
    1C6FCF6 - life points (WORD)
    1C6FCxx - various ammo (WORD)

    - Used 'bpmw 04A0134 w' in Softice to track down the code that modifies
    the time value.

    - Found code that counts down the time value at:
    0041C63E 48 dec eax

    - Wrote down the address and the original byte 48.
    Byte that eliminates the count down: 90 - nop.

    - Used 'bpmw 1C6FCF6 w' in Softice to track down the code that modifies
    the life points.

    - Found code that counts down the life points at:
    004657FA 668986F6FCC601 mov word ptr [esi+01C6FCF6], ax

    - Wrote down the address and the original bytes 66 89 86 F6 FC C6 01.
    Bytes that 'freeze' the life points 7 x 90 - nop.

    - Used 'bpmw 1C6FCxx w' on the various ammo values.

    - Found and eliminated all code that counts down ammo values at:
    0046BB0E 2B44241C sub eax, dword ptr [esp+1C]
    Bytes that eliminate the ammo count down: 4 x 90 - nop.

    - Testing showed that grenades aren't handled by the above ammo
    count down.

    - Used bpmw .... to track down the grenade count down code.

    - Found code that counts down grenades at:
    0041F53C 48 dec eax
    Byte that eliminates that code 90 - nop.

    - Used GameHack to get Expendable's window name: 'Expendable'

    - Set up the trainer engine to open Expendable's process:

    Code:
     HANDLE hWndTarget = FindWindow( NULL, "Expandable");
      DWORD dwProcessId;
      GetWindowThreadProcessId(hWndTarget, &dwProcessId);
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
    - Set up the trainer code to handle 3 hotkeys (time, life, ammo):

    Code:
    void CALLBACK PollKeys (HWND hWnd,UINT uMsg,UINT idEvent,DWORD dwTime)
      {
    	if (GetAsyncKeyState(VK_F10))
    	}
    TrainTime();
    	}
    	if (GetAsyncKeyState(VK_F11))
    	{
    TrainLife();
    	}
    	if (GetAsyncKeyState(VK_F12))
    	{
    TrainAmmo();
    	}
      }
    - Set up the trainer's engine like this:

    Code:
    BYTE bTimeDataNew = 0x90;   // the new code to patch
      BYTE bTimeDataOld = 0x48;   // the old code to restore
      BYTE temp = 0;              // guess
    
      void TrainTime()
      {
        WriteProcessMemory(hProcess, 41C63E, &bTimeDataNew, 1, NULL); // patch the memory
    
        BYTE temp = bTimeDataNew;     // swap bTimeDataNew
        bTimeDataNew = bTimeDataOld;  // with bTimeDataOld
        bTimeDataOld = temp;          // to make sure the next time the hotkey
      // is pressed the trainer switches correct on/off
      }
    - Did the same with ammo and life points.

    - Compiled. Tested. Ready.

    Appendix - Win32 functions used


    WriteProcessMemory

    The WriteProcessMemory function writes memory in a specified process.
    The entire area to be written to must be accessible, or the operation fails.

    BOOL WriteProcessMemory(
    HANDLE hProcess, // handle to process whose memory is written to
    LPVOID lpBaseAddress, // address to start writing to
    LPVOID lpBuffer, // pointer to buffer to write data to
    DWORD nSize, // number of bytes to write
    LPDWORD lpNumberOfBytesWritten // actual number of bytes written
    );


    Parameters

    hProcess

    Identifies an open handle to a process whose memory is to be written to.
    The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access to the process.

    lpBaseAddress

    Points to the base address in the specified process to be written to.
    Before any data transfer occurs, the system verifies that all data in the base address
    and memory of the specified size is accessible for write access.
    If this is the case, the function proceeds; otherwise, the function fails.

    lpBuffer

    Points to the buffer that supplies data to be written into the address space of the
    specified process.

    nSize

    Specifies the requested number of bytes to write into the specified process.

    lpNumberOfBytesWritten

    Points to the actual number of bytes transferred into the specified process.
    This parameter is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.


    Return Values

    If the function succeeds, the return value is nonzero.
    If the function fails, the return value is zero.
    To get extended error information, call GetLastError.
    The function will fail if the requested write operation crosses into an area of the process
    that is inaccessible.

    Remarks

    WriteProcessMemory copies the data from the specified buffer in the current process to the
    address range of the specified process. Any process that has a handle with PROCESS_VM_WRITE
    and PROCESS_VM_OPERATION access to the process to be written to can call the function.
    The process whose address space is being written to is typically, but not necessarily, being
    debugged. The entire area to be written to must be accessible. If it is not, the function
    fails as noted previously.


    FindWindow

    The FindWindow function retrieves the handle to the top-level window whose class name and
    window name match the specified strings. This function does not search child windows.

    HWND FindWindow(
    LPCTSTR lpClassName, // pointer to class name
    LPCTSTR lpWindowName // pointer to window name
    );


    Parameters

    lpClassName

    Points to a null-terminated string that specifies the class name or is an atom that
    identifies the class-name string. If this parameter is an atom, it must be a global atom
    created by a previous call to the GlobalAddAtom function. The atom, a 16-bit value, must
    be placed in the low-order word of lpClassName; the high-order word must be zero.

    lpWindowName

    Points to a null-terminated string that specifies the window name (the window's title).
    If this parameter is NULL, all window names match.


    Return Values

    If the function succeeds, the return value is the handle to the window that has the
    specified class name and window name. If the function fails, the return value is NULL.
    To get extended error information, call GetLastError.


    GetWindowThreadProcessId

    The GetWindowThreadProcessId function retrieves the identifier of the thread that created
    the specified window and, optionally, the identifier of the process that created the window.
    This function supersedes the GetWindowTask function.

    DWORD GetWindowThreadProcessId(
    HWND hWnd, // handle of window
    LPDWORD lpdwProcessId // address of variable for process identifier
    );


    Parameters

    hWnd

    Identifies the window.

    lpdwProcessId

    Points to a 32-bit value that receives the process identifier. If this parameter is not NULL,
    GetWindowThreadProcessId copies the identifier of the process to the 32-bit value; otherwise,
    it does not.


    Return Values

    The return value is the identifier of the thread that created the window.


    OpenProcess

    The OpenProcess function returns a handle of an existing process object.

    HANDLE OpenProcess(
    DWORD dwDesiredAccess, // access flag
    BOOL bInheritHandle, // handle inheritance flag
    DWORD dwProcessId // process identifier
    );


    Parameters

    dwDesiredAccess

    Specifies the access to the process object. For operating systems that support security
    checking, this access is checked against any security descriptor for the target process.
    Any combination of the following access flags can be specified in addition to the
    STANDARD_RIGHTS_REQUIRED access flags:

    Access Description
    PROCESS_ALL_ACCESS Specifies all possible access flags for the process object.
    PROCESS_CREATE_PROCESS Used internally.
    PROCESS_CREATE_THREAD Enables using the process handle in the CreateRemoteThread function
    to create a thread in the process.
    PROCESS_DUP_HANDLE Enables using the process handle as either the source or target process
    in the DuplicateHandle function to duplicate a handle.
    PROCESS_QUERY_INFORMATION Enables using the process handle in the GetExitCodeProcess and
    GetPriorityClass functions to read information from the process
    object.
    PROCESS_SET_INFORMATION Enables using the process handle in the SetPriorityClass function
    to set the priority class of the process.
    PROCESS_TERMINATE Enables using the process handle in the TerminateProcess function
    to terminate the process.
    PROCESS_VM_OPERATION Enables using the process handle in the VirtualProtectEx and
    WriteProcessMemory functions to modify the virtual memory of the
    process.
    PROCESS_VM_READ Enables using the process handle in the ReadProcessMemory function
    to read from the virtual memory of the process.
    PROCESS_VM_WRITE Enables using the process handle in the WriteProcessMemory function
    to write to the virtual memory of the process.
    SYNCHRONIZEWindows NT only: Enables using the process handle in any of the
    wait functions to wait for the process to terminate.

    bInheritHandle

    Specifies whether the returned handle can be inherited by a new process created by the
    current process. If TRUE, the handle is inheritable.

    dwProcessId

    Specifies the process identifier of the process to open.


    Return Values

    If the function succeeds, the return value is an open handle of the specified process.
    If the function fails, the return value is NULL. To get extended error information,
    call GetLastError.

    Remarks

    The handle returned by the OpenProcess function can be used in any function that requires a
    handle to a process, such as the wait functions, provided the appropriate access rights were
    requested. When you are finished with the handle, be sure to close it using the CloseHandle
    function.


    GetAsyncKeystate

    The GetAsyncKeyState function determines whether a key is up or down at the time the function
    is called, and whether the key was pressed after a previous call to GetAsyncKeyState.

    SHORT GetAsyncKeyState(
    int vKey // virtual-key code
    );

    Parameters

    vKey

    Specifies one of 256 possible virtual-key codes.

    Windows NT: You can use left- and right-distinguishing constants to specify certain keys.
    See the Remarks section for further information.
    Windows 95: Windows 95 does not support the left- and right-distinguishing constants available
    on Windows NT.


    Return Values

    If the function succeeds, the return value specifies whether the key was pressed since the
    last call to GetAsyncKeyState, and whether the key is currently up or down.
    If the most significant bit is set, the key is down, and if the least significant bit is set,
    the key was pressed after the previous call to GetAsyncKeyState. The return value is zero if
    a window in another thread or process currently has the keyboard focus.
    Windows 95: Windows 95 does not support the left- and right-distinguishing constants.
    If you call GetAsyncKeyState on the Windows 95 platform with these constants, the return value
    is zero.

    Remarks

    You can use the virtual-key code constants VK_SHIFT, VK_CONTROL, and VK_MENU as values for the
    vKey parameter. This gives the state of the SHIFT, CTRL, or ALT keys without distinguishing
    between left and right.


    SetTimer

    The SetTimer function creates a timer with the specified time-out value.

    UINT SetTimer(
    HWND hWnd, // handle of window for timer messages
    UINT nIDEvent, // timer identifier
    UINT uElapse, // time-out value
    TIMERPROC lpTimerFunc // address of timer procedure
    );

    Parameters

    hWnd

    Identifies the window to be associated with the timer. This window must be owned by the
    calling thread. If this parameter is NULL, no window is associated with the timer and the
    nIDEvent parameter is ignored.

    nIDEvent

    Specifies a nonzero timer identifier. If the hWnd parameter is NULL, this parameter is ignored.

    uElapse

    Specifies the time-out value, in milliseconds.

    lpTimerFunc

    Points to the function to be notified when the time-out value elapses. For more information
    about the function, see TimerProc.
    If lpTimerFunc is NULL, the system posts a WM_TIMER message to the application queue.
    The hwnd member of the message's MSG structure contains the value of the hWnd parameter.


    Return Values

    If the function succeeds, the return value is an integer identifying the new timer.
    An application can pass this value, or the string identifier, if it exists, to the KillTimer
    function to destroy the timer. If the function fails to create a timer, the return value
    is zero.

    Remarks

    An application can process WM_TIMER messages by including a WM_TIMER case statement in the
    window procedure or by specifying a TimerProc callback function when creating the timer.
    When you specify a TimerProc callback function, the DispatchMessage function simply calls
    the callback function instead of the window procedure. Therefore, you need to dispatch
    messages in the calling thread, even when you use TimerProc instead of processing WM_TIMER.

    The wParam parameter of the WM_TIMER message contains the value of the nIDEvent parameter.


    TimerProc

    The TimerProc function is an application-defined callback function that processes WM_TIMER
    messages.

    VOID CALLBACK TimerProc(
    HWND hwnd, // handle of window for timer messages
    UINT uMsg, // WM_TIMER message
    UINT idEvent, // timer identifier
    DWORD dwTime // current system time
    );

    Parameters

    hwnd

    Identifies the window associated with the timer.

    uMsg

    Specifies the WM_TIMER message.

    idEvent

    Specifies the timer's identifier.

    dwTime

    Specifies the number of milliseconds that have elapsed since Windows was started.
    This is the value returned by the GetTickCount function.


    Return Values

    This function does not return a value.

    Remarks

    TimerProc is a placeholder for the application-defined function name.

    Appendix - All the virtual keys with their values.


    /*
    * Virtual Keys, Standard Set
    */
    VK_LBUTTON 0x01
    VK_RBUTTON 0x02
    VK_CANCEL 0x03
    VK_MBUTTON 0x04 /* NOT contiguous with L & RBUTTON */

    VK_BACK 0x08
    VK_TAB 0x09

    VK_CLEAR 0x0C
    VK_RETURN 0x0D

    VK_SHIFT 0x10
    VK_CONTROL 0x11
    VK_MENU 0x12
    VK_PAUSE 0x13
    VK_CAPITAL 0x14


    VK_ESCAPE 0x1B

    VK_SPACE 0x20
    VK_PRIOR 0x21
    VK_NEXT 0x22
    VK_END 0x23
    VK_HOME 0x24
    VK_LEFT 0x25
    VK_UP 0x26
    VK_RIGHT 0x27
    VK_DOWN 0x28
    VK_SELECT 0x29
    VK_PRINT 0x2A
    VK_EXECUTE 0x2B
    VK_SNAPSHOT 0x2C
    VK_INSERT 0x2D
    VK_DELETE 0x2E
    VK_HELP 0x2F

    /* VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39) */
    /* VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A) */

    VK_LWIN 0x5B
    VK_RWIN 0x5C
    VK_APPS 0x5D

    VK_NUMPAD0 0x60
    VK_NUMPAD1 0x61
    VK_NUMPAD2 0x62
    VK_NUMPAD3 0x63
    VK_NUMPAD4 0x64
    VK_NUMPAD5 0x65
    VK_NUMPAD6 0x66
    VK_NUMPAD7 0x67
    VK_NUMPAD8 0x68
    VK_NUMPAD9 0x69
    VK_MULTIPLY 0x6A
    VK_ADD 0x6B
    VK_SEPARATOR 0x6C
    VK_SUBTRACT 0x6D
    VK_DECIMAL 0x6E
    VK_DIVIDE 0x6F
    VK_F1 0x70
    VK_F2 0x71
    VK_F3 0x72
    VK_F4 0x73
    VK_F5 0x74
    VK_F6 0x75
    VK_F7 0x76
    VK_F8 0x77
    VK_F9 0x78
    VK_F10 0x79
    VK_F11 0x7A
    VK_F12 0x7B
    VK_F13 0x7C
    VK_F14 0x7D
    VK_F15 0x7E
    VK_F16 0x7F
    VK_F17 0x80
    VK_F18 0x81
    VK_F19 0x82
    VK_F20 0x83
    VK_F21 0x84
    VK_F22 0x85
    VK_F23 0x86
    VK_F24 0x87

    VK_NUMLOCK 0x90
    VK_SCROLL 0x91

    /*
    * VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
    * Used only as parameters to GetAsyncKeyState() and GetKeyState().
    * No other API or message will distinguish left and right keys in this way.
    */
    VK_LSHIFT 0xA0
    VK_RSHIFT 0xA1
    VK_LCONTROL 0xA2
    VK_RCONTROL 0xA3
    VK_LMENU 0xA4
    VK_RMENU 0xA5

    #if(WINVER >= 0x0400)
    VK_PROCESSKEY 0xE5
    #endif /* WINVER >= 0x0400 */

    VK_ATTN 0xF6
    VK_CRSEL 0xF7
    VK_EXSEL 0xF8
    VK_EREOF 0xF9
    VK_PLAY 0xFA
    VK_ZOOM 0xFB
    VK_NONAME 0xFC
    VK_PA1 0xFD
    VK_OEM_CLEAR 0xFE
    Last edited by why06; 01-28-2010 at 07:30 AM.

    "Every gun that is made, every warship launched, every rocket fired signifies, in the final sense, a theft from those who hunger and are not fed, those who are cold and are not clothed. This world in arms is not spending money alone. It is spending the sweat of its laborers, the genius of its scientists, the hopes of its children. The cost of one modern heavy bomber is this: a modern brick school in more than 30 cities. It is two electric power plants, each serving a town of 60,000 population. It is two fine, fully equipped hospitals. It is some fifty miles of concrete pavement. We pay for a single fighter plane with a half million bushels of wheat. We pay for a single destroyer with new homes that could have housed more than 8,000 people. This is, I repeat, the best way of life to be found on the road the world has been taking. This is not a way of life at all, in any true sense. Under the cloud of threatening war, it is humanity hanging from a cross of iron."
    - Dwight D. Eisenhower

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

    AZN_Noob (07-19-2011),[MPGH]BACKD00R (11-12-2010),cgallagher21 (11-20-2010),ChiLLoNe (06-16-2010),Dusannn (06-02-2012),guidogol (12-05-2010),hack2learn (06-29-2011),ilovecookies (01-26-2010),immortal- (02-20-2010),leevi1995 (07-19-2010),LilChabs (08-20-2012),lolbie (05-23-2011),Melodia (05-25-2010),mesoldierboy (01-14-2011),Mr..Bob (02-08-2011),pdc1975 (06-06-2010),S0aD (05-30-2011),schakkka (06-11-2010),Shadeyz346 (08-21-2010),shefoalaao (08-27-2010),Smash C.A (11-14-2010),SpaceMan (02-18-2010),teufel123 (03-13-2010),Void (01-26-2010),xXShowmanXx (11-16-2010),ZKM (05-15-2010),Zoom (02-19-2010)

  3. #2
    Matrix_NEO006's Avatar
    Join Date
    Feb 2008
    Gender
    male
    Posts
    240
    Reputation
    12
    Thanks
    33
    My Mood
    Lonely
    really good. it would be much readable if u put the C++ codes in [code][code]

  4. #3
    Lolland's Avatar
    Join Date
    Feb 2009
    Gender
    male
    Location
    Lolland!
    Posts
    3,156
    Reputation
    49
    Thanks
    868
    My Mood
    Inspired
    TL;DR, but looks good.

    Thanks for the post.

    Hey why, why don't you use why06jz and your other accounts to post?

  5. #4
    Obama's Avatar
    Join Date
    Dec 2008
    Gender
    male
    Location
    The Black house
    Posts
    22,195
    Reputation
    870
    Thanks
    6,076
    My Mood
    Cool
    He doesn't want to feel like mpgh is controlling him so hes not going to be on while his main is banned.

  6. #5
    Koen's Avatar
    Join Date
    Jan 2010
    Gender
    male
    Location
    I don't know, you tell me bitch.
    Posts
    2,315
    Reputation
    19
    Thanks
    1,690
    My Mood
    Bored
    Amazing, I love this :P

    I'll go test it out again, it's been some time, I've grown rusty xD


    Check out my new topic : Medal of Honor 2010 Retail ESP / Wallhack Here



    Add me :
    On MSN
    Koenschouten@hotmail.com

  7. #6
    Hell_Demon's Avatar
    Join Date
    Mar 2008
    Gender
    male
    Location
    I love causing havoc
    Posts
    3,976
    Reputation
    343
    Thanks
    4,320
    My Mood
    Cheeky
    Ancient indeed, so ancient noone should ever want to use it unless they like to look like fools o__O
    engine hooking is so much better
    Ah we-a blaze the fyah, make it bun dem!

  8. #7
    ilovecookies's Avatar
    Join Date
    Oct 2009
    Gender
    male
    Location
    In the C++ Section
    Posts
    321
    Reputation
    10
    Thanks
    67
    My Mood
    Shocked
    Bookmarked! I'mma fux with Divinity II:Ego Draconis later, see if I can't write a basic trainer for it.

    @HD Go get your degree already mr. smart-e-pants. I still think you have better things in life to do than be a hooker. xD
    Quote Originally Posted by Jules Winnfield View Post
    I am the tyranny of evil men, and you are all the weak. But i'm trying Ringo,i'm trying real hard, to become the shepherd.
    excuse me miss, would you kindly reflect some photons off the epidermis covering your sternum directly into the camera iris or vacate the proximity immediately
    [IMG]https://i882.photobucke*****m/albums/ac23/miki_d420/RealizingYoureALeecher2copy.jpg[/IMG]









Similar Threads

  1. Help with the Trainer Making Kit
    By xaznvietlife in forum Combat Arms Help
    Replies: 5
    Last Post: 09-02-2009, 02:09 AM
  2. What is the most easy way to make a trainer? im a n00b..
    By metallica92 in forum WarRock - International Hacks
    Replies: 27
    Last Post: 05-27-2007, 08:00 AM
  3. new trainer(make)
    By blackmarth in forum WarRock - International Hacks
    Replies: 8
    Last Post: 05-24-2007, 02:31 PM
  4. Trainer Making
    By danxere in forum Hack Requests
    Replies: 1
    Last Post: 05-04-2007, 09:07 PM
  5. Tradeing Good trainer makeing site and addresses site for punkbuster spoof
    By lieutenent in forum WarRock - International Hacks
    Replies: 0
    Last Post: 04-17-2007, 04:19 AM