Что нового

Поиск строкого значения в памяти процесса

asdf8

Скриптер
Сообщения
564
Репутация
152
В качестве трюка можно попробовать следующее: не преобразовывать данные из структуры $v_Struct (в функции _MemRead), а сразу заносить в переменную, далее, с помощью StringInStr искать ":\", далее с помощью StringMid получать 256 символов в окрестностях найденной позиции и полученное значение проверять регэкспами.
По моему, можно получить очень конкретное увеличение быстродействия.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
asdf8 [?]
Для RegExp в PB нужно, чтобы строка не содержала Chr(0), а, так как функция mRead, которая кроме чтения памяти, переводит Chr(0) в Chr(1), работает медленно, то вряд ли получится существенный выигрыш в скорости.
Так может не нужно делать эту замену, я не буду искать символ Chr(0) :smile:

inververs [?]
FileInstall ("Artmoney.exe",...)
“Объёмный” трюк ;)
 

Medic84

Омега
Команда форума
Администратор
Сообщения
1,590
Репутация
341
Интересная цитата из программы на C++ по работе с памятью.
Этот цикл отвечает как раз за то, что наша программа не совершит лишних действий. Память в Windows в процессе делится на "регионы". У каждого региона свой уровень доступа: к какому-то доступ запрещен, какой-то можно только прочитать. Нам нужны регионы доступные для записи. Это позволит в разы ускорить работу поиска по памяти и избежать ошибок записи в память. Именно так работает ArtMoney.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
CreatoR [?]
asdf8 [?]Цитата
не преобразовывать данные из структуры $v_Struct
Это как?

Я думаю так:

Код:
#Include <WinAPI.au3>

$pid = ProcessExists('SciTE.exe')
If Not $pid Then Exit

$hProc = _WinAPI_OpenProcess(0x1F0FFF, 0, $pid)

Local $iStartAddress = 0x0, $iStopAddress = 0x06000000, $iStep = 1024
Local $tBuff = DllStructCreate('byte[' & $iStep & ']')

$dll = DllOpen('kernel32.dll')

