mirror of
https://github.com/waryas/WaryasSWHE.git
synced 2025-12-15 22:33:08 +08:00
Create old_variant_winmmabase_rop.cpp
An old variant that still works with some work.
This commit is contained in:
928
variant/old_variant_winmmabase_rop.cpp
Normal file
928
variant/old_variant_winmmabase_rop.cpp
Normal file
@@ -0,0 +1,928 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <winternl.h>
|
||||||
|
#include <consoleapi.h>
|
||||||
|
|
||||||
|
#define NTHEADER(ImgBase) PIMAGE_NT_HEADERS64(PIMAGE_DOS_HEADER(ImgBase)->e_lfanew + uint64_t(ImgBase))
|
||||||
|
#define SET_BIT(Val, Num, Up) { if(Up) { Val |= (1 << Num); } else { Val &= ~(1 << Num); } }
|
||||||
|
#define IS_BIT_SET(Val, Num) (Val & (1 << Num))
|
||||||
|
|
||||||
|
#define SHARED_MEM_MMIO_STRUCT 0x200
|
||||||
|
#define SHARED_MEM_SHADOW_VMT 0x300
|
||||||
|
#define SHARED_MEM_COMMAND_BUFFER 0x700
|
||||||
|
#define SHARED_MEM_STACK_OFFSET 0x800
|
||||||
|
#define SHARED_MEM_STACK_PLACEHOLDER_OFFSET (SHARED_MEM_STACK_OFFSET - 8)
|
||||||
|
|
||||||
|
#pragma comment(lib, "ntdll.lib")
|
||||||
|
|
||||||
|
class CProcess
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DWORD ThreadID{};
|
||||||
|
DWORD ProcessID{};
|
||||||
|
HWND ProcessHwnd{};
|
||||||
|
uintptr_t peb{};
|
||||||
|
uintptr_t discord_base{};
|
||||||
|
uintptr_t discord_framebuffer{};
|
||||||
|
uintptr_t base{};
|
||||||
|
|
||||||
|
bool Initialize(const wchar_t* WindowName, const wchar_t* ClassName = 0)
|
||||||
|
{
|
||||||
|
ProcessHwnd = FindWindowW(ClassName, WindowName);
|
||||||
|
if (!ProcessHwnd) return false;
|
||||||
|
|
||||||
|
ThreadID = GetWindowThreadProcessId(ProcessHwnd, &ProcessID);
|
||||||
|
return ThreadID != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class CExploit
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
CProcess *ProcessData{};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HMODULE winmm{};
|
||||||
|
FARPROC mmio_rename{};
|
||||||
|
|
||||||
|
uint64_t SharedCount{};
|
||||||
|
|
||||||
|
uint64_t PopRCX{};
|
||||||
|
uint64_t PopRDX{};
|
||||||
|
uint64_t PopRAX{};
|
||||||
|
uint64_t PopRSP{};
|
||||||
|
uint64_t MovRax_RCX{};
|
||||||
|
|
||||||
|
uint64_t ReadRCX_RAX{};
|
||||||
|
|
||||||
|
uint64_t WriteRCX_RDX{};
|
||||||
|
uint64_t WriteRDX_RCX{};
|
||||||
|
uint64_t WriteRCX_RAX{};
|
||||||
|
|
||||||
|
uint64_t AddRSP_58{};
|
||||||
|
uint64_t AddRCX_RBP{};
|
||||||
|
uint64_t PushRAX_PopRSP{};
|
||||||
|
|
||||||
|
bool LegacyWin10;
|
||||||
|
|
||||||
|
uint32_t ShellCursor{};
|
||||||
|
|
||||||
|
const char* ModulesToCheck[114] = { "ntdll.dll", "kernel32.dll", "user32.dll", "ws2_32.dll", "shell32.dll", "dxgi.dll", "crypt32.dll", "advapi32.dll", "ole32.dll", "secur32.dll", "psapi.dll", "rasadhlp.dll", "msctf.dll", "ntasn1.dll", "SHCore.dll", "avifil32.dll", "cryptsp.dll", "wbemsvc.dll", "winmmbase.dll", "combase.dll", "dwmapi.dll", "powrprof.dll", "ncrypt.dll", "MMDevAPI.dll", "msvcp_win.dll", "propsys.dll", "CoreMessaging.dll", "cryptbase.dll", "IPHLPAPI.DLL", "drvstore.dll", "gdi32full.dll", "version.dll", "midimap.dll", "coloradapterclient.dll", "wininet.dll", "Windows.UI.dll", "cryptnet.dll", "wbemcomn.dll", "bcrypt.dll", "mscms.dll", "schannel.dll", "userenv.dll", "amsi.dll", "rsaenh.dll", "ucrtbase.dll", "msvfw32.dll", "d3d11.dll", "devobj.dll", "dhcpcsvc6.dll", "wintrust.dll", "xinput1_3.dll", "mswsock.dll", "wdmaud.drv", "sxs.dll", "bcryptprimitives.dll", "ncryptsslp.dll", "gdi32.dll", "normaliz.dll", "clbcatq.dll", "fastprox.dll", "profapi.dll", "win32u.dll", "avrt.dll", "NapiNSP.dll", "WinTypes.dll", "pnrpnsp.dll", "CoreUIComponents.dll", "D3DCompiler_43.dll", "umpdc.dll", "XAudio2_9.dll", "KernelBase.dll", "ntmarta.dll", "sspicli.dll", "kernel.appcore.dll", "dhcpcsvc.dll", "ksuser.dll", "InputHost.dll", "msvcrt.dll", "imm32.dll", "AudioSes.dll", "dnsapi.dll", "wshbth.dll", "mskeyprotect.dll", "msacm32.dll", "twinapi.appcore.dll", "wbemprox.dll", "oleaut32.dll", "msasn1.dll", "winrnr.dll", "hid.dll", "setupapi.dll", "dsound.dll", "windows.storage.dll", "dbghelp.dll", "WindowManagementAPI.dll", "nlaapi.dll", "ResourcePolicyClient.dll", "comctl32.dll", "msacm32.drv", "dbgcore.dll", "imagehlp.dll", "rpcrt4.dll", "FWPUCLNT.DLL", "DXCore.dll", "uxtheme.dll", "TextInputFramework.dll", "winmm.dll", "Windows.Internal.Graphics.Display.DisplayColorManagement.dll", "Wldap32.dll", "shlwapi.dll", "wldp.dll", "sechost.dll", "nsi.dll" };
|
||||||
|
|
||||||
|
bool FindSharedMemory(HANDLE hProcess, uint64_t* MemoryOut, uint64_t* SharedCountOut)
|
||||||
|
{
|
||||||
|
uint8_t* Address{};
|
||||||
|
MEMORY_BASIC_INFORMATION Mbi{};
|
||||||
|
bool found = false;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!VirtualQueryEx(hProcess, Address, &Mbi, sizeof(Mbi)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (hProcess != GetCurrentProcess() && Mbi.State == MEM_COMMIT && Mbi.Protect == PAGE_READONLY && Mbi.RegionSize == 0x1000 && Mbi.Type == MEM_IMAGE) {
|
||||||
|
char filename[1024]{};
|
||||||
|
K32GetMappedFileNameA(hProcess, Mbi.BaseAddress, filename, sizeof(filename));
|
||||||
|
if (strstr(filename, "DiscordHook64"))
|
||||||
|
ProcessData->discord_base = (uintptr_t)Address;
|
||||||
|
else if (strstr(filename, "r5apex.exe"))
|
||||||
|
ProcessData->base = (uintptr_t)Address;
|
||||||
|
}
|
||||||
|
else if (Mbi.State == MEM_COMMIT && Mbi.Protect == PAGE_READWRITE && Mbi.RegionSize == 0x3201000 && Mbi.Type == MEM_MAPPED) {
|
||||||
|
ProcessData->discord_framebuffer = (uintptr_t)Address;
|
||||||
|
}
|
||||||
|
else if (!found && Mbi.State == MEM_COMMIT && Mbi.Protect == PAGE_READWRITE && Mbi.RegionSize == 0x1000 && Mbi.Type == MEM_MAPPED)
|
||||||
|
{
|
||||||
|
PSAPI_WORKING_SET_EX_INFORMATION WsInfo{};
|
||||||
|
WsInfo.VirtualAddress = Mbi.BaseAddress;
|
||||||
|
|
||||||
|
if (QueryWorkingSetEx(hProcess, &WsInfo, sizeof(WsInfo)))
|
||||||
|
{
|
||||||
|
if (hProcess == HANDLE(-1))
|
||||||
|
{
|
||||||
|
*MemoryOut = uint64_t(Mbi.BaseAddress);
|
||||||
|
*SharedCountOut = WsInfo.VirtualAttributes.ShareCount;
|
||||||
|
found = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (WsInfo.VirtualAttributes.ShareCount == *SharedCountOut)
|
||||||
|
{
|
||||||
|
|
||||||
|
*MemoryOut = uint64_t(Mbi.BaseAddress);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Address += Mbi.RegionSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<int SigLenT>
|
||||||
|
uint64_t FindROPSignatureInModule(const char* ModuleName, const char(&Signature)[SigLenT])
|
||||||
|
{
|
||||||
|
const int SigLen = SigLenT - 1;
|
||||||
|
|
||||||
|
uint64_t ModuleBase = uint64_t(GetModuleHandleA(ModuleName));
|
||||||
|
if (!ModuleBase)
|
||||||
|
{
|
||||||
|
ModuleBase = uint64_t(LoadLibraryA(ModuleName));
|
||||||
|
if (!ModuleBase)
|
||||||
|
{
|
||||||
|
printf("Failed to load module[%s]\n", ModuleName);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PIMAGE_NT_HEADERS64 NtHdr = NTHEADER(ModuleBase);
|
||||||
|
PIMAGE_SECTION_HEADER SectionHdr = IMAGE_FIRST_SECTION(NtHdr);
|
||||||
|
for (int i = 0; i < NtHdr->FileHeader.NumberOfSections; i++, SectionHdr++)
|
||||||
|
{
|
||||||
|
if (SectionHdr->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!(SectionHdr->Characteristics & IMAGE_SCN_MEM_EXECUTE))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (uint32_t u = 0; u < SectionHdr->Misc.VirtualSize - SigLen; u++)
|
||||||
|
{
|
||||||
|
uint64_t Addr = ModuleBase + SectionHdr->VirtualAddress + u;
|
||||||
|
if (memcmp((void*)Addr, Signature, SigLen) == 0)
|
||||||
|
{
|
||||||
|
return Addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<int SigLenT>
|
||||||
|
bool FindGadget(uint64_t* Out, const char* ModuleName, const char(&Signature)[SigLenT])
|
||||||
|
{
|
||||||
|
if (*Out)
|
||||||
|
return true;
|
||||||
|
*Out = FindROPSignatureInModule(ModuleName, Signature);
|
||||||
|
return *Out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
uint64_t LocalSharedMemory{};
|
||||||
|
uint64_t RemoteSharedMemory{};
|
||||||
|
uint64_t RemoteProcessBase{};
|
||||||
|
void InsertChain(uint64_t Val1, uint64_t Val2, bool Need2 = true)
|
||||||
|
{
|
||||||
|
*(volatile uint64_t*)(LocalSharedMemory + ShellCursor) = Val1;
|
||||||
|
ShellCursor += 8;
|
||||||
|
|
||||||
|
if (Need2)
|
||||||
|
{
|
||||||
|
*(volatile uint64_t*)(LocalSharedMemory + ShellCursor) = Val2;
|
||||||
|
ShellCursor += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetCursor()
|
||||||
|
{
|
||||||
|
ShellCursor = SHARED_MEM_STACK_OFFSET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixupControlFlow()
|
||||||
|
{
|
||||||
|
if (!LegacyWin10)
|
||||||
|
{
|
||||||
|
InsertChain(PopRCX, -0x71);
|
||||||
|
InsertChain(AddRCX_RBP, MovRax_RCX);
|
||||||
|
InsertChain(PushRAX_PopRSP, 0, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InsertChain(PopRAX, -0x71);
|
||||||
|
InsertChain(AddRCX_RBP, PushRAX_PopRSP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Hijack()
|
||||||
|
{
|
||||||
|
Sleep(0);
|
||||||
|
ShowWindow(ProcessData->ProcessHwnd, 0);
|
||||||
|
auto craftedinfo = (MMIOINFO*)(LocalSharedMemory + SHARED_MEM_MMIO_STRUCT);
|
||||||
|
memset(craftedinfo, 0, sizeof(*craftedinfo));
|
||||||
|
craftedinfo->dwFlags = 0x1000000;
|
||||||
|
craftedinfo->pIOProc = (LPMMIOPROC)(AddRSP_58);
|
||||||
|
craftedinfo->pchBuffer = (HPSTR)(PopRSP);
|
||||||
|
craftedinfo->pchNext = (HPSTR)(RemoteSharedMemory + SHARED_MEM_STACK_OFFSET);
|
||||||
|
|
||||||
|
auto hhook = SetWindowsHookExA(WH_SHELL, (HOOKPROC)(mmio_rename), winmm, ProcessData->ThreadID);
|
||||||
|
SendMessage(ProcessData->ProcessHwnd, WM_APPCOMMAND, 0, RemoteSharedMemory + SHARED_MEM_MMIO_STRUCT);
|
||||||
|
UnhookWindowsHookEx(hhook);
|
||||||
|
|
||||||
|
memset((void*)(LocalSharedMemory + SHARED_MEM_STACK_OFFSET), 0, 0x1000 - SHARED_MEM_STACK_OFFSET);
|
||||||
|
Sleep(0);
|
||||||
|
ShowWindow(ProcessData->ProcessHwnd, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ReadU64(uint64_t Address)
|
||||||
|
{
|
||||||
|
ResetCursor();
|
||||||
|
|
||||||
|
{
|
||||||
|
InsertChain(PopRCX, Address);
|
||||||
|
InsertChain(ReadRCX_RAX, 0, false);
|
||||||
|
InsertChain(PopRCX, RemoteSharedMemory + SHARED_MEM_STACK_PLACEHOLDER_OFFSET);
|
||||||
|
InsertChain(WriteRCX_RAX, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FixupControlFlow();
|
||||||
|
Hijack();
|
||||||
|
|
||||||
|
return *(volatile uint64_t*)(LocalSharedMemory + SHARED_MEM_STACK_PLACEHOLDER_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteU64(uint64_t Address, uint64_t Value)
|
||||||
|
{
|
||||||
|
ResetCursor();
|
||||||
|
|
||||||
|
{
|
||||||
|
InsertChain(PopRAX, Value);
|
||||||
|
InsertChain(PopRCX, Address);
|
||||||
|
InsertChain(WriteRCX_RAX, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FixupControlFlow();
|
||||||
|
Hijack();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool CloneAndReplaceVMT(uint64_t vtbl, size_t size) {
|
||||||
|
|
||||||
|
//Because of stack size issue and to be on the safe space, we don't allow Shadowing VMT bigger than 280 bytes.
|
||||||
|
if (size > 0x3f0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto orig_vtbl = ReadU64(vtbl);
|
||||||
|
if (!orig_vtbl)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (orig_vtbl != RemoteSharedMemory + SHARED_MEM_SHADOW_VMT)
|
||||||
|
{
|
||||||
|
ResetCursor();
|
||||||
|
bool leftover = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i += 8) {
|
||||||
|
|
||||||
|
InsertChain(PopRCX, orig_vtbl + i);
|
||||||
|
InsertChain(ReadRCX_RAX, PopRCX);
|
||||||
|
InsertChain(RemoteSharedMemory + SHARED_MEM_SHADOW_VMT + i, WriteRCX_RAX);
|
||||||
|
leftover = true;
|
||||||
|
if (i && (i % 0x140 == 0)) {
|
||||||
|
FixupControlFlow();
|
||||||
|
Hijack();
|
||||||
|
Sleep(1);
|
||||||
|
ResetCursor();
|
||||||
|
leftover = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftover) {
|
||||||
|
FixupControlFlow();
|
||||||
|
Hijack();
|
||||||
|
}
|
||||||
|
WriteU64(ProcessData->base + 0x16F1230, RemoteSharedMemory + SHARED_MEM_SHADOW_VMT);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Every write 8 bytes uses 40 bytes of stack and we have 2000 bytes of stack to work with
|
||||||
|
// So we can at most write 400 bytes at once
|
||||||
|
void WriteData(uint64_t Address, BYTE* Data, size_t size)
|
||||||
|
{
|
||||||
|
if (size >= 400)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ResetCursor();
|
||||||
|
|
||||||
|
for(int i = 0;i < size; i+= 8)
|
||||||
|
{
|
||||||
|
InsertChain(PopRAX, *(uint64_t*)(Data+i));
|
||||||
|
InsertChain(PopRCX, Address+i);
|
||||||
|
InsertChain(WriteRCX_RAX, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
FixupControlFlow();
|
||||||
|
Hijack();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Initialize(CProcess* Proc)
|
||||||
|
{
|
||||||
|
ProcessData = Proc;
|
||||||
|
|
||||||
|
winmm = LoadLibraryA("winmmbase.dll");
|
||||||
|
mmio_rename = GetProcAddress(winmm, "mmioRenameW");
|
||||||
|
|
||||||
|
// Load shared mem.
|
||||||
|
{
|
||||||
|
char Path[MAX_PATH];
|
||||||
|
SHGetFolderPathA(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locate shared memory in our client.
|
||||||
|
if (!FindSharedMemory(HANDLE(-1), &LocalSharedMemory, &SharedCount))
|
||||||
|
{
|
||||||
|
printf("Failed to find shared memory in current process\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locate shared memory in target process.
|
||||||
|
{
|
||||||
|
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, ProcessData->ProcessID);
|
||||||
|
if (hProcess == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
printf("Failed to open process[%d] error[%d]\n", ProcessData->ProcessID, GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FindSharedMemory(hProcess, &RemoteSharedMemory, &SharedCount))
|
||||||
|
{
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
printf("Failed to find shared memory in target process\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
PROCESS_BASIC_INFORMATION pbi{};
|
||||||
|
NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), 0);
|
||||||
|
ProcessData->peb = (uintptr_t)pbi.PebBaseAddress;
|
||||||
|
CloseHandle(hProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find gadgets.
|
||||||
|
{
|
||||||
|
bool AllGadgetsFound = false;
|
||||||
|
uint16_t LoadBitMap = 0;
|
||||||
|
for (int i = 0; i < ARRAYSIZE(ModulesToCheck); i++)
|
||||||
|
{
|
||||||
|
const char* ModuleName = ModulesToCheck[i];
|
||||||
|
SET_BIT(LoadBitMap, 0, FindGadget(&PopRCX, ModuleName, "\x59\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 1, FindGadget(&PopRDX, ModuleName, "\x5A\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 2, FindGadget(&PopRAX, ModuleName, "\x58\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 3, FindGadget(&PopRSP, ModuleName, "\x5C\xC3"));
|
||||||
|
|
||||||
|
SET_BIT(LoadBitMap, 4, FindGadget(&WriteRCX_RDX, ModuleName, "\x48\x89\x11\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 5, FindGadget(&ReadRCX_RAX, ModuleName, "\x48\x8B\x01\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 6, FindGadget(&WriteRDX_RCX, ModuleName, "\x48\x89\x0A\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 7, FindGadget(&WriteRCX_RAX, ModuleName, "\x48\x89\x01\xC3"));
|
||||||
|
|
||||||
|
SET_BIT(LoadBitMap, 8, FindGadget(&AddRSP_58, ModuleName, "\x48\x83\xC4\x58\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 9, FindGadget(&AddRCX_RBP, ModuleName, "\x48\x01\xE9\xC3"));
|
||||||
|
if (!AddRCX_RBP)
|
||||||
|
{
|
||||||
|
SET_BIT(LoadBitMap, 9, FindGadget(&AddRCX_RBP, ModuleName, "\x48\x01\xE8\xC3"));
|
||||||
|
if (AddRCX_RBP)
|
||||||
|
LegacyWin10 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SET_BIT(LoadBitMap, 10, FindGadget(&PushRAX_PopRSP, ModuleName, "\x50\x5C\xC3"));
|
||||||
|
SET_BIT(LoadBitMap, 11, FindGadget(&MovRax_RCX, ModuleName, "\x51\x58\xC3"));
|
||||||
|
|
||||||
|
if (LoadBitMap == 0xFFF)
|
||||||
|
{
|
||||||
|
AllGadgetsFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AllGadgetsFound)
|
||||||
|
{
|
||||||
|
printf("Failed to find some gadgets\n");
|
||||||
|
__debugbreak();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (ProcessData->peb)
|
||||||
|
ProcessData->base = ReadU64(ProcessData->peb + 0x10);
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define READ 1
|
||||||
|
#define WRITE 2
|
||||||
|
|
||||||
|
|
||||||
|
class CIpc {
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct command {
|
||||||
|
uint64_t lock{}; // 0
|
||||||
|
uint64_t operation{}; // 8
|
||||||
|
uint64_t val1{}; // 0x10 - READ : addr to read from | WRITE : value to write
|
||||||
|
uint64_t val2{}; // 0x18 - READ : where the read value goes | WRITE : address to write to
|
||||||
|
};
|
||||||
|
|
||||||
|
inline static command* cmd{};
|
||||||
|
|
||||||
|
static bool Setup(CProcess& proc, CExploit& exploit) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
auto installed = exploit.CloneAndReplaceVMT(proc.base + 0x16F1230, 0x1B8);
|
||||||
|
|
||||||
|
if (installed) {
|
||||||
|
|
||||||
|
uintptr_t dh64_present_tramp = exploit.ReadU64(proc.discord_base + 0xE9090);
|
||||||
|
|
||||||
|
if (!dh64_present_tramp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uintptr_t dh64_rwx = dh64_present_tramp & 0xFFFFFFFFFFFFF000;
|
||||||
|
|
||||||
|
BYTE ipc_shellcode[] = { 0x49, 0xBA, 0xAF, 0xFF, 0x7A, 0x1F, 0xBF, 0x01, 0x00, 0x00, 0xFF, 0xB0, 0xD8, 0x00, 0x00, 0x00, 0x41, 0x80, 0x3A, 0x00, 0x74, 0x2B, 0x41, 0x8A, 0x42, 0x08, 0x3C, 0x01, 0x75, 0x0D, 0x49, 0x8B, 0x42, 0x10, 0x48, 0x8B, 0x00, 0x49, 0x89, 0x42, 0x18, 0xEB, 0x0F, 0x3C, 0x02, 0x75, 0xE1, 0x49, 0x8B, 0x42, 0x10, 0x4D, 0x8B, 0x5A, 0x18, 0x49, 0x89, 0x03, 0x41, 0xC6, 0x42, 0x08, 0x00, 0xEB, 0xCF, 0xC3 };
|
||||||
|
|
||||||
|
uint64_t command_buffer = exploit.RemoteSharedMemory + SHARED_MEM_COMMAND_BUFFER;
|
||||||
|
|
||||||
|
|
||||||
|
cmd = (command*)(exploit.LocalSharedMemory + SHARED_MEM_COMMAND_BUFFER);
|
||||||
|
cmd->lock = 0;
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->val1 = 0;
|
||||||
|
cmd->val2 = 0;
|
||||||
|
memcpy(ipc_shellcode + 2, &command_buffer, sizeof(uint64_t));
|
||||||
|
exploit.WriteData(dh64_rwx + 0x12, (BYTE*)ipc_shellcode, sizeof(ipc_shellcode));
|
||||||
|
*(uint64_t*)(exploit.LocalSharedMemory + SHARED_MEM_SHADOW_VMT + (27 * 8)) = *(uint64_t*)(exploit.LocalSharedMemory + SHARED_MEM_SHADOW_VMT + (25 * 8));
|
||||||
|
*(uint64_t*)(exploit.LocalSharedMemory + SHARED_MEM_SHADOW_VMT + (26 * 8)) = dh64_rwx + 0x12;
|
||||||
|
*(uint64_t*)(exploit.LocalSharedMemory + SHARED_MEM_SHADOW_VMT + (25 * 8)) = proc.base + 0x2feaab;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmd = (command*)(exploit.LocalSharedMemory + SHARED_MEM_COMMAND_BUFFER);
|
||||||
|
cmd->lock = 0;
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->val1 = 0;
|
||||||
|
cmd->val2 = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
uintptr_t dh64_present_tramp = exploit.ReadU64(proc.discord_base + 0xE9090);
|
||||||
|
|
||||||
|
if (!dh64_present_tramp)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uintptr_t dh64_rwx = dh64_present_tramp & 0xFFFFFFFFFFFFF000;
|
||||||
|
|
||||||
|
BYTE ipc_shellcode[] = {
|
||||||
|
0x49, 0xBA, 0xAF, 0xFF, 0x7A, 0x1F, 0xBF, 0x01,
|
||||||
|
0x00, 0x00, 0x49, 0x83, 0x3A, 0x00, 0x74, 0x36,
|
||||||
|
0x49, 0x8B, 0x42, 0x08, 0x48, 0x83, 0xF8, 0x01,
|
||||||
|
0x75, 0x0D, 0x49, 0x8B, 0x42, 0x10, 0x48, 0x8B,
|
||||||
|
0x00, 0x49, 0x89, 0x42, 0x18, 0xEB, 0x15, 0x49,
|
||||||
|
0x8B, 0x42, 0x08, 0x48, 0x83, 0xF8, 0x02, 0x75,
|
||||||
|
0xD9, 0x49, 0x8B, 0x42, 0x10, 0x4D, 0x8B, 0x5A,
|
||||||
|
0x18, 0x49, 0x89, 0x03, 0x49, 0xC7, 0x42, 0x08,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0xEB, 0xC4, 0xE9, 0x00,
|
||||||
|
0x00, 0x00, 0x00 };
|
||||||
|
|
||||||
|
uint64_t command_buffer = exploit.RemoteSharedMemory + SHARED_MEM_COMMAND_BUFFER;
|
||||||
|
uint32_t distance_to_present_tramp = dh64_present_tramp - (dh64_rwx + 0x12 + sizeof(ipc_shellcode));
|
||||||
|
|
||||||
|
|
||||||
|
cmd = (command*)(exploit.LocalSharedMemory + SHARED_MEM_COMMAND_BUFFER);
|
||||||
|
cmd->lock = 0;
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->val1 = 0;
|
||||||
|
cmd->val2 = 0;
|
||||||
|
|
||||||
|
if (distance_to_present_tramp < 0x1000) { // If we relaunch, no need to overwrite it or it would crash because distance is going to be negative.
|
||||||
|
memcpy(ipc_shellcode + 2, &command_buffer, sizeof(uint64_t));
|
||||||
|
memcpy(ipc_shellcode + sizeof(ipc_shellcode) - 4, &distance_to_present_tramp, sizeof(uint32_t));
|
||||||
|
Sleep(10);
|
||||||
|
exploit.WriteData(dh64_rwx + 0x12, (BYTE*)ipc_shellcode, sizeof(ipc_shellcode));
|
||||||
|
Sleep(10);
|
||||||
|
exploit.WriteU64(proc.discord_base + 0xE9090, dh64_rwx + 0x12);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__forceinline static void LockFrame() {
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->lock = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
__forceinline static void UnlockFrame() {
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->lock = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t Read64(uintptr_t ptr) {
|
||||||
|
cmd->val2 = 0;
|
||||||
|
cmd->val1 = ptr;
|
||||||
|
cmd->operation = READ;
|
||||||
|
while (CIpc::cmd->operation == READ)
|
||||||
|
_mm_pause();
|
||||||
|
return cmd->val2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Write64(uintptr_t ptr, uint64_t val) {
|
||||||
|
cmd->val2 = ptr;
|
||||||
|
cmd->val1 = val;
|
||||||
|
cmd->operation = WRITE;
|
||||||
|
while (CIpc::cmd->operation == WRITE)
|
||||||
|
_mm_pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T Read(uintptr_t ptr) {
|
||||||
|
T ret{};
|
||||||
|
int size_of_T = sizeof(T);
|
||||||
|
|
||||||
|
|
||||||
|
int full_chunks = size_of_T / 8;
|
||||||
|
int remaining_bytes = size_of_T % 8;
|
||||||
|
|
||||||
|
for (int i = 0; i < full_chunks; i++) {
|
||||||
|
auto tmp = Read64(ptr + (i * 8));
|
||||||
|
memcpy(((char*)&ret) + (i * 8), &tmp, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remaining_bytes > 0 || full_chunks == 0) {
|
||||||
|
auto tmp = Read64(ptr + (full_chunks * 8));
|
||||||
|
memcpy(((char*)&ret) + (full_chunks * 8), &tmp, remaining_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Cleanup() {
|
||||||
|
if (cmd) {
|
||||||
|
UnlockFrame();
|
||||||
|
cmd->operation = 0;
|
||||||
|
cmd->val1 = 0;
|
||||||
|
cmd->val2 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
namespace DiscOverlay {
|
||||||
|
typedef struct _Header
|
||||||
|
{
|
||||||
|
UINT Magic; //0
|
||||||
|
uint64_t FrameCount; //4
|
||||||
|
UINT Width; //12
|
||||||
|
UINT Height; //16
|
||||||
|
} Header;
|
||||||
|
};
|
||||||
|
#pragma pack(pop,1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BOOL WINAPI ConsoleHandler(DWORD signal) {
|
||||||
|
if (signal == CTRL_C_EVENT || signal == CTRL_CLOSE_EVENT)
|
||||||
|
CIpc::Cleanup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onExit() {
|
||||||
|
CIpc::Cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
1FA 4000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include "C:\Users\Generic\python\font_glyphs.h"
|
||||||
|
|
||||||
|
class FrameBuffer {
|
||||||
|
private:
|
||||||
|
size_t width, height;
|
||||||
|
std::vector<uint32_t> buffer; // Buffer now uses uint32_t for each pixel
|
||||||
|
std::unordered_set<size_t> dirtyFlags;
|
||||||
|
std::unordered_set<size_t> previous_dirtyFlags;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FrameBuffer(size_t w, size_t h) : width(w), height(h) {
|
||||||
|
size_t totalPixels = w * h;
|
||||||
|
buffer.resize(totalPixels, 0);
|
||||||
|
size_t chunkCount = (totalPixels * sizeof(uint32_t) + 7) / 8;
|
||||||
|
dirtyFlags.reserve(chunkCount);
|
||||||
|
previous_dirtyFlags.reserve(chunkCount);
|
||||||
|
clearBuffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearBuffers() {
|
||||||
|
std::fill(buffer.begin(), buffer.end(), 0);
|
||||||
|
dirtyFlags.clear();
|
||||||
|
previous_dirtyFlags.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetPixel(size_t x, size_t y, uint32_t color) {
|
||||||
|
if (x >= width)
|
||||||
|
return;
|
||||||
|
if (y >= height)
|
||||||
|
return;
|
||||||
|
size_t index = y * width + x;
|
||||||
|
size_t chunkIndex = (index * sizeof(uint32_t)) / 8;
|
||||||
|
buffer[index] = color;
|
||||||
|
dirtyFlags.insert(chunkIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDirtyFlags() {
|
||||||
|
for (auto &i : previous_dirtyFlags)
|
||||||
|
dirtyFlags.insert(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flush(CProcess& proc) {
|
||||||
|
uintptr_t baseAddress = proc.discord_framebuffer + 20;
|
||||||
|
|
||||||
|
UpdateDirtyFlags();
|
||||||
|
previous_dirtyFlags.clear();
|
||||||
|
|
||||||
|
uint64_t* currentBuffer = reinterpret_cast<uint64_t*>(buffer.data());
|
||||||
|
int b = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (auto &i : dirtyFlags) {
|
||||||
|
size_t byteOffset = i * 8;
|
||||||
|
CIpc::Write64(baseAddress + byteOffset, currentBuffer[i]);
|
||||||
|
if (currentBuffer[i])
|
||||||
|
previous_dirtyFlags.insert(i);
|
||||||
|
currentBuffer[i] = 0;
|
||||||
|
b++;
|
||||||
|
}
|
||||||
|
dirtyFlags.clear();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool resize(size_t newWidth, size_t newHeight) {
|
||||||
|
if (width != newWidth || height != newHeight) {
|
||||||
|
width = newWidth;
|
||||||
|
height = newHeight;
|
||||||
|
size_t totalPixels = newWidth * newHeight;
|
||||||
|
buffer.resize(totalPixels);
|
||||||
|
size_t chunkCount = (totalPixels * sizeof(uint32_t) + 7) / 8;
|
||||||
|
dirtyFlags.reserve(chunkCount);
|
||||||
|
previous_dirtyFlags.reserve(chunkCount);
|
||||||
|
clearBuffers();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline void DrawTexts(UINT x, UINT y, const char* text, uint32_t color, int drawOutline = 1) {
|
||||||
|
UINT startX = x;
|
||||||
|
int baselineY = y; // Set to where you want the text baseline
|
||||||
|
|
||||||
|
for (size_t i = 0; text[i] != '\0'; i++) {
|
||||||
|
char c = text[i];
|
||||||
|
if (c < 32 || c > 126) continue;
|
||||||
|
|
||||||
|
const Glyph* glyph = &glyphs[c - 32];
|
||||||
|
const unsigned char* bitmap = glyph->data;
|
||||||
|
|
||||||
|
int glyphX = startX + glyph->offset_x;
|
||||||
|
int glyphY = baselineY + glyph->offset_y; // Adjust to place baseline correctly
|
||||||
|
|
||||||
|
if (drawOutline) {
|
||||||
|
uint32_t outlineColor = 0x000000; // Assuming black color for the outline
|
||||||
|
// Draw outline by setting pixels around each character pixel
|
||||||
|
for (int row = 0; row < glyph->height; row++) {
|
||||||
|
for (int col = 0; col < glyph->width; col++) {
|
||||||
|
int bit = (bitmap[row * ((glyph->width + 7) / 8) + col / 8] >> (7 - (col % 8))) & 1;
|
||||||
|
if (bit) {
|
||||||
|
for (int dy = -1; dy <= 1; dy++) {
|
||||||
|
for (int dx = -1; dx <= 1; dx++) {
|
||||||
|
if (dx != 0 || dy != 0) { // Avoid the center pixel
|
||||||
|
SetPixel(glyphX + col + dx, glyphY + row + dy, outlineColor + 0xFF000000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the actual character
|
||||||
|
for (int row = 0; row < glyph->height; row++) {
|
||||||
|
for (int col = 0; col < glyph->width; col++) {
|
||||||
|
int bit = (bitmap[row * ((glyph->width + 7) / 8) + col / 8] >> (7 - (col % 8))) & 1;
|
||||||
|
if (bit) {
|
||||||
|
SetPixel(glyphX + col, glyphY + row, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startX += glyph->width; // Advance to the start of the next character
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
inline void DrawLine(UINT x1, UINT y1, UINT x2, UINT y2, uint32_t color)
|
||||||
|
{
|
||||||
|
int dx = abs(static_cast<int>(x2 - x1)), sx = x1 < x2 ? 1 : -1;
|
||||||
|
int dy = -abs(static_cast<int>(y2 - y1)), sy = y1 < y2 ? 1 : -1;
|
||||||
|
int err = dx + dy, e2;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
SetPixel(x1, y1, color);
|
||||||
|
if (x1 == x2 && y1 == y2)
|
||||||
|
break;
|
||||||
|
e2 = 2 * err;
|
||||||
|
if (e2 >= dy) { err += dy; x1 += sx; }
|
||||||
|
if (e2 <= dx) { err += dx; y1 += sy; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DrawRectangle(UINT x1, UINT y1, UINT width, UINT height, uint32_t color)
|
||||||
|
{
|
||||||
|
for (UINT x = x1; x < x1 + width; x++)
|
||||||
|
{
|
||||||
|
SetPixel(x, y1, color);
|
||||||
|
SetPixel(x, y1 + height - 1, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (UINT y = y1; y < y1 + height; y++)
|
||||||
|
{
|
||||||
|
SetPixel(x1, y, color);
|
||||||
|
SetPixel(x1 + width - 1, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void DrawCircle(UINT x0, UINT y0, UINT radius, uint32_t color)
|
||||||
|
{
|
||||||
|
int x = radius;
|
||||||
|
int y = 0;
|
||||||
|
int radiusError = 1 - x;
|
||||||
|
|
||||||
|
while (x >= y)
|
||||||
|
{
|
||||||
|
SetPixel(x0 + x, y0 + y, color);
|
||||||
|
SetPixel(x0 - x, y0 + y, color);
|
||||||
|
SetPixel(x0 - x, y0 - y, color);
|
||||||
|
SetPixel(x0 + x, y0 - y, color);
|
||||||
|
SetPixel(x0 + y, y0 + x, color);
|
||||||
|
SetPixel(x0 - y, y0 + x, color);
|
||||||
|
SetPixel(x0 - y, y0 - x, color);
|
||||||
|
SetPixel(x0 + y, y0 - x, color);
|
||||||
|
|
||||||
|
y++;
|
||||||
|
if (radiusError < 0)
|
||||||
|
{
|
||||||
|
radiusError += 2 * y + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x--;
|
||||||
|
radiusError += 2 * (y - x + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Offset {
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma comment(lib, "winmm.lib")
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Init
|
||||||
|
{
|
||||||
|
SetConsoleCtrlHandler(ConsoleHandler, TRUE);
|
||||||
|
atexit(onExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
CProcess ApexProcess{};
|
||||||
|
CExploit Exploit{};
|
||||||
|
|
||||||
|
if (!ApexProcess.Initialize(L"Apex Legends", L"Respawn001"))
|
||||||
|
|
||||||
|
{
|
||||||
|
printf("Apex is not running\n");
|
||||||
|
// Sleep(5000);
|
||||||
|
// return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (!Exploit.Initialize(&ApexProcess))
|
||||||
|
{
|
||||||
|
printf("Failed to load exploit\n");
|
||||||
|
Sleep(5000);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ApexProcess.discord_base) {
|
||||||
|
printf("Discord overlay not enabled\n");
|
||||||
|
Sleep(5000);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ApexProcess.discord_framebuffer) {
|
||||||
|
printf("ESP can't be enabled, no discord overlay found\n");
|
||||||
|
Sleep(5000);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CIpc::Setup(ApexProcess, Exploit)) {
|
||||||
|
printf("Failed IPC setup\n");
|
||||||
|
Sleep(5000);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Sleep(10);
|
||||||
|
printf("starting...\n");
|
||||||
|
|
||||||
|
FrameBuffer* fb = new FrameBuffer(1920, 1080);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
LARGE_INTEGER frequency;
|
||||||
|
LARGE_INTEGER start;
|
||||||
|
LARGE_INTEGER end;
|
||||||
|
|
||||||
|
QueryPerformanceFrequency(&frequency);
|
||||||
|
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/*
|
||||||
|
QueryPerformanceFrequency(&frequency);
|
||||||
|
QueryPerformanceCounter(&start);
|
||||||
|
*/
|
||||||
|
QueryPerformanceCounter(&start);
|
||||||
|
|
||||||
|
CIpc::LockFrame();
|
||||||
|
|
||||||
|
auto overlay_header = CIpc::Read<DiscOverlay::Header>(ApexProcess.discord_framebuffer);
|
||||||
|
overlay_header.FrameCount++;
|
||||||
|
CIpc::Write64(ApexProcess.discord_framebuffer + 4, overlay_header.FrameCount);
|
||||||
|
|
||||||
|
|
||||||
|
if (fb->resize(overlay_header.Width, overlay_header.Height))
|
||||||
|
fb->Flush(ApexProcess);
|
||||||
|
else {
|
||||||
|
fb->DrawTexts(400, 400, "Just drawing with 0 handle or detection vector", 0xFFFFFFFF);
|
||||||
|
fb->DrawCircle(500, 500, 5, 0xFFFFFFFF);
|
||||||
|
fb->Flush(ApexProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CIpc::UnlockFrame();
|
||||||
|
|
||||||
|
|
||||||
|
QueryPerformanceCounter(&end);
|
||||||
|
double duration = (end.QuadPart - start.QuadPart) * 1.0e3 / frequency.QuadPart;
|
||||||
|
|
||||||
|
|
||||||
|
// Print the duration in milliseconds with four-digit accuracy using printf
|
||||||
|
printf("Time taken: %.4f ms\n", duration);
|
||||||
|
Sleep(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user