unit frmFindstaticsUnit;

{$MODE Delphi}

interface

uses
  {$ifdef darwin}
  macport,
  {$endif}
  {$ifdef windows}
  jwawindows, windows,
  {$endif}
  LCLIntf, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls, ComCtrls,disassembler,CEFuncProc,NewKernelHandler,
  {tlhelp32,}symbolhandler, LResources, commonTypeDefs;

type TStaticlist=record
  s: ptrUint;
  isstruct: boolean;
  referencecount: dword;
  //referals: array of dword;
end;

type TUpdateType= (AddEntry,UpdateEntry);

type TStaticscanner = class (TThread)
  private
    updatetype: TUpdateType;
    updateline: integer; //not used for addentry

    memoryregion: array of tmemoryregion;
    procedure UpdateList;
    procedure done;
  public
    filterstart:ptrUint;
    filterstop:ptrUint;
    startaddress: ptrUint;
    stopaddress: ptrUint;
    onlyexecutable: boolean;
    progressbar: TProgressbar;
    procedure execute; override;
  end;

type

  { TfrmFindStatics }

  TfrmFindStatics = class(TForm)
    ProgressBar1: TProgressBar;
    Panel1: TPanel;
    Button1: TButton;
    Panel2: TPanel;
    CheckBox1: TCheckBox;
    Edit1: TEdit;
    Edit2: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Edit3: TEdit;
    Label6: TLabel;
    Edit4: TEdit;
    Panel3: TPanel;
    ListView1: TListView;
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure ListView1ColumnClick(Sender: TObject; Column: TListColumn);
    procedure FormShow(Sender: TObject);
    procedure ListView1Compare(Sender: TObject; Item1, Item2: TListItem;
      Data: Integer; var Compare: Integer);
    procedure ListView1DblClick(Sender: TObject);
  private
    { Private declarations }
    Staticscanner:TStaticScanner;
  public
    { Public declarations }
  end;

var
  frmFindStatics: TfrmFindStatics;
  staticlist: array of TStaticlist;

  LastSortedColumn :integer;
  Ascending : boolean;


implementation

uses MemoryBrowserFormUnit, ProcessHandlerUnit, Parsers;


resourcestring
  strScan='Scan';
  strStop='Stop';
  strStopping='Stopping...';
  rsFSStructOrArray = 'struct or array';
  rsFSUnreadablePointer = 'Unreadable pointer';

procedure TStaticScanner.UpdateList;
var x: tlistitem;
    i:integer;
    point: ptrUint;
    ar: ptrUint;
begin
  if updatetype=Addentry then
  begin
    i:=length(staticlist)-1;
    //add latest line to the list
    x:=frmfindstatics.ListView1.Items.Add;
    x.Caption:=IntToHex(staticlist[i].s,8);
    if staticlist[i].isstruct then
      x.SubItems.Add(rsFSStructOrArray)
    else
    begin
      point:=0;
      if ReadProcessMemory(processhandle,pointer(staticlist[i].s),@point,processhandler.pointersize,ar) then
      begin
        x.SubItems.add(IntToHex(point,8));
      end
      else x.SubItems.add(rsFSUnreadablePointer);
    end;

    x.SubItems.Add('1');
  end
  else
  begin
    //update entry
    x:=frmfindstatics.ListView1.Items[updateline];
    if staticlist[updateline].isstruct then
      x.SubItems[0]:=rsFSStructOrArray;


    x.SubItems[1]:=IntToStR(staticlist[updateline].referencecount);
  end;
end;

procedure TStaticScanner.done;
begin
  progressbar.position:=0;
  frmfindstatics.Button1.Caption:=strScan;
  frmfindstatics.staticscanner:=nil;
end;


procedure TStaticScanner.execute;
var oldpos,currentpos: ptrUint;
    i,j,k: integer;
    x,opcode:string;

    hexcount,hexstart: integer;
    isstruct: boolean;
    isstatic: boolean;
    _static: ptrUint;
    found: boolean;

    mbi: _MEMORY_BASIC_INFORMATION;

    oldshowsymbols: boolean;
    oldshowmodules: boolean;
    d: TDisassembler;
    s: string;
begin
  freeonterminate:=true;

  currentpos:=startaddress;
  i:=0;

  try
    d:=TDisassembler.Create;
    d.showmodules:=false;
    d.showsymbols:=false;


    while (not terminated) and (currentpos<=stopaddress) do
    begin
      virtualqueryEx(processhandle,pointer(currentpos),mbi,sizeof(mbi));
      if (mbi.State<>mem_commit) or ( (onlyexecutable and ((mbi.AllocationProtect and (PAGE_EXECUTE or PAGE_EXECUTE_READ or PAGE_EXECUTE_READWRITE	or PAGE_EXECUTE_WRITECOPY))=0))
      or (not onlyexecutable))
      then
      begin
        inc(currentpos,mbi.RegionSize);

        inc(i);
        i:=i mod 40;
        if i=0 then progressbar.position:=(currentpos-startaddress);
        continue;
      end;

      oldpos:=currentpos;

      opcode:=d.disassemble(currentpos,x);

      j:=pos('[',opcode);
      if j>0 then
      begin
        //it's got a [xxxxx] part
        //check if it contains a 8 digit hex value
        x:=copy(opcode,j+1,pos(']',opcode)-j-1);

        hexcount:=0;
        hexstart:=-1;
        isstruct:=false;
        isstatic:=false;

        for j:=1 to length(x) do
          if x[j] in ['0'..'9','a'..'f','A'..'F'] then
          begin
            if hexstart=-1 then hexstart:=j;
            inc(hexcount);
            if hexcount>=8 then
            begin
              //found one
              s:='';
              for k:=j to length(x) do //find the length of this hexstring
                if not (x[k] in ['0'..'9','a'..'f','A'..'F']) then
                  s:=copy(x,hexstart,k-hexstart+1);

              if s='' then s:=copy(x,hexstart,length(x)-hexstart+1);


              _static:=StrToQWordEx('$'+s);
              isstatic:=true;
            end;
          end else
          begin
            hexcount:=0;
            hexstart:=-1;
            isstruct:=true;
          end;
      end;


      if isstatic and (_static>=filterstart) and (_static<=filterstop) then
      begin
        found:=false;
        for j:=0 to length(staticlist)-1 do
          if staticlist[j].s=_static then
          begin
            inc(staticlist[j].referencecount);
            updatetype:=updateEntry;
            updateline:=j;
            synchronize(updatelist);

            found:=true;
            break;
          end;

        if not found then
        begin
          //add it to the list.
          k:=length(staticlist);
          setlength(staticlist,k+1);
          staticlist[k].s:=_static;
          staticlist[k].isstruct:=isstruct;
          staticlist[k].referencecount:=1;



  //        setlength(staticlist[k].referals,1);
   //       staticlist[k].referals[0]:=oldpos;

          updatetype:=addentry;
          synchronize(updatelist);
        end;
      end;

      inc(i);
      i:=i mod 40;
      if i=0 then progressbar.position:=(currentpos-startaddress);
    end;

  finally
    synchronize(done);

    d.free;
  end;

