1 (изменено: Malcev, 2019-10-02 23:16:34)

Тема: AHK: Хук DirectX 11 и добавление надписи

Желательно запускать исходную программу в режиме suspended, после чего инжектить autohotkey.dll.
Потом узнавать адрес IDXGISwapChain_Present и хукать его с помощью minhook.dll.
inject.ahk - запускает программу в режиме suspended:
В TargetProgram вписываем путь к запускаемой программе.

TargetProgram := "GFXTest.exe"
AutoHotkeyDll := "AutoHotkey.dll"
HookFile := "minhook.ahk"



array := {"TargetProgram": TargetProgram, "AutoHotkey.dll": AutoHotkeyDll, "HookFile": HookFile}
For key, value in array
{
   if !FileExist(value)
   {
      msgbox %value% does not exist
      ExitApp
   }
   if (key = "HookFile")
      Continue
   oFile := FileOpen(value, "r")
   oFile.Seek(0x3c)
   oFile.Seek(oFile.ReadInt()+4)
   machineType := oFile.ReadUShort()
   oFile.Close()
   if (machineType = 0x14c) and (A_PtrSize = 8)
   {
      msgbox %key% 32 bit, but AutoHotkey.exe 64 bit
      ExitApp
   }
   if ((machineType = 0x8664) or (machineType = 0x200)) and (A_PtrSize = 4)
   {
      msgbox %key% 64 bit, but AutoHotkey.exe 32 bit
      ExitApp
   }
}


#Persistent
VarSetCapacity(StartupInfo, size := 9*A_PtrSize + 32, 0)
NumPut(size, StartupInfo)
VarSetCapacity(ProcessInfo, 2*A_PtrSize + 8, 0)
DllCall("CreateProcess", "ptr", 0, "str", TargetProgram, "ptr", 0, "ptr", 0, "int", 0, "int", CREATE_SUSPENDED := 4, "ptr", 0, "ptr", 0, "ptr", &StartupInfo, "ptr", &ProcessInfo)
DllCall("CloseHandle", Ptr, hThread := NumGet(ProcessInfo))
DllCall("CloseHandle", Ptr, hProcess := NumGet(&ProcessInfo + A_PtrSize))
PID := NumGet(&ProcessInfo + 2*A_PtrSize, "UInt")
rThread:=""
rThread:=InjectAhkDll(pid, AutoHotkeyDll,, 1)
h := DllCall("OpenProcess", "uint", PROCESS_ALL_ACCESS := 0x1F0FFF, "int", 0, "uint", PID, "ptr")
DllCall("ntdll.dll\NtResumeProcess", "ptr", h)
DllCall("CloseHandle", "ptr", h)
FileRead,HookScript, %HookFile%
rThread.Exec(HookScript)
return



