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.