Experimental BinaryObject implementation -- Only Read support; unfinished

This commit is contained in:
Robert MacGregor 2014-09-06 18:38:02 -04:00
parent 4c0f51b22b
commit b0702cf70f
9 changed files with 458 additions and 1 deletions

View file

@ -15,32 +15,42 @@
<ClCompile Include="source\DXAPI\DXAPI.cpp" />
<ClCompile Include="source\DXAPI\FlyingVehicle.cpp" />
<ClCompile Include="source\DXAPI\GameBase.cpp" />
<ClCompile Include="source\DXAPI\GameConnection.cpp" />
<ClCompile Include="source\DXAPI\GrenadeProjectile.cpp" />
<ClCompile Include="source\DXAPI\NetConnection.cpp" />
<ClCompile Include="source\DXAPI\NetObject.cpp" />
<ClCompile Include="source\DXAPI\Player.cpp" />
<ClCompile Include="source\DXAPI\Point3F.cpp" />
<ClCompile Include="source\DXAPI\Projectile.cpp" />
<ClCompile Include="source\DXAPI\SceneObject.cpp" />
<ClCompile Include="source\DXAPI\ScriptObject.cpp" />
<ClCompile Include="source\DXAPI\ShapeBase.cpp" />
<ClCompile Include="source\DXAPI\SimObject.cpp" />
<ClCompile Include="source\DXAPI\StaticShape.cpp" />
<ClCompile Include="source\DXAPI\TCPObject.cpp" />
<ClCompile Include="source\DXAPI\Vehicle.cpp" />
<ClCompile Include="source\DXBinaryObjects.cpp" />
<ClCompile Include="source\DXConCmds.cpp" />
<ClCompile Include="source\DXTCPObjects.cpp" />
<ClCompile Include="source\LinkerAPI.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\DXAPI\DXAPI.h" />
<ClInclude Include="include\DXAPI\FlyingVehicle.h" />
<ClInclude Include="include\DXAPI\GameBase.h" />
<ClInclude Include="include\DXAPI\GameConnection.h" />
<ClInclude Include="include\DXAPI\GrenadeProjectile.h" />
<ClInclude Include="include\DXAPI\NetConnection.h" />
<ClInclude Include="include\DXAPI\NetObject.h" />
<ClInclude Include="include\DXAPI\Player.h" />
<ClInclude Include="include\DXAPI\Point3F.h" />
<ClInclude Include="include\DXAPI\Projectile.h" />
<ClInclude Include="include\DXAPI\SceneObject.h" />
<ClInclude Include="include\DXAPI\ScriptObject.h" />
<ClInclude Include="include\DXAPI\ShapeBase.h" />
<ClInclude Include="include\DXAPI\SimObject.h" />
<ClInclude Include="include\DXAPI\StaticShape.h" />
<ClInclude Include="include\DXAPI\TCPObject.h" />
<ClInclude Include="include\DXAPI\Vehicle.h" />
<ClInclude Include="include\DXConCmds.h" />
<ClInclude Include="include\LinkerAPI.h" />
@ -91,6 +101,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;Dnsapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -107,6 +118,7 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>ws2_32.lib;Dnsapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View file

@ -30,9 +30,19 @@
#include <DXAPI/GameConnection.h>
#include <DXAPI/NetConnection.h>
#include <DXAPI/TCPObject.h>
#include <DXAPI/ScriptObject.h>
namespace DX
{
//! A typedef referring to some type of unresolved object in the game.
typedef void* UnresolvedObject;
const char *GetModPaths(void);
bool IsFile(const char *filename);
bool GetRelativePath(const char *filename, char *ret, int buffer_length);
bool GetRunningMod(char *ret, int buffer_length);
bool SanitizeFileName(char *ret, int buffer_length);
} // End NameSpace DX

View file

@ -0,0 +1,12 @@
#pragma once
#include <DXAPI/SimObject.h>
namespace DX
{
class ScriptObject : public SimObject
{
public:
ScriptObject(unsigned int obj);
};
} // End NameSpace DX