InjectAhkDll(PID,dll:="AutoHotkey64.dll",script:=0, deleteThread := false)
{
   static PROCESS_ALL_ACCESS:=0x1F0FFF,MEM_COMMIT := 0x1000,MEM_RELEASE:=0x8000,PAGE_EXECUTE_READWRITE:=64
   ,hKernel32:=DllCall("LoadLibrary","Str","kernel32.dll","PTR"),LoadLibraryA:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","LoadLibraryA","PTR")
   ,FreeLibrary:=DllCall("GetProcAddress","PTR",hKernel32,"AStr","FreeLibrary","PTR")
   ,TH32CS_SNAPMODULE:=0x00000008,INVALID_HANDLE_VALUE:=-1
   ,MAX_PATH:=260,MAX_MODULE_NAME32:=255,ModuleName:="",init:=VarSetCapacity(ModuleName,MAX_PATH*(A_IsUnicode?2:1))
   base:= deleteThread ? {__Call:"InjectAhkDll",__Delete:"InjectAhkDll"} : {__Call:"InjectAhkDll"}

   if IsObject(PID)
   {
      if (dll!="Exec" && script)
         return "Only Exec method can be used here!"

      hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID.PID,"PTR")
      if !hProc
         return "Could not open process for PID: " PID.PID

      if (!script) ; Free Library in remote process (object is being deleted)
      {
         ; Terminate the thread in ahkdll
         hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkTerminate, "PTR", 0, "UInt", 0, "PTR", 0,"PTR")
         DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
         ,DllCall("CloseHandle", "PTR", hThread)

         ; Free library in remote process
         hThread := DllCall("CreateRemoteThread", "PTR", hProc, "UInt", 0, "UInt", 0, "PTR", FreeLibrary, "PTR", PID.hModule, "UInt", 0, "UInt", 0,"PTR")
         DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
         ,DllCall("CloseHandle", "PTR", hThread),DllCall("CloseHandle", "PTR", hProc)
         return
      }

      nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
      ,StrPut(script,&nScript)

      ; Reserve memory in remote process where our script will be saved
      if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
         return "Could not reseve memory for process"
      ,DllCall("CloseHandle", "PTR", hProc)

      ; Write script to remote process memory
      DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)

      ; Start execution of code
      hThread := DllCall("CreateRemoteThread", "PTR", hProc, "PTR", 0, "PTR", 0, "PTR", PID.ahkExec, "PTR", pBufferRemote, "UInt", 0, "PTR", 0,"PTR")
      if !hThread
      {
         DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
         ,DllCall("CloseHandle", "PTR", hProc)
         return "Could not execute script in remote process"
      }

      ; Wait for thread to finish
      DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)

      ; Get Exit code returned by ahkExec (1 = script could be executed / 0 = script could not be executed)
      DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)
      if !lpExitCode
         return "Could not execute script in remote process"

      DllCall("CloseHandle", "PTR", hThread)
      ,DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nScriptLength,MEM_RELEASE)
      ,DllCall("CloseHandle", "PTR", hProc)
      return
   }
   else if !hDll:=DllCall("LoadLibrary","Str",dll,"PTR")
      return "Could not find " dll " library", DllCall("CloseHandle", "PTR", hProc)
   else
   {
      hProc := DllCall("OpenProcess","UInt", PROCESS_ALL_ACCESS, "Int",0,"UInt", DllCall("GetCurrentProcessId"),"PTR")
      DllCall("GetModuleFileName","PTR",hDll,"PTR",&ModuleName,"UInt",MAX_PATH)
      DllCall("CloseHandle","PTR",hProc)
   }
   ; Open Process to PID
   hProc := DllCall("OpenProcess", "UInt", PROCESS_ALL_ACCESS, "Int",0, "UInt", PID,"PTR")
   if !hProc
      return "Could not open process for PID: " PID
   
   Loop, Files, %dll%, F
      dll := A_LoopFileLongPath
   ; Reserve some memory and write dll path (ANSI)
   nDirLength := VarSetCapacity(nDir, StrLen(dll)+1, 0)
   ,StrPut(dll,&nDir,"CP0")

   ; Reserve memory in remote process
   if !pBufferRemote := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nDirLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
      return "Could not reseve memory for process", DllCall("CloseHandle", "PTR", hProc)

   ; Write dll path to remote process memory
   DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferRemote, "Ptr", &nDir, "PTR", nDirLength, "Ptr", 0)

   ; Start new thread loading our dll

   hThread:=DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",LoadLibraryA,"PTR",pBufferRemote,"UInt",0,"PTR",0,"PTR")
   if !hThread
   {
      DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,"Uint",MEM_RELEASE)
      ,DllCall("CloseHandle", "PTR", hProc)
      return "Could not load " dll " in remote process"
   }
   ; Wait for thread to finish
   DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)

   ; Get Exit code returned by thread (HMODULE for our dll)
   DllCall("GetExitCodeThread", "PTR", hThread, "UInt*", hModule)

   ; Close Thread
   DllCall("CloseHandle", "PTR", hThread)

   if (A_PtrSize=8)
   { ; use different method to retrieve base address because GetExitCodeThread returns DWORD only
      hModule:=0,VarSetCapacity(me32, (A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), 0) ;W:1080:1064, A:564:548
      ;  Take a snapshot of all modules in the specified process.
      hModuleSnap := DllCall("CreateToolhelp32Snapshot","UInt", TH32CS_SNAPMODULE,"UInt", PID, "PTR" )
      if ( hModuleSnap != INVALID_HANDLE_VALUE )
      {
         ; reset hModule and set the size of the structure before using it.
         NumPut((A_PtrSize=8?48:32)+(A_IsUnicode?1032:516), &me32, 0, "UInt") ;dwSize  ;W:1080:1064, A:564:548
         ;  Retrieve information about the first module,
         ;  and exit if unsuccessful
         if ( !DllCall("Module32First" (A_IsUnicode?"W":""),"PTR", hModuleSnap,"PTR", &me32 ) )
         {
            ; Free memory used for passing dll path to remote thread
            DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
            ,DllCall("CloseHandle","PTR", hModuleSnap ) ; Must clean up the snapshot object!
            return false
         }
         ;  Now walk the module list of the process,and display information about each module
         while(A_Index=1 || DllCall("Module32Next" (A_IsUnicode?"W":""),"PTR",hModuleSnap,"PTR", &me32 ) )
            if (StrGet(&me32+(A_PtrSize=8?48:32)+(A_IsUnicode?512:256))=dll) ;szExePath ;W:560:544, A:304:288
            {
               hModule := NumGet(me32, A_PtrSize=8?40:28, "Ptr") ;hModule
               break
            }
         DllCall("CloseHandle","PTR",hModuleSnap) ; clean up
      }
   }

   hDll:=DllCall("LoadLibrary","Str",dll,"PTR")

   ; Calculate pointer to ahkdll and ahkExec functions
   ahktextdll:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahktextdll","PTR")-hDll
   ahkExec:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkExec","PTR")-hDll
   ahkTerminate:=hModule+DllCall("GetProcAddress","PTR",hDll,"AStr","ahkTerminate","PTR")-hDll

   if script
   {
      nScriptLength := VarSetCapacity(nScript, (StrLen(script)+1)*(A_IsUnicode?2:1), 0)
      ,StrPut(script,&nScript)
      ; Reserve memory in remote process where our script will be saved
      if !pBufferScript := DllCall("VirtualAllocEx", "Ptr", hProc, "Ptr", 0, "PTR", nScriptLength, "UInt", MEM_COMMIT, "UInt", PAGE_EXECUTE_READWRITE, "Ptr")
         return "Could not reseve memory for process"
      ,DllCall("CloseHandle", "PTR", hProc)

      ; Write script to remote process memory
      DllCall("WriteProcessMemory", "Ptr", hProc, "Ptr", pBufferScript, "Ptr", &nScript, "PTR", nScriptLength, "Ptr", 0)
   }
   else
      pBufferScript:=0

   ; Run ahkdll function in remote thread
   hThread := DllCall("CreateRemoteThread","PTR",hProc,"PTR",0,"PTR",0,"PTR",ahktextdll,"PTR",pBufferScript,"PTR",0,"UInt",0,"PTR")
   if !hThread
   { ; could not start ahkdll in remote process
      ; Free memory used for passing dll path to remote thread
      DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
      DllCall("CloseHandle", "PTR", hProc)
      return "Could not start ahkdll in remote process"
   }
   DllCall("WaitForSingleObject", "PTR", hThread, "UInt", 0xFFFFFFFF)
   DllCall("GetExitCodeThread", "PTR", hThread, "UIntP", lpExitCode)

   ; Release memory and handles
   DllCall("VirtualFreeEx","PTR",hProc,"PTR",pBufferRemote,"PTR",nDirLength,MEM_RELEASE)
   DllCall("CloseHandle", "PTR", hThread)
   DllCall("CloseHandle", "PTR", hProc)

   if !lpExitCode ; thread could not be created.
      return "Could not create a thread in remote process"

   return {PID:PID,hModule:hModule,ahkExec:ahkExec,ahkTerminate:ahkTerminate,base:base}
}

