unit networkInterfaceApi;

{$mode objfpc}{$H+}

interface

uses
  {$ifdef JNI}
    Classes, SysUtils, networkinterface, unixporthelper, newkernelhandler;
  {$else}
  {$ifdef darwin}
  mactypes, macport,
  {$endif}
  {$ifdef windows}
  {jwawindows,} windows,
  {$endif}
  Classes, SysUtils, networkinterface, newkernelhandler, CEFuncProc;
  {$endif}


procedure InitializeNetworkInterface;
function getConnection: TCEConnection;
procedure disconnect;

function NetworkVersion(var name: string): integer;

function NetworkReadProcessMemory(hProcess: THandle; lpBaseAddress, lpBuffer: Pointer; nSize: size_t; var lpNumberOfBytesRead: ptruint): BOOL; stdcall;
function NetworkWriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: size_t; var lpNumberOfBytesWritten: ptruint): BOOL; stdcall;

function NetworkVirtualQueryEx(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD): DWORD; stdcall;
function NetworkOpenProcess(dwDesiredAccess:DWORD; bInheritHandle:WINBOOL; dwProcessId:DWORD):HANDLE; stdcall;
function NetworkCreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): HANDLE; stdcall;
function NetworkProcess32First(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; stdcall;
function NetworkProcess32Next(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; stdcall;

function NetworkModule32First(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; stdcall;
function NetworkModule32Next(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; stdcall;

function NetworkCloseHandle(handle: THandle):WINBOOL; stdcall;
function NetworkSetBreakpoint(handle: THandle; threadid: integer; debugregister: integer; address: PtrUInt; bptype: integer; bpsize: integer): boolean;
function NetworkRemoveBreakpoint(handle: THandle; threadid: integer; debugregister: integer; wasWatchpoint: boolean): boolean;

function NetworkGetRegionInfo(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD; var mapsline: string): DWORD; stdcall;

implementation

{$ifndef jni}
uses networkConfig;
{$endif}

resourcestring
  rsNoConnection = 'No connection';

threadvar connection: TCEConnection;


var threadManagerIsHooked: boolean=false;
    oldendthread: TEndThreadHandler;

function getConnection: TCEConnection;
begin
  //OutputDebugString('getConnection');
  result:=nil;

  if {$ifndef jni}networkconfig.{$endif}host.s_addr<>0 then
  begin
    //OutputDebugString('Valid host');
    if (connection=nil) or (not connection.connected) then
    begin
      OutputDebugString('connection=nil. creating');
      disconnect;

      connection:=TCEConnection.create;
      if connection.connected then
        result:=connection
      else
        OutputDebugString('connection.connected=false');

    end
    else
    begin
      //OutputDebugString('Already connected');
      result:=connection;
    end;

  end;
end;

procedure disconnect;
begin
  if connection<>nil then
    freeandnil(connection);
end;

function NetworkCloseHandle(handle: THandle):WINBOOL; stdcall;
begin
  if getConnection<>nil then
    result:=connection.CloseHandle(handle)
  else
    result:=false;
end;

function NetworkVirtualQueryEx_StartCache(hProcess: THandle; flags: dword): boolean;
begin
  if getConnection<>nil then
    result:=connection.VirtualQueryEx_StartCache(hProcess, flags)
  else
    result:=false;
end;

procedure NetworkVirtualQueryEx_EndCache(hProcess: THandle);
begin
  if getConnection<>nil then
    connection.VirtualQueryEx_EndCache(hProcess);
end;

function NetworkProcess32Next(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; stdcall;
begin
  if getConnection<>nil then
    result:=connection.Process32Next(hSnapshot, lppe)
  else
    result:=FALSE;
end;

function NetworkProcess32First(hSnapshot: HANDLE; var lppe: PROCESSENTRY32): BOOL; stdcall;
begin
  OutputDebugString('NetworkProcess32First');
  if getConnection<>nil then
    result:=connection.Process32First(hSnapshot, lppe)
  else
    result:=FALSE;
end;

function NetworkModule32Next(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; stdcall;
begin
  if getConnection<>nil then
    result:=connection.Module32Next(hSnapshot, lpme)
  else
    result:=FALSE;
end;

function NetworkModule32First(hSnapshot: HANDLE; var lpme: MODULEENTRY32): BOOL; stdcall;
begin
  if getConnection<>nil then
    result:=connection.Module32First(hSnapshot, lpme)
  else
    result:=FALSE;
end;

function NetworkCreateToolhelp32Snapshot(dwFlags, th32ProcessID: DWORD): HANDLE; stdcall;
begin
  if getConnection<>nil then
    result:=connection.CreateToolhelp32Snapshot(dwflags, th32ProcessId)
  else
    result:=INVALID_HANDLE_VALUE;
end;

function NetworkSetBreakpoint(handle: THandle; threadid: integer; debugregister: integer; address: PtrUInt; bptype: integer; bpsize: integer): boolean;
begin
  if getConnection<>nil then
    result:=connection.SetBreakpoint(handle, threadid, debugregister, address, bptype, bpsize)
  else
    result:=FALSE;
end;

function NetworkRemoveBreakpoint(handle: THandle; threadid: integer; debugregister: integer; wasWatchpoint: boolean): boolean;
begin
  if getConnection<>nil then
    result:=connection.RemoveBreakpoint(handle, threadid, debugregister, wasWatchpoint)
  else
    result:=FALSE;
end;

function NetworkReadProcessMemory(hProcess: THandle; lpBaseAddress, lpBuffer: Pointer; nSize: size_t; var lpNumberOfBytesRead: ptruint): BOOL; stdcall;
var a,b: dword;
    c,d: ptruint;
begin
  //log('NetworkReadProcessMemory');
  //log(format('Read %d bytes from %p into %p',[nsize, lpBaseAddress, lpBuffer]));

  if getConnection<>nil then
  begin
   // log('Has connection');

    result:=connection.readProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead);
    if (result=false) and (connection.connected=false) and (getConnection<>nil) then //try again one more time
    begin
    //  log('read fail1. Try smaller chunk');

      //try a smaller chunk
      a:=nsize div 2;
      b:=nsize-a;
      c:=0;
      d:=0;

      //log('a='+inttostr(a));
     // log('b='+inttostr(b));

      result:=connection.readProcessMemory(hProcess, lpBaseAddress, lpBuffer, a, c);

     // log('after read 1/2');

      if result and (b>0) and (c>0) then //first read succesful, there is something else to read, and it actually has read
      begin
        result:=connection.readProcessMemory(hProcess, pointer(ptruint(lpBaseAddress)+a), pointer(ptruint(lpBuffer)+a), b, d);
       // log('after read 2/2');
      end;

      if @lpNumberOfBytesRead<>nil then
        lpNumberOfBytesRead:=c+d;
    end;


  end
  else
    result:=false;

  //log('Returning from rpm');
end;

function NetworkWriteProcessMemory(hProcess: THandle; const lpBaseAddress: Pointer; lpBuffer: Pointer; nSize: size_t; var lpNumberOfBytesWritten: ptruint): BOOL; stdcall;
begin
  if getConnection<>nil then
    result:=connection.writeProcessMemory(hProcess, lpBaseAddress, lpBuffer, nSize, lpNumberOfBytesWritten)
  else
    result:=false;
end;


function NetworkGetRegionInfo(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD; var mapsline: string): DWORD; stdcall;
begin
  if getConnection<>nil then
    result:=connection.GetRegionInfo(hProcess, lpAddress, lpBuffer, dwLength, mapsline)
  else
    result:=0;
end;

function NetworkVirtualQueryEx(hProcess: THandle; lpAddress: Pointer; var lpBuffer: TMemoryBasicInformation; dwLength: DWORD): DWORD; stdcall;
begin
  if getConnection<>nil then
    result:=connection.VirtualQueryEx(hProcess, lpAddress, lpBuffer, dwLength)
  else
    result:=0;
end;

function NetworkVirtualAllocEx(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
begin
  if getconnection<>nil then
    result:=connection.VirtualAllocEx(hProcess, lpAddress, dwSize, flAllocationType, flProtect)
  else
    result:=nil;
end;

function NetworkVirtualFreeEx(hProcess: HANDLE; lpAddress: LPVOID; dwSize: SIZE_T; dwFreeType: DWORD): BOOL; stdcall;
begin
  if getconnection<>nil then
    result:=connection.VirtualFreeEx(hProcess, lpAddress, dwSize, dwFreeType)
  else
    result:=FALSE;
end;

function NetworkCreateRemoteThread(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer;  dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall;
begin
  if getconnection<>nil then
    result:=connection.CreateRemoteThread(hProcess, lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId)
  else
    result:=0;
end;

function NetworkOpenProcess(dwDesiredAccess:DWORD; bInheritHandle:WINBOOL; dwProcessId:DWORD):HANDLE; stdcall;
begin
  if getConnection<>nil then
    result:=connection.OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId)
  else
    result:=0;
end;

function NetworkVersion(var name: string): integer;
begin
  if getConnection<>nil then
    result:=connection.getVersion(name)
  else
  begin
    result:=-1;
    name:=rsNoConnection;
  end;
end;

Procedure EndThread(ExitCode : DWord); //called when a thread is terminated
begin
  if connection<>nil then
    freeandnil(connection);

  oldendthread(ExitCode);
end;

function NetworkVirtualProtectEx(hProcess: THandle; lpAddress: Pointer; dwSize, flNewProtect: DWORD; var OldProtect: DWORD): BOOL; stdcall;
begin
  //for now don't bother with this
  //todo: implement this someday
  result:=true;
end;

procedure InitializeNetworkInterface;
var tm: TThreadManager;
begin
  //hook the threadmanager if it hasn't been hooked yet

  {$ifdef windows}

  OutputDebugString('InitializeNetworkInterface');

  if not threadManagerIsHooked then
  begin
    GetThreadManager(tm);

    oldendthread:=tm.EndThread;
    tm.EndThread:=@EndThread;

    SetThreadManager(tm);

    threadManagerIsHooked:=true;
  end;

  newkernelhandler.OpenProcess:=@NetworkOpenProcess;
  newkernelhandler.ReadProcessMemory:=@NetworkReadProcessMemory;
  newkernelhandler.WriteProcessMemoryActual:=@NetworkWriteProcessMemory;
  newkernelhandler.VirtualProtectEx:=@NetworkVirtualProtectEx;
  newkernelhandler.VirtualQueryEx:=@NetworkVirtualQueryEx;
  newkernelhandler.CreateToolhelp32Snapshot:=@NetworkCreateToolhelp32Snapshot;
  newkernelhandler.Process32First:=@NetworkProcess32First;
  newkernelhandler.Process32Next:=@NetworkProcess32Next;
  newkernelhandler.Module32First:=@NetworkModule32First;
  newkernelhandler.Module32Next:=@NetworkModule32Next;
  newkernelhandler.closehandle:=@networkclosehandle;

  newkernelhandler.VirtualAllocEx:=@networkVirtualAllocEx;
  newkernelhandler.VirtualFreeEx:=@networkVirtualFreeEx;
  newkernelhandler.CreateRemoteThread:=@networkCreateRemoteThread;

  newkernelhandler.GetRegionInfo:=@NetworkGetRegionInfo;



  newkernelhandler.VirtualQueryEx_StartCache:=@NetworkVirtualQueryEx_StartCache;
  newkernelhandler.VirtualQueryEx_EndCache:=@NetworkVirtualQueryEx_EndCache;
   {$endif}

end;

end.