View file

@ -39,6 +39,17 @@ bool conTCPObjectDisconnect(Linker::SimObject *obj, S32 argc, const char *argv[]
// HTTPObject Commands ------------------------------
bool conHTTPObjectDoNothing(Linker::SimObject *obj, S32 argc, const char *argv[]);
// BinaryObject Commands ----------------------------
bool conBinaryObjectOpenForRead(Linker::SimObject *obj, S32 argc, const char *argv[]);
const char *conBinaryObjectReadU32(Linker::SimObject *obj, S32 argc, const char *argv[]);
const char *conBinaryObjectReadF32(Linker::SimObject *obj, S32 argc, const char *argv[]);
const char *conBinaryObjectReadU8(Linker::SimObject *obj, S32 argc, const char *argv[]);
const char *conBinaryObjectGetBufferLength(Linker::SimObject *obj, S32 argc, const char *argv[]);
bool conBinaryObjectSetBufferPointer(Linker::SimObject *obj, S32 argc, const char *argv[]);
const char *conBinaryObjectGetBufferPointer(Linker::SimObject *obj, S32 argc, const char *argv[]);
bool conBinaryObjectClose(Linker::SimObject *obj, S32 argc, const char *argv[]);
bool conBinaryObjectSave(Linker::SimObject *obj, S32 argc, const char *argv[]);
// General Commands ---------------------------------
const char* conSprintf(Linker::SimObject *obj, S32 argc, const char* argv[]);
bool conTSExtensionUpdate(Linker::SimObject *obj, S32 argc, const char *argv[]);

View file

@ -19,5 +19,107 @@
namespace DX
{
const char *GetModPaths(void)
{
int pointer = *(int*)0x9E8690;
pointer = *(int*)(pointer + 2048);
return (const char*)pointer;
}
bool IsFile(const char *filename)
{
typedef bool (*IsFileFuncPtr)(void*, const char *filename, int);
static IsFileFuncPtr IsFileFunc = (IsFileFuncPtr)0x440DF0;
// File Manager Object or something?
void *unknown = *(void**)0x9E8690;
int unknown_int = 0; // Not sure what this is
int result = 0;
__asm
{
push unknown_int;
push filename;
mov ecx, unknown;
lea eax, IsFileFunc;
mov eax, [eax];
call eax;
mov result, eax;
}
return result != 0;
}
bool GetRelativePath(const char *filename, char *ret, int buffer_length)
{
// Make sure T2 can see it first off, we don't want to be loading
// arbitrary files on disk
if (!IsFile(filename))
return false;
const char *modpaths = GetModPaths();
int modpaths_len = strlen(modpaths);
char *modpaths_temp = new char[modpaths_len + 1];
memcpy(modpaths_temp, modpaths, modpaths_len + 1);
// Now we process all the modpaths
int last_start = 0;
for (int iteration = 0; iteration < modpaths_len + 1; iteration++)
if (modpaths_temp[iteration] == ';' || iteration == modpaths_len)
{
memset(ret, 0x00, buffer_length);
modpaths_temp[iteration] = 0x00;
char *modname = &modpaths_temp[last_start];
sprintf_s(ret, buffer_length, "%s/%s", modname, filename);
// Check if it exists
FILE *handle = fopen(ret, "r");
if (handle)
{
fclose(handle);
delete[] modpaths_temp;
return true;
}
last_start = iteration + 1;
}
delete[] modpaths_temp;
return false;
}
bool GetRunningMod(char *ret, int buffer_length)
{
const char *modpaths = GetModPaths();
unsigned int start_point = (unsigned int)modpaths;
unsigned int end_point = (unsigned int)strstr(modpaths, ";");
// FIXME: Potential Buffer overflow
if (end_point == 0)
memcpy(ret, modpaths, strlen(modpaths));
else
memcpy(ret, modpaths, end_point - start_point);
// FIXME: Attackers can use setModPath() to attempt to trick my code into loading files
// outside of Tribes 2's reach
return SanitizeFileName(ret, buffer_length);
}
bool SanitizeFileName(char *ret, int buffer_length)
{
bool was_dirty = false;
for (unsigned int iteration = 0; iteration < strlen(ret); iteration++)
if (ret[iteration] == '.' || ret[iteration] == '\\' || ret[iteration] == '/' || ret[iteration] == '~')
{
was_dirty = true;
ret[iteration] == 0x20; // In the event the occurence is at the very end
for (unsigned int replace_iteration = iteration; replace_iteration < strlen(ret); replace_iteration++)
ret[replace_iteration] = ret[replace_iteration + 1];
}
return was_dirty;
}
}

View file

@ -0,0 +1,8 @@
#include <DXAPI/ScriptObject.h>
namespace DX
{
ScriptObject::ScriptObject(unsigned int obj) : SimObject(obj)
{
}
}

View file

@ -0,0 +1,291 @@
/**
*/
#include <LinkerAPI.h>
#include <DXAPI/DXAPI.h>
#include <map>
typedef struct
{
char *buffer;
int buffer_length;
int buffer_pointer;
bool disk_streaming;
FILE *handle;
} DXBinaryObject;
typedef std::map<unsigned int, DXBinaryObject*> DXBinaryObjectMapping;
static DXBinaryObjectMapping DXBinaryObjectMap;
bool conBinaryObjectOpenForRead(Linker::SimObject *obj, S32 argc, const char *argv[])
{
// By default we don't do disk streaming but this allows for the support
bool should_stream = false;
if (argc == 4)
{
int stream_value = atoi(argv[3]);
if (stream_value != 0)
should_stream = true;
}
DX::ScriptObject scriptobject((unsigned int)obj);
char filepath[256];
if (!DX::GetRelativePath(argv[2], filepath, 256))
{
scriptobject.CallMethod("onOpenFailed", 0);
return false;
}
// Already open
if (DXBinaryObjectMap.count(scriptobject.identifier) != 0)
{
// TODO: Make these report the actual errors
scriptobject.CallMethod("onOpenFailed", 0);
return false;
}
FILE *handle = fopen(filepath, "rb");
if (!handle)
{
Con::errorf(0, "Failed to open: %s", filepath);
scriptobject.CallMethod("onOpenFailed", 0);
return false;
}
fseek(handle, 0, SEEK_END);
// Init the Binobj
DXBinaryObject *binobj = new DXBinaryObject;
binobj->disk_streaming = should_stream;
binobj->buffer_length = ftell(handle);
binobj->buffer_pointer = binobj->buffer_length;
binobj->handle = handle;
if (!should_stream)
binobj->buffer = new char[binobj->buffer_length];
// We don't need the file object anymore after we've read the buffer
// so we ditch it.
fseek(handle, 0, SEEK_SET);
if (!should_stream)
{
fread(binobj->buffer, binobj->buffer_length, 1, handle);
fclose(handle);
}
DXBinaryObjectMap[scriptobject.identifier] = binobj;
return true;
}
const char *conBinaryObjectReadU32(Linker::SimObject *obj, S32 argc, const char *argv[])
{
DX::ScriptObject scriptobject((unsigned int)obj);
// Not open
if (DXBinaryObjectMap.count(scriptobject.identifier) == 0)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
DXBinaryObject *binobj = DXBinaryObjectMap[scriptobject.identifier];
// Okay, so we're open, now we need to make sure we have enough bytes left in the buffer
int read_length = sizeof(unsigned int);
if (binobj->buffer_length - (binobj->buffer_length - binobj->buffer_pointer) < read_length)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
unsigned int return_value = 0;
binobj->buffer_pointer -= read_length;
if (binobj->disk_streaming)
{
char out_memory[4];
fseek(binobj->handle, binobj->buffer_pointer, SEEK_SET);
fread(out_memory, 4, 1, binobj->handle);
return_value = *(unsigned int*)out_memory;
}
else
// Get a pointer to the object in the buffer and deref it
return_value = *(unsigned int*)&binobj->buffer[binobj->buffer_pointer];
char result[256];
sprintf_s<256>(result, "%u", return_value);
return result;
}
const char *conBinaryObjectReadF32(Linker::SimObject *obj, S32 argc, const char *argv[])
{
DX::ScriptObject scriptobject((unsigned int)obj);
// Not open
if (DXBinaryObjectMap.count(scriptobject.identifier) == 0)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
DXBinaryObject *binobj = DXBinaryObjectMap[scriptobject.identifier];
// Okay, so we're open, now we need to make sure we have enough bytes left in the buffer
int read_length = sizeof(float);
if (binobj->buffer_length - (binobj->buffer_length - binobj->buffer_pointer) < read_length)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
float return_value = 0;
binobj->buffer_pointer -= read_length;
if (binobj->disk_streaming)
{
char out_memory[4];
fseek(binobj->handle, binobj->buffer_pointer, SEEK_SET);
fread(out_memory, 4, 1, binobj->handle);
return_value = *(float*)out_memory;
}
else
// Get a pointer to the object in the buffer and deref it
return_value = *(float*)&binobj->buffer[binobj->buffer_pointer];
char result[256];
sprintf_s<256>(result, "%f", return_value);
return result;
}
const char *conBinaryObjectReadU8(Linker::SimObject *obj, S32 argc, const char *argv[])
{
DX::ScriptObject scriptobject((unsigned int)obj);
// Not open
if (DXBinaryObjectMap.count(scriptobject.identifier) == 0)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
DXBinaryObject *binobj = DXBinaryObjectMap[scriptobject.identifier];
// Okay, so we're open, now we need to make sure we have enough bytes left in the buffer
int read_length = sizeof(unsigned char);
if (binobj->buffer_length - (binobj->buffer_length - binobj->buffer_pointer) < read_length)
{
scriptobject.CallMethod("onReadFailed", 0);
return "-1";
}
unsigned char return_value = 0;
binobj->buffer_pointer -= read_length;
if (binobj->disk_streaming)
{
char out_memory[1];
fseek(binobj->handle, binobj->buffer_pointer, SEEK_SET);
fread(out_memory, 4, 1, binobj->handle);
return_value = *(unsigned char*)out_memory;
}
else
// Get a pointer to the object in the buffer and deref it
return_value = *(unsigned char*)&binobj->buffer[binobj->buffer_pointer];
char result[256];
sprintf_s<256>(result, "%u", return_value);
return result;
}
bool conBinaryObjectSetBufferPointer(Linker::SimObject *obj, S32 argc, const char *argv[])
{
// We don't need a full object instance for some of these
unsigned int identifier = atoi(argv[1]);
if (DXBinaryObjectMap.count(identifier) == 0)
return false;
DXBinaryObject *binobj = DXBinaryObjectMap[identifier];
int desired_value = atoi(argv[2]);
if (desired_value < 0 || desired_value > binobj->buffer_length)
return false;
binobj->buffer_pointer = desired_value;
return true;
}
const char *conBinaryObjectGetBufferPointer(Linker::SimObject *obj, S32 argc, const char *argv[])
{
unsigned int identifier = atoi(argv[1]);
if (DXBinaryObjectMap.count(identifier) == 0)
return "-1";
DXBinaryObject *binobj = DXBinaryObjectMap[identifier];
char result[256];
sprintf_s<256>(result, "%u", binobj->buffer_pointer);
return result;
}
const char *conBinaryObjectGetBufferLength(Linker::SimObject *obj, S32 argc, const char *argv[])
{
unsigned int identifier = atoi(argv[1]);
if (DXBinaryObjectMap.count(identifier) == 0)
return "-1";
DXBinaryObject *binobj = DXBinaryObjectMap[identifier];
char result[256];
sprintf_s<256>(result, "%u", binobj->buffer_length);
return result;
}
bool conBinaryObjectClose(Linker::SimObject *obj, S32 argc, const char *argv[])
{
unsigned int identifier = atoi(argv[1]);
if (DXBinaryObjectMap.count(identifier) == 0)
return false;
DXBinaryObject *binobj = DXBinaryObjectMap[identifier];
if (!binobj->disk_streaming)
delete binobj->buffer;
else
fclose(binobj->handle);
delete binobj;
DXBinaryObjectMap.erase(identifier);
return true;
}
bool conBinaryObjectSave(Linker::SimObject *obj, S32 argc, const char *argv[])
{
unsigned int identifier = atoi(argv[1]);
if (DXBinaryObjectMap.count(identifier) == 0)
return false;
char runningmod[256];
DX::GetRunningMod(runningmod, 256);
int runningmod_length = strlen(runningmod);
Con::errorf(0, "Got Mod: %s", runningmod);
return true;
int filename_length = strlen(argv[2]);
char *desired_filename = (char*)malloc(filename_length + runningmod_length + 2);
memcpy(desired_filename, argv[2], filename_length + 1);
DX::SanitizeFileName(desired_filename, filename_length);
sprintf(desired_filename, "%s/%s", runningmod, desired_filename);
//Con::errorf(0, "Wrote to: %s", desired_filename);
FILE *handle = fopen(desired_filename, "wb");
DXBinaryObject *binobj = DXBinaryObjectMap[identifier];
fwrite(binobj->buffer, binobj->buffer_length, 1, handle);
fclose(handle);
return true;
}

View file

@ -11,7 +11,7 @@
static unsigned int TSEXTENSION_RUNNINGTCPOBJECTCOUNT = 0;
static DX::TCPObject *TSEXTENSION_RUNNINGTCPOBJECTS[TCPOBJECT_MAXCOUNT];
// Since wants TS function call arguments to be of type char*, we use this
// Since TS wants function call arguments to be of type char*, we use this
// helper function to painlessly pass in unsigned int arguments for things
// such as the return value from WSAGetLastError().
__forceinline static char *S32ToCharPtr(unsigned int in)

View file

@ -54,6 +54,17 @@ extern "C"
Con::addMethodB("HTTPObject", "connect", &conHTTPObjectDoNothing, "Disconnects from the remote server", 6, 6);
Con::addMethodB("HTTPObject", "listen", &conHTTPObjectDoNothing, "Disconnects from the remote server", 6, 6);
// BinaryObject
Con::addMethodB("BinaryObject", "openforread", &conBinaryObjectOpenForRead, "Opens the input file for reading binary data", 3, 4);
Con::addMethodB("BinaryObject", "save", &conBinaryObjectSave, "Saves the binary object data to a file", 3, 3);
Con::addMethodS("BinaryObject", "readu32", &conBinaryObjectReadU32, "Reads an unsigned int from the buffer", 2, 2);
Con::addMethodS("BinaryObject", "readf32", &conBinaryObjectReadF32, "Reads a float from the buffer", 2, 2);
Con::addMethodS("BinaryObject", "readu8", &conBinaryObjectReadU8, "Reads a unsigned char from the buffer", 2, 2);
Con::addMethodB("BinaryObject", "setbufferpointer", &conBinaryObjectSetBufferPointer, "Sets the buffer pointer", 3, 3);
Con::addMethodS("BinaryObject", "getbufferlength", &conBinaryObjectGetBufferLength, "Returns the length of the buffer", 2, 2);
Con::addMethodS("BinaryObject", "getbufferpointer", &conBinaryObjectGetBufferPointer, "Returns the buffer pointer", 2, 2);
Con::addMethodB("BinaryObject", "close", &conBinaryObjectClose, "Closes the binary object", 2, 2);
// General
Con::addMethodS(NULL, "sprintf", &conSprintf,"Formats a string. See the C sprintf.", 2, 20);
Con::addMethodB(NULL, "tsExtensionUpdate", &conTSExtensionUpdate,"Updates the TSExtension.", 1, 1);