minhook.ahk
В TargetWinTitle вписываем название окна запускаемой программы.

TargetWinTitle := "ahk_class gfx_test"
MinhookDll := "MinHook.dll"
Global FW1FontWrapperDll := "FW1FontWrapper.dll"
Global IDXGISwapChain_Present_Original, IDXGISwapChain_ResizeBuffers_Original, ResolutionChanged, widthChanged, heightChanged



array := {"MinHook.dll": MinhookDll, "FW1FontWrapper.dll": FW1FontWrapperDll}
For key, value in array
{
   if !FileExist(value)
   {
      msgbox %value% does not exist
      ExitApp
   }
   oFile := FileOpen(value, "r")
   oFile.Seek(0x3c)
   oFile.Seek(oFile.ReadInt()+4)
   machineType := oFile.ReadUShort()
   oFile.Close()
   if (machineType = 0x14c) and (A_PtrSize = 8)
   {
      msgbox %key% 32 bit, but AutoHotkey.exe 64 bit
      ExitApp
   }
   if ((machineType = 0x8664) or (machineType = 0x200)) and (A_PtrSize = 4)
   {
      msgbox %key% 64 bit, but AutoHotkey.exe 32 bit
      ExitApp
   }
}

WinWait, % TargetWinTitle
hwnd := WinExist(TargetWinTitle)
SetWorkingDir %A_ScriptDir%
LoadDll()

