На 100% не уверен, что всё чики-пуки, но вроде работает. В начале идёт пример вызова и проверочный код, сама функция ниже. Ищется, собственно, последовательность байт заданной длины, поэтому число сперва пишется в буфер, а функции передаётся указатель на буфер и длина искомого. Строки, по идее, можно передавать непосредственно, если они передадутся в нужной кодировке, а если нет, то так же писать в буфер функцией StrPut с перекодировкой. Но строки я пока не пробовал искать.
Если вы ищете, скажем, число 0x44444444, а в памяти есть последовательность из 6 байт 44 44 44 44 44 44, то найдено будет 3 адреса: для 44 44 44 44 хх хх, для хх 44 44 44 44 хх и для хх хх 44 44 44 44. Т.к. не известно, с какого именно байта начинается ваше число.
Число 0x44444444 оказалось очень популярным в памяти Проводника.
SetBatchLines, -1
ProcessName := "explorer.exe"
Start := 0, End := 0x80000000
Number := 0x44444444 ; Число для поиска.
cbTarget := 4 ; Длина искомого в байтах. По умолчанию 4.
VarSetCapacity(Target, cbTarget)
NumPut(Number, Target, 0, "uint")
cAddrs := 100000 ; Сколько адресов искать. По умолчанию 100.
; Поиск. Функция вернёт массив адресов либо 0, если ничего не найдено.
StartTime := A_TickCount
Found := SearchProcess(ProcessName, Start, End, &Target, cbTarget, cAddrs)
Time := (A_TickCount - StartTime) / 1000
If (Found = 0) {
MsgBox, %Time% сек. Ничего не найдено
ExitApp
}
Else {
MsgBox, % Time " сек. Найдено " Found.MaxIndex()
}
; Проверка. Читаются числа по найденным адресам и сравниваются с заданным.
Process, Exist, %ProcessName%
If !(PID := ErrorLevel) {
MsgBox, Процесс не найден.
ExitApp
}
hProcess := DllCall("OpenProcess", "uint", 0x10, "int", False, "uint", PID, "ptr")
If (!hProcess) {
MsgBox, Не удалось открыть процесс.
ExitApp
}
VarSetCapacity(TestBuf, cbTarget, 0)
Matches = 0
Loop, % Found.MaxIndex()
{
Ret := DllCall("ReadProcessMemory", "ptr", hProcess, "ptr", Found[A_Index]
, "ptr", &TestBuf, "ptr", cbTarget
, "ptr *", BytesRead, "int")
If (!Ret) {
MsgBox, Не удалось прочитать.
Break
}
Number2 := NumGet(TestBuf, 0, "uint")
If (Number2 = Number) {
++Matches
}
}
DllCall("CloseHandle", "ptr", hProcess)
MsgBox, Совпадений %Matches%
; ============================== Функция =======================================
SearchProcess(NameOrPID, StartAddr, EndAddr, pTarget, cbTarget = 4, cAddrs = 100)
{
static Code := 0, MFunc, Len, Page := 0x1000, mbi, mbiSize
static stateOffset, protOffset, regSizeOffset
static Rights := 0x410 ; PROCESS_VM_READ | PROCESS_QUERY_INFORMATION
static MEM_COMMIT := 0x1000, PAGE_READWRITE := 4, PAGE_READONLY := 2
static PAGE_WRITECOPY := 0x08
static BufSize := 0x100000 ; 1 MB. Размер буфера для чтения из процесса.
If (Code != 0) ; Если не первый вызов, то машинный код уже готов.
GoTo Process
If (A_PtrSize = 8) { ; x64
mbiSize := 48, stateOffset := 32, protOffset := 36, regSizeOffset := 24
Code =
( Join LTrim
48894C240848895424104C894424184C894C2420555357564889E54883EC08488B
7D288B4D308B5540488B5D48C745F800000000FFCA29D1783B488B7538ACF2AE75
325189D1F3A60F94C04829D14801CF5984C0741B488D47FF482B45284803455848
89034883C308FF45F8FF4D50740485C975C58B45F8488D65005E5F5B5DC3
)
}
Else { ; x86
mbiSize := 28, stateOffset := 16, protOffset := 20, regSizeOffset := 12
Code =
( Join LTrim
5553575689E583EC048B7D148B4D188B55208B5D24C745FC000000004A29D17833
8B751CACF2AE752B5189D1F3A60F94C029D101CF5984C074168D47FF2B45140345
2C890383C304FF45FCFF4D28740485C975CD8B45FC89EC5E5F5B5DC21C00
)
}
Len := StrLen(Code) // 2
; Память под машинный код.
MFunc := DllCall("VirtualAlloc", "ptr", 0, "ptr", Len
, "uint", 0x3000, "uint", 0x40, "ptr")
Loop, % Len
NumPut("0x" . SubStr(Code, A_Index * 2 - 1, 2), MFunc + 0
, A_Index - 1, "uchar")
VarSetCapacity(mbi, mbiSize) ; Структура MEMORY_BASIC_INFORMATION
Process:
Process, Exist, %NameOrPID%
If !(PID := ErrorLevel) {
MsgBox, Процесс не найден
Return 0
}
hProcess := DllCall("OpenProcess", "uint", Rights, "int", False, "uint", PID, "ptr")
If(!hProcess) {
MsgBox, Не удалось открыть процесс.
Return 0
}
VarSetCapacity(AddrBuf, A_PtrSize * cAddrs, 0) ; Буфер для найденных адресов.
VarSetCapacity(Buf, BufSize) ; Буфер для считанной из процесса памяти.
CurAddr := (StartAddr // Page) * Page ; Текущий адрес. Округлить до начала страницы.
pAddrBuf := &AddrBuf ; Указатель на свободное место в буфере адресов.
FoundAll := 0 ; Всего найдено.
While(CurAddr < EndAddr) {
BytesRet := DllCall("VirtualQueryEx", "ptr", hProcess, "ptr", CurAddr
, "ptr", &mbi, "uint", mbiSize, "ptr")
If (BytesRet = 0) {
CurAddr += Page
Continue
}
RegionSize := NumGet(mbi, regSizeOffset, "uptr")
; Если память не выделена (адреса свободны или только зарезервированы),
; пропускаем весь этот регион.
If (NumGet(mbi, stateOffset, "uint") != MEM_COMMIT) {
CurAddr += RegionSize
If (A_PtrSize = 4 && CurAddr > 0xFFFFFFFF)
GoTo Done
Continue
}
Protect := NumGet(mbi, protOffset, "uint")
; Проверять только память для записи и чтения (данные).
If !(Protect = PAGE_READWRITE || Protect = PAGE_WRITECOPY
|| Protect = PAGE_READONLY) {
CurAddr += RegionSize
If (A_PtrSize = 4 && CurAddr > 0xFFFFFFFF)
GoTo Done
Continue
}
While(RegionSize) { ; Считывание в буфер и поиск в нём.
ReadSize := BufSize < RegionSize ? BufSize : RegionSize
RegionSize -= ReadSize
Ret := DllCall("ReadProcessMemory", "ptr", hProcess, "ptr", CurAddr
, "ptr", &Buf, "ptr", ReadSize
, "ptr *", BytesRead, "int")
If (!Ret) { ; Если ошибка чтения, идём дальше.
CurAddr += ReadSize
Continue
}
; Вызов машинной функции для поиска.
Found := DllCall(MFunc, "ptr", &Buf, "ptr", BytesRead
, "ptr", pTarget, "uint", cbTarget
, "ptr", pAddrBuf, "uint", cAddrs - FoundAll
, "ptr", CurAddr, "uint")
CurAddr += ReadSize
If (Found = 0) {
Continue
}
If ((FoundAll += Found) < cAddrs) {
pAddrBuf += Found * A_PtrSize
Continue
}
Goto Done
}
}
Done:
DllCall("CloseHandle", "ptr", hProcess)
If (FoundAll = 0)
Return 0
Array := []
Loop, %FoundAll%
Array.Insert(NumGet(&AddrBuf+(A_PtrSize * (A_Index - 1)), 0, "uptr"))
Return Array
}