unit d3dhookUnit; { This unit will inject the d3d hook dll into the target process and hook the apropriate functions. A shared object will be used for communicating states and data } {$mode delphi} interface uses windows, Classes, SysUtils, sharedMemory, forms, graphics, cefuncproc, newkernelhandler, controls, Clipbrd, strutils, LuaHandler, RemoteMemoryManager, math, syncobjs; type TCEMessage=packed record uMsg: DWORD; wParam: UINT64; lParam: UINT64; character: DWORD; end; TTextureEntry=packed record AddressOfTexture: UINT64; AddressOfFontmap: UINT64; size: integer; hasBeenUpdated: integer; end; TTextureEntryArray=Array [0..1000] of TTextureEntry; PTextureEntryArray=^TTextureEntryArray; type TRenderCommandEnum=(rcEndOfCommandlist=0, //Stop going through the list rcIgnored=1, //Ignore (Being updated) rcDrawSprite=2, //Render the sprite at the given position rcDrawFont=3); //Render some text at the given coordinates. The string is located at "addressoftext" TSpriteCommand=packed record width: integer; height: integer; textureid: integer; end; TFontCommand=packed record addressoftext: UINT64; fontid: integer; end; TRenderCommand=packed record command: integer; x: single; y: single; alphablend: single; centerX: single; centerY: single; rotation: single; case TRenderCommandEnum of rcDrawSprite: (Sprite: TSpriteCommand); rcDrawFont: (Font: TFontCommand); end; PRenderCommand=^TRenderCommand; TRenderCommandarray=array [0..100] of TRenderCommand; PRenderCommandArray=^TRenderCommandArray; TD3DHookShared=packed record cheatenginedir: array [0..255] of char; snapshotdir: array [0..255] of char; dxgi_present: UINT64; dxgi_resizebuffers: UINT64; d3d9_present: UINT64; d3d9_reset: UINT64; d3d9_drawprimitive: UINT64; d3d9_drawindexedprimitive: UINT64; d3d9_drawprimitiveup: UINT64; d3d9_drawindexedprimitiveup: UINT64; d3d9_drawrectpatch: UINT64; d3d9_drawtripatch: UINT64; d3d10_drawindexed: UINT64; d3d10_draw: UINT64; d3d10_drawindexedinstanced: UINT64; d3d10_drawinstanced: UINT64; d3d10_drawauto: UINT64; d3d11_drawindexed: UINT64; d3d11_draw: UINT64; d3d11_drawindexedinstanced: UINT64; d3d11_drawinstanced: UINT64; d3d11_drawauto: UINT64; dxgi_newpresent: UINT64; dxgi_newresizebuffers: UINT64; d3d9_newpresent: UINT64; d3d9_newreset: UINT64; d3d9_newdrawprimitive: UINT64; d3d9_newdrawindexedprimitive: UINT64; d3d9_newdrawprimitiveup: UINT64; d3d9_newdrawindexedprimitiveup: UINT64; d3d9_newdrawrectpatch: UINT64; d3d9_newdrawtripatch: UINT64; d3d10_newdrawindexed: UINT64; d3d10_newdraw: UINT64; d3d10_newdrawindexedinstanced: UINT64; d3d10_newdrawinstanced: UINT64; d3d10_newdrawauto: UINT64; d3d11_newdrawindexed: UINT64; d3d11_newdraw: UINT64; d3d11_newdrawindexedinstanced: UINT64; d3d11_newdrawinstanced: UINT64; d3d11_newdrawauto: UINT64; dxgi_originalpresent: UINT64; dxgi_originalresizebuffers: UINT64; d3d9_originalpresent: UINT64; d3d9_originalreset: UINT64; d3d9_originaldrawprimitive: UINT64; d3d9_originaldrawindexedprimitive: UINT64; d3d9_originaldrawprimitiveup: UINT64; d3d9_originaldrawindexedprimitiveup: UINT64; d3d9_originaldrawrectpatch: UINT64; d3d9_originaldrawtripatch: UINT64; d3d10_originaldrawindexed: UINT64; d3d10_originaldraw: UINT64; d3d10_originaldrawindexedinstanced: UINT64; d3d10_originaldrawinstanced: UINT64; d3d10_originaldrawauto: UINT64; d3d11_originaldrawindexed: UINT64; d3d11_originaldraw: UINT64; d3d11_originaldrawindexedinstanced: UINT64; d3d11_originaldrawinstanced: UINT64; d3d11_originaldrawauto: UINT64; wireframe: integer; disabledzbuffer: integer; hookwnd: integer; clipmouseinwindow: integer; clickedoverlay: integer; clickedx: integer; clickedy: integer; console: packed record hasconsole: integer; consolevisible: integer; consolekey: dword; overlayid: integer; cursorid: integer; lastmessage: TCEMessage; end; lastHwnd: DWORD; texturelistHasUpdate: integer; //If 1 this means that CE might be waiting for the HasHandledTextureUpdate event (if it is a high priority update) textureCount: integer; texturelist: UINT64 ; //offset into texturelist based on the start of the shared object (once setup, this does not change) TextureLock: UINT64 ; //target process handle commandListLock: UINT64; //lockinforder: FIRST commandlist, THEN texturelist. useCommandListLock: integer; hasOnKey: integer; snapshotKey: DWORD; smallSnapshotKey: DWORD; snapshotDone: UINT64; //Event to signal that a snapshot is done snapshotImageFormat: integer; snapshotcount: integer; progressiveSnapshot: integer; //set to 1 if you do not wish the snapshot to clear the screen before each draw. (This makes it easier to see how a scene was build up) alsoClearDepthBuffer: integer; //set to 1 if you also want the depth buffer to be cleared before each draw savePNGSeperateAsWell: integer; canDoSnapshot: integer; initialized: DWORD; //followed by the rendercommands end; PD3DHookShared=^TD3DHookShared; type TD3DHook_RenderObject=class; TD3DClickEvent=procedure(renderobject: TObject; x,y: integer) of object; TD3DKeyDownEvent=function(VirtualKey: dword; char: pchar): boolean of object; TD3DHook=class; TD3DHook_Texture=class; TD3DHook_Sprite=class; TD3DHook_Fontmap=class; TD3DHook_TextContainer=class; TD3DMessageHandler=class(tthread) private owner: TD3DHook; rendercommandid, x,y: integer; //clicked overlay lastmessage: TCEMessage; console: record log: Tstringlist; cursorpos: integer; backgroundtexture: TD3DHook_Texture; background: TD3DHook_Sprite; cursortexture: TD3DHook_Texture; cursor: TD3DHook_sprite; seperator: TD3dhook_sprite; //horizontal line splitting commandline from output fontmap: TD3DHook_Fontmap; output: TD3DHook_textcontainer; //history/output commandline: TD3DHook_TextContainer; //commandline end; procedure setConsoleCursorPos; procedure doclick; procedure dokeyboard; procedure handleSnapshot; public procedure execute; override; end; TD3DHook_Texture=class(TObject) private resource: ptruint; //address where the current resource is located protected id: integer; owner: TD3DHook; fheight: integer; fwidth: integer; public function getID: integer; { procedure LoadTextureFromFile(filename: string); constructor Create(filename: string); overload; } procedure LoadTextureByPicture(picture: TPicture); constructor Create(owner: TD3DHook; picture: TPicture); constructor Create(owner: TD3DHook); destructor destroy; override; published property Height: integer read fheight; property Width: integer read fwidth; end; TD3DHook_FontMap=class(TD3DHook_Texture) private fontmapdata: ptruint; localfontmapcopy: TMemorystream; //used by calculateFontwidth public function calculateFontWidth(s:string): integer; procedure ChangeFont(font: TFont); constructor Create(owner: TD3DHook; font: TFont); destructor destroy; override; end; TD3DHook_RenderObject=class(TObject) private fx: single; fy: single; fcenterX: single; fcenterY: single; fRotation: single; //stored as deg here, internally uses rad falphablend: single; fvisible: boolean; procedure setRotation(v: single); procedure setCenterX(v: single); procedure setCenterY(v: single); procedure setX(v: single); procedure setY(v: single); procedure setAlphaBlend(v: single); procedure setVisible(state: boolean); procedure setZOrder(newpos: integer); protected owner: TD3DHook; updatecount: integer; public function getIndex: integer; procedure beginUpdate; procedure endUpdate; procedure UpdateRenderCommand; virtual; abstract; destructor destroy; override; constructor create(owner: TD3DHook); published property ZOrder: integer read getIndex write setZOrder; property Alphablend: single read falphablend write setAlphablend; property X: single read fx write setX; property Y: single read fy write setY; property CenterX: single read fcenterx write setCenterX; property CenterY: single read fcentery write setCenterY; property Rotation: single read frotation write setRotation; property Visible: boolean read fVisible write setVisible; end; TD3Dhook_TextContainer=class(TD3DHook_RenderObject) //class for rendering text private maxTextLength: integer; AddressOfText: QWORD; fFontMap: TD3DHook_FontMap; fText: string; addressChangedThisUpdate: boolean; oldAddressOfText: QWORD; private procedure setFontMap(fm: TD3DHook_FontMap); procedure setText(s: string); public procedure UpdateRenderCommand; override; destructor destroy; override; constructor create(owner: TD3DHook; fontmap: TD3DHook_FontMap; x,y: single; text: string; minsize: integer=0); published property FontMap: TD3DHook_FontMap read fFontMap write setFontMap; property Text: String read fText write setText; end; TD3DHook_Sprite=class(TD3DHook_RenderObject) //clas for rendering textures private ftexture: TD3DHook_Texture; fwidth: integer; fheight: integer; procedure setTexture(s: TD3DHook_Texture); procedure setWidth(v: integer); procedure setHeight(v: integer); public procedure UpdateRenderCommand; override; constructor create(owner: TD3DHook; texture: TD3DHook_Texture); published property Width: integer read fWidth write setWidth; property Height: integer read fHeight write setHeight; property Texture: TD3DHook_Texture read ftexture write setTexture; end; TD3DHook=class(TObject) private hooked: boolean; fonKeyDown: TD3DKeyDownEvent; fonclick: TD3DClickEvent; sharename: string; shared: PD3DHookShared; //local address of the D3DHookShared structure tea: PTextureEntryArray; fmhandle: THandle; textures: TList; commandlist: TList; //collection of pointer to objects used for lookup and id management. 1-on-1 relation to the renderCommands list renderCommandList: PRenderCommandArray; isupdating: integer; //update counter for the texture list isupdatingCL: integer; //update counter for the command list maxsize: integer; fprocessid: dword; hasclickevent: THandle; hashandledclickevent: THandle; haskeyboardevent: THandle; //todo: combine into one "HasMessage" event, but for now, to make sure I don't break anything, this method... hashandledkeyboardevent: THandle; SnapshotDone: THandle; texturelock: THandle; CommandListLock: THandle; messagehandler: TD3DMessageHandler; memman: TRemoteMemoryManager; commandlistCS: TCriticalSection; procedure setOnKeyDown(s: TD3DKeyDownEvent); public procedure beginTextureUpdate; procedure endTextureUpdate; procedure beginCommandListUpdate; procedure endCommandListUpdate; function createTexture(f: string): TD3DHook_Texture; overload; function createTexture(p: TPicture): TD3DHook_Texture; overload; function createSprite(texture: TD3DHook_Texture): TD3DHook_Sprite; function createFontMap(f: TFont): TD3DHook_FontMap; function createTextContainer(fontmap: TD3DHook_FontMap; x,y: single; text: string): TD3Dhook_TextContainer; function getDisabledZBuffer: boolean; procedure setDisabledZBuffer(state: boolean); function getWireframeMode: boolean; procedure setWireframeMode(state: boolean); function getMouseClip: boolean; procedure setMouseClip(state: boolean); function getWidth: integer; function getHeight: integer; procedure setCommandListLockFeature(state: boolean); procedure enableConsole(virtualkey: DWORD); procedure setSnapshotOptions(path: string; full, small: dword; Progressive: boolean; cleardepthbuffer: boolean; savepng: boolean; pictureFormat: integer); constructor create(size: integer; hookhwnd: boolean=true); destructor destroy; override; published property Width: integer read getWidth; property Height: integer read getHeight; property DisabledZBuffer: boolean read GetDisabledZBuffer write setDisabledZBuffer; property WireframeMode: boolean read GetWireframeMode write setWireframeMode; property MouseClip: boolean read GetMouseclip write setMouseClip; property Processid: dword read fprocessid; property OnKeyDown: TD3DKeyDownEvent read fonKeyDown write setOnKeyDown; property OnClick: TD3DClickEvent read fonclick write fonclick; end; var D3DHook: TD3DHook; function safed3dhook(size: integer=16*1024*1024; hookwindow: boolean=true): TD3DHook; procedure FixAlpha(aPNG: TPortableNetworkGraphic); implementation uses frmautoinjectunit, autoassembler, MainUnit, frmSaveSnapshotsUnit, frmsnapshothandlerUnit, symbolhandler, ProcessHandlerUnit, Globals; resourcestring rsTheD3dhookObjectHasNotBeenCreatedYet = 'The d3dhook object has not been created yet'; rsD3DHookFailureToOpenTheSharedMemoryObject = 'D3DHook: Failure to open the shared memory object'; rsD3DHookFailureToMapTheSharedMemoryObject = 'D3DHook: Failure to map the shared memory object'; procedure TD3DMessageHandler.handleSnapshot; begin if frmSaveSnapshots=nil then frmSaveSnapshots:=TfrmSaveSnapshots.create(application); frmSaveSnapshots.btnCombinedSelect.visible:=owner.shared.progressiveSnapshot=0; frmSaveSnapshots.initialize(owner.shared.snapshotdir, owner.shared.snapshotcount); frmSaveSnapshots.showmodal; owner.shared.canDoSnapshot:=1; if frmSaveSnapshots.saved.Count>0 then begin if frmsnapshothandler=nil then frmsnapshothandler:=TfrmSnapshotHandler.create(application); frmsnapshothandler.show; frmsnapshothandler.loadsnapshots(frmSaveSnapshots.saved); end; //exit the function and wait for a new event end; procedure TD3DMessageHandler.setConsoleCursorPos; var s: string; begin if console.cursor<>nil then begin console.cursor.y:=console.commandline.y; s:=copy(console.commandline.Text,1, console.cursorpos); console.cursor.x:=console.fontmap.calculateFontWidth(s); end; end; procedure TD3DMessageHandler.dokeyboard; var virtualkey: dword; scancode: dword; c,c2: string; s: pchar; old: tstrings; p: tpicture; f: tfont; maxlines: integer; currentlines: integer; i: integer; l: string; begin //check the size of the window. If it's changed, reinitialize the consoleoverlay owner.shared.console.lastmessage.uMsg:=0; //don't handle the keypress in the target process if assigned(owner.onkeydown) then begin if owner.onkeydown(lastmessage.wParam, pchar(@lastmessage.character)) then owner.shared.console.lastmessage.uMsg:=1; //if it returns true, let it go through end; if (owner.shared.console.hasconsole=0) or (owner.shared.console.consolevisible=0) then exit; //just create or show the console if console.background=nil then begin owner.beginTextureUpdate; try p:=tpicture.Create; p.png.canvas.brush.color:=clBlack; p.png.width:=1; p.png.height:=1; console.backgroundtexture:=TD3DHook_Texture.Create(owner, p); p.free; console.background:=TD3DHook_Sprite.create(owner, console.backgroundtexture); console.background.width:=-1; //full width console.background.height:=owner.getHeight div 2; console.background.x:=0; console.background.y:=owner.getHeight div 2; console.background.alphaBlend:=0.80; p:=tpicture.Create; p.png.canvas.brush.color:=clWhite; p.png.width:=1; p.png.height:=1; p.png.Canvas.Pixels[0,0]:=clWhite; console.cursortexture:=TD3DHook_Texture.Create(owner, p); p.free; console.cursor:=TD3DHook_sprite.create(owner, console.cursortexture); f:=tfont.create; f.Assign(mainform.Font); f.color:=clWhite; f.Size:=f.size*2; console.fontmap:=TD3DHook_FontMap.Create(owner, f); console.commandline:=TD3Dhook_TextContainer.create(owner, console.fontmap, 0, owner.getHeight-console.fontmap.height,'',256); console.seperator:=TD3DHook_sprite.create(owner, console.cursortexture); //same color console.seperator.x:=0; console.seperator.y:=owner.getHeight-console.fontmap.height; console.seperator.height:=1; console.seperator.width:=-1; //full width console.output:=TD3Dhook_TextContainer.create(owner, console.fontmap, 0, owner.getHeight div 2,'',8192); console.cursor.width:=3; console.cursor.height:=console.fontmap.height; // finally owner.endTextureUpdate; end; end; setConsoleCursorPos; if lastmessage.uMsg=$ffffffff then begin console.background.visible:=true; console.seperator.visible:=true; console.cursor.visible:=true; console.output.visible:=true; console.commandline.visible:=true; exit; //nothing else to do end; if lastmessage.uMsg=$fffffffe then begin console.background.visible:=false; console.seperator.visible:=false; console.cursor.visible:=false; console.output.visible:=false; console.commandline.visible:=false; exit; end; //handle the key virtualkey:=lastmessage.wParam; scancode:=(lastmessage.lparam shr 16) and $FF; owner.beginTextureUpdate; //handle keys like delete, backspace, etc... case virtualkey of VK_RETURN: begin //execute the command console.log.add(console.commandline.Text); LuaCS.enter; old:=lua_oldprintoutput; lua_setPrintOutput(console.log); lua_dostring(LuaVM, pchar(console.commandline.Text)); lua_setPrintOutput(old); luacs.Leave; //calculate the max number of lines maxlines:=trunc((console.seperator.y-console.background.y) / console.output.FontMap.height); currentlines:=0; l:=''; for i:=max(0, console.log.Count-maxlines) to console.log.Count-1 do begin l:=l+console.log[i]+#13#10; inc(currentlines); if (currentlines>=maxlines) then break; end; console.output.y:=console.seperator.y-(currentlines*console.output.FontMap.height); console.output.Text:=l; console.commandline.Text:=''; console.cursorpos:=0; end; VK_LEFT: begin if console.cursorpos>0 then dec(console.cursorpos); end; VK_RIGHT: begin if console.cursorpos0 then begin c:=copy(console.commandline.text, 1, console.cursorpos-1); c2:=copy(console.commandline.text, console.cursorpos+1, length(console.commandline.Text)); console.commandline.text:=c+c2; dec(console.cursorpos); end; end; VK_DELETE: begin //delete the character after the current cursor c:=copy(console.commandline.text, 1, console.cursorpos); c2:=copy(console.commandline.text, console.cursorpos+2, length(console.commandline.text)); console.commandline.text:=c+c2; end else begin //not a control key, check if it's a character if lastmessage.character<>0 then begin //it's a character s:=@lastmessage.character; c:=copy(console.commandline.text, 1, console.cursorpos); c2:=copy(console.commandline.text, console.cursorpos+1, length(console.commandline.text)); console.commandline.text:=c+s+c2; inc(console.cursorpos); end; end; end; setConsoleCursorPos; owner.endTextureUpdate; end; procedure TD3DMessageHandler.doclick; begin if assigned(owner.onclick) then owner.onclick(TD3DHook_RenderObject(owner.commandlist[rendercommandid]), x,y); end; procedure TD3DMessageHandler.execute; var eventlist: array of THandle; r: dword; cursor: boolean; cursorstart: dword; begin cursorstart:=gettickcount; console.log:=tstringlist.create; console.cursorpos:=0; setlength(eventlist,3); eventlist[0]:=owner.hasclickevent; eventlist[1]:=owner.haskeyboardevent; eventlist[2]:=owner.SnapshotDone; while (not terminated) do begin r:=WaitForMultipleObjects(3, @eventlist[0], false, 100); case r of WAIT_OBJECT_0: begin //click event: quickly save the variables and tell the game to continue x:=owner.shared.clickedx; y:=owner.shared.clickedy; rendercommandid:=owner.shared.clickedoverlay; SetEvent(owner.hashandledclickevent); Synchronize(doclick); end; WAIT_OBJECT_0+1: begin //keyboard event lastmessage:=owner.shared.console.lastmessage; Synchronize(dokeyboard); SetEvent(owner.hashandledkeyboardevent); cursorstart:=GetTickCount; end; WAIT_OBJECT_0+2: begin //snapshot event synchronize(handlesnapshot); end; end; if (owner.shared.console.consolevisible=1) and (console.cursor<>nil) then //toggle the cursor visible or invisible based on the current time and if a key was pressed (keep the cursor visible rigth after pressing a key, so reset the timerstart) console.cursor.visible:=(((GetTickCount-cursorstart) mod 1000)<500); end; end; //----------------------------D3DHook_RenderObject------------------------------ procedure TD3DHook_RenderObject.setRotation(v: single); begin if fRotation=v then exit; beginUpdate; fRotation:=v; endUpdate; end; procedure TD3DHook_RenderObject.setCenterX(v: single); begin if fcenterX=v then exit; beginUpdate; fcenterX:=v; endUpdate; end; procedure TD3DHook_RenderObject.setCenterY(v: single); begin if fcenterY=v then exit; beginUpdate; fcenterY:=v; endUpdate; end; procedure TD3DHook_RenderObject.setAlphaBlend(v: single); begin if falphablend=v then exit; BeginUpdate; falphablend:=v; endUpdate; end; procedure TD3DHook_RenderObject.setX(v: single); begin if fx=v then exit; beginUpdate; fx:=v; endUpdate; end; procedure TD3DHook_RenderObject.setY(v: single); begin if fy=v then exit; beginUpdate; fy:=v; endUpdate; end; procedure TD3DHook_RenderObject.setVisible(state: boolean); begin if fvisible=state then exit; //no change, no update beginUpdate; fvisible:=state; endUpdate; end; procedure TD3DHook_RenderObject.setZOrder(newpos: integer); var mypos: integer; replaced: TD3DHook_RenderObject; begin //change the order of the commandlist mypos:=zorder; if (newpos<>mypos) and (newposnil then replaced.UpdateRenderCommand; owner.endCommandListUpdate; end; end; function TD3DHook_RenderObject.getIndex: integer; begin result:=owner.commandlist.IndexOf(self); end; procedure TD3DHook_RenderObject.beginUpdate; begin inc(updatecount); end; procedure TD3DHook_RenderObject.endUpdate; begin if updatecount>0 then dec(updatecount); if updatecount=0 then UpdateRenderCommand; end; destructor TD3DHook_RenderObject.destroy; var index: integer; begin index:=getindex; owner.renderCommandList^[Index].command:=integer(rcIgnored); owner.commandlist[index]:=nil; inherited destroy; end; constructor TD3DHook_RenderObject.create(owner: TD3DHook); begin self.owner:=owner; falphablend:=1; fvisible:=true; owner.commandlist.Add(self); end; //----------------------------D3DHook_TextContainer----------------------------- procedure TD3Dhook_TextContainer.setText(s: string); var x: ptruint; nul: byte; begin BeginUpdate; fText:=s; if length(s)+1>maxTextLength then //+1 for 0 terminator begin if AddressOfText<>0 then //free the old block owner.memman.dealloc(AddressOfText); maxTextLength:=length(s)+16; AddressOfText:=owner.memman.alloc(maxTextLength); end; if s='' then begin nul:=0; WriteProcessMemory(processhandle, pointer(AddressOfText), @nul, 1, x); end else WriteProcessMemory(processhandle, pointer(AddressOfText), @s[1], length(s)+1, x); //just write endUpdate; end; procedure TD3Dhook_TextContainer.setFontMap(fm: TD3DHook_FontMap); begin BeginUpdate; fFontMap:=fm; endUpdate; end; procedure TD3Dhook_TextContainer.UpdateRenderCommand; var index: integer; begin index:=getindex; owner.beginCommandListUpdate; if (index<>-1) and (fFontmap<>nil) and (fVisible) then begin if owner.renderCommandList^[index].command<>integer(rcDrawFont) then //something completely new owner.renderCommandList^[index].command:=integer(rcIgnored); owner.renderCommandList^[index].x:=x; owner.renderCommandList^[index].y:=y; owner.renderCommandList^[index].rotation:=degtorad(Rotation); owner.renderCommandList^[index].centerx:=centerx; owner.renderCommandList^[index].centery:=centery; owner.renderCommandList^[index].alphablend:=alphablend; owner.renderCommandList^[index].Font.fontid:=fFontmap.getID; owner.renderCommandList^[index].Font.addressoftext:=AddressOfText; owner.renderCommandList^[index].command:=integer(rcDrawFont); end else owner.renderCommandList^[index].command:=integer(rcIgnored); owner.endCommandListUpdate; end; destructor TD3Dhook_TextContainer.destroy; begin if AddressOfText<>0 then owner.memman.dealloc(addressoftext); inherited destroy; end; constructor TD3Dhook_TextContainer.create(owner: TD3DHook; fontmap: TD3DHook_FontMap; x,y: single; text: string; minsize: integer=0); begin inherited create(owner); beginupdate; if minsize<>0 then begin maxTextLength:=minsize; AddressOfText:=owner.memman.alloc(maxTextLength); end; self.x:=x; self.y:=y; self.FontMap:=fontmap; self.text:=text; endUpdate; end; //-------------------------------D3DHook_Sprite--------------------------------- procedure TD3DHook_Sprite.setWidth(v: integer); begin beginUpdate; fwidth:=v; endUpdate; end; procedure TD3DHook_Sprite.setHeight(v: integer); begin beginUpdate; fheight:=v; endUpdate; end; procedure TD3DHook_Sprite.setTexture(s: TD3DHook_Texture); begin BeginUpdate; ftexture:=s; width:=s.width; height:=s.height; EndUpdate; end; procedure TD3DHook_Sprite.UpdateRenderCommand; var index: integer; begin index:=getindex; owner.beginCommandListUpdate; if (index<>-1) and (texture<>nil) and (fVisible) then begin if owner.renderCommandList^[index].command<>integer(rcDrawSprite) then //something completely new owner.renderCommandList^[index].command:=integer(rcIgnored); owner.renderCommandList^[index].x:=x; owner.renderCommandList^[index].y:=y; owner.renderCommandList^[index].rotation:=degtorad(rotation); owner.renderCommandList^[index].centerX:=centerx; owner.renderCommandList^[index].centerY:=centery; owner.renderCommandList^[index].alphablend:=alphablend; owner.renderCommandList^[index].Sprite.width:=width; owner.renderCommandList^[index].Sprite.height:=height; owner.renderCommandList^[index].Sprite.textureid:=texture.getID; owner.renderCommandList^[index].command:=integer(rcDrawSprite); end else owner.renderCommandList^[index].command:=integer(rcIgnored); owner.endCommandListUpdate; end; constructor TD3DHook_Sprite.create(owner: TD3DHook; texture: TD3DHook_Texture); begin inherited create(owner); beginupdate; setTexture(texture); endUpdate; end; //-------------------------------d3dhook_font----------------------------------- function TD3DHook_FontMap.calculateFontWidth(s:string): integer; var cwa: PWordarray; i: integer; c: byte; begin result:=0; cwa:=localfontmapcopy.memory; for i:=1 to length(s) do begin c:=ord(s[i]); if c in [32..127] then inc(result,cwa[c-32+1]); end; end; procedure FixAlpha(aPNG: TPortableNetworkGraphic); type TColor32 = packed record B, G, R, A: Byte; end; PColor32=^TColor32; TColor32Array = array[0..0] of TColor32; PColor32Array = ^TColor32Array; var x, y: Integer; Line: PColor32Array; R,G,B: Integer; begin R := aPng.TransparentColor and $ff; G := (aPng.TransparentColor shr 8) and $ff; B := (aPng.TransparentColor shr 16) and $ff; for y := 0 to aPNG.Height - 1 do begin Line := aPNG.ScanLine[y]; for x := 0 to aPNG.Width - 1 do begin if (Line^[x].R=R) and (Line^[x].G=G) and (Line^[x].B=B) then Line^[x].A := 0 //100% see through else Line^[x].A := 255; end; end; end; procedure TD3DHook_FontMap.ChangeFont(font: TFont); var s: string; i: integer; charpos: integer; p: TPicture; charwidth: word; //65535 if the max size of one character newblock: pointer; x: ptruint; begin p:=TPicture.Create; p.PNG.PixelFormat:=pf32bit; p.png.canvas.font:=font; p.png.canvas.brush.color:=InvertColor(font.color); p.png.Transparent:=true; p.png.TransparentColor:=p.png.canvas.brush.color; p.png.width:=100; p.png.height:=100; //initial setup //create a fontmap picture with this font and set that as the texture //also fill in the fontmap layout s:=''; for i:=32 to 127 do s:=s+chr(i); fheight:=p.png.canvas.GetTextHeight(s); //get the max height //calculate the width based on a per character rendering. (perhaps the whole string might have been using a half pixel at some spots...) fwidth:=0; for i:=32 to 127 do fwidth:=fwidth+p.png.canvas.GetTextWidth(chr(i)); //setup the image p.png.Width:=fwidth; p.png.height:=fheight; if localfontmapcopy<>nil then freeandnil(localfontmapcopy); localfontmapcopy:=tmemorystream.create; //buffer to obtain the fontmap data localfontmapcopy.WriteBuffer(fheight, sizeof(word)); //2 bytes for the height of all characters //now fill it in charpos:=0; for i:=32 to 127 do begin p.PNG.canvas.TextOut(charpos, 0, chr(i)); charwidth:=p.png.Canvas.GetTextWidth(chr(i)); localfontmapcopy.WriteBuffer(charwidth,2); charpos:=charpos+charwidth; end; //laz 1.6 makes 32bit png's 100% transparant //so manually make it visible FixAlpha(p.PNG); newblock:=pointer(owner.memman.alloc(localfontmapcopy.size)); if newblock<>nil then begin if WriteProcessMemory(processhandle, newblock, localfontmapcopy.memory, localfontmapcopy.size, x) then begin owner.beginTextureUpdate; //set the font state owner.tea[id].AddressOfFontmap:=qword(newblock); owner.tea[id].hasBeenUpdated:=1; //free old block if possible if (resource<>0) then owner.memman.dealloc(fontmapdata); LoadTextureByPicture(p); owner.endTextureUpdate; fontmapdata:=qword(newblock); end; end; // p.SaveToFile('c:\bla.png'); end; destructor TD3DHook_FontMap.destroy; begin if fontmapdata<>0 then owner.memman.dealloc(fontmapdata); inherited destroy; end; constructor TD3DHook_FontMap.Create(owner: TD3DHook; font: TFont); begin inherited create(owner); ChangeFont(font); end; //-------------------------------d3dhook_texture-------------------------------- procedure TD3DHook_Texture.LoadTextureByPicture(picture: TPicture); var m: tmemorystream; newblock: pointer; x: ptruint; msp: pointer; s: integer; begin m:=TMemoryStream.create; picture.PNG.SaveToStream(m); owner.beginTextureUpdate; newblock:=pointer(owner.memman.alloc(m.size)); if newblock<>nil then begin msp:=m.Memory; s:=m.size; if WriteProcessMemory(processhandle, newblock, m.memory, m.size, x) then begin owner.tea[id].AddressOfTexture:=qword(newblock); owner.tea[id].Size:=m.size; owner.tea[id].hasBeenUpdated:=1; //free old block if possible if (resource<>0) then owner.memman.dealloc(resource); fheight:=picture.Height; fwidth:=picture.Width; end; resource:=qword(newblock); end; m.free; owner.endTextureUpdate; end; function TD3DHook_Texture.getID: integer; begin result:=id; end; destructor TD3DHook_Texture.destroy; begin owner.beginTextureUpdate; owner.tea[id].AddressOfTexture:=0; owner.tea[id].hasBeenUpdated:=1; //marks for deletion if resource<>0 then owner.memman.dealloc(resource); owner.textures[id]:=nil; owner.endTextureUpdate; inherited destroy; end; constructor TD3DHook_Texture.Create(owner: TD3DHook); var i: integer; begin self.owner:=owner; id:=-1; //find a empty slot, and if not found, add it instead for i:=0 to owner.textures.count-1 do if owner.textures[i]=nil then begin id:=i; owner.textures[id]:=self; break; end; if id=-1 then id:=owner.textures.add(self); end; constructor TD3DHook_Texture.Create(owner: TD3DHook; picture: TPicture); var m: TMemoryStream; begin create(owner); LoadTextureByPicture(picture); end; //----------------------------------D3dhook------------------------------------- procedure TD3DHook.setSnapshotOptions(path: string; full, small: dword; Progressive: boolean; cleardepthbuffer: boolean; savepng: boolean; pictureFormat: integer); begin shared.snapshotdir:=IncludeTrailingPathDelimiter(path); shared.snapshotKey:=full; if Progressive then begin shared.progressiveSnapshot:=1; shared.smallSnapshotKey:=0; end else begin shared.progressiveSnapshot:=0; shared.smallSnapshotKey:=small; end; shared.snapshotImageFormat:=pictureFormat; if cleardepthbuffer then shared.alsoClearDepthBuffer:=1 else shared.alsoClearDepthBuffer:=0; if savepng then shared.savePNGSeperateAsWell:=1 else shared.savePNGSeperateAsWell:=0; end; procedure TD3DHook.setOnKeyDown(s: TD3DKeyDownEvent); begin if assigned(s) then begin fonKeyDown:=s; shared.hasOnKey:=1; end else shared.hasOnKey:=0; end; procedure TD3DHook.enableConsole(virtualkey: DWORD); begin shared.console.hasconsole:=1; shared.console.consolekey:=virtualkey; end; function TD3DHook.getDisabledZBuffer: boolean; begin result:=shared.disabledzbuffer=1; end; procedure TD3DHook.setDisabledZBuffer(state: boolean); begin if state then shared.disabledzbuffer:=1 else shared.disabledzbuffer:=0; end; function TD3DHook.getWireframeMode: boolean; begin result:=shared.wireframe=1; end; procedure TD3DHook.setWireframeMode(state: boolean); begin if state then shared.wireframe:=1 else shared.wireframe:=0; end; function TD3DHook.getMouseClip: boolean; begin result:=shared.clipmouseinwindow=1; end; procedure TD3DHook.setMouseClip(state: boolean); begin if state then shared.clipmouseinwindow:=1 else shared.clipmouseinwindow:=0; end; function TD3DHook.getWidth: integer; var x: trect; begin if (shared<>nil) and (GetClientRect(shared.lastHwnd, x)) then result:=x.Right-x.left else result:=0; end; function TD3DHook.getHeight: integer; var x: trect; begin if (shared<>nil) and (GetClientRect(shared.lastHwnd, x)) then result:=x.bottom-x.top else result:=0; end; procedure TD3DHook.setCommandListLockFeature(state: boolean); begin shared.useCommandListLock:=integer(state); end; procedure TD3DHook.beginCommandListUpdate; begin if self=nil then raise exception.create(rsTheD3dhookObjectHasNotBeenCreatedYet); commandlistCS.enter; if isupdatingCL=0 then //start of an edit WaitForSingleObject(CommandListLock, INFINITE); //obtain lock inc(isupdatingCL); end; procedure TD3DHook.endCommandListUpdate; begin renderCommandList^[commandlist.count].command:=integer(rcEndOfCommandlist); if isupdatingCL>0 then begin dec(isupdatingCL); if isupdatingCL=0 then SetEvent(CommandListLock); //release the lock end; commandlistCS.leave; end; procedure TD3DHook.beginTextureUpdate; begin if isupdating=0 then //start of an edit begin beginCommandListUpdate; WaitForSingleObject(TextureLock, INFINITE); //obtain lock end; inc(isupdating); end; procedure TD3DHook.endTextureUpdate; begin if isupdating>0 then begin dec(isupdating); if isupdating=0 then begin shared.textureCount:=textures.count; shared.texturelistHasUpdate:=1; SetEvent(TextureLock); //release the lock endCommandListUpdate; end; end; end; //++++++++++++++++++++++++++++++++++++++++++++++++++++++version 2 function TD3DHook.createFontMap(f: TFont): TD3DHook_FontMap; begin beginTextureUpdate; result:=TD3DHook_FontMap.Create(self, f); endTextureUpdate; end; function TD3DHook.createTextContainer(fontmap: TD3DHook_FontMap; x,y: single; text: string): TD3Dhook_TextContainer; begin beginCommandListUpdate; try result:=TD3Dhook_TextContainer.create(self, fontmap, x,y,text); finally endCommandListUpdate; end; end; function TD3DHook.createTexture(f: string): TD3DHook_Texture; overload; var p: TPicture; begin p:=tpicture.Create; beginTextureUpdate; try p.LoadFromFile(f); result:=TD3DHook_Texture.Create(self, p); finally p.free; endTextureUpdate; end; end; function TD3DHook.createTexture(p: TPicture): TD3DHook_Texture; begin beginTextureUpdate; result:=TD3DHook_Texture.Create(self, p); endTextureUpdate; end; function TD3DHook.createSprite(texture: TD3DHook_Texture): TD3DHook_Sprite; begin beginCommandListUpdate; result:=TD3DHook_Sprite.create(self, texture); endCommandListUpdate; end; destructor TD3DHook.Destroy; var i: integer; begin if messagehandler<>nil then begin messagehandler.terminate; messagehandler.Free; end; if hooked then begin beginCommandListUpdate; for i:=0 to commandlist.Count-1 do if commandlist[i]<>nil then TD3DHook_RenderObject(commandlist[i]).free; //make sure all commands are gone: if commandlist.count>0 then renderCommandList^[0].command:=integer(rcIgnored); endCommandListUpdate; beginTextureUpdate; for i:=0 to textures.Count-1 do if textures[i]<>nil then TD3DHook_Texture(textures[i]).Free; endTextureUpdate; end; UnmapViewOfFile(shared); closehandle(fmhandle); commandlist.free; textures.free; memman.free; d3dhook:=nil; inherited destroy; end; constructor TD3DHook.create(size: integer; hookhwnd: boolean=true); var h: thandle; s: TStringList; alreadyhooked: boolean; begin memman:=TRemoteMemoryManager.create; textures:=TList.create; commandlist:=TList.create; commandlistCS:=TCriticalSection.create; sharename:='CED3D_'+inttostr(processhandler.ProcessID); fprocessid:=processhandler.processid; maxsize:=size; createSharedMemory(sharename, sizeof(TD3DHookShared)+maxsize); fmhandle:=OpenFileMapping(FILE_MAP_EXECUTE or FILE_MAP_READ or FILE_MAP_WRITE, false, pchar(sharename)); if fmhandle=0 then raise exception.create(rsD3DHookFailureToOpenTheSharedMemoryObject); shared:=MapViewOfFile(fmhandle,FILE_MAP_EXECUTE or FILE_MAP_READ or FILE_MAP_WRITE, 0,0,0 ); if shared=nil then raise exception.create(rsD3DHookFailureToMapTheSharedMemoryObject); alreadyhooked:=shared.initialized=$dbcedbce; if not alreadyhooked then begin ZeroMemory(shared, sizeof(TD3DHookShared)); shared.texturelist:=sizeof(TD3DHookShared)+(maxsize div 2); shared.cheatenginedir:=CheatEngineDir; shared.useCommandListLock:=1; end; tea:=PTextureEntryArray(ptruint(shared)+shared.texturelist); renderCommandList:=PRenderCommandArray(ptruint(shared)+sizeof(TD3dHookShared)); if alreadyhooked then begin DuplicateHandle(processhandle, shared.TextureLock, GetCurrentProcess, @TextureLock, 0, false,DUPLICATE_SAME_ACCESS); DuplicateHandle(processhandle, shared.CommandListLock, GetCurrentProcess, @CommandListLock, 0, false,DUPLICATE_SAME_ACCESS); DuplicateHandle(processhandle, shared.SnapshotDone, GetCurrentProcess, @SnapshotDone, 0, false,DUPLICATE_SAME_ACCESS); beginTextureUpdate; renderCommandList^[0].command:=integer(rcEndOfCommandlist); //clear the command list shared.textureCount:=0; endTextureUpdate; end else begin if hookhwnd then shared.hookwnd:=1; h:=CreateEventA(nil, true, false, pchar(sharename+'_READY') ); if (h<>0) then begin if hookhwnd then begin hasclickevent:=CreateEventA(nil, false, false, pchar(sharename+'_HASCLICK') ); hashandledclickevent:=CreateEventA(nil, false, true, pchar(sharename+'_HANDLEDCLICK') ); haskeyboardevent:=CreateEventA(nil, false, false, pchar(sharename+'_HASKEYBOARD') ); hashandledkeyboardevent:=CreateEventA(nil, false, true, pchar(sharename+'_HANDLEDKEYBOARD') ); messagehandler:=TD3DMessageHandler.Create(true); messagehandler.owner:=self; messagehandler.start; end; TextureLock:=CreateEventA(nil, false, true, nil); DuplicateHandle(GetCurrentProcess, TextureLock, processhandle, @shared.TextureLock,0, false,DUPLICATE_SAME_ACCESS); CommandListLock:=CreateEventA(nil, false, true, nil); DuplicateHandle(GetCurrentProcess, CommandListLock, processhandle, @shared.CommandListLock,0, false,DUPLICATE_SAME_ACCESS); SnapshotDone:=CreateEventA(nil, false, false, nil); DuplicateHandle(GetCurrentProcess, SnapshotDone, processhandle, @shared.SnapshotDone,0, false,DUPLICATE_SAME_ACCESS); shared.canDoSnapshot:=1; //now inject the dll symhandler.reinitialize; symhandler.waitforsymbolsloaded(true, 'kernel32.dll'); if processhandler.is64Bit then injectdll(cheatenginedir+'d3dhook64.dll') else injectdll(cheatenginedir+'d3dhook.dll'); //wait till the injection is done WaitForSingleObject(h, INFINITE); closehandle(h); //and hook the functions //(script: tstrings; address: string; addresstogoto: string; addresstostoreneworiginalfunction: string=''; nameextension:string='0'); s:=tstringlist.create; try if shared.d3d9_present<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_present,8), inttohex(shared.d3d9_newpresent,8), inttohex(shared.d3d9_originalpresent,8), '0'); if shared.d3d9_reset<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_reset,8), inttohex(shared.d3d9_newreset,8), inttohex(shared.d3d9_originalreset,8), '1'); if shared.dxgi_present<>0 then generateAPIHookScript(s, inttohex(shared.dxgi_present,8), inttohex(shared.dxgi_newpresent,8), inttohex(shared.dxgi_originalpresent,8), '2'); if shared.d3d9_drawprimitive<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawprimitive,8), inttohex(shared.d3d9_newdrawprimitive,8), inttohex(shared.d3d9_originaldrawprimitive,8), '3'); if shared.d3d9_drawindexedprimitive<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawindexedprimitive,8), inttohex(shared.d3d9_newdrawindexedprimitive,8), inttohex(shared.d3d9_originaldrawindexedprimitive,8), '4'); if shared.d3d9_drawprimitiveup<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawprimitiveup,8), inttohex(shared.d3d9_newdrawprimitiveup,8), inttohex(shared.d3d9_originaldrawprimitiveup,8), '5'); if shared.d3d9_drawindexedprimitiveup<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawindexedprimitiveup,8), inttohex(shared.d3d9_newdrawindexedprimitiveup,8), inttohex(shared.d3d9_originaldrawindexedprimitiveup,8), '6'); if shared.d3d9_drawrectpatch<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawrectpatch,8), inttohex(shared.d3d9_newdrawrectpatch,8), inttohex(shared.d3d9_originaldrawrectpatch,8), '7'); if shared.d3d9_drawtripatch<>0 then generateAPIHookScript(s, inttohex(shared.d3d9_drawtripatch,8), inttohex(shared.d3d9_newdrawtripatch,8), inttohex(shared.d3d9_originaldrawtripatch,8), '8'); if shared.d3d10_drawindexed<>0 then generateAPIHookScript(s, inttohex(shared.d3d10_drawindexed,8), inttohex(shared.d3d10_newdrawindexed,8), inttohex(shared.d3d10_originaldrawindexed,8), '9'); if shared.d3d10_draw<>0 then generateAPIHookScript(s, inttohex(shared.d3d10_draw,8), inttohex(shared.d3d10_newdraw,8), inttohex(shared.d3d10_originaldraw,8), '10'); if shared.d3d10_drawindexedinstanced<>0 then generateAPIHookScript(s, inttohex(shared.d3d10_drawindexedinstanced,8), inttohex(shared.d3d10_newdrawindexedinstanced,8), inttohex(shared.d3d10_originaldrawindexedinstanced,8), '11'); if shared.d3d10_drawinstanced<>0 then generateAPIHookScript(s, inttohex(shared.d3d10_drawinstanced,8), inttohex(shared.d3d10_newdrawinstanced,8), inttohex(shared.d3d10_originaldrawinstanced,8), '12'); if shared.d3d10_drawauto<>0 then generateAPIHookScript(s, inttohex(shared.d3d10_drawauto,8), inttohex(shared.d3d10_newdrawauto,8), inttohex(shared.d3d10_originaldrawauto,8), '13'); if shared.d3d11_drawindexed<>0 then generateAPIHookScript(s, inttohex(shared.d3d11_drawindexed,8), inttohex(shared.d3d11_newdrawindexed,8), inttohex(shared.d3d11_originaldrawindexed,8), '14'); if shared.d3d11_draw<>0 then generateAPIHookScript(s, inttohex(shared.d3d11_draw,8), inttohex(shared.d3d11_newdraw,8), inttohex(shared.d3d11_originaldraw,8), '15'); if shared.d3d11_drawindexedinstanced<>0 then generateAPIHookScript(s, inttohex(shared.d3d11_drawindexedinstanced,8), inttohex(shared.d3d11_newdrawindexedinstanced,8), inttohex(shared.d3d11_originaldrawindexedinstanced,8), '16'); if shared.d3d11_drawinstanced<>0 then generateAPIHookScript(s, inttohex(shared.d3d11_drawinstanced,8), inttohex(shared.d3d11_newdrawinstanced,8), inttohex(shared.d3d11_originaldrawinstanced,8), '17'); if shared.d3d11_drawauto<>0 then generateAPIHookScript(s, inttohex(shared.d3d11_drawauto,8), inttohex(shared.d3d11_newdrawauto,8), inttohex(shared.d3d11_originaldrawauto,8), '18'); if shared.dxgi_resizebuffers<>0 then generateAPIHookScript(s, inttohex(shared.dxgi_resizebuffers,8), inttohex(shared.dxgi_newresizebuffers,8), inttohex(shared.dxgi_originalresizebuffers,8), '19'); // clipboard.AsText:=s.text; //if there is a script execute it. if (s.count>0) and (autoassemble(s,false)=false) then begin //on error write the script to the clipboard clipboard.AsText:=s.text; //debug end; shared.initialized:=$dbcedbce; finally s.free; end; end; end; hooked:=true; end; function safed3dhook(size: integer=16*1024*1024; hookwindow: boolean=true): TD3DHook; //Calls the d3dhook constructor but captures exceptions var wr: DWORD; begin if d3dhook=nil then begin try d3dhook:=TD3DHook.Create(size, hookwindow); except d3dhook:=nil; end; end else begin if d3dhook.processid<>processid then //different process begin d3dhook.Free; try d3dhook:=TD3DHook.Create(size, hookwindow); except d3dhook:=nil; end; end else begin //same process wr:=WaitForSingleObject(processhandle, 0); if wr<>WAIT_TIMEOUT then //not alive anymore freeandnil(d3dhook); end; end; result:=d3dhook; end; end.