VarSetCapacity(SwapChainDesc, 48+A_PtrSize*3, 0)
NumPut(60, SwapChainDesc, 8, "uint")   ; DXGI_MODE_DESC DXGI_RATIONAL Numerator
NumPut(1, SwapChainDesc, 12, "uint")   ; DXGI_MODE_DESC DXGI_RATIONAL Denominator
NumPut(DXGI_FORMAT_R8G8B8A8_UNORM := 28, SwapChainDesc, 16, "uint")   ; DXGI_MODE_DESC Format
NumPut(1, SwapChainDesc, 28, "uint")   ; DXGI_SAMPLE_DESC count
NumPut(DXGI_USAGE_RENDER_TARGET_OUTPUT := 32, SwapChainDesc, 36, "uint")   ; BufferUsage
NumPut(1, SwapChainDesc, 40, "uint")   ; BufferCount
NumPut(hwnd, SwapChainDesc, 40+A_PtrSize, "ptr")   ; OutputWindow
NumPut(1, SwapChainDesc, 40+2*A_PtrSize, "int")   ; Windowed
NumPut(DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH := 2, SwapChainDesc, 48+2*A_PtrSize, "uint")   ; Flags

VarSetCapacity(D3D_FEATURE_LEVEL, 4, 0)
NumPut(D3D_FEATURE_LEVEL_11_0 := 0xb000, D3D_FEATURE_LEVEL, 0, "uint")
D3D11CreateDeviceAndSwapChain(0, D3D_DRIVER_TYPE_HARDWARE := 1, 0, 0, &D3D_FEATURE_LEVEL, 1, D3D11_SDK_VERSION := 7, &SwapChainDesc, IDXGI_SwapChain, ID3D11Device, 0, ID3D11DeviceContext)
IDXGISwapChain_Present := IDXGISwapChain_Present_Get(IDXGI_SwapChain)
IDXGISwapChain_ResizeBuffers := IDXGISwapChain_ResizeBuffers_Get(IDXGI_SwapChain)
Release(IDXGI_SwapChain)
Release(ID3D11DeviceContext)
Release(ID3D11Device)
IDXGI_SwapChain := ID3D11DeviceContext := ID3D11Device := ""

MinHook_Init(MinhookDll)
MH_CreateHook(IDXGISwapChain_Present, RegisterCallback("IDXGISwapChain_Present_Hook", "F"), IDXGISwapChain_Present_Original)
MH_CreateHook(IDXGISwapChain_ResizeBuffers, RegisterCallback("IDXGISwapChain_ResizeBuffers_Hook", "F"), IDXGISwapChain_ResizeBuffers_Original)
MH_EnableHook()
return