end;

procedure TfrmFindStatics.FormClose(Sender: TObject;
  var Action: TCloseAction);
var i: integer;
begin
  if staticscanner<>nil then
  begin
    staticscanner.Terminate;
    staticscanner:=nil;
  end else
  begin
  //  for i:=0 to length(staticlist)-1 do
 //     setlength(staticlist[i].referals,0);

    setlength(staticlist,0);
  end;
end;

procedure TfrmFindStatics.FormCreate(Sender: TObject);
var ths: thandle;
    me32: MODULEENTRY32;
    x: pchar;
    first:boolean;
    diff: integer;
begin
  if processhandler.is64bit then
  begin
    diff:=edit1.width;
    edit1.MaxLength:=16;
    edit2.MaxLength:=16;
    edit3.MaxLength:=16;
    edit4.MaxLength:=16;


    edit1.width:=edit1.width*2;
    edit2.width:=edit1.width;
    Edit3.width:=edit1.width;
    Edit4.width:=edit1.width;

    Edit3.text:='0000000000000000';
    Edit4.text:='7fffffffffffffff';


    width:=width+diff;
    panel1.width:=panel1.width+diff;
    button1.left:=(panel1.clientwidth div 2) - (button1.width div 2)
  end;

  ths:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,processid);
  if ths<>0 then
  begin
    try
      first:=true;
      zeromemory(@me32,sizeof(me32));
      me32.dwSize:=sizeof(me32);
      if module32first(ths,me32) then
      begin
        edit1.text:=inttohex(ptrUint(me32.modBaseAddr),8);
        edit2.text:=inttohex(ptrUint(me32.modBaseAddr)+me32.modBaseSize,8);
      end;
    finally
      closehandle(ths);
    end;
  end;

  LastSortedColumn := -1;
  Ascending := True;
  button1.Caption:=strScan;



end;

procedure TfrmFindStatics.Button1Click(Sender: TObject);
begin
  if button1.Caption=strstopping then exit;

  if button1.Caption=strstop then
  begin
    button1.caption:=strStopping;
    staticscanner.Terminate;
    staticscanner:=nil;
  end
  else
  begin
    listview1.Clear;
    setlength(staticlist,0);
    
    staticscanner:=TStaticscanner.Create(true);
    staticscanner.startaddress:=StrToQWordEx('$'+edit1.Text);
    staticscanner.stopaddress:=StrToQWordEx('$'+edit2.Text);
    staticscanner.filterstart:=StrToQWordEx('$'+edit3.Text);
    staticscanner.filterstop:=StrToQWordEx('$'+edit4.Text);

    staticscanner.progressbar:=progressbar1;
    button1.Caption:=strStop;

    progressbar1.Max:=staticscanner.stopaddress-staticscanner.startaddress;

    staticscanner.onlyexecutable:=checkbox1.checked;

    staticscanner.start;
  end;

end;

procedure TfrmFindStatics.ListView1ColumnClick(Sender: TObject;
  Column: TListColumn);
var i: integer;
begin

  if staticscanner<>nil then exit;
  
  if Column.Index = LastSortedColumn then
    Ascending := not Ascending
  else
    LastSortedColumn := Column.Index;

  TListView(Sender).SortColumn:=column.index;
end;

procedure TfrmFindStatics.FormShow(Sender: TObject);
var diff: integer;
begin
  listview1.clear;

  if processhandler.is64Bit then
  begin
    edit1.Constraints.MinWidth:=canvas.TextWidth('DDDDDDDDDDDDDDDD')+4;
  end;

end;

procedure TfrmFindStatics.ListView1Compare(Sender: TObject; Item1,
  Item2: TListItem; Data: Integer; var Compare: Integer);
begin
  case data of
    0: Compare := AnsiCompareText(Item1.Caption, Item2.Caption);
    1: Compare := AnsiCompareText(Item1.SubItems[0],Item2.SubItems[0]);
    2: Compare := StrToQWordEx(Item1.SubItems[1])-StrToQWordEx(Item2.SubItems[1]);
  end;

  if not Ascending then
    Compare := -Compare;
end;

procedure TfrmFindStatics.ListView1DblClick(Sender: TObject);
begin
  if listview1.Selected<>nil then
    memorybrowser.memoryaddress:=StrToQWordEx('$'+listview1.selected.caption);
end;

initialization
  {$i frmFindstaticsUnit.lrs}

end.

