unit networkInterface; {$mode objfpc}{$H+} //playing arround with the objfpc notation. The pointer based arrays look interesting interface uses {$ifdef JNI} Classes, SysUtils, Sockets, resolve, ctypes,syncobjs, math, zstream, newkernelhandler, unixporthelper, processhandlerunit, gutil, gmap,VirtualQueryExCache; {$else} {$ifdef darwin} lcltype, MacTypes,macport, {$endif} {$ifdef windows} jwawindows, windows, {$endif} Classes, SysUtils, Sockets, resolve, ctypes, networkconfig, cefuncproc, newkernelhandler, math, zstream, syncobjs, ProcessHandlerUnit, VirtualQueryExCache, gutil, gmap; {$endif} {$ifdef jni} const networkcompression=0; {$endif} type TNetworkDebugEvent=packed record signal: integer; threadid: qword; case integer of -2: (//create process createProcess: packed record maxBreakpointCount: uint8; maxWatchpointCount: uint8; maxSharedBreakpoints: uint8; end; ); 5: (address: qword; ); end; TNetworkEnumSymCallback=function(modulename: string; symbolname: string; address: ptruint; size: integer; secondary: boolean ): boolean of object; TVQEMapCmp = specialize TLess; TVQEMap = specialize TMap; TCEServerOptionType=(netParent=0, //just a piece of text with child options under it netBoolean=1, netInteger=2, netFloat=3, netDouble=4, netText=5); TCEServerOption=record optname: string; //this is the internal name of the option parentoptname: string; //if this is a sibling, this holds the parent optname, else empty optdescription: string; //text shown to the user acceptablevalues: string; //allowed values (when set) currentvalue: string; //the current value (1 for true in boolean) optiontype: TCEServerOptionType; end; TCEServerOptions=array of TCEServerOption; TCEConnection=class private socket: cint; fConnected: boolean; version: integer; executesInsideTarget: boolean; //if true, ptrace won't work. Use sigtrap for debugging //todo: change rpmcache to a map rpmcache: array [0..15] of record //every connection is thread specific, so each thread has it's own rpmcache lastupdate: TLargeInteger; //contains the last time this page was updated baseaddress: PtrUInt; memory: array [0..4095] of byte; end; WriteProcessMemoryBufferCount: integer; //to deal with recursive calls WriteProcessMemoryBuffer: array of record processhandle: thandle; baseaddress: PtrUInt; memory: array of byte; end; WriteProcessMemoryBufferCS: TCriticalSection; VirtualQueryExCacheMap: TVQEMap; VirtualQueryExCacheMapCS: TCriticalSection; function receiveString16: string; //read a string that is preceded by a 16 bit length indicator function receive(buffer: pointer; size: integer): integer; function send(buffer: pointer; size: integer): integer; function CReadProcessMemory(hProcess: THandle; lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: PTRUINT): BOOL; function NReadProcessMemory(hProcess: THandle; lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: PTRUINT): BOOL; public function isNetworkHandle(handle: THandle): boolean; function Module32Next(hSnapshot: HANDLE; var lpme: MODULEENTRY32; isfirst: boolean=false): BOOL; function Module32First(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; function Thread32Next(hSnapshot: HANDLE; var lpte: THREADENTRY32; isfirst: boolean=false): BOOL; function Thread32First(hSnapshot: HANDLE; var lpte: THREADENTRY32): BOOL; function Process32Next(hSnapshot: HANDLE; var lppe: PROCESSENTRY32; isfirst: boolean=false): BOOL; function Process32First(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; function CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): HANDLE; function CloseHandle(handle: THandle):WINBOOL; function OpenProcess(dwDesiredAccess:DWORD; bInheritHandle:WINBOOL; dwProcessId:DWORD):HANDLE; function CreateRemoteThread(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; function VirtualAllocEx(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; function VirtualFreeEx(hProcess: HANDLE; lpAddress: LPVOID; dwSize: SIZE_T; dwFreeType: DWORD): BOOL; function VirtualQueryEx(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD): DWORD; function VirtualQueryEx_StartCache(hProcess: THandle; flags: DWORD): boolean; procedure VirtualQueryEx_EndCache(hProcess: THandle); function VirtualProtectEx(hProcess: THandle; lpAddress: Pointer; dwSize, flNewProtect: DWORD; var OldProtect: DWORD): BOOL; function GetRegionInfo(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD; var mapsline: string): DWORD; function ReadProcessMemory(hProcess: THandle; lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: PTRUINT): BOOL; function WriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesWritten: PTRUINT): BOOL; procedure beginWriteProcessMemory; function endWriteProcessMemory: boolean; function StartDebug(hProcess: THandle): BOOL; function WaitForDebugEvent(hProcess: THandle; timeout: integer; var devent: TNetworkDebugEvent):BOOL; function ContinueDebugEvent(hProcess: THandle; threadid: dword; continuemethod: integer): BOOL; function SetBreakpoint(hProcess: THandle; threadid: integer; debugregister: integer; address: PtrUInt; bptype: integer; bpsize: integer): boolean; function RemoveBreakpoint(hProcess: THandle; threadid: integer; debugregister: integer; wasWatchpoint: boolean): boolean; function AllocateAndGetContext(hProcess: Thandle; threadid: integer): Pointer; function setContext(hProcess: Thandle; threadid: integer; context: pointer; contextsize: integer): boolean; function getVersion(var name: string): integer; function getArchitecture(hProcess: THandle): integer; function getABI: integer; function enumSymbolsFromFile(modulepath: string; fileoffset: dword; modulebase: ptruint; callback: TNetworkEnumSymCallback; nameaddendum: string=''): boolean; function loadModule(hProcess: THandle; modulepath: string): boolean; function loadModuleEx(hProcess: THandle; dlopenaddress: ptruint; modulepath: string): boolean; function loadExtension(hProcess: Thandle): boolean; function speedhack_setSpeed(hProcess: THandle; speed: single): boolean; procedure setConnectionName(name: string); procedure getOptions(var options: TCEServerOptions); function getOption(name: string):string; procedure setOption(name: string; value: string); function connectNamedPipe(name: string; timeout:integer=0): HANDLE; function readPipe(h: THandle; destination: pointer; size: integer; timeout:integer=0): boolean; function writePipe(h: THandle; source: pointer; size: integer; timeout:integer=0): boolean; function getServerPath: string; function isAndroid: boolean; function setCurrentPath(path: string): boolean; function getCurrentPath: string; procedure enumfiles(path: string; list: tstrings); function getFilePermissions(path: string; out perms: UINT32): boolean; function setFilePermissions(path: string; perms: UINT32): boolean; function getFile(path: string; s: tstream): boolean; function putFile(path: string; s: tstream): boolean; function createDir(path: string): boolean; function deleteFile(path: string): boolean; procedure TerminateServer; constructor create; destructor destroy; override; published property connected: boolean read fConnected; property path: string read getServerPath; end; {$ifdef jni} var host: THostAddr; port: integer; {$endif} implementation uses elfsymbols, Globals, maps; const CMD_GETVERSION =0; CMD_CLOSECONNECTION= 1; CMD_TERMINATESERVER= 2; CMD_OPENPROCESS= 3; CMD_CREATETOOLHELP32SNAPSHOT =4; CMD_PROCESS32FIRST= 5; CMD_PROCESS32NEXT= 6; CMD_CLOSEHANDLE=7; CMD_VIRTUALQUERYEX=8; CMD_READPROCESSMEMORY=9; CMD_WRITEPROCESSMEMORY=10; CMD_STARTDEBUG=11; CMD_STOPDEBUG=12; CMD_WAITFORDEBUGEVENT=13; CMD_CONTINUEFROMDEBUGEVENT=14; CMD_SETBREAKPOINT=15; CMD_REMOVEBREAKPOINT=16; CMD_SUSPENDTHREAD=17; CMD_RESUMETHREAD=18; CMD_GETTHREADCONTEXT=19; CMD_SETTHREADCONTEXT=20; CMD_GETARCHITECTURE=21; CMD_MODULE32FIRST=22; CMD_MODULE32NEXT=23; CMD_GETSYMBOLLISTFROMFILE=24; CMD_LOADEXTENSION=25; CMD_ALLOC=26; CMD_FREE=27; CMD_CREATETHREAD=28; CMD_LOADMODULE=29; CMD_SPEEDHACK_SETSPEED=30; // CMD_VIRTUALQUERYEXFULL=31; CMD_GETREGIONINFO=32; //extended version of VirtualQueryEx which also get the full string CMD_GETABI=33; //for c-code compilation //4 CMD_SET_CONNECTION_NAME=34; CMD_CREATETOOLHELP32SNAPSHOTEX =35; CMD_CHANGEMEMORYPROTECTION=36; CMD_GETOPTIONS=37; CMD_GETOPTION=38; CMD_SETOPTION=39; CMD_PTRACE_MMAP=40; CMD_OPENNAMEDPIPE=41; CMD_PIPEREAD=42; CMD_PIPEWRITE=43; CMD_GETCESERVERPATH=44; CMD_ISANDROID=45; CMD_LOADMODULEEX=46; CMD_SETCURRENTPATH=47; CMD_GETCURRENTPATH=48; CMD_ENUMFILES=49; CMD_GETFILEPERMISSIONS=50; CMD_SETFILEPERMISSIONS=51; CMD_GETFILE=52; CMD_PUTFILE=53; CMD_CREATEDIR=54; CMD_DELETEFILE=55; type TLocalModuleListEntry=class baseaddress: ptruint; fileoffset: dword; size: dword; part: integer; name: string; end; TLocalThreadListEntry=class threadid: dword; ownerprocessid: dword; end; { TLocalProcessListEntry=class end;} TToolhelp32SnapshotInfo=class handle: dword; snapshottype: dword; list: tfplist; public constructor create; destructor destroy; override; end; var LocalToolhelpSnapshotsCS: TCriticalSection; LocalToolhelpSnapshots: array of TToolhelp32SnapshotInfo; constructor TToolhelp32SnapshotInfo.create; begin list:=tfplist.create; end; destructor TToolhelp32SnapshotInfo.destroy; var i: integer; begin for i:=0 to list.count-1 do tobject(list[i]).destroy; list.free; list:=nil; inherited destroy; end; procedure TCEConnection.TerminateServer; var command: byte; begin command:=CMD_TERMINATESERVER; send(@command, sizeof(command)); end; function TCEConnection.CloseHandle(handle: THandle):WINBOOL; var CloseHandleCommand: packed record command: byte; handle: dword; end; r: integer; ths: TToolhelp32SnapshotInfo; begin if ((handle shr 24) and $ff)= $cd then //local fake handle begin try LocalToolhelpSnapshotsCS.enter; try ths:=LocalToolhelpSnapshots[handle and $ffffff]; LocalToolhelpSnapshots[handle and $ffffff]:=nil; finally LocalToolhelpSnapshotsCS.Leave; end; if ths=nil then begin OutputDebugString('Tried to close invalid fake handle'); exit(false); end; ths.free; result:=true; except result:=false; end; end else if ((handle shr 24) and $ff)= $ce then begin CloseHandleCommand.command:=CMD_CLOSEHANDLE; CloseHandleCommand.handle:=handle and $ffffff; send(@CloseHandleCommand, sizeof(CloseHandleCommand)); receive(@r,sizeof(r)); result:=true; end {$ifdef windows} else //not a network handle result:=windows.CloseHandle(handle) {$endif}; end; function TCEConnection.Module32Next(hSnapshot: HANDLE; var lpme: MODULEENTRY32; isfirst: boolean=false): BOOL; var ModulelistCommand: packed record command: byte; handle: dword; end; r: packed record result: integer; modulebase: qword; modulepart: dword; modulesize: dword; modulefileoffset: dword; stringlength: dword; end; mname: pchar; mnames: string; ths: TToolhelp32SnapshotInfo; index: integer; mle: TLocalModuleListEntry; begin result:=false; if ((hSnapshot shr 24) and $ff)= $cd then begin //local snapshot LocalToolhelpSnapshotsCS.Enter; try ths:=LocalToolhelpSnapshots[hSnapshot and $ffffff]; finally LocalToolhelpSnapshotsCS.Leave; end; if ths=nil then begin OutputDebugString('Module32First/Next on an invalid toolhelp handle'); exit; end; if ths.snapshottype<>TH32CS_SNAPMODULE then exit; if isfirst then lpme.th32ModuleID:=0; if lpme.th32ModuleID>=ths.list.Count then exit(false); mle:=TLocalModuleListEntry(ths.list[lpme.th32ModuleID]); lpme.hModule:=mle.baseaddress; lpme.modBaseAddr:=pointer(mle.baseaddress); lpme.modBaseSize:=mle.size; lpme.GlblcntUsage:=mle.part; lpme.ProccntUsage:=mle.fileoffset; mnames:=mle.name; if mle.part>0 then asm nop end; copymemory(@lpme.szExePath[0], @mnames[1], min(length(mnames)+1, MAX_PATH)); lpme.szExePath[MAX_PATH-1]:=#0; if mle.part<>0 then mnames:=mnames+'.'+inttostr(mle.part); copymemory(@lpme.szModule[0], @mnames[1], min(length(mnames)+1, MAX_MODULE_NAME32)); lpme.szModule[MAX_MODULE_NAME32-1]:=#0; inc(lpme.th32ModuleID); exit(true); end; if ((hSnapshot shr 24) and $ff)= $ce then begin if isfirst then ModulelistCommand.command:=CMD_MODULE32FIRST else ModulelistCommand.command:=CMD_MODULE32NEXT; ModulelistCommand.handle:=hSnapshot and $ffffff; if send(@ModulelistCommand, sizeof(ModulelistCommand)) > 0 then begin ZeroMemory(@r,sizeof(r)); if receive(@r, sizeof(r))>0 then begin result:=r.result<>0; if result then begin //it has a string getmem(mname, r.stringlength+1); receive(mname, r.stringlength); mname[r.stringlength]:=#0; mnames:=mname; if mname<>nil then FreeMemAndNil(mname); ZeroMemory(@lpme, sizeof(lpme)); lpme.hModule:=r.modulebase; lpme.modBaseAddr:=pointer(r.modulebase); lpme.modBaseSize:=r.modulesize; lpme.GlblcntUsage:=r.modulepart; lpme.ProccntUsage:=r.modulefileoffset; {$ifdef darwin} lpme.is64bit:=processhandler.is64Bit; {$endif} if r.modulepart>0 then asm nop end; copymemory(@lpme.szExePath[0], @mnames[1], min(length(mnames)+1, MAX_PATH)); lpme.szExePath[MAX_PATH-1]:=#0; if r.modulepart<>0 then mnames:=mnames+'.'+inttostr(mle.part); copymemory(@lpme.szModule[0], @mnames[1], min(length(mnames)+1, MAX_MODULE_NAME32)); lpme.szModule[MAX_MODULE_NAME32-1]:=#0; end; end; end; end; end; function TCEConnection.Module32First(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; begin result:=module32next(hSnapshot, lpme, true); end; function TCEConnection.Thread32Next(hSnapshot: HANDLE; var lpte: THREADENTRY32; isfirst: boolean=false): BOOL; var ths: TToolhelp32SnapshotInfo; index: integer; tle: TLocalThreadListEntry; begin result:=false; if ((hSnapshot shr 24) and $ff)= $cd then begin //local snapshot LocalToolhelpSnapshotsCS.Enter; try ths:=LocalToolhelpSnapshots[hSnapshot and $ffffff]; finally LocalToolhelpSnapshotsCS.Leave; end; if ths=nil then begin OutputDebugString('Module32First/Next on an invalid toolhelp handle'); exit; end; if ths.snapshottype<>TH32CS_SNAPTHREAD then exit; if isfirst then lpte.dwFlags:=0; //use dwFlags as counter if lpte.dwFlags>=ths.list.Count then exit(false); tle:=TLocalThreadListEntry(ths.list[lpte.dwFlags]); lpte.cntUsage:=0; lpte.tpDeltaPri:=0; lpte.tpBasePri:=0; lpte.th32OwnerProcessID:=tle.ownerprocessid; lpte.th32ThreadID:=tle.ThreadID; inc(lpte.dwFlags); exit(true); end; //else unhandled. end; function TCEConnection.Thread32First(hSnapshot: HANDLE; var lpte: THREADENTRY32): BOOL; begin exit(thread32next(hSnapshot, lpte, true)); end; function TCEConnection.Process32Next(hSnapshot: HANDLE; var lppe: PROCESSENTRY32; isfirst: boolean=false): BOOL; var ProcesslistCommand: packed record command: byte; handle: dword; end; r: packed record result: integer; pid: dword; stringlength: dword; end; pname: pchar; begin result:=false; //OutputDebugString('TCEConnection.Process32Next'); if ((hSnapshot shr 24) and $ff)= $ce then begin //OutputDebugString('Valid network handle'); if isfirst then ProcesslistCommand.command:=CMD_PROCESS32FIRST else ProcesslistCommand.command:=CMD_PROCESS32NEXT; ProcesslistCommand.handle:=hSnapshot and $ffffff; if send(@ProcesslistCommand, sizeof(ProcesslistCommand)) > 0 then begin if receive(@r, sizeof(r))>0 then begin result:=r.result<>0; if result then begin //it has a string getmem(pname, r.stringlength+1); receive(pname, r.stringlength); pname[r.stringlength]:=#0; ZeroMemory(@lppe, sizeof(lppe)); lppe.th32ProcessID:=r.pid; CopyMemory(@lppe.szExeFile[0], pname, min(r.stringlength+1, MAX_PATH)); lppe.szExeFile[MAX_PATH-1]:=#0; FreeMemAndNil(pname); end; end; end; end; end; function TCEConnection.Process32First(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; begin OutputDebugString('TCEConnection.Process32First'); result:=process32next(hSnapshot, lppe, true); end; function TCEConnection.CreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): HANDLE; var CTSCommand: packed record command: byte; dwFlags: dword; th32ProcessID: dword; end; r: integer; r2: packed record result: integer; modulebase: qword; modulepart: dword; modulesize: dword; modulefileoffset: dword; stringlength: dword; end; eol: boolean; mname: pchar; mnamesize: integer; ths: TToolhelp32SnapshotInfo; mle: TLocalModuleListEntry; tle: TLocalThreadListEntry; i: integer; threadlist: array of integer; begin result:=0; OutputDebugString('TCEConnection.CreateToolhelp32Snapshot()'); CTSCommand.command:=CMD_CREATETOOLHELP32SNAPSHOTEX; CTSCommand.dwFlags:=dwFlags; CTSCommand.th32ProcessID:=th32ProcessID; r:=0; if send(@CTSCommand, sizeof(CTSCommand))>0 then begin if (dwFlags and TH32CS_SNAPTHREAD)=TH32CS_SNAPTHREAD then begin //it'll send a list of threadid's now ths:=TToolhelp32SnapshotInfo.create; ths.handle:=0; ths.snapshottype:=TH32CS_SNAPTHREAD; LocalToolhelpSnapshotsCS.enter; try for i:=0 to length(LocalToolhelpSnapshots)-1 do begin if LocalToolhelpSnapshots[i]=nil then begin LocalToolhelpSnapshots[i]:=ths; ths.handle:=$cd000000 or i; break; end; end; if ths.handle=0 then begin i:=length(LocalToolhelpSnapshots); setlength(LocalToolhelpSnapshots,length(LocalToolhelpSnapshots)+1); LocalToolhelpSnapshots[i]:=ths; ths.handle:=$cd000000 or i; end; finally LocalToolhelpSnapshotsCS.leave; end; result:=ths.handle; if receive(@r, sizeof(r))>0 then begin setlength(threadlist, r); if receive(@threadlist[0],sizeof(integer)*r)>0 then begin for i:=0 to length(threadlist)-1 do begin tle:=TLocalThreadListEntry.create; tle.ownerprocessid:=th32processid; tle.threadid:=threadlist[i]; ths.list.add(tle); end; end; end; end else if (dwFlags and TH32CS_SNAPMODULE)=TH32CS_SNAPMODULE then begin //it'll send me a list of modules now ths:=TToolhelp32SnapshotInfo.create; ths.handle:=0; ths.snapshottype:=TH32CS_SNAPMODULE; LocalToolhelpSnapshotsCS.enter; try for i:=0 to length(LocalToolhelpSnapshots)-1 do begin if LocalToolhelpSnapshots[i]=nil then begin LocalToolhelpSnapshots[i]:=ths; ths.handle:=$cd000000 or i; break; end; end; if ths.handle=0 then begin i:=length(LocalToolhelpSnapshots); setlength(LocalToolhelpSnapshots,length(LocalToolhelpSnapshots)+1); LocalToolhelpSnapshots[i]:=ths; ths.handle:=$cd000000 or i; end; finally LocalToolhelpSnapshotsCS.leave; end; result:=ths.handle; eol:=false; mnamesize:=512; getmem(mname, mnamesize); while not eol do begin if receive(@r2, sizeof(r2))>0 then begin if r2.stringlength>mnamesize then //needs more memory begin freemem(mname); mnamesize:=r2.stringlength; getmem(mname, mnamesize); end; receive(mname, r2.stringlength); mname[r2.stringlength]:=#0; eol:=r2.result<>1; if not eol then begin mle:=TLocalModuleListEntry.create; mle.baseaddress:=r2.modulebase; mle.part:=r2.modulepart; mle.size:=r2.modulesize; mle.fileoffset:=r2.modulefileoffset; mle.name:=mname; ths.list.add(mle); end; end; end; end else //not handled yet if receive(@r, sizeof(r))>0 then begin if (r>0) then r:=r or $ce000000; result:=r; end; end; end; function TCEConnection.CReadProcessMemory(hProcess: THandle; lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: PTRUINT): BOOL; //cached read process memory. Split up into pagechunks and check which pages need to be cached, and then read from the caches type TPageInfo=record startaddress: ptruint; memory: pbyte; end; var pages: array of TPageInfo; pagecount: ptruint; i,j: integer; oldest: integer; freq: tlargeinteger; currenttime: tlargeinteger; x: ptruint; blockoffset, blocksize: dword; currentbase: ptruint; currenttarget: ptruint; m: PByte; begin //log(format('TCEConnection.CReadProcessMemory: Read %d bytes from %p into %p',[nsize, lpBaseAddress, lpBuffer])); result:=false; lpNumberOfBytesRead:=0; currenttarget:=ptruint(lpBaseAddress)+nsize-1; if currenttarget networkRPMCacheTimeout then //too old, refetch oldest:=i //so it gets reused else //not too old, can still be used pages[i].memory:=@rpmcache[j].memory[0]; break; end; if (rpmcache[j].lastupdate128 then input.compressed:=networkcompression else input.compressed:=0; if send(@input, sizeof(input))>0 then begin if input.compressed<>0 then begin if receive(@compressedresult, sizeof(compressedresult))>0 then begin compressedbuffer:=tmemorystream.create; try compressedbuffer.Size:=compressedresult.compressedsize; if receive(compressedbuffer.Memory, compressedresult.compressedsize)>0 then begin //decompress this d:=Tdecompressionstream.create(compressedbuffer, false); try d.ReadBuffer(lpbuffer^, compressedresult.uncompressedsize); result:=compressedresult.uncompressedsize>0; lpNumberOfBytesRead:=compressedresult.uncompressedsize; finally d.free; end; end; finally compressedbuffer.free; end; end; end else begin //not compressed if receive(@output, sizeof(output))>0 then begin if output.bytesread>0 then begin if receive(lpBuffer, output.bytesread)>0 then begin result:=true; lpNumberOfBytesRead:=output.bytesread; end; end; end;//else connection error end; end; end; function TCEConnection.ReadProcessMemory(hProcess: THandle; lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesRead: PTRUINT): BOOL; begin // log(format('TCEConnection.ReadProcessMemory: Read %d bytes from %p into %p',[nsize, lpBaseAddress, lpBuffer])); if ((hProcess shr 24) and $ff)= $ce then begin // Log('hProcess is valid'); result:=false; lpNumberOfBytesRead:=0; hProcess:=hProcess and $ffffff; if nsize=0 then exit; if (nsize<=8192) then begin //log('nsize<=8192. Calling CReadProcessMemory'); result:=CReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nsize, lpNumberOfBytesRead); end else //just fetch it all from the net , ce usually does not fetch more than 8KB for random accesses, so would be a waste of time begin // log('nsize>8192. Calling NReadProcessMemory'); result:=NReadProcessMemory(hProcess, lpBaseaddress, lpbuffer, nsize, lpNumberOfBytesRead); end; end {$ifdef windows} else result:=windows.ReadProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead) {$endif}; end; function TCEConnection.WriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: DWORD; var lpNumberOfBytesWritten: PTRUINT): BOOL; type TWPMrecord=packed record command: byte; handle: integer; baseaddress: qword; size: integer; //buffer end; PWPMRecord=^TWPMRecord; var input: PWPMrecord; i: pbyte; output: packed record byteswritten: integer; //followed by the bytes end; j,k: integer; b: ptruint; begin WriteProcessMemoryBufferCS.enter; try if WriteProcessMemoryBufferCount=0 then begin if ((hProcess shr 24) and $ff)= $ce then begin result:=false; lpNumberOfBytesWritten:=0; input:=getmem(sizeof(TWPMRecord)+nSize); input^.command:=CMD_WRITEPROCESSMEMORY; input^.handle:=hProcess and $ffffff; input^.baseaddress:=ptruint(lpBaseAddress); input^.size:=nSize; CopyMemory(@input[1], lpBuffer, nSize); if send(input, sizeof(TWPMRecord)+nSize)>0 then begin if receive(@output, sizeof(output))>0 then begin if output.byteswritten>0 then begin result:=true; lpNumberOfBytesWritten:=output.byteswritten; if (lpNumberOfBytesWritten>0) then //for 1 byte changes begin //clear rpm cache for this entry if there is one b:=ptruint(lpBaseAddress) and (not $fff); for j:=0 to 15 do if rpmcache[j].baseaddress=b then rpmcache[j].lastupdate:=0; //set to outdated end; end; end; end; FreeMemAndNil(input); end {$ifdef windows} else result:=windows.WriteProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten) {$endif}; end else begin //add it to the buffer result:=true; lpNumberOfBytesWritten:=nsize; k:=length(WriteProcessMemoryBuffer); setlength(WriteProcessMemoryBuffer, k+1); WriteProcessMemoryBuffer[k].processhandle:=ProcessHandle; WriteProcessMemoryBuffer[k].baseaddress:=ptruint(lpBaseAddress); setlength(WriteProcessMemoryBuffer[k].memory, nsize); CopyMemory(@WriteProcessMemoryBuffer[k].memory[0], lpBuffer, nsize); end; finally WriteProcessMemoryBufferCS.leave; end; end; procedure TCEConnection.beginWriteProcessMemory; begin WriteProcessMemoryBufferCS.Enter; inc(WriteProcessMemoryBufferCount); //todo: Change the network interface to actually send ALL writes in one command end; function TCEConnection.endWriteProcessMemory: boolean; var i,j,k: integer; x: ptruint; grouped: boolean; begin result:=true; dec(WriteProcessMemoryBufferCount); if WriteProcessMemoryBufferCount=0 then begin //group the blocks i:=0; while i0 then begin setlength(WriteProcessMemoryBuffer[i].memory, length(WriteProcessMemoryBuffer[i].memory)+k); MoveMemory(@WriteProcessMemoryBuffer[i].memory[k], @WriteProcessMemoryBuffer[i].memory[0], k ); WriteProcessMemoryBuffer[i].baseaddress:=WriteProcessMemoryBuffer[j].baseaddress; end; //set the end k:=(WriteProcessMemoryBuffer[j].baseaddress+length(WriteProcessMemoryBuffer[j].memory))-(WriteProcessMemoryBuffer[i].baseaddress+length(WriteProcessMemoryBuffer[i].memory)); //get rhe bytes that need to be added if k>0 then //increase the size setlength(WriteProcessMemoryBuffer[i].memory, length(WriteProcessMemoryBuffer[i].memory)+k); //copy the bytes k:=WriteProcessMemoryBuffer[j].baseaddress-WriteProcessMemoryBuffer[i].baseaddress; copymemory(@WriteProcessMemoryBuffer[i].memory[k], @WriteProcessMemoryBuffer[j].memory[0], length(WriteProcessMemoryBuffer[j].memory)); grouped:=true; //delete this from the list for k:=j to length(WriteProcessMemoryBuffer)-2 do WriteProcessMemoryBuffer[k]:=WriteProcessMemoryBuffer[k+1]; setlength(WriteProcessMemoryBuffer, length(WriteProcessMemoryBuffer)-1); end else inc(j); end; if not grouped then //next one inc(i); end; //write for i:=0 to length(WriteProcessMemoryBuffer)-1 do begin if not WriteProcessMemory(WriteProcessMemoryBuffer[i].processhandle, pointer(WriteProcessMemoryBuffer[i].baseaddress), @WriteProcessMemoryBuffer[i].memory[0], length(WriteProcessMemoryBuffer[i].memory), x) then result:=false; end; end; WriteProcessMemoryBufferCS.Leave; end; function TCEConnection.CreateRemoteThread(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; var input: packed record command: byte; hProcess: integer; startaddress: qword; parameter: qword; end; output: integer; begin if isNetworkHandle(hProcess) then begin result:=0; input.command:=CMD_CREATETHREAD; input.hProcess:=hProcess and $ffffff; input.startaddress:=ptruint(lpStartAddress); input.parameter:=ptruint(lpParameter); if send(@input, sizeof(input))>0 then begin output:=0; receive(@output, sizeof(output)); result:=output; if (result>0) then //mark it as a network handle result:=result or $ce000000; lpThreadId:=result; //for now end; end {$ifdef windows} else result:=windows.CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); {$endif} end; function TCEConnection.VirtualAllocEx(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): pointer; var input: packed record command: byte; hProcess: integer; preferedBase: qword; size: integer; windowsprotection: integer; end; output: UINT64; begin result:=nil; if isNetworkHandle(hProcess) then begin input.command:=CMD_ALLOC; input.hProcess:=hProcess and $ffffff; input.preferedBase:=ptruint(lpAddress); input.windowsprotection:=flProtect; input.size:=dwsize; if send(@input, sizeof(input))>0 then begin output:=0; receive(@output, sizeof(output)); result:=pointer(output); end; end {$ifdef windows} else result:=windows.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect); {$endif} end; function TCEConnection.VirtualFreeEx(hProcess: HANDLE; lpAddress: LPVOID; dwSize: SIZE_T; dwFreeType: DWORD): BOOL; var input: packed record command: byte; hProcess: integer; address: qword; size: integer; end; r: UINT32; begin r:=0; if isNetworkHandle(hProcess) then begin result:=false; input.command:=CMD_FREE; input.hProcess:=hProcess and $ffffff; input.address:=ptruint(lpAddress); input.size:=dwsize; if send(@input, sizeof(input))>0 then begin r:=0; receive(@r, sizeof(r)); result:=r<>0; end; end {$ifdef windows} else result:=windows.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType); {$endif} end; function TCEConnection.VirtualQueryEx_StartCache(hProcess: THandle; flags: DWORD): boolean; var vqec: TVirtualQueryExCache; input: packed record command: byte; handle: integer; flags: byte; end; vqe_entry: packed record baseaddress: qword; size: qword; protection: dword; _type: dword; end; mbi: TMEMORYBASICINFORMATION; count: UINT32; i: UINT32; nextAddress: qword; begin result:=false; //check if this processhandle is already cached, and if so, first call endcache on it VirtualQueryEx_EndCache(hProcess); //clean up if there is already one if isNetworkHandle(hProcess) then begin vqec:=TVirtualQueryExCache.create(hProcess); //fill it input.command:=CMD_VIRTUALQUERYEXFULL; input.handle:=hProcess and $ffffff; input.flags:=flags; if send(@input, sizeof(input))>0 then begin // //ceserver will now send the number of entries followed by the entries themself nextAddress:=0; receive(@count, sizeof(count)); for i:=0 to count-1 do begin receive(@vqe_entry, sizeof(vqe_entry)); if (vqe_entry.baseaddress<>nextAddress) then begin mbi.baseaddress:=pointer(nextAddress); mbi.allocationbase:=mbi.BaseAddress; mbi.AllocationProtect:=PAGE_NOACCESS; mbi.protect:=PAGE_NOACCESS; mbi.State:=MEM_FREE; mbi._Type:=0; mbi.RegionSize:=vqe_entry.baseaddress-nextaddress; vqec.AddRegion(mbi); end; mbi.BaseAddress:=pointer(vqe_entry.baseaddress); mbi.AllocationBase:=mbi.BaseAddress; mbi.AllocationProtect:=PAGE_EXECUTE_READWRITE; mbi.Protect:=vqe_entry.protection; if vqe_entry.protection=PAGE_NOACCESS then begin mbi.State:=MEM_FREE; mbi._Type:=0; end else begin mbi.State:=MEM_COMMIT; mbi._Type:=vqe_entry._type; end; mbi.RegionSize:=vqe_entry.size; vqec.AddRegion(mbi); nextaddress:=vqe_entry.baseaddress+vqe_entry.size; end; end else exit; //fail to cache //and add it to the map VirtualQueryExCacheMapCS.enter; try VirtualQueryExCacheMap.insert(hProcess, vqec); finally VirtualQueryExCacheMapCS.Leave; end; end; //don't do anything end; procedure TCEConnection.VirtualQueryEx_EndCache(hProcess: THandle); var vqecache: TVirtualQueryExCache; begin //find the current cache of this hProcess VirtualQueryExCacheMapCS.enter; try if VirtualQueryExCacheMap.TryGetValue(hProcess, vqecache) then begin //cleanup VirtualQueryExCacheMap.Delete(hProcess); vqecache.free; end; finally VirtualQueryExCacheMapCS.leave; end; end; function TCEConnection.VirtualQueryEx(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD): DWORD; var input: packed record command: byte; handle: integer; baseaddress: qword; end; output: packed record result: byte; protection: dword; _type: dword; baseaddress: qword; size: qword; end; vqecache: TVirtualQueryExCache; begin result:=0; if VirtualQueryExCacheMap.TryGetValue(hProcess, vqecache) then //check if there is a cache going on for this handle begin //yes, get it from the cache if vqecache.getRegion(ptruint(lpAddress), lpBuffer) then result:=sizeof(lpBuffer) else result:=0; end else begin //no, use the slow method instead if isNetworkHandle(hProcess) then begin result:=0; input.command:=CMD_VIRTUALQUERYEX; input.handle:=hProcess and $ffffff; input.baseaddress:=qword(lpAddress); if send(@input, sizeof(input))>0 then begin if receive(@output, sizeof(output))>0 then begin if output.result>0 then begin lpBuffer.BaseAddress:=pointer(output.baseaddress); lpBuffer.AllocationBase:=lpBuffer.BaseAddress; lpbuffer.AllocationProtect:=PAGE_NOACCESS; lpbuffer.Protect:=output.protection; if output.protection=PAGE_NOACCESS then begin lpbuffer.State:=MEM_FREE; lpbuffer._Type:=0; end else begin lpbuffer.State:=MEM_COMMIT; lpbuffer._Type:=output._type; end; lpbuffer.RegionSize:=output.size; result:=dwlength; end else result:=0; end; end; end {$ifdef windows} else result:=windows.VirtualQueryEx(hProcess, lpAddress, lpBuffer, dwLength) {$endif}; end; end; function TCEConnection.VirtualProtectEx(hProcess: THandle; lpAddress: Pointer; dwSize, flNewProtect: DWORD; var OldProtect: DWORD): BOOL; var input: packed record command: byte; handle: integer; address: qword; size: UINT32; newprotect: uint32; end; output: packed record result: uint32; oldprotection: uint32; end; begin result:=false; if isNetworkHandle(hProcess) then begin input.command:=CMD_CHANGEMEMORYPROTECTION; input.handle:=hProcess and $ffffff; input.address:=qword(lpAddress); input.size:=dwsize; input.newprotect:=flNewProtect; if send(@input, sizeof(input))>0 then begin if receive(@output, sizeof(output))>0 then begin if output.result=0 then begin oldprotect:=output.oldprotection; exit(true); end else exit(false); end else begin OutputDebugString('CMD_CHANGEMEMORYPROTECTION failed'); end; end; end {$ifdef windows} else result:=windows.VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, @OldProtect); {$endif}; end; function TCEConnection.GetRegionInfo(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD; var mapsline: string): DWORD; var input: packed record command: byte; handle: integer; baseaddress: qword; end; output: packed record result: byte; protection: dword; _type: dword; baseaddress: qword; size: qword; end; mapslinesize: byte; ml: pchar; begin result:=0; log('TCEConnection.GetRegionInfo'); if isNetworkHandle(hProcess) then begin log('valid handle'); result:=0; input.command:=CMD_GETREGIONINFO; input.handle:=hProcess and $ffffff; input.baseaddress:=qword(lpAddress); if send(@input, sizeof(input))>0 then begin if receive(@output, sizeof(output))>0 then begin if output.result>0 then begin lpBuffer.BaseAddress:=pointer(output.baseaddress); lpBuffer.AllocationBase:=lpBuffer.BaseAddress; lpbuffer.AllocationProtect:=PAGE_NOACCESS; lpbuffer.Protect:=output.protection; if output.protection=PAGE_NOACCESS then begin lpbuffer.State:=MEM_FREE; lpbuffer._Type:=0; end else begin lpbuffer.State:=MEM_COMMIT; lpbuffer._Type:=output._type; end; lpbuffer.RegionSize:=output.size; result:=dwlength; end else result:=0; end; //extended part of CMD_GETREGIONINFO; log('receiving extended state'); if receive(@mapslinesize, sizeof(mapslinesize))>0 then begin log('received extended state'); log('mapelinesize='+inttostr(mapslinesize)); getmem(ml, mapslinesize+1); if (ml<>nil) then begin receive(ml, mapslinesize); ml[mapslinesize]:=#0; mapsline:=ml; FreeMemAndNil(ml); end; end; end; end; end; function TCEConnection.OpenProcess(dwDesiredAccess:DWORD; bInheritHandle:WINBOOL; dwProcessId:DWORD):HANDLE; var OpenProcessCommand: packed record command: byte; pid: integer; end; var h: integer; begin result:=0; h:=0; OpenProcessCommand.command:=CMD_OPENPROCESS; OpenProcessCommand.pid:=dwProcessID; if send(@OpenProcessCommand, sizeof(OpenProcessCommand))>0 then if receive(@h, sizeof(h))>0 then begin if (h>0) then h:=h or $ce000000; result:=h; end; end; function TCEConnection.StartDebug(hProcess: THandle): BOOL; var Input: packed record command: byte; handle: integer; end; var Output: packed record result: integer; end; begin result:=false; if ((hProcess shr 24) and $ff)= $ce then begin input.command:=CMD_STARTDEBUG; input.handle:=hProcess and $ffffff; if send(@input, sizeof(input))>0 then begin if receive(@output, sizeof(output))>0 then begin result:=output.result<>0; end; end; end; end; function TCEConnection.ContinueDebugEvent(hProcess: THandle; threadid: dword; continuemethod: integer): BOOL; var input: packed record command: byte; handle: integer; threadid: dword; continuemethod: integer; end; r: integer; begin result:=false; if ((hProcess shr 24) and $ff)= $ce then begin input.command:=CMD_CONTINUEFROMDEBUGEVENT; input.handle:=hProcess and $ffffff; input.threadid:=threadid; input.continuemethod:=continuemethod; if send(@input, sizeof(input))>0 then begin if receive(@r, sizeof(r))>0 then result:=r<>0; end; end; end; function TCEConnection.WaitForDebugEvent(hProcess: THandle; timeout: integer; var devent: TNetworkDebugEvent):BOOL; var Input: packed record command: byte; handle: integer; timeout: integer; end; r: integer; begin result:=false; if ((hProcess shr 24) and $ff)= $ce then begin input.command:=CMD_WAITFORDEBUGEVENT; input.handle:=hProcess and $ffffff; input.timeout:=timeout; if send(@input, sizeof(input))>0 then begin if receive(@r, sizeof(r))>0 then begin result:=r<>0; if result then result:=receive(@devent, sizeof(TNetworkDebugEvent))>0; end; end; end; end; function TCEConnection.SetBreakpoint(hProcess: THandle; threadid: integer; debugregister: integer; address: PtrUInt; bptype: integer; bpsize: integer): boolean; var input: packed record command: byte; handle: integer; tid: integer; debugregister: integer; address: qword; bptype: integer; bpsize: integer; end; r: integer; begin result:=false; if ((hProcess shr 24) and $ff)= $ce then begin input.command:=CMD_SETBREAKPOINT; input.handle:=hProcess and $ffffff; input.tid:=threadid; input.debugregister:=debugregister; input.address:=address; input.bptype:=bptype; input.bpsize:=bpsize; if send(@input, sizeof(input))>0 then begin if receive(@r, sizeof(r))>0 then result:=r<>0; end; end; end; function TCEConnection.RemoveBreakpoint(hProcess: THandle; threadid: integer; debugregister: integer; wasWatchpoint: boolean): boolean; var input: packed record command: byte; handle: integer; tid: integer; debugregister: integer; wasWatchpoint: integer; end; r: integer; begin result:=false; if ((hProcess shr 24) and $ff)= $ce then begin input.command:=CMD_REMOVEBREAKPOINT; input.handle:=hProcess and $ffffff; input.tid:=threadid; input.debugregister:=debugregister; if wasWatchpoint then input.wasWatchpoint:=1 else input.wasWatchpoint:=0; if send(@input, sizeof(input))>0 then begin if receive(@r, sizeof(r))>0 then result:=r<>0; end; end; end; function TCEConnection.setContext(hProcess: Thandle; threadid: integer; context: pointer; contextsize: integer): boolean; var input: packed record command: UINT8; hProcess: uint32; threadid: uint32; contextsize: uint32; end; r: uint32; begin result:=false; input.command:=CMD_SETTHREADCONTEXT; input.hProcess:=hProcess and $ffffff; input.threadid:=threadid; input.contextsize:=ContextSize; if send(@input, sizeof(input))>0 then begin if send(context, contextsize)>0 then begin if receive(@r,sizeof(r))>0 then result:=r<>0; end; end; end; function TCEConnection.AllocateAndGetContext(hProcess: Thandle; threadid: integer): pointer; //get he context and save it in an allocated memory block of variable size. The caller is responsible for freeing this block var Input: packed record command: UINT8; hprocess: uint32; threadid: uint32; end; contextsize: UINT32; r: integer; begin result:=nil; input.command:=CMD_GETTHREADCONTEXT; input.hprocess:=hProcess and $ffffff; input.threadid:=threadid; if send(@input, sizeof(input))>0 then begin if receive(@r, sizeof(r))>0 then begin if (r<>0) and (receive(@contextsize, sizeof(contextsize))>0) then begin getmem(result, contextsize); if receive(result, contextsize)=0 then begin FreeMemAndNil(result); result:=nil; end; end; end; end; end; function TCEConnection.getVersion(var name: string): integer; var CeVersion: packed record version: integer; stringsize: byte; end; _name: pchar; command: byte; begin result:=0; command:=CMD_GETVERSION; if send(@command, 1)>0 then begin if receive(@CeVersion, sizeof(CeVersion))>0 then begin getmem(_name, CeVersion.stringsize+1); receive(_name, CeVersion.stringsize); _name[CeVersion.stringsize]:=#0; name:=_name; FreeMemAndNil(_name); result:=CeVersion.version; if copy(_name,1,3)='lib' then executesInsideTarget:=true; self.version:=result; end; end; end; function TCEConnection.getArchitecture(hProcess: THandle): integer; var input: packed record command: byte; hprocess: uint32; end; r: byte; begin result:=0; input.command:=CMD_GETARCHITECTURE; input.hprocess:=hProcess and $ffffff; if send(@input, sizeof(input))>0 then if receive(@r, 1)>0 then result:=r; end; function TCEConnection.getABI: integer; var command: byte; r: byte; begin result:=0; command:=CMD_GETABI; if send(@command, 1)>0 then if receive(@r, 1)>0 then result:=r; end; function ShortenLinuxModuleName(modulename: string): string; var i: integer; begin //build the shortenedmodulename //parse the modulepath and strip the version and .so part and everything after it //formats: libxxx-#.#.so.#.#.# //keep in mind names like :libdbusmenu-gtk.so.4.0.12 and libdbusmenu-glib.so.4.0.12 should become libdbusmenu-gtk and libdbusmenu-glib respectively for i:=1 to length(modulename)-1 do begin case modulename[i] of '-': begin //check if modulename[i+1] is a number, if so, cut from here if (length(modulename)>=i+1) and (modulename[i+1] in ['0'..'9']) then begin result:=copy(modulename, 1, i-1); exit; end; end; '.': begin //check if it is .so if (length(modulename)>=i+2) and (uppercase(modulename[i+1])='S') and (uppercase(modulename[i+2])='O') then begin result:=copy(modulename, 1, i-1); exit; end; end; end; end; //still here result:=modulename; end; function TCEConnection.enumSymbolsFromFile(modulepath: string; fileoffset: dword; modulebase: ptruint; callback: TNetworkEnumSymCallback; nameaddendum: string=''): boolean; type TCeGetSymbolList=packed record command: byte; fileoffset: uint32; symbolpathsize: uint32; path: array [0..0] of char; end; PCeGetSymbolList=^TCeGetSymbolList; TNetworkSymbolInfo=packed record address: uint64; size: int32; _type: int32; namelength: uint8; end; PNetworkSymbolInfo=^TNetworkSymbolInfo; var msg: PCeGetSymbolList; msgsize: integer; compressedsize: uint32; decompressedsize: uint32; d: Tdecompressionstream; compressedbuffer: TMemorystream; decompressed: PByte; currentsymbol: PNetworkSymbolInfo; modulename: string; pos: integer; symname: pchar; maxsymname: integer; isexe: uint32; shortenedmodulename: string=''; //the name of the module with nothing after .so i: integer; begin result:=true; if modulepath='[vdso]' then begin //special module with no clear filepath result:=EnumElfSymbols('vdso', modulebase, callback); exit; end; msgsize:=1+4+4+length(modulepath); getmem(msg, msgsize); msg^.command:=CMD_GETSYMBOLLISTFROMFILE; msg^.fileoffset:=fileoffset; msg^.symbolpathsize:=length(modulepath); CopyMemory(@msg^.path, @modulepath[1], length(modulepath)); if fileoffset<>0 then asm nop end; if send(msg, msgsize)>0 then begin if receive(@isexe, sizeof(isexe))>0 then begin if receive(@compressedsize, sizeof(compressedsize))>0 then begin if compressedsize>0 then begin if receive(@decompressedsize, sizeof(decompressedsize))>0 then begin compressedbuffer:=tmemorystream.create; compressedbuffer.Size:=compressedsize-3*sizeof(uint32); if receive(compressedbuffer.Memory, compressedbuffer.size)>0 then begin //decompress it d:=Tdecompressionstream.Create(compressedbuffer, false); getmem(decompressed, decompressedsize); d.ReadBuffer(decompressed^, decompressedsize); d.free; //parse through the decompressed block and fill in the results if copy(modulepath,1,1)<>'[' then begin modulename:=extractfilename(modulepath); shortenedmodulename:=ShortenLinuxModuleName(modulename); end else modulename:=modulepath; pos:=0; maxsymname:=256; getmem(symname, maxsymname); while pos=maxsymname then begin //need more memory maxsymname:=currentsymbol^.namelength+1; FreeMemAndNil(symname); getmem(symname, maxsymname); end; CopyMemory(symname, @decompressed[pos], currentsymbol^.namelength); symname[currentsymbol^.namelength]:=#0; inc(pos, currentsymbol^.namelength); if currentsymbol^.namelength>0 then begin if isexe<>0 then begin if callback(modulename, symname, currentsymbol^.address, currentsymbol^.size,false)=false then break; end else begin if (callback(shortenedmodulename+nameaddendum, symname, modulebase+currentsymbol^.address, currentsymbol^.size, false) and callback(modulename+nameaddendum, symname, modulebase+currentsymbol^.address, currentsymbol^.size, true))=false then break; end; end; end; FreeMemAndNil(symname); FreeMemAndNil(decompressed); end; compressedbuffer.free; end; end; end; end; end; end; function TCEConnection.loadModuleEx(hProcess: THandle; dlopenaddress: ptruint; modulepath: string): boolean; type TInput=packed record command: uint8; handle: uint32; dlopenaddress: uint64; modulepathlength: uint32; modulename: packed record end; end; PInput=^TInput; var input: Pinput; r:uint64; begin result:=false; if isNetworkHandle(hProcess) then begin getmem(input, sizeof(TInput)+length(modulepath)); input^.command:=CMD_LOADMODULEEX; input^.handle:=hProcess and $ffffff; input^.dlopenaddress:=dlopenaddress; input^.modulepathlength:=Length(modulepath); CopyMemory(@input^.modulename, @modulepath[1], length(modulepath)); if send(input, sizeof(TInput)+length(modulepath))>0 then begin receive(@r, sizeof(r)); result:=r<>0; end; FreeMemAndNil(input); end; end; function TCEConnection.loadModule(hProcess: THandle; modulepath: string): boolean; type TInput=packed record command: uint8; handle: uint32; modulepathlength: uint32; modulename: packed record end; end; PInput=^TInput; var input: Pinput; r:uint64; begin OutputDebugString('TCEConnection.loadModule('+modulepath+')'); result:=false; if isNetworkHandle(hProcess) then begin getmem(input, sizeof(TInput)+length(modulepath)+1); input^.command:=CMD_LOADMODULE; input^.handle:=hProcess and $ffffff; input^.modulepathlength:=Length(modulepath)+1; CopyMemory(@input^.modulename, @modulepath[1], length(modulepath)+1); //also include 0 terminator if send(input, sizeof(TInput)+length(modulepath)+1)>0 then begin receive(@r, sizeof(r)); OutputDebugString('CMD_LOADMODULE returned '+r.ToString); result:=r<>0; end; FreeMemAndNil(input); end; end; function TCEConnection.loadExtension(hProcess: THandle): boolean; var loadExtensionCommand: packed record command: byte; handle: uint32; end; r:uint32; begin result:=false; if isNetworkHandle(hProcess) then begin loadExtensionCommand.command:=CMD_LOADEXTENSION; loadExtensionCommand.handle:=hProcess and $ffffff; if send(@loadExtensionCommand, sizeof(loadExtensionCommand))>0 then begin receive(@r, sizeof(r)); result:=r<>0; end; end; end; function TCEConnection.speedhack_setSpeed(hProcess: THandle; speed: single): boolean; var speedhackSetSpeedCommand: packed record command: byte; handle: uint32; speed: single; end; r:uint32; begin result:=false; if isNetworkHandle(hProcess) then begin speedhackSetSpeedCommand.command:=CMD_SPEEDHACK_SETSPEED; speedhackSetSpeedCommand.handle:=hProcess and $ffffff; speedhackSetSpeedCommand.speed:=speed; if send(@speedhackSetSpeedCommand, sizeof(speedhackSetSpeedCommand))>0 then begin receive(@r, sizeof(r)); result:=r<>0; end; end; end; procedure TCEConnection.setConnectionName(name: string); type TInput=packed record command: uint8; namelength: uint32; name: packed record end; end; PInput=^TInput; var input: Pinput; ignored: string; begin if (getVersion(ignored)>=4) then begin getmem(input, sizeof(TInput)+length(name)); input^.command:=CMD_SET_CONNECTION_NAME; input^.namelength:=length(name); copymemory(@input^.name, @name[1], length(name)); send(input, sizeof(TInput)+length(name)); end; end; procedure TCEConnection.getOptions(var options: TCEServerOptions); var command: uint8; optioncount: UINT16; i: integer; b: byte; t: integer; begin command:=CMD_GETOPTIONS; send(@command,1); receive(@optioncount,sizeof(optioncount)); setlength(options, optioncount); for i:=0 to optioncount-1 do begin options[i].optname:=receiveString16; options[i].parentoptname:=receiveString16; options[i].optdescription:=receiveString16; options[i].acceptablevalues:=receiveString16; options[i].currentvalue:=receiveString16; receive(@t, sizeof(t)); options[i].optiontype:=TCEServerOptionType(t); end; end; function TCEConnection.getOption(name: string): string; var buf: tmemorystream; begin buf:=tmemorystream.create; buf.WriteByte(CMD_GETOPTION); buf.WriteWord(length(name)); buf.WriteBuffer(name[1],length(name)); send(buf.Memory, buf.Size); buf.free; result:=receiveString16; end; procedure TCEConnection.setOption(name: string; value: string); var buf: tmemorystream; begin buf:=tmemorystream.create; buf.WriteByte(CMD_SETOPTION); buf.WriteWord(length(name)); buf.WriteBuffer(name[1],length(name)); buf.WriteWord(length(value)); if length(value)>0 then buf.WriteBuffer(value[1], length(value)); send(buf.Memory, buf.Size); buf.free; end; function TCEConnection.connectNamedPipe(name: string; timeout:integer=0): HANDLE; var buf: tmemorystream; r: uint32; begin buf:=tmemorystream.Create; buf.WriteByte(CMD_OPENNAMEDPIPE); buf.WriteWord(length(name)); buf.WriteBuffer(name[1],length(name)); buf.WriteDWord(timeout); send(buf.memory, buf.size); buf.free; receive(@r,sizeof(r)); if r<>0 then result:=$ce000000 or r else result:=INVALID_HANDLE_VALUE; end; function TCEConnection.readPipe(h: THandle; destination: pointer; size: integer; timeout:integer=0): boolean; var input: packed record command: byte; h: uint32; size: uint32; timeout: uint32; end; actualsize: int32; begin if isNetworkHandle(h) then begin h:=h and $ffffff; input.command:=CMD_PIPEREAD; input.h:=h; input.size:=size; input.timeout:=timeout; send(@input, sizeof(input)); receive(@actualsize, sizeof(actualsize)); if actualsize>size then begin //oh no.... fConnected:=false; if socket<>0 then CloseSocket(socket); socket:=0; exit(false); end; receive(destination, actualsize); result:=actualsize=size; end else result:=false; end; function TCEConnection.writePipe(h: THandle; source: pointer; size: integer; timeout:integer=0): boolean; var buf: tmemorystream; c: int32; begin if isNetworkHandle(h) then begin h:=h and $ffffff; buf:=tmemorystream.create; buf.WriteByte(CMD_PIPEWRITE); buf.WriteDWord(h); buf.writeDword(size); buf.writeDword(timeout); buf.WriteBuffer(source^,size); send(buf.memory, buf.size); buf.free; receive(@c,sizeof(c)); result:=c=size; end else result:=false; end; function TCEConnection.getServerPath: string; var c: byte; begin c:=CMD_GETCESERVERPATH; send(@c,1); result:=receiveString16; end; function TCEConnection.isAndroid: boolean; var command, r: byte; begin command:=CMD_ISANDROID; send(@command,1); receive(@r,1); result:=r<>0; end; function TCEConnection.setCurrentPath(path: string): boolean; var buf: Tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.writebyte(CMD_SETCURRENTPATH); buf.WriteWord(length(path)); buf.WriteBuffer(path[1],word(length(path))); send(buf.Memory, buf.Size); buf.free; receive(@r,1); result:=r<>0; end; function TCEConnection.getCurrentPath: string; var command: byte; begin command:=CMD_GETCURRENTPATH; send(@command,1); result:=receiveString16; end; procedure TCEConnection.enumfiles(path: string; list: tstrings); var buf: tmemorystream; filecount: uint32; i: uint32; s: string; t: byte; begin buf:=tmemorystream.create; buf.WriteByte(CMD_ENUMFILES); buf.writeword(length(path)); buf.writebuffer(path[1],word(length(path))); send(buf.memory,buf.size); buf.free; list.clear; repeat s:=receivestring16; if s<>'' then begin receive(@t,1); list.AddObject(s, tobject(pointer(t))); end; until s=''; end; function TCEConnection.getFilePermissions(path: string; out perms: UINT32): boolean; var buf: tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.Writebyte(CMD_GETFILEPERMISSIONS); buf.writeword(length(path)); buf.writebuffer(path[1],word(length(path))); send(buf.memory,buf.size); buf.free; receive(@r,1); if r<>0 then receive(@perms, sizeof(perms)); result:=r<>0; end; function TCEConnection.setFilePermissions(path: string; perms: UINT32): boolean; var buf: tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.Writebyte(CMD_SETFILEPERMISSIONS); buf.writeword(length(path)); buf.writebuffer(path[1],word(length(path))); buf.writedword(perms); send(buf.memory,buf.size); buf.free; receive(@r,1); result:=r<>0; end; function TCEConnection.getFile(path: string; s: tstream): boolean; var buf: tmemorystream; filelength: uint32; //do not bother with 4GB+ files... f: pointer; begin buf:=tmemorystream.create; buf.writeByte(CMD_GETFILE); buf.writeword(length(path)); buf.writebuffer(path[1],word(length(path))); send(buf.memory,buf.size); buf.free; receive(@filelength,sizeof(filelength)); if filelength<>$ffffffff then begin getmem(f,filelength); receive(f,filelength); s.Position:=0; s.Size:=0; s.WriteBuffer(f^,filelength); freemem(f); end; result:=filelength<>$ffffffff; end; function TCEConnection.putFile(path: string; s: tstream): boolean; var buf: tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.WriteByte(CMD_PUTFILE); buf.WriteWord(length(path)); buf.writebuffer(path[1],word(length(path))); buf.WriteDWord(s.Size); buf.CopyFrom(s,0); send(buf.Memory,buf.size); receive(@r,1); result:=r<>0; end; function TCEConnection.createDir(path: string): boolean; var buf: tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.WriteByte(CMD_CREATEDIR); buf.WriteWord(length(path)); buf.writebuffer(path[1],word(length(path))); send(buf.Memory,buf.size); receive(@r,1); result:=r<>0; end; function TCEConnection.deleteFile(path: string): boolean; var buf: tmemorystream; r: byte; begin buf:=tmemorystream.create; buf.WriteByte(CMD_DELETEFILE); buf.WriteWord(length(path)); buf.writebuffer(path[1],word(length(path))); send(buf.Memory,buf.size); receive(@r,1); result:=r<>0; end; function TCEConnection.isNetworkHandle(handle: THandle): boolean; begin result:=((handle shr 24) and $ff)= $ce; end; function TCEConnection.send(buffer: pointer; size: integer): integer; var i: integer; B: BOOL=TRUE; begin result:=0; while (result0 then CloseSocket(socket); socket:=0; result:=i; //error exit; end; inc(result, i); end; end; function TCEConnection.receive(buffer: pointer; size: integer): integer; var i: integer; begin //{$ifdef windows} //xp doesn't support MSG_WAITALL result:=0; while (result0 then CloseSocket(socket); socket:=0; result:=i; //error OutputDebugString('Error during receive'); exit; end; inc(result, i); end; // {$else} // result:=fprecv(socket, buffer, size, MSG_WAITALL); //{$endif} end; function TCEConnection.receiveString16: string; var l: uint16; r: pchar; begin receive(@l,sizeof(l)); if l=0 then exit(''); getmem(r,l); receive(r,l); r[l]:=#0; result:=r; freemem(r); end; constructor TCEConnection.create; var SockAddr: TInetSockAddr; retry: integer; B: BOOL; begin OutputDebugString('Inside TCEConnection.create'); WriteProcessMemoryBufferCS:=TCriticalSection.create; VirtualQueryExCacheMapCS:=TCriticalSection.create; VirtualQueryExCacheMap:=TVQEMap.Create; socket:=cint(INVALID_SOCKET); if (host.s_addr=0) or (port=0) then exit; //connect socket:=FPSocket(AF_INET, SOCK_STREAM, 0); if (socket=cint(INVALID_SOCKET)) then begin OutputDebugString('Socket creation failed. Check permissions'); exit; end; OutputDebugString('socket='+inttostr(socket)); SockAddr.sin_family := AF_INET; SockAddr.sin_port := port; SockAddr.sin_addr.s_addr := host.s_addr; B:=TRUE; fpsetsockopt(socket, IPPROTO_TCP, TCP_NODELAY, @B, sizeof(B)); retry:=0; while not (fConnected) and (retry<5) do begin if fpconnect(socket, @SockAddr, sizeof(SockAddr)) >=0 then begin b:=TRUE; fpsetsockopt(socket, IPPROTO_TCP, TCP_NODELAY, @B, sizeof(B)); //just to be sure fConnected:=true; end else begin inc(retry); // OutputDebugString('fail '+inttostr(retry)); end; end; if not fconnected then OutputDebugString('Connection failure'); end; destructor TCEConnection.destroy; begin if not ((socket=cint(INVALID_SOCKET)) or (socket=0)) then CloseSocket(socket); if VirtualQueryExCacheMap<>nil then VirtualQueryExCacheMap.free; if VirtualQueryExCacheMapCS<>nil then VirtualQueryExCacheMapCS.free; if WriteProcessMemoryBufferCS<>nil then WriteProcessMemoryBufferCS.free; end; initialization LocalToolhelpSnapshotsCS:=TCriticalSection.create; finalization LocalToolhelpSnapshotsCS.free; end.