cheat-engine/Cheat Engine/frmProcessWatcherUnit.pas
cheatengine@gmail.com 0f35a52416 move the processid and handle from cefuncproc to processhandlerunit
move the processlist function from cefuncproc to it's own unit
start work on the jni library for java
2014-09-08 12:00:14 +00:00

515 lines
14 KiB
ObjectPascal

unit frmProcessWatcherUnit;
{$MODE Delphi}
interface
uses
jwawindows, windows, LCLIntf, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,NewKernelHandler, ExtCtrls, ComCtrls, StdCtrls,CEDebugger,kerneldebugger,
CEFuncProc, Menus, LResources,{tlhelp32,} symbolhandler, debugHelper, syncobjs;
type tthreaddata=record
threadid: dword;
end;
type tprocessdata=record
processid: DWORD;
peprocess: uint64;
threadlist: array of tthreaddata;
end;
type tprocesswatchthread=class(tthread)
private
error: string;
created:boolean;
pid: dword;
peprocess: uint64;
tid:dword;
procedure crash;
procedure UpdateList;
procedure Updatelist2;
procedure UpdateThreadcount(processid:dword;count:dword);
public
openedByAutoAttach: boolean;
procedure execute; override;
end;
type
TfrmProcessWatcher = class(TForm)
tvProcesslist: TTreeView;
Panel1: TPanel;
btnOpen: TButton;
btnAttach: TButton;
pmthreadid: TPopupMenu;
ShowThreadIDs1: TMenuItem;
procedure FormCreate(Sender: TObject);
procedure tvProcesslistDblClick(Sender: TObject);
procedure btnOpenClick(Sender: TObject);
procedure btnAttachClick(Sender: TObject);
procedure ShowThreadIDs1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Panel1Resize(Sender: TObject);
private
{ Private declarations }
processwatchthread: tprocesswatchthread;
procedure PWOP(ProcessIDString:string);
public
{ Public declarations }
processes: array of tprocessdata;
processesCS: TCriticalSection;
end;
var
frmProcessWatcher: TfrmProcessWatcher;
implementation
uses formsettingsunit, MainUnit,AdvancedOptionsUnit, MemoryBrowserFormUnit,
frmProcesswatcherExtraUnit,plugin, processhandlerunit;
resourcestring
rsProcesswatcherThreadError = 'processwatcher: thread error:%s';
rsFailedStartingTheProcessWatcher = 'Failed starting the process watcher';
rsIsnTAValidProcessID = '%s isn''t a valid processID';
rsFirstSelectAProcess = 'First select a process!';
procedure tprocesswatchthread.crash;
begin
showmessage(Format(rsProcesswatcherThreadError, [error]));
end;
procedure TProcesswatchthread.UpdateThreadcount(processid:dword;count:dword);
var i,j: integer;
begin
//find the processd in the treeview and update the threadcount
frmProcessWatcher.processesCS.enter;
try
with frmProcessWatcher do
for i:=0 to tvprocesslist.Items.Count-1 do
if (tvprocesslist.Items[i].Level=0) and (copy(tvprocesslist.Items[i].Text,1,8)=IntToHex(processid,8)) then
begin
for j:=length(tvprocesslist.Items[i].Text) downto 1 do
if tvprocesslist.Items[i].Text[j]='(' then //found the start
begin
//change the text
tvprocesslist.Items[i].Text:=copy(tvprocesslist.Items[i].Text,1,j-1)+'('+inttostr(count)+')';
break;
end;
break;
end;
finally
frmProcessWatcher.processesCS.leave;
end;
end;
procedure Tprocesswatchthread.Updatelist2;
{
used vars:
Created:BOOL;
ProcessID:DWORD;
ThreadID:dword;
PEThread:DWORD;
}
var i,j,k:integer;
found:boolean;
begin
if frmProcessWatcher=nil then exit;
//add/remove the thread from the processlist
//and show the threadcount to the list
//find the process
frmProcessWatcher.processesCS.Enter;
try
for i:=0 to length(frmprocesswatcher.processes)-1 do
if frmprocesswatcher.processes[i].processid=self.pid then
begin
if not self.created then
begin
//find the thread and delete it
found:=false;
for j:=0 to length(frmprocesswatcher.processes[i].threadlist)-1 do
if frmprocesswatcher.processes[i].threadlist[j].threadid=self.tid then
begin
//memo1.Lines.Add(inttostr(self.processid)+' - removed thread '+inttostr(self.threadid));
//remove it from the list
for k:=j to length(frmprocesswatcher.processes[i].threadlist)-2 do
frmprocesswatcher.processes[i].threadlist[k]:=frmprocesswatcher.processes[i].threadlist[k+1];
setlength(frmprocesswatcher.processes[i].threadlist,length(frmprocesswatcher.processes[i].threadlist)-1);
UpdateThreadcount(self.pid,length(frmprocesswatcher.processes[i].threadlist));
found:=true;
break;
end;
break;
end
else
begin
//memo1.Lines.Add(inttostr(self.processid)+' - added thread '+inttostr(self.threadid));
//add the thread to the list
setlength(frmprocesswatcher.processes[i].threadlist,length(frmprocesswatcher.processes[i].threadlist)+1);
frmprocesswatcher.processes[i].threadlist[length(frmprocesswatcher.processes[i].threadlist)-1].threadid:=self.tid;
UpdateThreadcount(self.pid,length(frmprocesswatcher.processes[i].threadlist));
end;
break;
end;
finally
frmProcessWatcher.processesCS.Leave;
end;
end;
procedure tprocesswatchthread.UpdateList;
var i,j:integer;
deleted:boolean;
processname: pchar;
tn: ttreenode; //treenode of new process
autoAttachThisProcess: boolean;
begin
autoAttachThisProcess:=false;
//first fix the processes array
frmProcessWatcher.processesCS.Enter;
try
with frmProcessWatcher do
begin
if not created then
begin
//find it and delete it
for i:=0 to length(processes)-1 do
if processes[i].peprocess=peprocess then
begin
//found it
setlength(processes[i].threadlist,0);
for j:=i to length(processes)-2 do
processes[j]:=processes[j+1];
setlength(processes,length(processes)-1);
break;
end;
//find in the treeview and delete it
deleted:=false;
for i:=0 to tvprocesslist.Items.Count-1 do
if (tvprocesslist.Items[i].Level=0) and (copy(tvprocesslist.Items[i].Text,1,8)=IntToHex(pid,8)) then
begin
tvprocesslist.Items[i].DeleteChildren;
tvprocesslist.Items[i].Delete;
deleted:=true;
break;
end;
end else
begin
//add it
setlength(processes,length(processes)+1);
processes[length(processes)-1].processid:=self.pid;
processes[length(processes)-1].peprocess:=self.peprocess;
setlength(processes[length(processes)-1].threadlist,0); //init on 0
getmem(processname,16);
try
if GetProcessNameFromPEProcess(peprocess,processname,16)=-1 then
tvprocesslist.items.Add(nil,IntToHex(self.pid,8)+' ('+IntToHex(self.peprocess,8)+') - ??? (0)')
else
begin
tn:=tvprocesslist.items.Add(nil,IntToHex(self.pid,8)+' ('+IntToHex(self.peprocess,8)+') - '+processname+' (0)');
if (processid=0) and (processhandle=0) and (mainform.autoattachlist.Count>0) then
begin
//check if we should automatically open this one
if mainform.autoattachlist.IndexOf(processname)<>-1 then //it is in the list
autoAttachThisProcess:=true;
end;
end;
finally
freemem(processname);
end;
end;
end;
finally
frmProcessWatcher.processesCS.Leave;
end;
if autoattachthisProcess then
begin
//open it
openedByAutoAttach:=true;
frmProcessWatcher.tvProcesslist.Selected:=tn;
frmProcessWatcher.btnOpen.Click; //won't take too long so an be done in this thread
end else openedByAutoAttach:=false;
end;
procedure tprocesswatchthread.execute;
type tprocesseventstruct=record
Created:UINT64;
ProcessID:UINT64;
PEProcess:UINT64;
end;
type pprocesseventstruct=^tprocesseventstruct;
type tthreadeventstruct=record
Created:UINT64;
ProcessID:UINT64;
ThreadID:UINT64;
end;
type pthreadeventstruct=^tthreadeventstruct;
var processevents: pointer;
threadevents: pointer;
y: pprocesseventstruct;
z: pthreadeventstruct;
i: integer;
count: byte;
res: dword;
begin
getmem(processevents,50*sizeof(tprocesseventstruct)+1);
getmem(threadevents,50*sizeof(tthreadeventstruct)+1);
while not terminated do
begin
res:=WaitForProcessListData(processevents,threadevents,3000);
//processevent
count:=pbyte(processevents)^;
y:=pprocesseventstruct(ptrUint(processevents)+1);
for i:=0 to count-1 do
begin
self.created:=y^.Created>0;
self.pid:=y^.ProcessID;
self.peprocess:=y^.PEProcess;
synchronize(updatelist);
pluginhandler.handlenewprocessplugins(y^.ProcessID,y^.PEProcess, created);
inc(y); //next element
end;
//threadevent
count:=pbyte(threadevents)^;
z:=pthreadeventstruct(ptrUint(threadevents)+1);
for i:=0 to count-1 do
begin
self.created:=z^.Created>0;
self.pid:=z^.ProcessID;
self.tid:=z^.threadid;
if not terminated then
synchronize(updatelist2);
inc(z); //next element
end;
end;
freemem(processevents);
freemem(threadevents);
end;
//-------------------------------------------------
procedure TfrmProcessWatcher.FormCreate(Sender: TObject);
begin
if @startprocesswatch=nil then loaddbk32;
if StartProcessWatch then
begin
//start the thread that gets the data
processesCS:=TCriticalSection.create;
processwatchthread:=tprocesswatchthread.Create(true);
processwatchthread.start;
end else raise exception.Create(rsFailedStartingTheProcessWatcher);
end;
procedure TFrmProcessWatcher.PWOP(ProcessIDString:string);
var i:integer;
begin
val('$'+ProcessIDString,ProcessHandler.processid,i);
if i<>0 then raise exception.Create(Format(rsIsnTAValidProcessID, [processidstring]));
if Processhandle<>0 then
begin
CloseHandle(ProcessHandle);
ProcessHandler.ProcessHandle:=0;
end;
with mainform do
begin
if GetSystemType>=4 then
begin
cbSpeedhack.checked:=false;
cbSpeedhack.Enabled:=true;
cbspeedhack.Checked:=false;
cbspeedhack.Enabled:=true;
end;
end;
Open_Process;
end;
procedure TfrmProcessWatcher.tvProcesslistDblClick(Sender: TObject);
begin
if (tvprocesslist.Selected<>nil) and (tvprocesslist.Selected.Level=0) then
btnopen.Click;
end;
procedure TfrmProcessWatcher.btnOpenClick(Sender: TObject);
var ProcessIDString: String;
i: Integer;
processnode: ttreenode;
begin
if (tvprocesslist.Selected<>nil) then
begin
unpause;
DetachIfPossible;
ProcessIDString:='';
i:=1;
if tvprocesslist.Selected.Level=0 then
processnode:=tvprocesslist.Selected
else
processnode:=tvprocesslist.Selected.Parent;
while processnode.text[i]<>' ' do
begin
ProcessIDString:=ProcessIDString+processnode.text[i];
inc(i);
end;
PWOP(ProcessIDString);
MainForm.ProcessLabel.caption:=processnode.Text;
mainform.enableGui(false);
sleep(100); //wait a bit for the process to get fully created if it is a auto attach
symhandler.reinitialize;
mainform.reinterpretaddresses;
if self.visible then
self.hide;
end;
end;
procedure TfrmProcessWatcher.btnAttachClick(Sender: TObject);
var ProcessIDString: String;
i: Integer;
begin
if tvprocesslist.Selected<>nil then
begin
unpause;
DetachIfPossible;
ProcessIDString:='';
i:=1;
while tvprocesslist.Selected.Text[i]<>' ' do
begin
ProcessIDString:=ProcessIDString+tvprocesslist.Selected.Text[i];
inc(i);
end;
val('$'+ProcessIDString,ProcessHandler.processid,i);
if Processhandle<>0 then
begin
CloseHandle(ProcessHandle);
ProcessHandler.ProcessHandle:=0;
end;
Debuggerthread:=TDebuggerThread.MyCreate2(processid);
mainform.ProcessLabel.Caption:=tvprocesslist.Selected.Text[i];
ProcessSelected:=true;
mainform.debugproc:=true;
mainform.enableGui(false);
close;
end else showmessage(rsFirstSelectAProcess);
end;
procedure TfrmProcessWatcher.ShowThreadIDs1Click(Sender: TObject);
var i,j: integer;
ths: thandle;
tE: threadentry32;
begin
if tvProcesslist.Selected<>nil then
begin
i:=tvProcesslist.Selected.AbsoluteIndex;
with tfrmprocesswatcherextra.create(self) do
begin
data.lines.add('ProcessID='+inttohex(processes[i].processid,8));
data.lines.add('PEPROCESS='+inttohex(processes[i].peprocess,8));
for j:=0 to length(processes[i].threadlist)-1 do
data.Lines.Add('ThreadID:'+inttohex(processes[i].threadlist[j].threadid,8));
data.Lines.add('');
data.Lines.add('----Conventional ID''s----');
ths:=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
if ths<>0 then
begin
te.dwSize:=sizeof(te);
if Thread32First(ths,te) then
begin
repeat
if te.th32OwnerProcessID=processes[i].processid then
data.lines.add('ThreadID:'+IntToHex(te.th32ThreadID,8));
until not thread32Next(ths,te);
end;
end;
closehandle(ths);
showmodal;
end;
end;
end;
procedure TfrmProcessWatcher.FormDestroy(Sender: TObject);
begin
if processwatchthread<>nil then
begin
processwatchthread.Terminate;
processwatchthread.WaitFor;
processwatchthread.Free;
end;
end;
procedure TfrmProcessWatcher.Panel1Resize(Sender: TObject);
begin
btnOpen.Left:=(panel1.clientwidth div 2) - (btnopen.width div 2);
btnAttach.Left:=(panel1.clientwidth div 2) - (btnAttach.width div 2);
end;
initialization
{$i frmProcessWatcherUnit.lrs}
end.