For $iAddress = $iStartAddress To $iStopAddress Step $iStep
	$aRet = DllCall($dll, 'bool', 'ReadProcessMemory', 'handle', $hProc, 'ptr', $iAddress, 'ptr', DllStructGetPtr($tBuff), 'ulong_ptr', $iStep, 'ulong_ptr*', 0)
	If @Error Then
		ConsoleWrite('-@Error>' & @Error & @CRLF)
		ExitLoop
	EndIf
	If Not $aRet[5] Then ExitLoop
	
	$iReadData = BinaryToString(DllStructGetData($tBuff, 1))
	$iPos = 1
	While $iPos
		$iPos = StringInStr($iReadData, ':\', 1)
		If Not $iPos Then ExitLoop
		$aTmp = StringRegExp(StringMid($iReadData, $iPos - 1, 256), '(?i)^([a-z]:\\.*)', 3)
		If IsArray($aTmp) Then
			ConsoleWrite('-OutStr>' & $aTmp[0] & @CRLF)
			ExitLoop 2
		EndIf
		$iPos += 3
	WEnd
Next

DllClose($dll)

_WinAPI_CloseHandle($hProc)


К сожалению не могу проверить работоспособность кода - у меня вызов "ReadProcessMemory" всегда заканчивается ошибкой 299 (Запрос ReadProcessMemory или WriteProcessMemory был выполнен только частично).
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
asdf8 [?]
К сожалению не могу проверить работоспособность кода - у меня вызов "ReadProcessMemory" всегда заканчивается ошибкой 299
Вот так работает:

Код:
$hProc = _WinAPI_OpenProcess(0x1F0FFF, 0, $iPID)

Local $iStartAddress = 0x00100000, $iStopAddress = 0x06000000, $iStep = 1024
Local $tBuff = DllStructCreate('byte[' & $iStep & ']')

$dll = DllOpen('kernel32.dll')
$iCount = 0

For $iAddress = $iStartAddress To $iStopAddress Step $iStep
    $aRet = DllCall($dll, 'int', 'ReadProcessMemory', 'int', $hProc, 'ptr', $iAddress, 'ptr', DllStructGetPtr($tBuff), 'int', $iStep, 'int', '')
    
	If @Error Then
        ConsoleWrite('-@Error>' & @Error & @CRLF)
        ExitLoop
    EndIf
	
    If Not $aRet[3] Then ExitLoop
    
    $iReadData = BinaryToString(DllStructGetData($tBuff, 1))
    $iPos = 1
	
    While $iPos
        $iPos = StringInStr($iReadData, ':\', 2)
        
		If Not $iPos Then
			ExitLoop
		EndIf
		
        $aTmp = StringRegExp(StringMid($iReadData, $iPos - 1, 256), '(?i)^([a-z]:\\.+(?=\.[^\.]+))$', 3)
        
		If IsArray($aTmp) Then
            ConsoleWrite('-OutStr>' & $aTmp[0] & @CRLF)
			$iCount += 1
			If $iCount = 20 Then ExitLoop 2
        EndIf
		
        $iPos += 3
    WEnd
Next

DllClose($dll)
_WinAPI_CloseHandle($hProc)


но опять же, медленно.
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
CreatoR
Немного странно, комбинация (?=\.[^\.]+) обычно неудачна. В ней должен явный шаблон, т.е. без * или + которые задают неизвестный диапазон. Может я не прав, но сколько раз тестил неработало.
StringMid -относительно медленная, она точно нужна? StringRegExp позволяет задать offset/смещение.
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
AZJIO [?]
Немного странно, комбинация (?=\.[^\.]+) обычно неудачна. В ней должен явный шаблон, т.е. без * или + которые задают неизвестный диапазон
Это поиск текста не преследуемый расширением файла.
У меня что-то не получается найти шаблон для этого...

Т.е нужно примерно вот что:

Код:
$vTest = 'C:\test.au3' ;Должно возвращать 0
;~ $vTest = 'C:\test' ;Должно возвращать 1

$sRet = StringRegExp($vTest, '(?i)^[a-z]:\\.*(?!\.au3)$') ;Поиск пути к файлу не преследуемый расширением файла

ConsoleWrite("Result: " & $sRet & @LF)



P.S.
Но это не критично, настоящая проблема в медленном поиске по памяти процесса.
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
CreatoR
Код:
$test=StringRegExp($test, '(?i)^([a-z]:\\.+\\)([^\\.]+)$', 3)

[a-z]:\\.+ начальное совпадение до последнего "\"
а после "\" обязательно имя [^\\.]+ то есть любое не включающее точку и "\"

Кстати проверил цикл, вставив в $iReadData = 'приблизительный текст с путём' так у меня просто повис процесс.

Но это не критично, настоящая проблема в медленном поиске по памяти процесса.
То есть если опустошить цикл, то ячейки памяти будут всё равно медленно читаться? Или всё таки в цикле дело. Там шаг +3 ($iPos += 3) что он значит?
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
А что вообще нужно в результате сделать? Если только изменить умолчальный путь, то не проще ли использовать "/installfolder" параметр? Он же и будет отображаться в диалоговом окне.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
CreatoR [?]
настоящая проблема в медленном поиске по памяти процесса.

CreatoR у меня такой код отрабатывает достаточно быстро:

Код:
#Include <WinAPI.au3>

$iPID = ProcessExists('SciTE.exe')
If Not $iPID Then Exit
$begin = TimerInit()
$hProc = _WinAPI_OpenProcess(0x1F0FFF, 0, $iPID)

Local $iStartAddress = 0x00100000, $iStopAddress = 0x06000000, $iStep = 1024
Local $tBuff = DllStructCreate('byte[' & $iStep & ']')

$dll = DllOpen('kernel32.dll')
$iCount = 0

For $iAddress = $iStartAddress To $iStopAddress Step $iStep
	$aRet = DllCall($dll, 'int', 'ReadProcessMemory', 'int', $hProc, 'ptr', $iAddress, 'ptr', DllStructGetPtr($tBuff), 'int', $iStep, 'int', '')
	
	If @Error Then
		ConsoleWrite('-@Error>' & @Error & @CRLF)
		ExitLoop
	EndIf
	
	If Not $aRet[3] Then ExitLoop
	
	$iReadData = BinaryToString(DllStructGetData($tBuff, 1))
	$iPos = 1
	While $iPos
		$iPos = StringInStr($iReadData, ':\', 2, 1, $iPos)
		
		If Not $iPos Then ExitLoop
		
		$aTmp = StringRegExp(StringMid($iReadData, $iPos - 1, 256), '(?i)^([a-z]:\\.*)', 3)
		
		If IsArray($aTmp) Then
			ConsoleWrite('-OutStr>' & $aTmp[0] & @CRLF)
			$iCount += 1
			If $iCount = 100 Then ExitLoop 2
		EndIf
		$iPos += 3
	WEnd
Next

DllClose($dll)
_WinAPI_CloseHandle($hProc)
ConsoleWrite('-TimerDiff>' & TimerDiff($begin) & @CRLF)
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
AZJIO
Вот рабочий шаблон: (?i)^[a-z]:\\(.+\\)?[^\\.]+$

[?]
Там шаг +3 ($iPos += 3) что он значит?
Даже и не знаю...

Yashied [?]
А что вообще нужно в результате сделать?

[?]
я ищу универсальный метод получения значения из Edit поля.
В данном случае (это, возможно, самый сложный случай, впрочем именно поэтому я его и выбрал), Edit поле нарисовано графикой, т.е нет элемента, поэтому мне кажется что самый лучший способ, это поиск по рег. выражению некого пути в памяти, чтобы определить путь установки.

[?]
изменить умолчальный путь
Не изменить, а получить.

asdf8 [?]
у меня такой код отрабатывает достаточно быстро
А с установщиком Opera? :smile:

Я убрал из цикла всё кроме вызова ReadProcessMemory, всё равно медленно.
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
То что предложил asdf8 не есть поиск в памяти: этот пример выводит переменные окружения процесса, подгружаемые dll.
 

asdf8

Скриптер
Сообщения
564
Репутация
152
Вот что получилось на РВ (правда с использованием сишных функций, штатные функции поиска в данных, в итоге, все сводят к быстродействию AutoIt):

Код:
#Include <Array.au3>

$iPID = ProcessExists('Opera.exe')
If Not $iPID Then Exit

$begin = TimerInit()
Local $iStartAddress = 0x00100000, $iStopAddress = 0x06000000, $iStep = 1024
$ret = DllCall('mread.dll', 'str', 'FindMemLikePath', 'int', $iPID, 'int', $iStartAddress, 'int', $iStopAddress, 'int', $iStep)
If IsArray($ret) Then
	$aTmp = StringSplit($ret[0], @CRLF, 1)
	For $i = 1 To $aTmp[0]
		$aTmp[$i] = StringRegExpReplace($aTmp[$i], '(?is)^([a-z]:\\[^\x00-\x1f]*).*', '\1')
	Next
	ConsoleWrite('-TimerDiff>' & TimerDiff($begin) & @CRLF)
	_ArrayDisplay($aTmp)
EndIf
 
Автор
CreatoR

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
asdf8 [?]
Вот что получилось на РВ
Неплохо, довольно быстро ищет (правда не всегда находит), а можно в dll'ку добавить параметр для указания RegExp поиска?
И ещё, Artmoney может искать текст как юникод, можно добавить такую возможность и в эту Dll'ку?
 

asdf8

Скриптер
Сообщения
564
Репутация
152
CreatoR [?]
а можно в dll'ку добавить параметр для указания RegExp поиска?

В дллке нет регэкспов, если их добавить, то размер дллки увеличится раз в 15 (добавится библиотека PCRE) и вряд ли ощутимо увеличит скорость - выходных строк не очень много.

И ещё, Artmoney может искать текст как юникод, можно добавить такую возможность и в эту Dll'ку?

В РВ это определяется на стадии компиляции. Сейчас попробовал перекомпилировать в юникоде - фигня получается - ничего не ищет.

правда не всегда находит

Первое подозрение, что, возможно, это связано с Chr(0), или, может, данные попадают на границу читаемого блока, надо будет еще раз код проверить, может я где накосячил.

Сам код (РВ 4.51 win x86):
Код:
ImportC ""
	strstr(a, b)
EndImport

ProcedureDLL.s FindMemLikePath(Pid, AdrStart, AdrEnd, iSize)
	Protected sOut.s = "", ProcessHandle, lWritten
	ProcessHandle = OpenProcess_(#PROCESS_ALL_ACCESS, 0, Pid)
	If ProcessHandle
		sBuffer = AllocateMemory(iSize + 1)
		If sBuffer
			For iAdr = AdrStart To AdrEnd
				ReadProcessMemory_(ProcessHandle, iAdr, sBuffer, iSize, @lWritten)
				If sBuffer And lWritten
					i = strstr(sBuffer, @":\")
					While i
						tmp$ = PeekS(i - 1, 256)
						tmp = Asc(UCase(Left(tmp$, 1)))
						If tmp > 64 And tmp < 91
							sOut + ReplaceString(tmp$, #CRLF$, #TAB$+#TAB$) + #CRLF$
						EndIf
						i + 1
						i = strstr(i, @":\")
					Wend
				EndIf
				iAdr + iSize - 1
			Next
			FreeMemory(sBuffer)
		Else
			sOut = "-2"; ошибка выделения памяти
		EndIf
		CloseHandle_(ProcessHandle)
		ProcedureReturn sOut
	EndIf
	ProcedureReturn "-1"; не открыт хэндл процесса
EndProcedure
 
Верх