Jump to content
  • Advertisement
Sign in to follow this  
dave

Rendering Over The Back Buffer Of Another Game?

This topic is 4697 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Im curious as to how a program like FRAPS overlays the FPS over the backbuffer of any other game. Im interested in writing a program that could be used for tactics in FPS online games, where through certain key combinations you could flash common tactical commands on everyones screens, in a corner or something. This brings me to another problem, again like fraps, how do you track hotkeys for things like this, when the keyboard is supposedly exclusive to the game that is running. Any help is appreciated, ace

Share this post


Link to post
Share on other sites
Advertisement
I don't think this belong here per say. windows has something called hooks that allow you to monitor messages to windows, I used it to write a key logger once. as for the screen I'd assume your application would need highter prioprity to the screen buffer then normal. I can't remember how to do this with directx, but it can be done.

here are the links for the hooks
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/abouthooks.asp

in order to hook applications that aren't yours you need to set up a system hook and needs to be implimented in a dll, but I think if your program is the one invoking the game, you could possibly get around this(shrugs).

[Edited by - timw on September 1, 2005 8:47:38 PM]

Share this post


Link to post
Share on other sites
I didn't notice you wanted to render to back-buffer, I thought you only wanted to render to the front-buffer.

I've been thinking of this problem and noted you wanted to render to the back buffer, if their back buffer is in main memory you could use virtual memory opperations to write to them. I once wrote a program to modify a games memory to cheat for money and such, for modifying virtual memory check out this link. the problem of accessing another processes memory is really just a matter of getting a sutible handle to the process(that is a handle with VM_OPERATION access) or something like that. the problem with this approach, is I'm not sure exactly how video memory is mapped to virtual memory. the problem is what exactly does the directX funciton lock() do? and when it's not locked what does that mean exactly? Does it mean the memory is no longer aliased in the mmu? or does it mean the memory is simply flipped from read-write to read only? to be honest I really don't know the answer to that question, but if you figure it please post.


http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dngenlib/html/msdn_virtmm.asp

If I recall, the functions of most interest to you is VirtualQueryEx() and the ReadProcessMemroy(), WriteProcessMemory() functions, which will return you a pointer into the processes virtual memory.


ohhh oohhh think about this for a second I think I have an idea I know this would work. suppose you had access to the memory locations of the required arguments to the programs call to lock(), (the pointer to the com objects and such). this could be easily done with some hacking and a little time. you simply read in those values with using the virtualqueryEx function and you make use that com object to make a call to the function lock() and bam you have a pointer to the processes surface. now I THINK this pointer would only be valid in the context of the other programs virtual memory, so you'd have to use this address to another call to virtualQueryEx(), and WriteProcessMemory() you can't simply use the pointers as you would your own, because the mmu mapping is different on your application then theirs, however it could be mapped to your process too, I really don't know, Ive never tried anything like this, but I do know if you can't do it with direct-x this is the way to go, because it WILL work, if done properly, the only problem is doing it properly, lol which I leave to you.

Tim

[Edited by - timw on September 1, 2005 9:40:51 PM]

Share this post


Link to post
Share on other sites
What you'd be looking for are called hooks, or wrappers. This is a common way to make cheats, hacks, (aimbots, wallhacks etc) for games. You don't really write to the back buffer, instead you just add in your own code to the games graphics functions, such as glDrawElements or something similar that you know the game uses. I'm not sure how exactly FRAPS does it, most likely I'm thinking a general hook to the window OpenGL32.dll or the directx dll.

Here is a simple OpenGL wrapper, with the bonus of being able to draw whatever text you want wherever you want. When you compile, it will compile as the opengl32.dll. Put this file into your game directory and for SOME OpenGL games it will "wrap" your functions around the normal OpenGL commands. Newer games probably won't work because this is a common way to hack, which in case you need to look into hooking, which involves injecting your "wrapper" at runtime, once the program has already been started. You can also look at clienthooking, which will even give you access to the memory that the game is using, not just the graphics functions. This is, as you can imagine, a bit harder, and may be more complicated than what you're looking for. If this wrapper is all you're looking for, feel free to ask questions, I'm fairly knowledgeable on wrappers. Hope this helped! Enjoy!

