A trick to extend the un-extendable

by acha11 8. May 2009 01:29

Update 2009-10-29:

iRacing have implemented a check to restrict use of this technique... more here.

 

This post’s about a nice trick I came across to inject your own code into a third party app that’s not designed to be extensible.

The app in question is iRacing, a racing simulator. The extension is Stephane’s Telemetry HUD Plugin, which adds extra timing and telemetry info as a HUD on top of the standard 3d view that iRacing renders.

iRacing without HUD

iRacing old-school

 

iRacingSim with HUD

iRacing + Telemetry HUD (note the lap times in the top-left and RPMs on the top right)

The Telemetry HUD Plugin ships as a single DLL, d3d9.dll, which you copy into your main iRacing program folder (the folder which contains iRacing.exe).

When I first saw this arrangement, I assumed the trick worked like this:

  1. iRacing asks to load the “real” Direct3D d3d9.dll.
  2. Windows applies its defined search orderfor DLLs, and decides to load Stephane’s d3d9.dll (since it’s in the local app folder), instead of the “real” dll that’s off in %windir%\system32.
  3. Stephane’s d3d9.dll somehow (maybe by calling LoadLibrary? Hey, I’m not Stephane, alright?) causes the real d3d9.dll to be loaded as well so that iRacing can still get at the Direct3D functions it needs. (But what about naming collisions? Is it even possible to have two DLLs with the same name loaded? Answers below…)

I wanted to find out whether my hunch was on the right track, and how the details really worked. So first I fired up Dependency Walker and loaded iRacingSim.exe (the main iRacing executable) while the plugin was installed:

image

The top-left panel lists the DLLs that iRacingSim.exe depends on. I’ve highlighted D3D9.DLL – notice that it’s loaded from the iRacing folder, since I’ve got the Telemetry HUD Plugin installed, and Dependency Walker is smart enough to apply the same DLL resolution rules as Windows.

Because I’ve highlighted D3D9.DLL, the second-from-the-top-on-the-right panel is listing all the functions it exports, and the top-right panel is listing just those exported functions which are actually called by iRacingSim.exe. We can see that D3D9.DLL only exports Direct3DCreate9(), and that’s the only function which iRacingSim.exe actually calls. Contrast that with a view of the “real” D3D9.dll:

image

Here you can see that the real D3D9.DLL exports a whole bunch of functions (in the panel second from the top on the right), but Dependency Walker still says that only one of them is actually called by iRacingSim.exe.

So at this point, it looks to me as though Stephane either:

  1. chose to fake a DLL that iRacingSim.exe called very few exports from (saving time and effort); or
  2. chose D3D9.DLL because a call to Direct3DCreate9() was a convenient time to hook in and do his own initialisation (since by that point, he knows that the graphic subsystem is being initialised).

One question remains: When iRacing calls Direct3DCreate9() in what it thinks is Direct3D but is actually Stephane’s fake DLL, how does Direct3D actually get initialised? Does Stephane just pass the call through to the real D3D9.DLL? Fire up Dependency Walker and load the Telemetry HUD Plugin d3d9.dll:

image

Well, we can tell from the tree at the top left that Dependency Walker can’t see a dependency from the fake D3D9.DLL to the real one. Maybe Dependency Walker can only see static, declared (declarative) inter-DLL dependencies, though – perhaps if Stephane’s code imperatively sets up the dependency by calling LoadLibrary, passing a path to the real D3D9.dll, he can then call to the real thing? Maybe we could check that by checking the string table in his DLL?

Fire up devenv.exe and open the fake D3D9.dll using the binary editor. Ctrl-F to look for occurrences of “d3d9” – maybe we can find some clue as to how the real D3D9.dll gets loaded?

image

That’s a pretty interesting string – “\d3d9.dll”. And immediately before and after it, there’s talk of something called PROXYDLL, which looks a bit library-ish. Google is our friend, and finds us “Intercept Calls to DirectX with a Proxy DLL”. That’s an article on CodeGuru, including sample code, which uses a pretty familiar-sounding technique to inject code into a Direct3D 8 or 9 app and render extra stuff on the screen.

After downloading the sample source, I run into the following chunks of code…

