Как получить доступ к данным HBITMAP и восстановить их из отлаженного процессаC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Как получить доступ к данным HBITMAP и восстановить их из отлаженного процесса

Сообщение Anonymous »

Я разрабатываю расширение Visual Studio 2022 для извлечения объектов HBITMAP из памяти отлаженного процесса для анализа.
В идеале я хочу получить доступ к необработанным растровым данным и восстановить их напрямую, без необходимости конвертировать каждый формат HBITMAP в формат PNG.
Что я пробовал:
Дублирование дескриптора HBITMAP с помощью DuplicationHandle — но происходит сбой с ошибкой:

Дескриптор неверно.

Чтение данных HBITMAP с помощью ReadProcessMemory, но происходит сбой:
< blockquote>
Выполнена только часть запроса ReadProcessMemory или WriteProcessMemory.

Преобразование HBITMAP в PNG (работает, но дорого).
Мне удалось преобразовать HBITMAP в байтовый массив PNG и прочитать его в своем расширении, но это требует дополнительных затрат для каждого преобразования растрового изображения, чего я хочу избежать.
Вопрос:
Есть ли альтернативные подходы, которые я мог бы попробовать читать и восстанавливать HBITMAP напрямую из памяти отлаживаемого процесса?
Или существует более эффективный способ обработки и передачи данных HBITMAP между процессами, которые могли бы избежать преобразования в PNG?
-- для воспроизведения --
Источник расширения Visual Studio:
using EnvDTE;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using Task = System.Threading.Tasks.Task;
using System.ComponentModel;

namespace VSIX;

[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
[Guid(VSIXPackage.PackageGuidString)]
[ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)]
public sealed class VSIXPackage : AsyncPackage
{
public const string PackageGuidString = "f9a8aea3-f579-4816-9cb5-4ae3a5d68ef7";
private DebugWatcher _debugWatcher;

protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress)
{
await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
_debugWatcher = new DebugWatcher();
}
}

public class DebugWatcher
{
private DTE _dte;
private DebuggerEvents _debuggerEvents;

public DebugWatcher()
{
_ = InitializeAsync();
}

private async Task InitializeAsync()
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
_dte = await ServiceProvider.GetGlobalServiceAsync(typeof(DTE)) as DTE;
if (_dte == null)
return;

_debuggerEvents = _dte.Events.DebuggerEvents;
_debuggerEvents.OnEnterBreakMode += OnEnterBreakMode;
}

[DllImport("kernel32.dll")]
private static extern uint GetLastError();

[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

[DllImport("gdi32.dll")]
private static extern bool DeleteObject(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle, uint dwDesiredAccess, bool bInheritHandle, uint dwOptions);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);

private void OnEnterBreakMode(dbgEventReason Reason, ref dbgExecutionAction ExecutionAction)
{
ThreadHelper.ThrowIfNotOnUIThread();
int debuggedProcessId = _dte.Debugger.CurrentProcess.ProcessID;

const uint PROCESS_DUP_HANDLE = 0x0040;
const uint PROCESS_VM_READ = 0x0010;
const uint PROCESS_QUERY_INFORMATION = 0x0400;

var sourceProcessHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, debuggedProcessId);
var targetProcess = System.Diagnostics.Process.GetCurrentProcess();
var targetProcessHandle = targetProcess.Handle;

if (sourceProcessHandle == null || targetProcessHandle == null)
return;

try
{
/*

--- Reading from HBITMAP ---- prints only: Name: unused, Type : , Value :
}

string address = hbm.Value.Replace("0x", "");
if (!long.TryParse(hbm.Value.Replace("0x", ""), System.Globalization.NumberStyles.HexNumber, null, out long hbmLong))
return;

IntPtr bitmapHandle = new IntPtr(hbmLong);
const int BITMAPINFOHEADER_SIZE = 40;
byte[] headerBuffer = new byte[BITMAPINFOHEADER_SIZE];

if (!ReadProcessMemory(sourceProcessHandle, bitmapHandle, headerBuffer, BITMAPINFOHEADER_SIZE, out var bytesReadHeader))
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: Only part of a ReadProcessMemory or WriteProcessMemory request was completed
}

const uint DUPLICATE_SAME_ACCESS = 0x00000002;
bool success = DuplicateHandle(
sourceProcessHandle, // Source process (debugged process)
bitmapHandle, // The bitmap handle we want to duplicate
targetProcessHandle, // Target process (our VS extension)
out IntPtr duplicatedBitmapHandle, // Where the new handle will be stored
0, // Access (0 because we're using DUPLICATE_SAME_ACCESS)
false, // Don't inherit handle
DUPLICATE_SAME_ACCESS // Copy same access rights

);

if (!success)
{
uint error = GetLastError();
Debug.WriteLine($"Error: {new Win32Exception((int)error).Message}"); // Error: The handle is invalid
}

/*

--- Reading from std::vector ---- cleanups omitted for brevity
std::vector hBitmapToPngArray(HBITMAP hBitmap, const wchar_t* pngPath)
{
std::vector pngData;
IStream* pStream = nullptr;
if (FAILED(CreateStreamOnHGlobal(NULL, TRUE, &pStream)))
return pngData;

std::unique_ptr bitmap(new Gdiplus::Bitmap(hBitmap, NULL));
if (!bitmap || bitmap->GetLastStatus() != Gdiplus::Ok)
return pngData;

CLSID pngClsid;
UINT num = 0;
UINT size = 0;
GetImageEncodersSize(&num, &size);
if (size == 0)
return pngData;

std::vector buffer(size);
Gdiplus::ImageCodecInfo* pImageCodecInfo = (Gdiplus::ImageCodecInfo*)buffer.data();
GetImageEncoders(num, size, pImageCodecInfo);

for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, L"image/png") == 0)
{
pngClsid = pImageCodecInfo[j].Clsid;
break;
}
}

Gdiplus::Status status = bitmap->Save(pStream, &pngClsid, NULL);
if (status != Gdiplus::Ok)
return pngData;

STATSTG statstg = { 0 };
if (FAILED(pStream->Stat(&statstg, STATFLAG_DEFAULT)))
return pngData;

LARGE_INTEGER seekPos = { 0 };
pStream->Seek(seekPos, STREAM_SEEK_SET, NULL);
pngData.resize(statstg.cbSize.LowPart);
ULONG bytesRead;

bitmap->Save(pngPath, &pngClsid, NULL);
pStream->Release();

return pngData;
}

int main()
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

HWND hwnd = FindWindow(NULL, L"Untitled - Notepad");
if (!hwnd)
{
MessageBox(NULL, L"Window not found!", L"Error", MB_ICONERROR);
return 0;
}

RECT rc;
GetWindowRect(hwnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;

HDC hdcWindow = GetDC(hwnd);
HDC hdcMemDC = CreateCompatibleDC(hdcWindow);
HBITMAP hbm = CreateCompatibleBitmap(hdcWindow, width, height);
SelectObject(hdcMemDC, hbm);

if (!PrintWindow(hwnd, hdcMemDC, PW_RENDERFULLCONTENT))
{
MessageBox(NULL, L"PrintWindow failed!", L"Error", MB_ICONERROR);
return 0;
}

std::vector pngData = hBitmapToPngArray(hbm, L"C:\\Users\\Cesar\\Downloads\\process.png");
while (true)
{
Sleep(100);
}

return 0;
}


Подробнее здесь: https://stackoverflow.com/questions/791 ... ed-process
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»