IDXGISwapChain_Present_Hook(SwapChain, SyncInterval, Flags)
{
   Critical
   static Init, iFontWrapper, ImmediateContext, ResolutionOK, width, height
   if (ResolutionChanged = 1)
      ResolutionOk := ResolutionChanged := "", width := widthChanged, height := heightChanged
   if (ResolutionOK = "")
   {
      if ImmediateContext
      {
         Release(ImmediateContext)
         ImmediateContext := ""
      }
      IDXGISwapChain_GetDevice(SwapChain, ID3D11Device)
      ID3D11Device_GetImmediateContext(ID3D11Device, ImmediateContext)
      if (Init = "")
      {
         if (width = "")
         {
            VarSetCapacity(SwapChainDesc, 48+A_PtrSize*3, 0)
            IDXGISwapChain_GetDesc(SwapChain, &SwapChainDesc)
            width := NumGet(SwapChainDesc, 0, "uint")
            height := NumGet(SwapChainDesc, 4, "uint")
         }
         FW1CreateFactory(pFW1Factory)
         iFW1Factory := FW1Factory_Query(pFW1Factory)
         IFW1Factory_CreateFontWrapper(iFW1Factory, ID3D11Device, "Arial", pFontWrapper)
         iFontWrapper := FW1FontWrapper_Query(pFontWrapper)
         ObjRelease(iFW1Factory)
         iFW1Factory := ""
         Init := 1
      }
      ResolutionOK := 1
   }
   IFW1FontWrapper_DrawString(iFontWrapper, ImmediateContext, "Hello, World!", width*0.1, width/2, height/2, 0xFFFFFFFF, 0x1|0x4|0x800)   ; FW1_CENTER|FW1_VCENTER|FW1_RESTORESTATE
   DllCall(IDXGISwapChain_Present_Original, "ptr", SwapChain, "uint", SyncInterval, "uint", Flags)
   return 
}

IDXGISwapChain_ResizeBuffers_Hook(SwapChain, BufferCount, SwapChainWidth, SwapChainHeight, NewFormatBuf, SwapChainFlags)
{
   widthChanged := SwapChainWidth, heightChanged := SwapChainHeight
   ResolutionChanged := 1
   DllCall(IDXGISwapChain_ResizeBuffers_Original, "ptr", SwapChain, "uint", BufferCount, "uint", SwapChainWidth, "uint", SwapChainHeight, "uint", NewFormatBuf, "uint", SwapChainFlags)
   return 
}



LoadDll()
{
   if !DllCall("GetModuleHandle","str","DXGI", "ptr")
   {
      MsgBox, 16, Error, DXGI failed.
      ExitApp
   }
   if !DllCall("GetModuleHandle","str","D3D11", "ptr")
   {
      MsgBox, 16, Error, D3D11 failed.
      ExitApp
   }
   if !DllCall("GetModuleHandle","str","FW1FontWrapper")
      DllCall("LoadLibrary","Str", FW1FontWrapperDll, "ptr")
   return
}


