Что нового

[Процессы] Сканирование памяти или Как прочитать текст в Label

nicki2004

Новичок
Сообщения
13
Репутация
1
Задача: есть форма программы на Delphi. На ней есть несколько Label в которых находится текст, который постоянно меняется и его нужно получить.

Как известно в Delphi есть 2 схожих объекта это Label и Static. Найденые мной отличия: Label может иметь прозрачный фон, но нет Handle. У Static все наоборот. У Label текст получить сложно, но можно. У Static текст получить легко.

Поковыряв память процесса с помощью ArtMoney нашел законерность. Как получить строку:
1. Находим строку в которой записано имя нужного нам Label.
2. По адресу этой строки находим ячейку памяти с таким же значением. Это будет указатель на эту строку и этот адрес будет базовым для этого Label. Назовем его Label1, для простоты.
3. Таким же методом находим смещения относительно базы для собственно текста этой лайбл. Смещение для Caption(нужный нам текст) +0x5C, Hint(подсказка при наведении мыши) +0x78.

Теперь осталось все это перевести на AutoIt. Было бы здорово если адреса не менялись, то проблем бы небыло. Но загрузив программу в разное врмемя и на разных компах, адрес где хранится объект, меняется :( Т.е. нужно организовать поиск в пямити процесса. Вот тут засада. Получаю кусок памяти, посмотреть его могу визуально но обратится к нему не могу :mad: Вот кусок кода:
Код:
$hW = _WinAPI_FindWindow("TForm1", "Form1")
If $hW Then 
	$PId=0
	_WinAPI_GetWindowThreadProcessId($hW, $PId)
	If $PId Then 

		$DllInformation = _MemoryOpen($PId)
		
		$lpAddress=0x00AD0000 ;намеренно укоротил опласть поиска для быстроты
		
		DllCall($DllInformation[0], 'int', 'VirtualQueryEx', 'int', $DllInformation[1], 'ptr', $lpAddress, 'ptr', DllStructGetPtr($stMBI), 'int', DllStructGetSize($stMBI))
				if (  (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE)  Or (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE) )  Then
					;Если область памяти доступна для записи, работаем с ним.

					$strTypeBuffer = "byte[" & DllStructGetData( $stMBI, "RegionSize") & "]"
					;$stTemp=DllStructCreate($strTypeBuffer)
					;MsgBox(0,"", $strTypeBuffer)

					$stTemp = _MemoryRead($lpAddress,$DllInformation, $strTypeBuffer)
					MsgBox(0,"", $stTemp)
					$stTemp = 0
				EndIf
		;WEnd
	EndIf
EndIf


ни как немогу обратится к одной ячейки памяти из $stTemp, чтобы перебором найти там Label1.
 

Belfigor

Модератор
Локальный модератор
Сообщения
3 591
Репутация
938
Так и не понял ты роешься в DMA приложении или нет? Если да то где у тебя в коде обращение по офсетам относительно от базового адреса?
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
Чето не понял, а зачем относительно базого адреса? Я знаю точно (посмотрел в через ArtMoney) вэтом куске памяти процесса есть строка Label1 мненужно найти ее адрес. Для этого скпировал кусок памяти и теперь нужно ее найти.

непонятно только почему
Код:
MsgBox(0,"", "DllStructGetData($stTemp, 1, 1)="&DllStructGetData($stTemp, 1, 3))


выдает "DllStructGetData($stTemp, 1, 1)=0" когда

Код:
MsgBox(0,"", $stTemp)


Выдает полную картину всего куска памяти где видно что на третьем месте стоит значение "AD".

На данном этапе у меня проблема с элементарными вещами :-[ Как прочитать побайтово буфер из нескольких килобайт
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
А ControlGetText() не работает?
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
Yashied сказал(а):
А ControlGetText() не работает?
я в первом сообщение вроде попытался объяснить. Повторюсь. В Delphi Label не имеет хендл окна и ControlGetText() к ней не применить. Поэтому такие танцы с бубном вокруг памяти.

т.е. мне нужно считать кусок памяти, и найти там позицию в которой начинается Label1
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
Спасибо за помощь, сам разобрался :whistle:
Код:
#include <WinAPI.au3>
#include <Memory.au3>
#include <NomadMemory.au3>
#include <APIConstants.au3>
#include <Array.au3>

Global $iBytesRead
Global Const $PAGE_WRITECOPY = 0x0008

$stMBI=DllStructCreate("Ptr BaseAddress; Ptr AllocationBase;  DWORD AllocationProtect; DWORD RegionSize; DWORD State; DWORD Protect;  DWORD Type;")


;Получаем некие данные о системе, нас больше интерисуют данные об адресах
$stMSI=DllStructCreate("ushort wProcessorArchitecture; ushort wReserved; dword dwPageSize; ptr lpMinimumApplicationAddress; ptr lpMaximumApplicationAddress; dword_ptr dwActiveProcessorMask; dword dwNumberOfProcessors; dword dwProcessorType; dword dwAllocationGranularity; ushort wProcessorLevel; ushort wProcessorRevision")
DllCall("kernel32.dll", "none", "GetSystemInfo", "ptr", DllStructGetPtr($stMSI))



$strPoiskTexta = "Label"
$strTypeBuffer = "byte[" & StringLen($strPoiskTexta) & "]"
$stTemp=DllStructCreate($strTypeBuffer)

DllStructSetData($stTemp, 1, $strPoiskTexta)

Global $arrLabelName[1]
Global $arrLabelPtr[1]
Global $arrObjLabelPtr[1]

;$hW = _WinAPI_FindWindow("TForm1", "Form1")
If $hW Then 
	$PId=0
	_WinAPI_GetWindowThreadProcessId($hW, $PId)
	If $PId Then 

		$DllInformation = _MemoryOpen($PId)
		
		;$lpAddress=0x00AD3178
		$lpAddress=DllStructGetData( $stMSI, "lpMinimumApplicationAddress")
		
		;Ищем где у нас находятся лаблы
		While $lpAddress <= DllStructGetData( $stMSI, "lpMaximumApplicationAddress")
			DllCall($DllInformation[0], 'int', 'VirtualQueryEx', 'int', $DllInformation[1], 'ptr', $lpAddress, 'ptr', DllStructGetPtr($stMBI), 'int', DllStructGetSize($stMBI))
				if (  (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE)  Or (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE) )  Then
					;Если он доступен для записи, работаем с ним.
					$iRegionSize=DllStructGetData( $stMBI, "RegionSize")
					$strTypeBuffer = "byte[" & $iRegionSize & "]"
					$stTemp=DllStructCreate($strTypeBuffer)
					
					;$stTemp = _MemoryRead($lpAddress,$DllInformation, $strTypeBuffer)
					
					;читаем все область памяти
					DllCall($DllInformation[0], 'int', 'ReadProcessMemory', 'int', $DllInformation[1], 'int', $lpAddress, 'ptr', DllStructGetPtr($stTemp), 'int', DllStructGetSize($stTemp), 'int', '')
					
					
					For $i=0 To $iRegionSize-1
						$bFindStr=True
						$strFindString = ""

 						for $ii=1 To StringLen($strPoiskTexta)
							If StringMid($strPoiskTexta,$ii,1) <> Chr(DllStructGetData($stTemp, 1, $i+$ii)) Then
								$bFindStr = False
								ExitLoop
							Else
								$strFindString = $strFindString  & Chr(DllStructGetData($stTemp, 1, $i+$ii))
							EndIf
								
						Next
						
						;найдена строка Label теперь нужно определить ее концовку
						If $bFindStr Then
							
							$lpAddressTemp = $i + $ii ;+ 1
							While $lpAddressTemp <= $iRegionSize
								$iOneSymbol = DllStructGetData($stTemp, 1, $lpAddressTemp)
								If ( ($iOneSymbol>47) And ($iOneSymbol<58) ) Then
									$strFindString = $strFindString  & Chr($iOneSymbol)
								EndIf
								
								If  ($iOneSymbol=0 ) Then
									_ArrayAdd($arrLabelName, $strFindString)
									_ArrayAdd($arrLabelPtr, $lpAddress + $i)
									$lpAddressTemp = $iRegionSize + 1
								EndIf
								
								$lpAddressTemp = $lpAddressTemp + 1
							WEnd
						EndIf
							
					Next
					
					$stTemp = 0
					
					$lpAddress=$lpAddress + $iRegionSize
				Else
					$lpAddress=$lpAddress + $iRegionSize
				EndIf
		WEnd
	EndIf
EndIf

_ArrayDisplay($arrLabelName, "")
_ArrayDisplay($arrLabelPtr, "")


это еще не текст из Label, но остальное уже понятнее. Как допишу выложу окончательный вариант. Может кому пригодится. Правда код напильником еще дорабатывать т.к. работает медленно. Память в 8 мегабайт проходит гдето за 10 секунд. Для сравнения тот же ArtMoney одну метку Label ищет за 0,032 секунды!
 

r35p3ct

Продвинутый
Сообщения
228
Репутация
60
nicki2004
Когда я пытался тоже разобраться с получением текста из Delphi, то выяснилось, что элементы лажат на фиксированых адресах относильно формы, соотв. не нужно прогонять большой кусок памяти для поиска значения.
Для вычисления смещения используется софтинка: DeDe
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
r35p3ct сказал(а):
nicki2004
Когда я пытался тоже разобраться с получением текста из Delphi, то выяснилось, что элементы лажат на фиксированых адресах относильно формы, соотв. не нужно прогонять большой кусок памяти для поиска значения.
Для вычисления смещения используется софтинка: DeDe

эту тему читал. Программку видел и от туда и узнал какие Label существуют в приложении. Только вот не понял как вычислял смещение по которому лежит форма? по такому же методу как я или еще как? За подсказку про форму спасибо.
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
вот функция котораянаходит строку впамяти и возвращает адрес указателя на эту строку. Вообщем кому надо тот разберется, поэтому считаю тему раскрытой. Для моей задачи подходит.

Код:
Func FindPtrString($lpAddressStart, $lpAddressStop, $ah_Handle, $strStrokaPoiska)
		;структура вкоторой находятся данные о куске памяти
		Local $stMBI=DllStructCreate("Ptr BaseAddress; Ptr AllocationBase;  DWORD AllocationProtect; DWORD RegionSize; DWORD State; DWORD Protect;  DWORD Type;")

		$lpAddressCur=$lpAddressStart
		$ptrTemp=0
		
		;Ищем где у нас находятся текст
		While $lpAddressCur <= $lpAddressStop
				
				;заполняем структуру для определения параметров куска памяти
				DllCall($ah_Handle[0], 'int', 'VirtualQueryEx', 'int', $ah_Handle[1], 'ptr', $lpAddressCur, 'ptr', DllStructGetPtr($stMBI), 'int', DllStructGetSize($stMBI))
				if (  (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE)  Or (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE) )  Then
					;Если он доступен для записи, работаем с ним.

					$iRegionSize=DllStructGetData( $stMBI, "RegionSize")
					$strTypeBuffer = "byte[" & $iRegionSize & "]"
					$stTemp=DllStructCreate($strTypeBuffer)
					
										
					;читаем все область памяти
					DllCall($ah_Handle[0], 'int', 'ReadProcessMemory', 'int', $ah_Handle[1], 'int', $lpAddressCur, 'ptr', DllStructGetPtr($stTemp), 'int', DllStructGetSize($stTemp), 'int', '')
					
					
					For $i=0 To $iRegionSize-1
						$bFindStr=True
						$strFindString = ""
						for $ii=1 To StringLen($strStrokaPoiska)
							If StringMid($strStrokaPoiska,$ii,1) <> Chr(DllStructGetData($stTemp, 1, $i+$ii)) Then
								$bFindStr = False
								ExitLoop
							Else
								$strFindString = $strFindString  & Chr(DllStructGetData($stTemp, 1, $i+$ii))
							EndIf
								
						Next
						
						;найдена строка
						If $bFindStr Then
							$ptrTemp=$lpAddressCur + $i
							ExitLoop
						EndIf
					Next

					$stTemp = 0
					$lpAddressCur=$lpAddressCur + $iRegionSize
				Else
					$lpAddressCur=$lpAddressCur + $iRegionSize
				EndIf
		WEnd
					
		;если строка найдена, то ищем указатель на нее
		If $ptrTemp>0 Then
			
			$lpAddressCur=$lpAddressStart
		
			;Ищем где у нас находятся указатель на найденную строку
			While $lpAddressCur <= $lpAddressStop
				DllCall($ah_Handle[0], 'int', 'VirtualQueryEx', 'int', $ah_Handle[1], 'ptr', $lpAddressCur, 'ptr', DllStructGetPtr($stMBI), 'int', DllStructGetSize($stMBI))
				if (  (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE)  Or (DllStructGetData( $stMBI, "Protect")=$PAGE_READWRITE) )  Then
					;Если он доступен для записи, работаем с ним.
					
					$strTypeBuffer = "Ptr"
					$stTemp=DllStructCreate($strTypeBuffer)
				
					;читаем все область памяти
					DllCall($ah_Handle[0], 'int', 'ReadProcessMemory', 'int', $ah_Handle[1], 'int', $lpAddressCur, 'ptr', DllStructGetPtr($stTemp), 'int', DllStructGetSize($stTemp), 'int', '')
					
					;ищем адрес нужного нам указателя
					If DllStructGetData($stTemp, 1) = $ptrTemp Then
						Return $lpAddressCur
						ExitLoop
					EndIf
				
					$lpAddressCur=$lpAddressCur + 4
				Else
					$lpAddressCur=$lpAddressCur + $iRegionSize
				EndIf				
			WEnd
			
			Return 0
			
		Else
			Return 0
		EndIf

EndFunc
 

r35p3ct

Продвинутый
Сообщения
228
Репутация
60
nicki2004 [?]
Только вот не понял как вычислял смещение по которому лежит форма?
1. Через DeDe. Там можно посмотреть адреса форм и самих элементов(Label к примеру)
2. На delphi делается так(По заголовку окна):
Код:
function GetFormPtrAndPid(const FormCaption:string;
                          var FormPtr: pChar;
                          var pid: integer):boolean;
var
  S: array[0..31] of Char;
  A: TAtom;
  hwnd: integer;
begin
  hwnd := FindWindow(nil, pChar(FormCaption));
  Result := hwnd <> 0;
  if Result then
  begin
    GetWindowThreadProcessID(hwnd, @pid);
    StrFmt(S, 'Delphi%.8X', [pid]);       
    A := GlobalFindAtom(S);
    FormPtr := pointer(GetProp(hwnd, MakeIntAtom(A))); 
  end;
end;


Вот полный код для получения текста из Label:
Код:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var wh:hwnd;
    ProcessID:cardinal;
    ProcessHandle:cardinal;
    Card:cardinal;
    i,l:cardinal;
    Buf:array[0..104]of char;
begin
     wh:=FindWindow(nil,'ABC');
     if wh=0 then exit;
     GetWindowThreadProcessID(wh,ProcessID);
     ProcessHandle:=OpenProcess(PROCESS_VM_READ,false,ProcessID);
     if ProcessHandle=0 then exit;
 
 
     if not ReadProcessMemory(ProcessHandle,pointer($0047DC30),@Card,4,l) then exit; //$0047DC30 - адрес из DeDe
     inc(Card,$2f8); //$2f8 - Адрес конкретного лейбла из DeDe
     if not ReadProcessMemory(ProcessHandle,pointer(Card),@Card,4,l) then exit;
     inc(Card,100); //100 - Стандартное смещение для текста у Label
     if not ReadProcessMemory(ProcessHandle,pointer(Card),@Card,4,l) then exit;
 
 
     i:=0;
     while i<100 do begin
           if not ReadProcessMemory(ProcessHandle,pointer(Card+i),@Buf[i],4,l) then break;
           if buf[i]=#0 then break;
           inc(i);
           if buf[i]=#0 then break;
           inc(i);
           if buf[i]=#0 then break;
           inc(i);
           if buf[i]=#0 then break;
           inc(i);
     end;
     Buf[i]:=#0;
     Edit1.Text:=Buf;
     CloseHandle(ProcessHandle);
end;

end.
 
Автор
N

nicki2004

Новичок
Сообщения
13
Репутация
1
r35p3ct

большое спасибо за код
 
Верх