mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
381 lines
11 KiB
ObjectPascal
Executable file
381 lines
11 KiB
ObjectPascal
Executable file
unit pluginconfig;
|
|
|
|
{$IFDEF FPC}
|
|
{$MODE Delphi}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses windows, sysutils, cepluginsdk, Contnrs, syncobjs, StrUtils,
|
|
classes, lua, lauxlib, lualib;
|
|
|
|
function CEPlugin_GetVersion(var PluginVersion:TpluginVersion; sizeofpluginversion:integer):BOOL; stdcall;
|
|
function CEPlugin_InitializePlugin(ExportedFunctions: PExportedFunctions; pluginid: dword):BOOL; stdcall;
|
|
function CEPlugin_DisablePlugin:BOOL; stdcall;
|
|
|
|
|
|
function GetModuleFileNameEx(hProcess: HANDLE; hModule: HMODULE; lpFilename: pchar; nSize: DWORD): DWORD; stdcall; external 'psapi.dll' name 'GetModuleFileNameExA';
|
|
|
|
|
|
type PHModule=^HModule;
|
|
|
|
|
|
type TCreateRemoteThread=function(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
|
|
type TVirtualAllocEx=function(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
|
|
|
|
var versionname: pchar;
|
|
ce_exported: TExportedFunctions;
|
|
luavm: Plua_State;
|
|
thispluginid: integer;
|
|
|
|
OriginalCRT: TCreateRemoteThread;
|
|
OriginalVAE: TVirtualAllocEx;
|
|
ModuleBase: pointer;
|
|
|
|
isWow64Process: function (processhandle: THandle; var isWow: BOOL): BOOL; stdcall;
|
|
|
|
function forceModule(path: string): boolean;
|
|
|
|
implementation
|
|
|
|
function MyVirtualAllocEx(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
|
|
begin
|
|
result:=OriginalVAE(hProcess, lpAddress, dwSize, flAllocationType, flProtect);
|
|
|
|
if (ModuleBase=nil) and (dwSize>4096) and (hProcess<>-1) then
|
|
modulebase:=result;
|
|
end;
|
|
|
|
function myCreateRemoteThread(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
|
|
var base: PtrUInt;
|
|
path: pchar;
|
|
br: ptruint;
|
|
script: tstringlist;
|
|
success: boolean;
|
|
|
|
header: PImageDosHeader;
|
|
ImageNTHeader: PImageNtHeaders;
|
|
|
|
entrypoint: ptruint;
|
|
|
|
begin
|
|
//check the injected code to see if it's an dll injection or a control command
|
|
//how: The injectdll routine allocates a block of memory and in there first writes the path to the dll, followed by the code to load it.
|
|
//This means that if we check the beginning of this block for a file path, and it points to an existing file, it's a dll injection
|
|
|
|
//Problem1: the pluginhandler's loadModule does not automatically execute the entrypoint.
|
|
//Solution, use the file to read the pe header and find out where the entrypoint is
|
|
|
|
//Problem2: The base address is unknown
|
|
//Solution: Hook VirtualAllocEx and watch when loadModule is executed for allocs in the OpenedProcessHandle (should be 1)
|
|
|
|
//Problem3: .sys files do not use VirtualAllocEx but KernelAlloc(64)
|
|
//Solution: Screw them
|
|
|
|
|
|
|
|
//ce_exported.showmessage('Creating a new thread in the target process');
|
|
|
|
// r.Add('registerSymbol([['+symbolname+']], '+address+')');
|
|
|
|
|
|
|
|
base:=ptruint(lpStartAddress) and $fffffffffffff000;
|
|
|
|
getmem(path,MAX_PATH);
|
|
|
|
if ReadProcessMemory(ce_exported.OpenedProcessHandle^, pointer(base), path, MAX_PATH, br) then
|
|
begin
|
|
if FileExists(path) then
|
|
begin
|
|
//it's a dll injection
|
|
|
|
//hook VirtualAllocEx to find out where it allocates it. (todo: Update the plugin handler to tell me that info for next version, it's already there in the moduleloader class)
|
|
|
|
|
|
|
|
|
|
ModuleBase:=nil;
|
|
|
|
OriginalVAE:=TVirtualAllocEx(ce_exported.VirtualAllocEx^);
|
|
TVirtualAllocEx(ce_exported.VirtualAllocEx^):=@MyVirtualAllocEx;
|
|
|
|
success:=ForceModule(path);
|
|
|
|
|
|
TVirtualAllocEx(ce_exported.VirtualAllocEx^):=OriginalVAE; //remove the hook
|
|
|
|
|
|
if success and (modulebase<>nil) then
|
|
begin
|
|
// ce_exported.showmessage(pchar('Module loaded at :'+inttohex(dword(modulebase),8)));
|
|
//get the entrypoint
|
|
|
|
getmem(header, 4096);
|
|
if ReadProcessMemory(ce_exported.OpenedProcessHandle^, modulebase, header, 4096, br) then
|
|
begin
|
|
if header.e_magic=IMAGE_DOS_SIGNATURE then
|
|
begin
|
|
if header._lfanew<4096 then
|
|
begin
|
|
ImageNTHeader:=PImageNtHeaders(ptruint(header)+header._lfanew);
|
|
|
|
entrypoint:=ptruint(modulebase)+ImageNTHeader.OptionalHeader.AddressOfEntryPoint;
|
|
if entrypoint>base then
|
|
begin
|
|
|
|
script:=TStringList.create;
|
|
script.add('alloc(dllentry,1024)');
|
|
script.Add('createThread(dllentry)');
|
|
script.add('');
|
|
script.add('dllentry:');
|
|
script.add('push 0'); //lpvreserved
|
|
script.add('push 1'); //fdwReason (DLL_PROCESS_ATTACH)
|
|
script.add('push '+inttohex(ptruint(modulebase),8)); //hinstance/hmodule
|
|
script.add('call '+inttohex(entrypoint,8));
|
|
script.add('ret');
|
|
|
|
ce_exported.AutoAssemble(pchar(script.text));
|
|
//ce_exported.showmessage(pchar(script.text));
|
|
|
|
script.free;
|
|
end;
|
|
end;
|
|
|
|
// IMAGE_OPTIONAL_HEADER32
|
|
// ce_exported.showmessage('ok');;
|
|
|
|
end; //else no valid header (bug?)
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//replace the injection script with a script that returns success (exitcode 1)
|
|
script:=TStringList.create;
|
|
script.Add(IntToHex(ptruint(lpStartAddress),8)+':');
|
|
if success then
|
|
script.add('mov eax,1')
|
|
else
|
|
script.add('mov eax,2');
|
|
|
|
script.add('ret');
|
|
ce_exported.AutoAssemble(pchar(script.text));
|
|
|
|
script.free;
|
|
end;
|
|
end;
|
|
|
|
freemem(path);
|
|
|
|
|
|
|
|
//execute the thread (either the original, or modified one)
|
|
result:=OriginalCRT(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId);
|
|
|
|
|
|
end;
|
|
|
|
function forceModule(path: string): boolean;
|
|
var
|
|
exportlist: pchar;
|
|
i,j: integer;
|
|
maxsize: integer;
|
|
|
|
exlist: tstringlist;
|
|
r: tstringlist;
|
|
symbolname, address: string;
|
|
s: string;
|
|
mname: string;
|
|
is64bit: boolean;
|
|
|
|
begin
|
|
result:=false;
|
|
|
|
|
|
|
|
mname:=ExtractFileName(path);
|
|
maxsize:=64*1024; //64kb should be enough for most exports...
|
|
getmem(exportlist, maxsize);
|
|
if ce_exported.loadModule(pchar(path), exportlist, @maxsize) then
|
|
begin
|
|
|
|
exportlist[maxsize]:=#0;
|
|
|
|
//parse the exportlist and add these addresses to the symbollist
|
|
// ce_exported.showMessage(exportlist);
|
|
exlist:=tstringlist.create;
|
|
exlist.Text:=exportlist;
|
|
|
|
|
|
|
|
r:=tstringlist.create;
|
|
|
|
for i:=0 to exlist.count-1 do
|
|
begin
|
|
s:=exlist[i];
|
|
|
|
//ce_exported.showmessage(pchar(s));
|
|
|
|
j:=Pos(' - ', s);
|
|
if j<>0 then
|
|
begin
|
|
address:='0x'+copy(s, 1, j);
|
|
symbolname:=copy(s,j+3, length(s));
|
|
|
|
// ce_exported.showmessage(pchar(address+' - '+symbolname));
|
|
|
|
r.Add('unregisterSymbol([['+symbolname+']])');
|
|
r.Add('unregisterSymbol([['+mname+'!'+symbolname+']])');
|
|
r.Add('unregisterSymbol([['+ChangeFileExt(mname,'')+'.'+symbolname+']])');
|
|
|
|
r.Add('registerSymbol([['+symbolname+']], '+address+')');
|
|
r.Add('registerSymbol([['+mname+'!'+symbolname+']], '+address+')');
|
|
r.Add('registerSymbol([['+ChangeFileExt(mname,'')+'.'+symbolname+']], '+address+')');
|
|
end ;
|
|
|
|
end;
|
|
|
|
s:=r.text;
|
|
|
|
// ce_exported.showmessage(pchar(s));
|
|
lua_dostring(luavm, pchar(s));
|
|
|
|
result:=true;
|
|
|
|
r.free;
|
|
exlist.free;
|
|
|
|
|
|
end
|
|
else
|
|
ce_exported.showmessage(pchar('Failure loading module: '+path));
|
|
|
|
freemem(exportlist);
|
|
end;
|
|
|
|
function lua_forceModule(L: PLua_State): integer; cdecl;
|
|
var
|
|
parameters: Qword;
|
|
p: string;
|
|
|
|
begin
|
|
result:=0;
|
|
try
|
|
parameters:=lua_gettop(L);
|
|
if parameters=1 then
|
|
begin
|
|
p:=lua_tostring(L,1);
|
|
lua_pop(L, parameters);
|
|
|
|
lua_pushboolean(L, forceModule(p));
|
|
|
|
result:=1;
|
|
end;
|
|
except
|
|
//this should really never happen
|
|
result:=0;
|
|
lua_pop(L, lua_gettop(L));
|
|
end;
|
|
end;
|
|
|
|
|
|
function CEPlugin_GetVersion(var PluginVersion:TpluginVersion; sizeofpluginversion:integer):BOOL; stdcall;
|
|
var s: string;
|
|
begin
|
|
result:=false;
|
|
if sizeofpluginversion<>sizeof(TPluginVersion) then exit;
|
|
|
|
s:='Forced Injection: Replaces the normal dll injector with a manual load version';
|
|
|
|
getmem(versionname,length(s)+1);
|
|
copymemory(versionname,@s[1],length(s));
|
|
versionname[length(s)]:=#0;
|
|
|
|
PluginVersion.version:=PluginVersionSDK;
|
|
PluginVersion.pluginname:=VersionName;
|
|
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
function CEPlugin_InitializePlugin(ExportedFunctions: PExportedFunctions; pluginid: dword):BOOL; stdcall;
|
|
var script: tstringlist;
|
|
originalpid: dword;
|
|
s: pchar;
|
|
s2: string;
|
|
z: pointer;
|
|
z2: ptruint;
|
|
begin
|
|
ce_exported:=ExportedFunctions^;
|
|
thispluginid:=pluginid;
|
|
|
|
luavm:=ce_exported.GetLuaState();
|
|
|
|
IsWow64Process:=GetProcAddress(GetModuleHandle('kernel32.dll'), 'IsWow64Process');
|
|
|
|
|
|
lua_register(LuaVM, 'forceModule', lua_forceModule);
|
|
|
|
//hook createRemoteThread
|
|
OriginalCRT:=TCreateRemoteThread(ce_exported.CreateRemoteThread^);
|
|
TCreateRemoteThread(ce_exported.CreateRemoteThread^):=@MyCreateRemoteThread;
|
|
|
|
|
|
//hook EnumProcessModulesEx to fix a bug in ce
|
|
|
|
|
|
//Tce_generateAPIHookScript=function(address, addresstojumpto, addresstogetnewcalladdress: string; script: pchar; maxscriptsize: integer): BOOL; stdcall;
|
|
{
|
|
originalpid:=ce_exported.OpenedProcessID^;
|
|
ce_exported.openProcessEx(GetCurrentProcessId);
|
|
|
|
script:=TStringList.create;
|
|
script.add('reinitializeSymbolhandler()');
|
|
|
|
|
|
z2:=ptruint(@POriginalSymEnumerateModules64);
|
|
s2:=inttohex(z2,8);
|
|
|
|
script.add('s=generateAPIHookScript("SymEnumerateModules64", "'+inttohex(ptruint(@FixedSymEnumerateModules64),8)+'", "'+s2+'")');
|
|
script.add('autoAssemble(s)');
|
|
|
|
|
|
z2:=ptruint(@POriginalSymEnumerateSymbols64);
|
|
s2:=inttohex(z2,8);
|
|
script.add('s=generateAPIHookScript("SymEnumerateSymbols64", "'+inttohex(ptruint(@FixedSymEnumerateSymbols64),8)+'", "'+s2+'")');
|
|
script.add('autoAssemble(s)');
|
|
|
|
|
|
lua_dostring(luavm,pchar(script.text));
|
|
}
|
|
|
|
//ce_exported.showmessage(pchar(script.text));
|
|
|
|
|
|
// getmem(s,16*1024);
|
|
|
|
//ce_exported.ce_generateAPIHookScript('SymEnumerateModules64', , , s, 16*1024);
|
|
|
|
|
|
// script.free;
|
|
|
|
|
|
//ce_exported.openProcessEx(originalpid);
|
|
lua_dostring(luavm,'reinitializeSymbolhandler()');
|
|
|
|
|
|
|
|
|
|
result:=true;
|
|
end;
|
|
|
|
function CEPlugin_DisablePlugin:BOOL; stdcall;
|
|
begin
|
|
result:=true;
|
|
end;
|
|
|
|
end.
|