http://www.angelfire.com/space2/artib/SimpleOpenGLWrapper.rar

Share this post


Link to post
Share on other sites
you have to "catch" calls to DirectX functions. First you need to load your dll with the target process, then replace the Direct*() function. Than change values in vtable of produced interface and then you will have access to whatever you want. Go to www.combovideos.com, here you will find a section called "fierce gear" grab my source (GPLd) and you will have a complete COM memory attack.

Share this post


Link to post
Share on other sites
Bjogio, how would you load your dll with the target process, a global hook?? I know you could do it this way, but is there a simpler way?? I to am interested in this.

Tim

Share this post


Link to post
Share on other sites
is rather simple and you can look at the entire source code.
Basically I made these steps:

1) CreateProcess(name, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, Folder, &si, &pi ));
in this way I begin the new process but it is suspended.

2) then I use these source:


/*
The Fierce Gear project
Copyright (C) 2005 Francesco Bigiarini

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#include "Inject.h"
#include "FierceGear.h"
#include <tchar.h>
#include <malloc.h>
#include "Accctrl.h"
#include "Aclapi.h"

namespace Inject
{

BOOL AdjustDacl(HANDLE h, DWORD DesiredAccess)
{
SID world = { SID_REVISION, 1, SECURITY_WORLD_SID_AUTHORITY, 0 };

EXPLICIT_ACCESS ea =
{
DesiredAccess, SET_ACCESS, NO_INHERITANCE,
{
0, NO_MULTIPLE_TRUSTEE, TRUSTEE_IS_SID, TRUSTEE_IS_USER,
reinterpret_cast<LPTSTR>(&world)
}
};

ACL* pdacl = 0;
DWORD err = SetEntriesInAcl(1, &ea, 0, &pdacl);
if(err == ERROR_SUCCESS)
{
err = SetSecurityInfo(h, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, 0, 0, pdacl, 0);
LocalFree(pdacl);
return(err == ERROR_SUCCESS);
} else return FALSE;
}

BOOL EnableTokenPrivilege(HANDLE htok, LPCTSTR szPrivilege, TOKEN_PRIVILEGES& tpOld)
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if(LookupPrivilegeValue(0, szPrivilege, &tp.Privileges[0].Luid))
{
DWORD cbOld = sizeof tpOld;
if (AdjustTokenPrivileges(htok, FALSE, &tp, cbOld, &tpOld, &cbOld))
return(ERROR_NOT_ALL_ASSIGNED != GetLastError());
else return FALSE;
} else return FALSE;
}


// Corresponding restoration helper function
BOOL RestoreTokenPrivilege(HANDLE htok, const TOKEN_PRIVILEGES& tpOld)
{
return(AdjustTokenPrivileges(htok, FALSE, const_cast<TOKEN_PRIVILEGES*>(&tpOld), 0, 0, 0));
}

HANDLE GetProcessHandleWithEnoughRights(DWORD PID, DWORD AccessRights)
{
HANDLE hProcess = ::OpenProcess(AccessRights, FALSE, PID);
if(hProcess == NULL)
{
HANDLE hpWriteDAC = OpenProcess(WRITE_DAC, FALSE, PID);
if(hpWriteDAC == NULL)
{
HANDLE htok;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &htok))
return(FALSE);

TOKEN_PRIVILEGES tpOld;
if (EnableTokenPrivilege(htok, SE_TAKE_OWNERSHIP_NAME, tpOld))
{
HANDLE hpWriteOwner = OpenProcess(WRITE_OWNER, FALSE, PID);
if (hpWriteOwner != NULL)
{
BYTE buf[512];
DWORD cb = sizeof buf;
if(GetTokenInformation(htok, TokenUser, buf, cb, &cb))
{
DWORD err =
SetSecurityInfo(hpWriteOwner, SE_KERNEL_OBJECT,
OWNER_SECURITY_INFORMATION,
reinterpret_cast<TOKEN_USER*>(buf)->User.Sid,
0, 0, 0);
if(err == ERROR_SUCCESS)
{
if (!DuplicateHandle(GetCurrentProcess(), hpWriteOwner,
GetCurrentProcess(), &hpWriteDAC, WRITE_DAC, FALSE, 0) )
hpWriteDAC = NULL;
}
}
CloseHandle(hpWriteOwner);
}
RestoreTokenPrivilege(htok, tpOld);
}
CloseHandle(htok);
}

if(hpWriteDAC)
{
AdjustDacl(hpWriteDAC, AccessRights);

if(!DuplicateHandle( GetCurrentProcess(), hpWriteDAC, GetCurrentProcess(),
&hProcess, AccessRights, FALSE, 0))
hProcess = NULL;
CloseHandle(hpWriteDAC);
}
}
return hProcess;
}

BOOL WINAPI InjectLibW(DWORD dwProcessId, PCWSTR pszLibFile)
{
BOOL fOk = FALSE;
HANDLE hProcess = NULL, hThread = NULL;
PWSTR pszLibFileRemote = NULL;

hProcess = GetProcessHandleWithEnoughRights(dwProcessId,
PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION | PROCESS_VM_WRITE);

if(hProcess == NULL)
return FALSE;

int cch = 1 + lstrlenW(pszLibFile);
int cb = cch * sizeof(WCHAR);

pszLibFileRemote = (PWSTR)VirtualAllocEx(hProcess, NULL, cb, MEM_COMMIT, PAGE_READWRITE);

if(pszLibFileRemote != NULL)
{
if(WriteProcessMemory(hProcess, pszLibFileRemote, (PVOID)pszLibFile, cb, NULL))
{
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)
GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
if(pfnThreadRtn != NULL)
{
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRtn,
pszLibFileRemote, 0, NULL);

if(hThread != NULL)
{
WaitForSingleObject(hThread, INFINITE);
fOk = TRUE;
CloseHandle(hThread);
}
}
}
VirtualFreeEx(hProcess, pszLibFileRemote, 0, MEM_RELEASE);
}
CloseHandle(hProcess);
return fOk;
}

BOOL WINAPI InjectLibA(DWORD dwProcessId, PCSTR pszLibFile)
{
PWSTR pszLibFileW = (PWSTR) _alloca((lstrlenA(pszLibFile) + 1) * sizeof(WCHAR));
wsprintfW(pszLibFileW, L"%S", pszLibFile);
return(InjectLibW(dwProcessId, pszLibFileW));
}
}



you call InjectLibW and you've done.
The code is self explanatory.

Share this post


Link to post
Share on other sites
Windows Hooking doesn't support "hijacking" API calls, not directly anyway. You can just install a hook procedure that is called whenever some events(like windows messages) are triggered. I've pretty much figured out how FRaps is doing it with OpenGL. I'm not sure, but this is how I think in general:

(1) You run Fraps.exe. First, it installs a "hook" procedure with SetWindowsHookEx. It sets dwTreadId to 0, so that the hook procedure is associated with all threads in the desktop. For that to happen, the hook procedure must be in a .dll(fraps.dll), which is specified in the hMod parameter.

Now, after that, the hook is installed. When an application is launched and a certain event is triggered, Windows load fraps.dll into the address space of the process, and calls the hook procedure. From what I've seen, that happens when the application creates its window(fraps.dll is loaded even in apps that have nothing to do with OpenGL or D3D). Note that the dll is loaded into the private address space, so it has read/write access to the memory of that process. I don't know when fraps.dll executes the code it needs to "hack" OpenGL, maybe when the hook procedure is called, or maybe just once when it's loaded(using DllMain).

(2) The next step is pure hacking: Using GetProcAddress, it gets the address of wglSwapLayerBuffers()(and wglSwapBuffers). It goes there, and ovewrites the first 6 bytes of the code, putting a JMP instruction that transfers the execution to their own function.

In disassembly, this is how wglSwapLayerBuffers() looks when fraps is not running:


5F076A77 push ebp
5F076A78 mov ebp,esp
5F076A7A sub esp,38h
5F076A7D push esi
5F076A7E mov esi,dword ptr [ebp+8]
5F076A81 push esi
5F076A82 call 5F078A15
5F076A87 test eax,eax
5F076A89 je 5F076A9A





And this is how it looks when fraps is on:


5F076A77 jmp 63569321
5F076A7C cmp byte ptr [esi-75h],dl
5F076A7F jne 5F076A89
5F076A81 push esi
5F076A82 call 5F078A15
5F076A87 test eax,eax
5F076A89 je 5F076A9A






Note that wglSwapLayerBuffers() is now totally screwed, because they have messed with the length of the first instruction, thus changing the opcodes of all the next instructions. They must keep a copy of the original function somewhere, which they call after they're done drawing their own stuff.

[Edited by - mikeman on September 3, 2005 2:15:14 PM]

Share this post


Link to post
Share on other sites
catch a function exported by a dll is trivial, for example we want to catch Direct3DCreate8 and obtain a pointer to a IDirect3D8:
the dll main of a project of mine (it uses detours lib from Ms but you can isolate it and take only the part you want:

/*
The Fierce Gear project
Copyright (C) 2005 Francesco Bigiarini

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#define DBG_TRACE 0

#include "MemoryScanner.h"
#include "MemorySystem.h"
#include "Detours.h"
#include <dinput.h>
#include <mmreg.h>
#include <dsound.h>
#include <fstream>

extern "C"
{

DETOUR_TRAMPOLINE(HRESULT WINAPI Real_DirectInput8Create(
HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut,
LPUNKNOWN punkOuter), DirectInput8Create);

DETOUR_TRAMPOLINE(HRESULT WINAPI Real_DirectSoundCreate(
LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS,
LPUNKNOWN pUnkOuter), DirectSoundCreate);

DETOUR_TRAMPOLINE(HRESULT WINAPI Real_DirectSoundCreate8(
LPCGUID pcGuidDevice, LPDIRECTSOUND8 *ppDS,
LPUNKNOWN pUnkOuter), DirectSoundCreate8);

DETOUR_TRAMPOLINE(IDirect3D8* WINAPI Real_Direct3DCreate8(
UINT SDKVersion), Direct3DCreate8);

HRESULT (WINAPI *Real_DirectInputCreateEx)(
HINSTANCE hinst, DWORD dwVersion, REFIID riidltf,
LPVOID *ppvOut, LPUNKNOWN punkOuter) = NULL;

IDirect3D8* WINAPI Direct3DCreate8Replace(UINT SDKVersion)
{
IDirect3D8* dev = Real_Direct3DCreate8(SDKVersion);
MemoryScanner::RealDirect3D8 = (IDirect3D8*)dev;

if(gMemoryScanner.Init(MemoryScanner::DeviceDirect3D))
{
gMemoryScanner.Out << "Call(Direct3DCreate8) " << SDKVersion << std::endl;
gMemoryScanner.COMAttack((void*)MemoryScanner::RealDirect3D8,
&gMemoryScanner.Attack_Direct3D8_CreateDevice);
}
return dev;
}

HRESULT WINAPI DirectInput8CreateReplace(HINSTANCE hinst,
DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN pUnkOuter)
{
HRESULT hr;

hr = Real_DirectInput8Create(hinst, dwVersion, riidltf, ppvOut, pUnkOuter);
MemoryScanner::RealDirectInput8 = (LPDIRECTINPUT8)*ppvOut;

if(gMemoryScanner.Init(MemoryScanner::DeviceDirectInput))
{
gMemoryScanner.Out << "Call(DirectInput8Create) " << dwVersion << std::endl;
gMemoryScanner.COMAttack((void*)MemoryScanner::RealDirectInput8,
&gMemoryScanner.Attack_DirectInput_CreateDevice);
}
return hr;
}

HRESULT WINAPI DirectInputCreateExReplace(HINSTANCE hinst,
DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN pUnkOuter)
{
HRESULT hr;

hr = Real_DirectInputCreateEx(hinst, dwVersion, riidltf, ppvOut, pUnkOuter);
MemoryScanner::RealDirectInput7 = (LPDIRECTINPUT7)*ppvOut;

if(gMemoryScanner.Init(MemoryScanner::DeviceDirectInput))
{
gMemoryScanner.Out << "Call(DirectInputCreateEx) " << dwVersion << std::endl;
gMemoryScanner.COMAttack((void*)MemoryScanner::RealDirectInput7,
&gMemoryScanner.Attack_DirectInput_CreateDeviceEx);
}
return hr;
}

HRESULT WINAPI DirectSoundCreateReplace(LPCGUID pcGuidDevice,
LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
{
HRESULT hr;
hr = Real_DirectSoundCreate(pcGuidDevice, ppDS, pUnkOuter);
MemoryScanner::RealDirectSound = (LPDIRECTSOUND)*ppDS;

if(gMemoryScanner.Init(MemoryScanner::DeviceDirectSound))
{
gMemoryScanner.Out << "Call(DirectSoundCreate)" << std::endl;
gMemoryScanner.COMAttack((void*)MemoryScanner::RealDirectSound,
&gMemoryScanner.Attack_DirectSound_CreateSoundBuffer);
}
return hr;
}

HRESULT WINAPI DirectSoundCreate8Replace(LPCGUID pcGuidDevice,
LPDIRECTSOUND8 *ppDS, LPUNKNOWN pUnkOuter)
{
return DirectSoundCreateReplace(pcGuidDevice, (LPDIRECTSOUND*)ppDS, pUnkOuter);
}

static LONG TlsIndent = -1;
static LONG TlsThread = -1;
static LONG ThreadCnt = 0;

BOOL ThreadAttach(HMODULE hDll)
{
if(TlsIndent >= 0) TlsSetValue(TlsIndent, (PVOID)0);
if(TlsThread >= 0)
{
LONG Thread = InterlockedIncrement(&ThreadCnt);
TlsSetValue(TlsThread, (PVOID)Thread);
}
return TRUE;
}

BOOL ThreadDetach(HMODULE hDll)
{
if(TlsIndent >= 0) TlsSetValue(TlsIndent, (PVOID)0);
if (TlsThread >= 0) TlsSetValue(TlsThread, (PVOID)0);
return TRUE;
}

BOOL ProcessAttach(HMODULE hDll)
{
TlsIndent = TlsAlloc();
TlsThread = TlsAlloc();
DetourFunctionWithTrampoline((PBYTE)Real_DirectSoundCreate,
(PBYTE)DirectSoundCreateReplace);
DetourFunctionWithTrampoline((PBYTE)Real_DirectSoundCreate8,
(PBYTE)DirectSoundCreate8Replace);
DetourFunctionWithTrampoline((PBYTE)Real_DirectInput8Create,
(PBYTE)DirectInput8CreateReplace);
DetourFunctionWithTrampoline((PBYTE)Real_Direct3DCreate8,
(PBYTE)Direct3DCreate8Replace);

HRESULT (WINAPI *DEx)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN) =
((HRESULT(WINAPI*)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN))
DetourFindFunction("dinput.dll", "DirectInputCreateEx"));

Real_DirectInputCreateEx =
((HRESULT(WINAPI*)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN))
DetourFunction((PBYTE)DEx, (PBYTE)DirectInputCreateExReplace));
ThreadAttach(hDll);
return TRUE;
}

BOOL ProcessDetach(HMODULE hDll)
{
ThreadDetach(hDll);

if(TlsIndent >= 0) TlsFree(TlsIndent);
if(TlsThread >= 0) TlsFree(TlsThread);
gMemoryScanner.Close();
return TRUE;
}

BOOL APIENTRY DllMain(HINSTANCE Module, DWORD Reason, PVOID Reserved)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH: return ProcessAttach(Module);
case DLL_PROCESS_DETACH: return ProcessDetach(Module);
case DLL_THREAD_ATTACH: return ThreadAttach(Module);
case DLL_THREAD_DETACH: return ThreadDetach(Module);
}
return TRUE;
}

}




as you can see from this source I catch DirectInput, Direct3D8 and DirectSound. When you have the interface pointer you can do what you want.

EDIT: don't be lamer guys. It is only for fun.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!