void InitInstance(HANDLE hModule)
{
    OutputDebugString("PROXYDLL: InitInstance called.\r\n");

Well, given that “PROXYDLL: InitInstance called.” shows up in the binary screenshot above character for character, I’m pretty sure that Stephane’s work’s based off this sample, so we can be confident the D3D9.dll technique used by the sample’s also used by Stephane’s fake D3D9.dll.

// Exported function (faking d3d9.dll's one-and-only export)
IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion)
{
    if (!gl_hOriginalDll) LoadOriginalDll(); // looking for the "right d3d9.dll"

Here we go, how does LoadOriginalDll() work?

void LoadOriginalDll(void)
{
    char buffer[MAX_PATH];
    // Getting path to system dir and to d3d8.dll
    ::GetSystemDirectory(buffer,MAX_PATH);

    // Append dll name
    strcat(buffer,"\\d3d9.dll");
    // try to load the system's d3d9.dll, if pointer empty
    if (!gl_hOriginalDll) gl_hOriginalDll = ::LoadLibrary(buffer);

    // Debug
    if (!gl_hOriginalDll)
    {
        OutputDebugString("PROXYDLL: Original d3d9.dll not loaded ERROR ****\r\n");
        ::ExitProcess(0); // exit the hard way
    }
}

And there’s the resolution (see what I did there?): a quick call to GetSystemDirectory tells us where system32 is, and we ask LoadLibrary to load d3d9.dll from that folder.

None of this was insanely complicated, but it’s something I wouldn’t have ever tried, I think.

For starters, I would have been sceptical that it’d be so easy to subvert the DLL resolution rules for what is effectively a system library. Having said that, I see how there’s no sensible measure to prevent a user running whatever code they want on their machine, of course.

Second, I’m really surprised that iRacing, an online multiplayer game (where you’d expect some cheat/hack countermeasures on the part of the developers), doesn’t have parasite detection code that picks up either unrecognised or suspicious-looking DLLs, or at least funky stuff like “hey, wait a sec, I have TWO d3d9.dlls loaded in my process space! uncool!”.

It’ll be interesting to see how iRacing respond to this plug-in. Traditionally, iRacing have patched their software only in the one week break between quarterly seasons; only the most egregious bug exploits (for example, cuttable corners on the track) have been patched “out-of-band”. Given that the inter-season break was last week, and that Stephane released his tool a few months back, it looks like they’ve chosen to tolerate his hack-let for now.

This tool is grey-hat. It surfaces information to the driver which otherwise isn’t available: extremely detailed and precise, real-time feedback on whether you’ve fallen behind or pulled ahead of your best lap ever up to this point on the current lap – incredibly useful, with overtones of mild cheatiness.

My concern is that it’s not that far from here to a genuine black-hat tool which observes your acceleration and accelerator levels, reasons that you must be spinning the rear wheels, and eases off the accelerator by filtering the values fed from your wheel and pedals to iRacing (using a similar technique to that found above).

That class of hacks would basically ruin the key characteristic of iRacing that makes it the greatest ever online racing league: a competitive and even playing field populated by (almost universally) non-cheating (mostly) non-idiots.

Authenticating the caller by IP address – is IP spoofing a realistic threat?

by acha11 24. September 2008 11:00

in a network zone protected by firewalls, you'll often have to secure server-to-server communication channels. for example, in an enterprise app, your workflow server might be calling in to your middle-tier business logic web service.

imagine that, for whatever reason, it's difficult to negotiate a web-services based authentication scheme for use between your middle tier web service and the workflow server calling into it. you might consider a network identity-based approach to authenticating the caller: the middle-tier web service could be configure to reject calls not originating from a set of known, trusted IP addresses.

start out assuming that we can trust that the source IP address is actually the IP address of the originating machine. source IP address-based security doesn't adhere to the principle of defence-in-depth: any attacker able to run code capable of talking of the network from a machine with a trusted IP address can then make whatever calls he wants to the exposed service. this differs from, say, an approach where credentials must be presented by the client because an attacker in that case would have to compromise the credential store, which in turn presumably is secured based on a service account's credentials.

now we should question the assumption that the IP address is reliably the IP address of the originating machine: if you read the IP spec, you'll see the "source IP address" field in the IP packet header. this is specified by the sender, so we're vulnerable to IP spoofing, in which an attacker sends his web services-call packet from an untrusted machine, but (here's the spoof) specifies the source address of one of the trusted machines.

there are, however, a few mechanisms which protect against IP spoofing.

firstly, TCP, layered on top of IP, introduces a handshake that initiates a reliable conversation. the initiator sends a SYN, the other party responds with a SYN/ACK, and the initiator finalises the setup with an ACK. if the source IP address has been spoofed, in the second step, the "other" party's SYN/ACK response will go to the source IP address identified in the IP header, not the attacker. this machine's not expecting the SYN/ACK, so it won't send an ACK, so the conversation won't be initiated, so the attack fails.

this leaves the possibility of hijacking a pre-established conversation between a legitimate IP address and the server. TCP guards against conversation hijacking by introducing sequence numbers. running out of time. not sure exactly how sequence numbering protects against malicious attack - surely if the attacker observes the sequence (assuming it's incremental), he can predict the next value (x + 1!) and insert his own malicious packet. later, the legitimate party to the conversation will send a packet with the same sequence number, but it will be dropped as invalid. but the attacker's packet was accepted, so maybe the attack succeeds?

what i may be missing is some cryptographic aspect to sequence numbering which means that the sequence is cryptographically hard to predict.

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

RecentComments

Comment RSS