FW1CreateFactory(ByRef ppFactory)
{
   hr := DllCall(FW1FontWrapperDll "\FW1CreateFactory", "uint", FW1_VERSION := 0x110f, "ptr*", ppFactory)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

FW1Factory_Query(pFactory)
{ 
   hr := ComObjQuery(pFactory, "{8004DB2B-B5F9-4420-A6A2-E17E15E4C336}")
   if !hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   return hr
}

IFW1Factory_CreateFontWrapper(this, pDevice, pszFontFamily, ByRef ppFontWrapper)
{
   hr := DllCall(NumGet(NumGet(this+0)+5*A_PtrSize), "ptr", this, "ptr", pDevice, "str", pszFontFamily, "ptr*", ppFontWrapper)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

FW1FontWrapper_Query(pFontWrapper)
{ 
   hr := ComObjQuery(pFontWrapper, "{83347A5C-B0B1-460e-A35C-427E8B85F9F4}")
   if !hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
   return hr
}

IFW1FontWrapper_DrawString(this, pContext, pszString, FontSize, X, Y, Color, Flags)
{
   hr := DllCall(NumGet(NumGet(this+0)+14*A_PtrSize), "ptr", this, "ptr", pContext, "str", pszString, "float", FontSize, "float", X, "float", Y, "uint", Color, "uint", Flags)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}



D3D11CreateDeviceAndSwapChain(pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion, SwapChainDesc, ByRef ppSwapChain, ByRef ppDevice, pFeatureLevel, ByRef ppImmediateContext)
{
   hr := DllCall("D3D11\D3D11CreateDeviceAndSwapChain", "ptr", pAdapter, "int", DriverType, "ptr", Software, "uint", Flags, "ptr", pFeatureLevels, "uint", FeatureLevels, "uint", SDKVersion, "ptr", SwapChainDesc, "ptr*", ppSwapChain, "ptr*", ppDevice, "ptr", pFeatureLevel, "ptr*", ppImmediateContext)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGISwapChain_GetDevice(this, ByRef ppDevice)
{
   GUID(riid, "{db6f6ddb-ac77-4e88-8253-819df9bbf140}")
   hr := DllCall(NumGet(NumGet(this+0)+7*A_PtrSize), "ptr", this, "ptr", &riid, "ptr*", ppDevice)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

ID3D11Device_GetImmediateContext(this, ByRef ppImmediateContext)
{
   DllCall(NumGet(NumGet(this+0)+40*A_PtrSize), "ptr", this, "ptr*", ppImmediateContext)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGISwapChain_GetDesc(this, pDesc)
{
   hr := DllCall(NumGet(NumGet(this+0)+12*A_PtrSize), "ptr", this, "ptr", pDesc)
   if hr or ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

IDXGISwapChain_Present_Get(this)
{
   return NumGet(NumGet(this+0)+8*A_PtrSize)
}

IDXGISwapChain_ResizeBuffers_Get(this)
{
   return NumGet(NumGet(this+0)+13*A_PtrSize)
}

Release(this)
{
   DllCall(NumGet(NumGet(this+0)+2*A_PtrSize), "ptr", this)
   if ErrorLevel
      _Error(A_ThisFunc " error: " hr "`nErrorLevel: " ErrorLevel)
}

GUID(ByRef GUID, sGUID)
{
    VarSetCapacity(GUID, 16, 0)
    return DllCall("ole32\CLSIDFromString", "WStr", sGUID, "Ptr", &GUID) >= 0 ? &GUID : ""
}

_Error(val)
{
   msgbox % val
   ExitApp
}


MinHook_Init(MinhookDll)
{
   Static h
   If Not h
   {
      h:=DllCall("LoadLibrary","Str",MinhookDll, "Ptr")
      MH_Initialize()
   }
}

; Creates a Hook for the specified target function, in disabled state.
; Parameters:
;   pTarget    [in]  A pointer to the target function, which will be
;                    overridden by the detour function.
;   pDetour    [in]  A pointer to the detour function, which will override
;                    the target function.
;   ppOriginal [out] A pointer to the trampoline function, which will be
;                    used to call the original target function.
;                    This parameter can be NULL.
MH_CreateHook(pTarget, pDetour, ByRef ppOriginal := 0) {
	return DllCall("MinHook\MH_CreateHook"
	               , "ptr", pTarget
	               , "ptr", pDetour
	               , "uptr*", ppOriginal )
}

; Initialize the MinHook library. You must call this function EXACTLY ONCE
; at the beginning of your program.
MH_Initialize() {
	return DllCall("MinHook\MH_Initialize")
}

; Uninitialize the MinHook library. You must call this function EXACTLY
; ONCE at the end of your program.
MH_Uninitialize() {
	return DllCall("MinHook\MH_Uninitialize")
}

; Removes an already created hook.
; Parameters:
;   pTarget [in] A pointer to the target function.
MH_RemoveHook(pTarget) {
	return DllCall("MinHook\MH_RemoveHook", "ptr", pTarget)
}

/*
	#define MH_ALL_HOOKS NULL
*/

; Enables an already created hook.
; Parameters:
;   pTarget [in] A pointer to the target function.
;                If this parameter is MH_ALL_HOOKS, all created hooks are
;                enabled in one go.
MH_EnableHook(pTarget := 0) {
	return DllCall("MinHook\MH_EnableHook", "ptr", pTarget)
}

; Disables an already created hook.
; Parameters:
;   pTarget [in] A pointer to the target function.
;                If this parameter is MH_ALL_HOOKS, all created hooks are
;                disabled in one go.
MH_DisableHook(pTarget := 0) {
	return DllCall("MinHook\MH_DisableHook", "ptr", pTarget)
}

Так как в DirectX11 отсутствует поддержка Direct2D / Fonts, то можно использовать FW1FontWrapper библиотеку, что я и сделал.
dll и саму программу прикрепил в приложении.

Post's attachments

hook.zip 1.49 mb, 7 downloads since 2019-09-19 

You don't have the permssions to download the attachments of this post.

2

Re: AHK: Хук DirectX 11 и добавление надписи

Руки чешутся поюзать! Жаль, времени нету.

3

Re: AHK: Хук DirectX 11 и добавление надписи

А можно подробнее, что именно программа делает?

4 (изменено: Malcev, 2019-09-20 13:08:00)

Re: AHK: Хук DirectX 11 и добавление надписи

Какая именно программа?
Если вы имеете в виду этот код, то inject.ahk, запускает программу GFXTest.exe (программа, использующая directx движок), после чего инжектит в неё autohotkey.dll и запускает уже в ней код minhook.ahk.
Для перехвата вывода изображения в directx11 нам надо найти адрес IDXGISwapChain_Present метода.
https://docs.microsoft.com/en-us/window … in-present
Для этого мы создаём D3d11 устройство и получаем хендл на swap chain.
https://docs.microsoft.com/en-us/window … dswapchain
Потом высчитываем адрес метода present у swap chain.
После чего устанавливаем на этот адрес хук с помощью minhook.dll.
В самом хуке мы узнаем высоту, ширину свепчейна, хендл связанного с ним устройства и для добавления текста находим хендл на ID3D11DeviceContext interface:
https://docs.microsoft.com/en-us/window … icecontext
Потом создаем текст через FW1FontWrapper.dll и накладываем его на ID3D11DeviceContext interface.
После чего выводим получившиеся изображение на экран.

5

Re: AHK: Хук DirectX 11 и добавление надписи

Спасибо за подробности, но сначала меня интересует практический момент. Можно выводить любые символы поверх игры работающей на DirectX11? А что ещё можно? Например, использовать как wallhack или что-то ещё?

6

Re: AHK: Хук DirectX 11 и добавление надписи

Я на играх не тестировал.
Думаю, там возможен ряд защит.
Кстати, надо бы скачать какую-нибудь и протестировать.
Символы можно любые.
Думаю, что эти хаки используют также перехват Directx и шаманство с текстурами.

7

Re: AHK: Хук DirectX 11 и добавление надписи

В общем скачал пару игр для теста.
Пока получается следующее:
1) Если игра написана на движке Unity, то в полноэкранном режиме надпись пропадает.
2) Если на игре используется стимовский оверлей, то хук не ставится - видно стим сам ее хукает.
Чтобы следить за изменением размера игры нужно еще хукать IDXGISwapChain_ResizeBuffers.
Код в первом посте переписал и вставил проверку на совпадение битностей автохотки, всех длл и исходной программы.

8 (изменено: belyankin12, 2019-09-22 21:17:32)

Re: AHK: Хук DirectX 11 и добавление надписи

Так то оверлеев много существует: от банальных фрапсов/шадоуплэй до овервульфов и тех самых стимов. Потому я думаю для использования ahk скриптов в качестве оверлея все же лучше использовать соответствующую наработку. Есть даже версия с поддержкой кириллицы, однако она приводила к крашу игру, в которой я её использовал.

Меня другое интересует: получается эта библиотека как бы перехватывает изображение, которое рендерится на мониторе? Возможно неправильно объяснил, я в терминологии все же не силен, но с помощью этой библиотеки можно захватывать моментальные скриншоты полноэкранных приложений?

Когда вы говорите что не можете сделать, вам всего-лишь не хватает фантазии придумать какой-нибудь костыль.

9

Re: AHK: Хук DirectX 11 и добавление надписи

Фрапс далеко не банален.
Соответствующая нароботка только для directx9.
Скриншоты, думаю, конечно, получится, но не совсем правильно.
Правильно обрабатывать скриншоты в отдельном потоке, чтоб меньше тормозило, но я не знаю, как его запустить уже из инжектнутого приложения.