-
Notifications
You must be signed in to change notification settings - Fork 5
/
unpacker.ahk
1291 lines (1196 loc) · 73.3 KB
/
unpacker.ahk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
SendMode Input
SetWorkingDir %A_ScriptDir%
#Warn All, Off
#UseHook
#NoEnv
#SingleInstance force
Gui, New, Caption, %name%
Gui, Font, S11 CDefault, Arial
Gui, Add, Text, x12 y10 w370 h20 , Введите имя файла (скрипт должен быть запущен):
Gui, Add, Edit, x12 y30 w354 h25 +Center vMyEdit gUnpackEnter
Gui, Add, CheckBox, x12 y56 w354 h25 +Checked vIndent, Расставить табуляцию
Gui, Add, Button, x12 y83 w354 h38 gUnpack, Распаковать
Gui, Font, S8 CDefault, Arial
Gui, Add, Link, x162 y122 w71 h17, <a href="http://blast.hk/threads/15597/">BlastHack</a>
Gui, Show, h140 w383, %name%
return
UnpackEnter:
KeyWait, Enter, D
IfWinNotActive, %name%
return
Unpack:
GuiControlGet, MyEdit
GuiControlGet, Indent
if(!strlen(MyEdit))
{
MsgBox, 16, %name%, Укажите название процесса для распаковки.
return
}
if(!RegExMatch(MyEdit, "\.exe"))
MyEdit .= ".exe"
if(MyEdit == A_ScriptName)
{
MsgBox, 67, %name%, Исходники этого скрипта можно скачать с форума`nОткрыть форум (Yes) или всё равно продолжить распаковку (No)?
IfMsgBox, Yes
{
Run % "http://blast.hk/threads/15597/"
return
}
IfMsgBox, Cancel
return
}
processId := ""
for process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
{
if process.Name == MyEdit
processId := process.processId
}
if(!strlen(processId))
{
MsgBox, 16, %name%, Процесс %MyEdit% не найден.
return
}
mem := new _ClassMemory(processId)
if(!processId || !mem)
{
DllCall("OpenProcess", "UInt", 0x001F0FFF, "Int", False, "UInt", processId, "Ptr")
err := DllCall("GetLastError", "UInt")
errs := {5: ": Доступ запрещен.", 6: ": The handle is invalid."}
if(err == 5 || err == 6)
{
MsgBox, 52, %name%, Нет доступа к процессу %MyEdit%. Возможно, он запущен с более высокими привилегиями, чем Unpacker.`n`nПерезапустить Unpacker с повышенными привилегиями?
IfMsgBox, Yes
{
Run *RunAs %A_ScriptFullPath% ,, UseErrorLevel
return
}
}
MsgBox, 16, %name%, % "Ошибка при открытии процесса #" err . errs[err]
return
}
pattern := mem.hexStringToPattern("3B 20 3C 43 4F 4D 50 49 4C 45 52 3A")
addr := mem.processPatternScan(,, pattern*)
if(!addr || addr == -1)
{
MsgBox, 52, %name%, Ошибка при чтении процесса %MyEdit%`nФайл не является AHK_L скриптом, либо закриптован`n`nЕсли вы уверены, что это AHK скрипт, то отправьте его мне в личные сообщения на форуме для анализа.`nОткрыть форум с профилем?
IfMsgBox, Yes
Run % "http://blast.hk/conversations/add?to=asdzxcjqwe"
return
}
sName := getFreeName("source", "txt")
Gui, Hide
Gui, New, Caption, %name%
Gui, Font, S11 CDefault Bold, Arial
Gui, Add, Text, x12 y10 w320 h20, Распаковка в процессе...
Gui, Font, S11 CDefault, Arial
Gui, Add, Text, x12 y30 w320 h20, Это окно будет закрыто после распаковки
Gui, Add, Text, x12 y50 w450 h20, Код будет сохранен в файл %sName%
Gui, Show, h83 w364, %name%
length := 0
while(true)
{
str := mem.readString(addr+length, 1)
if(!strlen(str))
break
length++
}
code := mem.readString(addr, length)
mem.closeHandle(mem)
FileAppend, %code%, %sName%, cp1251
if(Indent)
MakeAutoIndent(sName)
Run, %sName%
ExitApp
return
GuiClose:
GuiEscape:
ExitApp
return
getFreeName(prefix, ext)
{
while(true)
{
Filename := prefix "" i "." ext
i++
if !FileExist(Filename)
break
}
return Filename
}
MakeAutoIndent(filename)
{
source := "", brackets := 0
Loop, Read, %filename%
{
if(RegExMatch(A_LoopReadLine, "\}"))
brackets--
i := 0, indent := ""
while(brackets > i)
{
indent := " " indent
i++
}
source .= indent "" A_LoopReadLine "`n"
if(RegExMatch(A_LoopReadLine, "\{"))
brackets++
}
FileDelete, %filename%
FileAppend, %source%, %filename%, cp1251
}
update()
{
Gui, New, Caption, %name%
Gui, Font, S11 CDefault Bold, Arial
Gui, Add, Text, x62 y20 w200 h20, Загрузка обновления...
Gui, Add, Text, x57 y37 w190 h20, Не закрывайте это окно
Gui, Show, h76 w293, %name%
URLDownloadToFile, http://rumist.aiq.ru/unpacker/unpacker.exe, unpacker.exe.dl
batfile := "@echo off`ntaskkill /f /im " A_ScriptName "`ndel /f /q " A_ScriptName "`nren unpacker.exe.dl unpacker.exe`nstart unpacker.exe`ndel /f /q __update.bat"
FileDelete, __update.bat
FileAppend, %batfile%, __update.bat
Run, __update.bat,, Hide
}
MD5(ByRef V, L=0)
{
VarSetCapacity( MD5_CTX,104,0 ), DllCall( "advapi32\MD5Init", Str,MD5_CTX )
DllCall( "advapi32\MD5Update", Str,MD5_CTX, Str,V, UInt,L ? L : VarSetCapacity(V) )
DllCall( "advapi32\MD5Final", Str,MD5_CTX )
Loop % StrLen( Hex:="123456789abcdef0" )
N := NumGet( MD5_CTX,87+A_Index,"Char"), MD5 .= SubStr(Hex,N>>4,1) . SubStr(Hex,N&15,1)
Return MD5
}
;~ internal stuff
class _ClassMemory
{
; List of useful accessible values. Some of these inherited values (the non objects) are set when the new operator is used.
static baseAddress, hProcess, PID, currentProgram
, insertNullTerminator := True
, readStringLastError := False
, isTarget64bit := False
, ptrType := "UInt"
, aTypeSize := { "UChar": 1, "Char": 1
, "UShort": 2, "Short": 2
, "UInt": 4, "Int": 4
, "UFloat": 4, "Float": 4
, "Int64": 8, "Double": 8}
, aRights := { "PROCESS_ALL_ACCESS": 0x001F0FFF
, "PROCESS_CREATE_PROCESS": 0x0080
, "PROCESS_CREATE_THREAD": 0x0002
, "PROCESS_DUP_HANDLE": 0x0040
, "PROCESS_QUERY_INFORMATION": 0x0400
, "PROCESS_QUERY_LIMITED_INFORMATION": 0x1000
, "PROCESS_SET_INFORMATION": 0x0200
, "PROCESS_SET_QUOTA": 0x0100
, "PROCESS_SUSPEND_RESUME": 0x0800
, "PROCESS_TERMINATE": 0x0001
, "PROCESS_VM_OPERATION": 0x0008
, "PROCESS_VM_READ": 0x0010
, "PROCESS_VM_WRITE": 0x0020}
; Method: __new(program, dwDesiredAccess := "", byRef handle := "", windowMatchMode := 3)
; Example: derivedObject := new _ClassMemory("ahk_exe calc.exe")
; This is the first method which should be called when trying to access a program's memory.
; If the process is successfully opened, an object is returned which can be used to read that processes memory space.
; [derivedObject].hProcess stores the opened handle.
; If the target process closes and re-opens, simply free the derived object and use the new operator again to open a new handle.
; Parameters:
; program The program to be opened. This can be any AHK windowTitle identifier, such as
; ahk_exe, ahk_class, ahk_pid, or simply the window title. e.g. "ahk_exe calc.exe" or "Calculator".
; It's safer not to use the window title, as some things can have the same window title e.g. an open folder called "Starcraft II"
; would have the same window title as the game itself.
; dwDesiredAccess The access rights requested when opening the process.
; If this parameter is null the process will be opened with the following rights
; PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE
; This access level is sufficient to allow all of the methods in this class to work.
; Specific process access rights are listed here http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
; handle (Output) Optional variable in which a copy of the opened processes handle will be stored.
; Values:
; Null OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin. Consult A_LastError for more information.
; 0 The program isn't running (not found) or you passed an incorrect program identifier parameter.
; Positive Integer A handle to the process. (Success)
; windowMatchMode - Determines the matching mode used when finding the program (windowTitle).
; The default value is 3 i.e. an exact match. Refer to AHK's setTitleMathMode for more information.
; Return Values:
; Object On success an object is returned which can be used to read the processes memory.
; Null Failure. A_LastError and the optional handle parameter can be consulted for more information.
__new(program, dwDesiredAccess := "", byRef handle := "", windowMatchMode := 3)
{
; This default access level is sufficient to read and write memory addresses, and to perform pattern scans.
; if the program is run using admin privileges, then this script will also need admin privileges
if (program+0) is integer
this.PID := program
else
this.PID := handle := this.findPID(program, windowMatchMode)
if(this.PID)
{
if dwDesiredAccess is not integer
dwDesiredAccess := this.aRights.PROCESS_QUERY_INFORMATION | this.aRights.PROCESS_VM_OPERATION | this.aRights.PROCESS_VM_READ | this.aRights.PROCESS_VM_WRITE
if this.hProcess := handle := this.OpenProcess(this.PID, dwDesiredAccess) ; NULL/Blank if failed to open process for some reason
{
this.readStringLastError := False
this.currentProgram := program
if this.isTarget64bit := this.isTargetProcess64Bit(this.PID, this.hProcess, dwDesiredAccess)
this.ptrType := "Int64"
else this.ptrType := "UInt" ; If false or Null (fails) assume 32bit
; if script is 64 bit, getModuleBaseAddress() should always work
; if target app is truly 32 bit, then getModuleBaseAddress()
; will work when script is 32 bit
if (A_PtrSize != 4 || !this.isTarget64bit)
this.BaseAddress := this.getModuleBaseAddress()
; If the above failed or wasn't called, fall back to alternate method
if this.BaseAddress < 0 || !this.BaseAddress
this.BaseAddress := this.getProcessBaseAddress(program, windowMatchMode)
return this
}
}
return
}
__delete()
{
this.closeHandle(this.hProcess)
return
}
version()
{
return 2.6
}
findPID(program, windowMatchMode := "3")
{
if windowMatchMode
{
; This is a string and will not contain the 0x prefix
mode := A_TitleMatchMode
; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and user passed an int (unquoted)
StringReplace, windowMatchMode, windowMatchMode, 0x
SetTitleMatchMode, %windowMatchMode%
}
WinGet, pid, pid, %program%
if windowMatchMode
SetTitleMatchMode, %mode% ; In case executed in autoexec
return pid ? pid : 0 ; PID is null on fail, return 0
}
; Method: openProcess(PID, dwDesiredAccess)
; ***Note: This is an internal method which shouldn't be called directly unless you absolutely know what you are doing.
; This is because the new operator, in addition to calling this method also sets other values
; which are required for the other methods to work correctly.
; Parameters:
; PID The Process ID of the target process.
; dwDesiredAccess The access rights requested when opening the process.
; Specific process access rights are listed here http://msdn.microsoft.com/en-us/library/windows/desktop/ms684880(v=vs.85).aspx
; Return Values:
; Null/blank OpenProcess failed. If the target process has admin rights, then the script also needs to be ran as admin.
; Positive integer A handle to the process.
openProcess(PID, dwDesiredAccess)
{
return DllCall("OpenProcess", "UInt", dwDesiredAccess, "Int", False, "UInt", PID, "Ptr") ; NULL/Blank if failed to open process for some reason
}
; Method: closeHandle(hProcess)
; Note: This is an internal method which is automatically called when the script exits or the derived object is freed/destroyed.
; There is no need to call this method directly. If you wish to close the handle simply free the derived object.
; i.e. derivedObject := [] ; or derivedObject := ""
; Parameters:
; hProcess The handle to the process, as returned by openProcess().
; Return Values:
; Non-Zero Success
; 0 Failure
closeHandle(hProcess)
{
return DllCall("CloseHandle", "Ptr", hProcess)
}
; Method: read(address, type := "UInt", aOffsets*)
; Reads various integer type values
; Parameters:
; address - The memory address of the value or if using the offset parameter,
; the base address of the pointer.
; type - The integer type.
; Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
; Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
; When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which holds the integer.
; Return Values:
; integer - Indicates success.
; Null - Indicates failure. Check ErrorLevel and A_LastError for more information.
; Note: Since the returned integer value may be 0, to check for success/failure compare the result
; against null i.e. if (result = "") then an error has occurred.
; When reading doubles, adjusting "SetFormat, float, totalWidth.DecimalPlaces"
; may be required depending on your requirements.
read(address, type := "UInt", aOffsets*)
{
; If invalid type RPM() returns success (as bytes to read resolves to null in dllCall())
; so set errorlevel to invalid parameter for DLLCall() i.e. -2
if !this.aTypeSize.hasKey(type)
return "", ErrorLevel := -2
if DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, type "*", result, "Ptr", this.aTypeSize[type], "Ptr",0)
return result
return
}
; Method: readRaw(address, byRef buffer, bytes := 4, aOffsets*)
; Reads an area of the processes memory and stores it in the buffer variable
; Parameters:
; address - The memory address of the area to read or if using the offsets parameter
; the base address of the pointer which points to the memory region.
; buffer - The unquoted variable name for the buffer. This variable will receive the contents from the address space.
; This method calls varsetCapcity() to ensure the variable has an adequate size to perform the operation.
; If the variable already has a larger capacity (from a previous call to varsetcapcity()), then it will not be shrunk.
; Therefore it is the callers responsibility to ensure that any subsequent actions performed on the buffer variable
; do not exceed the bytes which have been read - as these remaining bytes could contain anything.
; bytes - The number of bytes to be read.
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which is to be read
; Return Values:
; Non Zero - Indicates success.
; Zero - Indicates failure. Check errorLevel and A_LastError for more information
;
; Notes: The contents of the buffer may then be retrieved using AHK's NumGet() and StrGet() functions.
; This method offers significant (~30% and up) performance boost when reading large areas of memory.
; As calling ReadProcessMemory for four bytes takes a similar amount of time as it does for 1,000 bytes.
readRaw(address, byRef buffer, bytes := 4, aOffsets*)
{
VarSetCapacity(buffer, bytes)
return DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", &buffer, "Ptr", bytes, "Ptr", 0)
}
; Method: readString(address, sizeBytes := 0, encoding := "utf-8", aOffsets*)
; Reads string values of various encoding types
; Parameters:
; address - The memory address of the value or if using the offset parameter,
; the base address of the pointer.
; sizeBytes - The size (in bytes) of the string to be read.
; If zero is passed, then the function will read each character until a null terminator is found
; and then returns the entire string.
; encoding - This refers to how the string is stored in the program's memory.
; UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which holds the string.
;
; Return Values:
; String - On failure an empty (null) string is always returned. Since it's possible for the actual string
; being read to be null (empty), then a null return value should not be used to determine failure of the method.
; Instead the property [derivedObject].ReadStringLastError can be used to check for success/failure.
; This property is set to 0 on success and 1 on failure. On failure ErrorLevel and A_LastError should be consulted
; for more information.
; Notes:
; For best performance use the sizeBytes parameter to specify the exact size of the string.
; If the exact size is not known and the string is null terminated, then specifying the maximum
; possible size of the string will yield the same performance.
; If neither the actual or maximum size is known and the string is null terminated, then specifying
; zero for the sizeBytes parameter is fine. Generally speaking for all intents and purposes the performance difference is
; inconsequential.
readString(address, sizeBytes := 0, encoding := "UTF-8", aOffsets*)
{
bufferSize := VarSetCapacity(buffer, sizeBytes ? sizeBytes : 100, 0)
this.ReadStringLastError := False
if aOffsets.maxIndex()
address := this.getAddressFromOffsets(address, aOffsets*)
if !sizeBytes ; read until null terminator is found or something goes wrong
{
; Even if there are multi-byte-characters (bigger than the encodingSize i.e. surrogates) in the string, when reading in encodingSize byte chunks they will never register as null (as they will have bits set on those bytes)
if (encoding = "utf-16" || encoding = "cp1200")
encodingSize := 2, charType := "UShort", loopCount := 2
else encodingSize := 1, charType := "Char", loopCount := 4
Loop
{ ; Lets save a few reads by reading in 4 byte chunks
if !DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", address + ((outterIndex := A_index) - 1) * 4, "Ptr", &buffer, "Ptr", 4, "Ptr", 0) || ErrorLevel
return "", this.ReadStringLastError := True
else loop, %loopCount%
{
if NumGet(buffer, (A_Index - 1) * encodingSize, charType) = 0 ; NULL terminator
{
if (bufferSize < sizeBytes := outterIndex * 4 - (4 - A_Index * encodingSize))
VarSetCapacity(buffer, sizeBytes)
break, 2
}
}
}
}
if DllCall("ReadProcessMemory", "Ptr", this.hProcess, "Ptr", address, "Ptr", &buffer, "Ptr", sizeBytes, "Ptr", 0)
return StrGet(&buffer,, encoding)
return "", this.ReadStringLastError := True
}
; Method: writeString(address, string, encoding := "utf-8", aOffsets*)
; Encodes and then writes a string to the process.
; Parameters:
; address - The memory address to which data will be written or if using the offset parameter,
; the base address of the pointer.
; string - The string to be written.
; encoding - This refers to how the string is to be stored in the program's memory.
; UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which is to be written to.
; Return Values:
; Non Zero - Indicates success.
; Zero - Indicates failure. Check errorLevel and A_LastError for more information
; Notes:
; By default a null terminator is included at the end of written strings.
; This behaviour is determined by the property [derivedObject].insertNullTerminator
; If this property is true, then a null terminator will be included.
writeString(address, string, encoding := "utf-8", aOffsets*)
{
encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
requiredSize := StrPut(string, encoding) * encodingSize - (this.insertNullTerminator ? 0 : encodingSize)
VarSetCapacity(buffer, requiredSize)
StrPut(string, &buffer, StrLen(string) + (this.insertNullTerminator ? 1 : 0), encoding)
return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", &buffer, "Ptr", requiredSize, "Ptr", 0)
}
; Method: write(address, value, type := "Uint", aOffsets*)
; Writes various integer type values to the process.
; Parameters:
; address - The memory address to which data will be written or if using the offset parameter,
; the base address of the pointer.
; type - The integer type.
; Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
; Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
; When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which is to be written to.
; Return Values:
; Non Zero - Indicates success.
; Zero - Indicates failure. Check errorLevel and A_LastError for more information
; Null - An invalid type was passed. Errorlevel is set to -2
write(address, value, type := "Uint", aOffsets*)
{
if !this.aTypeSize.hasKey(type)
return "", ErrorLevel := -2
return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, type "*", value, "Ptr", this.aTypeSize[type], "Ptr", 0)
}
; Method: writeRaw(address, byRef buffer, byRef bufferSize := 0, aOffsets*)
; Writes a buffer to the process.
; Parameters:
; address - The memory address to which the contents of the buffer will be written
; or if using the offset parameter, the base address of the pointer.
; pBuffer - A pointer to the buffer which is to be written.
; This does not necessarily have to be the beginning of the buffer itself e.g. pBuffer := &buffer + offset
; sizeBytes - The number of bytes which are to be written from the buffer.
; aOffsets* - A variadic list of offsets. When using offsets the address parameter should equal the base address of the pointer.
; The address (bass address) and offsets should point to the memory address which is to be written to.
; Return Values:
; Non Zero - Indicates success.
; Zero - Indicates failure. Check errorLevel and A_LastError for more information
writeRaw(address, pBuffer, sizeBytes, aOffsets*)
{
return DllCall("WriteProcessMemory", "Ptr", this.hProcess, "Ptr", aOffsets.maxIndex() ? this.getAddressFromOffsets(address, aOffsets*) : address, "Ptr", pBuffer, "Ptr", sizeBytes, "Ptr", 0)
}
; Method: pointer(address, finalType := "UInt", offsets*)
; This is an internal method. Since the other various methods all offer this functionality, they should be used instead.
; This will read integer values of both pointers and non-pointers (i.e. a single memory address)
; Parameters:
; address - The base address of the pointer or the memory address for a non-pointer.
; finalType - The type of integer stored at the final address.
; Valid types are UChar, Char, UShort, Short, UInt, Int, Float, Int64 and Double.
; Note: Types must not contain spaces i.e. " UInt" or "UInt " will not work.
; When an invalid type is passed the method returns NULL and sets ErrorLevel to -2
; aOffsets* - A variadic list of offsets used to calculate the pointers final address.
; Return Values: (The same as the read() method)
; integer - Indicates success.
; Null - Indicates failure. Check ErrorLevel and A_LastError for more information.
; Note: Since the returned integer value may be 0, to check for success/failure compare the result
; against null i.e. if (result = "") then an error has occurred.
; If the target application is 64bit the pointers are read as an 8 byte Int64 (this.PtrType)
pointer(address, finalType := "UInt", offsets*)
{
For index, offset in offsets
address := this.Read(address, this.ptrType) + offset
Return this.Read(address, finalType)
}
; Method: getAddressFromOffsets(address, aOffsets*)
; Returns the final address of a pointer.
; This is an internal method used by various methods however, this method may be useful if you are
; looking to eliminate the overhead overhead associated with reading pointers which only change
; on startup or map/level change. In other words you can cache the final address and
; read from this address directly.
; Parameters:
; address The base address of the pointer.
; aOffsets* A variadic list of offsets used to calculate the pointers final address.
; At least one offset must be present.
; Return Values:
; Positive integer The final memory address pointed to by the pointer.
; Negative integer Failure
; Null Failure
; Note: If the target application is 64bit the pointers are read as an 8 byte Int64 (this.PtrType)
getAddressFromOffsets(address, aOffsets*)
{
return aOffsets.Remove() + this.pointer(address, this.ptrType, aOffsets*) ; remove the highest key so can use pointer() to find final memory address (minus the last offset)
}
; Interesting note:
; Although handles are 64-bit pointers, only the less significant 32 bits are employed in them for the purpose
; of better compatibility (for example, to enable 32-bit and 64-bit processes interact with each other)
; Here are examples of such types: HANDLE, HWND, HMENU, HPALETTE, HBITMAP, etc.
; http://www.viva64.com/en/k/0005/
; The base address for some programs is dynamic. This can retrieve the current base address of the main module (e.g. calc.exe),
; which can then be added to your various offsets.
; This function will return the correct address regardless of the
; bitness (32 or 64 bit) of both the AHK exe and the target process.
; Method: getProcessBaseAddress(WindowTitle, windowMatchMode := 3)
; Returns the base address of a process. In most cases this will provide the same result calling getModuleBaseAddress() (when passing
; a null value as the module parameter), however getProcessBaseAddress() will usually work regardless of the bitness
; of both the AHK exe and the target process.
; ***If this returns an incorrect value, try using (the more reliable) getModuleBaseAddress() instead.***
; Parameters:
; windowTitle This can be any AHK windowTitle identifier, such as
; ahk_exe, ahk_class, ahk_pid, or simply the window title. e.g. "ahk_exe calc.exe" or "Calculator".
; It's safer not to use the window title, as some things can have the same window title e.g. an open folder called "Starcraft II"
; would have the same window title as the game itself.
; windowMatchMode Determines the matching mode used when finding the program's window (windowTitle).
; The default value is 3 i.e. an exact match. The current matchmode will be used if the parameter is null or 0.
; Refer to AHK's setTitleMathMode for more information.
; Return Values:
; Positive integer The base address of the process (success).
; Null The process's window couldn't be found.
; 0 The GetWindowLong or GetWindowLongPtr call failed. Try getModuleBaseAddress() instead.
getProcessBaseAddress(windowTitle, windowMatchMode := "3")
{
if (windowMatchMode && A_TitleMatchMode != windowMatchMode)
{
mode := A_TitleMatchMode ; This is a string and will not contain the 0x prefix
StringReplace, windowMatchMode, windowMatchMode, 0x ; remove hex prefix as SetTitleMatchMode will throw a run time error. This will occur if integer mode is set to hex and matchmode param is passed as an number not a string.
SetTitleMatchMode, %windowMatchMode% ;mode 3 is an exact match
}
WinGet, hWnd, ID, %WindowTitle%
if mode
SetTitleMatchMode, %mode% ; In case executed in autoexec
if !hWnd
return ; return blank failed to find window
; GetWindowLong returns a Long (Int) and GetWindowLongPtr return a Long_Ptr
return DllCall(A_PtrSize = 4 ; If DLL call fails, returned value will = 0
? "GetWindowLong"
: "GetWindowLongPtr"
, "Ptr", hWnd, "Int", -6, A_Is64bitOS ? "Int64" : "UInt")
; For the returned value when the OS is 64 bit use Int64 to prevent negative overflow when AHK is 32 bit and target process is 64bit
; however if the OS is 32 bit, must use UInt, otherwise the number will be huge (however it will still work as the lower 4 bytes are correct)
; Note - it's the OS bitness which matters here, not the scripts/AHKs
}
; http://winprogger.com/getmodulefilenameex-enumprocessmodulesex-failures-in-wow64/
; http://stackoverflow.com/questions/3801517/how-to-enum-modules-in-a-64bit-process-from-a-32bit-wow-process
; Method: getModuleBaseAddress(module := "", byRef aModuleInfo := "")
; Parameters:
; module - The file name of the module/dll to find e.g. "calc.exe", "GDI32.dll", "Bass.dll" etc
; If no module (null) is specified, the address of the base module - main()/process will be returned
; e.g. for calc.exe the following two method calls are equivalent getModuleBaseAddress() and getModuleBaseAddress("calc.exe")
; aModuleInfo - (Optional) A module Info object is returned in this variable.
; This object contains the keys: lpBaseOfDll, SizeOfImage, and EntryPoint
; Return Values:
; Positive integer - The module's base/load address (success).
; -1 - Module not found
; -3 - EnumProcessModulesEx failed
; -4 - The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process. Or the target process has been closed.
; -5 - GetModuleInformation failed.
; Notes: A 64 bit AHK can enumerate the modules of a target 64 or 32 bit process.
; A 32 bit AHK can only enumerate the modules of a 32 bit process
; This method requires PROCESS_QUERY_INFORMATION + PROCESS_VM_READ access rights. These are included by default with this class.
getModuleBaseAddress(module := "", byRef aModuleInfo := "")
{
if (A_PtrSize = 4 && this.IsTarget64bit)
return -4 ; AHK is 32bit and target process is 64 bit, this function wont work
if (module = "")
mainExeFullPath := this.GetModuleFileNameEx() ; mainExeName = main executable module of the process (will include full directory path)
if !moduleCount := this.EnumProcessModulesEx(lphModule)
return -3
loop % moduleCount
{
; module will contain directory path as well e.g C:\Windows\syswow65\GDI32.dll
moduleFullPath := this.GetModuleFileNameEx(hModule := numget(lphModule, (A_index - 1) * A_PtrSize))
SplitPath, moduleFullPath, fileName ; strips the path so = GDI32.dll
if (module = "" && mainExeFullPath = moduleFullPath) || (module != "" && module = filename)
return this.GetModuleInformation(hModule, aModuleInfo) ? aModuleInfo.lpBaseOfDll : -5 ; Failed to get module info
}
return -1 ; not found
}
; SeDebugPrivileges is required to read/write memory in some programs.
; This only needs to be called once when the script starts,
; regardless of the number of programs being read (or if the target programs restart)
; Call this before attempting to call any other methods in this class
; i.e. call _ClassMemory.setSeDebugPrivilege() at the very start of the script.
setSeDebugPrivilege(enable := True)
{
h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", DllCall("GetCurrentProcessId"), "Ptr")
; Open an adjustable access token with this process (TOKEN_ADJUST_PRIVILEGES = 32)
DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
VarSetCapacity(ti, 16, 0) ; structure of privileges
NumPut(1, ti, 0, "UInt") ; one entry in the privileges array...
; Retrieves the locally unique identifier of the debug privilege:
DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
NumPut(luid, ti, 4, "Int64")
if enable
NumPut(2, ti, 12, "UInt") ; enable this privilege: SE_PRIVILEGE_ENABLED = 2
; Update the privileges of this process with the new access token:
r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
DllCall("CloseHandle", "Ptr", t) ; close this access token handle to save memory
DllCall("CloseHandle", "Ptr", h) ; close this process handle to save memory
return r
}
; Method: isTargetProcess64Bit(PID, hProcess := "", currentHandleAccess := "")
; Determines if a process is 64 bit.
; Parameters:
; PID The Process ID of the target process. If required this is used to open a temporary process handle.
; hProcess (Optional) A handle to the process, as returned by openProcess() i.e. [derivedObject].hProcess
; currentHandleAccess (Optional) The dwDesiredAccess value used when opening the process handle which has been
; passed as the hProcess parameter. If specifying hProcess, you should also specify this value.
; Return Values:
; True The target application is 64 bit.
; False The target application is 32 bit.
; Null The method failed.
; Notes:
; This is an internal method which is called when the new operator is used. It is used to set the pointer type for 32/64 bit applications so the pointer methods will work.
; This operation requires a handle with PROCESS_QUERY_INFORMATION or PROCESS_QUERY_LIMITED_INFORMATION access rights.
; If the currentHandleAccess parameter does not contain these rights (or not passed) or if the hProcess (process handle) is invalid (or not passed)
; a temporary handle is opened to perform this operation. Otherwise if hProcess and currentHandleAccess appear valid
; the passed hProcess is used to perform the operation.
isTargetProcess64Bit(PID, hProcess := "", currentHandleAccess := "")
{
if !A_Is64bitOS
return False
; If insufficient rights, open a temporary handle
else if !hProcess || !(currentHandleAccess & (this.aRights.PROCESS_QUERY_INFORMATION | this.aRights.PROCESS_QUERY_LIMITED_INFORMATION))
closeHandle := hProcess := this.openProcess(PID, this.aRights.PROCESS_QUERY_INFORMATION)
if (hProcess && DllCall("IsWow64Process", "Ptr", hProcess, "Int*", Wow64Process))
result := !Wow64Process
return result, closeHandle ? this.CloseHandle(hProcess) : ""
}
/*
_Out_ PBOOL Wow64Proces value set to:
True if the process is running under WOW64 - 32bit app on 64bit OS.
False if the process is running under 32-bit Windows!
False if the process is a 64-bit application running under 64-bit Windows.
*/
; Method: suspend() / resume()
; Notes:
; These are undocumented Windows functions which suspend and resume the process. Here be dragons.
; The process handle must have PROCESS_SUSPEND_RESUME access rights.
; That is, you must specify this when using the new operator, as it is not included.
; Some people say it requires more rights and just use PROCESS_ALL_ACCESS, however PROCESS_SUSPEND_RESUME has worked for me.
; Suspending a process manually can be quite helpful when reversing memory addresses and pointers, although it's not at all required.
; As an unorthodox example, memory addresses holding pointers are often stored in a slightly obfuscated manner i.e. they require bit operations to calculate their
; true stored value (address). This obfuscation can prevent Cheat Engine from finding the true origin of a pointer or links to other memory regions. If there
; are no static addresses between the obfuscated address and the final destination address then CE wont find anything (there are ways around this in CE). One way around this is to
; suspend the process, write the true/deobfuscated value to the address and then perform your scans. Afterwards write back the original values and resume the process.
suspend()
{
return DllCall("ntdll\NtSuspendProcess", "Ptr", this.hProcess)
}
resume()
{
return DllCall("ntdll\NtResumeProcess", "Ptr", this.hProcess)
}
; Method: getModules(byRef aModules, useFileNameAsKey := False)
; Stores the process's loaded modules as an array of object modules in the aModules parameter.
; Parameters:
; aModules An unquoted variable name. The loaded modules of the process are stored in this variable as an array of objects.
; Each object in this array has the following keys: Name, lpBaseOfDll, SizeOfImage, and EntryPoint.
; useFileNameAsKey When true, the file name e.g. GDI32.dll is used as the lookup key for each module object.
; Return Values:
; Positive integer The size of the aModules array. (Success)
; -3 EnumProcessModulesEx failed.
; -4 The AHK script is 32 bit and you are trying to access the modules of a 64 bit target process.
getModules(byRef aModules, useFileNameAsKey := False)
{
if (A_PtrSize = 4 && this.IsTarget64bit)
return -4 ; AHK is 32bit and target process is 64 bit, this function wont work
aModules := []
if !moduleCount := this.EnumProcessModulesEx(lphModule)
return -3
loop % moduleCount
{
this.GetModuleInformation(hModule := numget(lphModule, (A_index - 1) * A_PtrSize), aModuleInfo)
aModuleInfo.Name := this.GetModuleFileNameEx(hModule)
filePath := aModuleInfo.Name
SplitPath, filePath, fileName
aModuleInfo.fileName := fileName
if useFileNameAsKey
aModules[fileName] := aModuleInfo
else aModules.insert(aModuleInfo)
}
return moduleCount
}
getEndAddressOfLastModule(byRef aModuleInfo := "")
{
if !moduleCount := this.EnumProcessModulesEx(lphModule)
return -3
hModule := numget(lphModule, (moduleCount - 1) * A_PtrSize)
if this.GetModuleInformation(hModule, aModuleInfo)
return aModuleInfo.lpBaseOfDll + aModuleInfo.SizeOfImage
return -5
}
; lpFilename [out]
; A pointer to a buffer that receives the fully qualified path to the module.
; If the size of the file name is larger than the value of the nSize parameter, the function succeeds
; but the file name is truncated and null-terminated.
; If the buffer is adequate the string is still null terminated.
GetModuleFileNameEx(hModule := 0)
{
; ANSI MAX_PATH = 260 (includes null) - unicode can be ~32K.... but no one would ever have one that size
; So just give it a massive size and don't bother checking. Most coders just give it MAX_PATH size anyway
VarSetCapacity(lpFilename, 2048 * (A_IsUnicode ? 2 : 1))
DllCall("psapi\GetModuleFileNameEx"
, "Ptr", this.hProcess
, "Ptr", hModule
, "Str", lpFilename
, "Uint", 2048 / (A_IsUnicode ? 2 : 1))
return lpFilename
}
; dwFilterFlag
; LIST_MODULES_DEFAULT 0x0
; LIST_MODULES_32BIT 0x01
; LIST_MODULES_64BIT 0x02
; LIST_MODULES_ALL 0x03
; If the function is called by a 32-bit application running under WOW64, the dwFilterFlag option
; is ignored and the function provides the same results as the EnumProcessModules function.
EnumProcessModulesEx(byRef lphModule, dwFilterFlag := 0x03)
{
size := VarSetCapacity(lphModule, 4)
loop
{
DllCall("psapi\EnumProcessModulesEx"
, "Ptr", this.hProcess
, "Ptr", &lphModule
, "Uint", size
, "Uint*", reqSize
, "Uint", dwFilterFlag)
if ErrorLevel
return 0
else if (size >= reqSize)
break
else size := VarSetCapacity(lphModule, reqSize)
}
return reqSize // A_PtrSize ; module count ; sizeof(HMODULE) - enumerate the array of HMODULEs
}
GetModuleInformation(hModule, byRef aModuleInfo)
{
VarSetCapacity(MODULEINFO, A_PtrSize * 3), aModuleInfo := []
return DllCall("psapi\GetModuleInformation"
, "Ptr", this.hProcess
, "Ptr", hModule
, "Ptr", &MODULEINFO
, "UInt", A_PtrSize * 3)
, aModuleInfo := { lpBaseOfDll: numget(MODULEINFO, 0, "Ptr")
, SizeOfImage: numget(MODULEINFO, A_PtrSize, "UInt")
, EntryPoint: numget(MODULEINFO, A_PtrSize * 2, "Ptr") }
}
; Method: hexStringToPattern(hexString)
; Converts the hex string parameter into an array of bytes pattern (AOBPattern) that
; can be passed to the various pattern scan methods i.e. modulePatternScan(), addressPatternScan(), rawPatternScan(), and processPatternScan()
;
; Parameters:
; hexString - A string of hex bytes. The '0x' hex prefix is optional.
; Bytes can optionally be separated using the space or tab characters.
; Each byte must be two characters in length i.e. '04' or '0x04' (not '4' or '0x4')
; ** Unlike the other methods, wild card bytes MUST be denoted using '??' (two question marks)**
;
; Return Values:
; Object Success - The returned object contains the AOB pattern.
; -1 An empty string was passed.
; -2 Non hex character present. Acceptable characters are A-F, a-F, 0-9, ?, space, tab, and 0x (hex prefix).
; -3 Non-even wild card character count. One of the wild card bytes is missing a '?' e.g. '?' instead of '??'.
; -4 Non-even character count. One of the hex bytes is probably missing a character e.g. '4' instead of '04'.
;
; Examples:
; pattern := hexStringToPattern("DEADBEEF02")
; pattern := hexStringToPattern("0xDE0xAD0xBE0xEF0x02")
; pattern := hexStringToPattern("DE AD BE EF 02")
; pattern := hexStringToPattern("0xDE 0xAD 0xBE 0xEF 0x02")
;
; This will mark the third byte as wild:
; pattern := hexStringToPattern("DE AD ?? EF 02")
; pattern := hexStringToPattern("0xDE 0xAD ?? 0xEF 0x02")
;
; The returned pattern can then be passed to the various pattern scan methods, for example:
; pattern := hexStringToPattern("DE AD BE EF 02")
; memObject.processPatternScan(,, pattern*) ; Note the '*'
hexStringToPattern(hexString)
{
AOBPattern := []
hexString := RegExReplace(hexString, "(\s|0x)")
StringReplace, hexString, hexString, ?, ?, UseErrorLevel
wildCardCount := ErrorLevel
if !length := StrLen(hexString)
return -1 ; no str
else if RegExMatch(hexString, "[^0-9a-fA-F?]")
return -2 ; non hex character and not a wild card
else if Mod(wildCardCount, 2)
return -3 ; non-even wild card character count
else if Mod(length, 2)
return -4 ; non-even character count
loop, % length/2
{
value := "0x" SubStr(hexString, 1 + 2 * (A_index-1), 2)
AOBPattern.Insert(value + 0 = "" ? "?" : value)
}
return AOBPattern
}
; Method: stringToPattern(string, encoding := "UTF-8", insertNullTerminator := False)
; Converts a text string parameter into an array of bytes pattern (AOBPattern) that
; can be passed to the various pattern scan methods i.e. modulePatternScan(), addressPatternScan(), rawPatternScan(), and processPatternScan()
;
; Parameters:
; string The text string to convert.
; encoding This refers to how the string is stored in the program's memory.
; UTF-8 and UTF-16 are common. Refer to the AHK manual for other encoding types.
; insertNullTerminator Includes the null terminating byte(s) (at the end of the string) in the AOB pattern.
; This should be set to 'false' unless you are certain that the target string is null terminated and you are searching for the entire string or the final part of the string.
;
; Return Values:
; Object Success - The returned object contains the AOB pattern.
; -1 An empty string was passed.
;
; Examples:
; pattern := stringToPattern("This text exists somewhere in the target program!")
stringToPattern(string, encoding := "UTF-8", insertNullTerminator := False)
{
if !length := StrLen(string)
return -1 ; no str
AOBPattern := []
encodingSize := (encoding = "utf-16" || encoding = "cp1200") ? 2 : 1
requiredSize := StrPut(string, encoding) * encodingSize - (insertNullTerminator ? 0 : encodingSize)
VarSetCapacity(buffer, requiredSize)
StrPut(string, &buffer, length + (insertNullTerminator ? 1 : 0), encoding)
loop, % requiredSize
AOBPattern.Insert(NumGet(buffer, A_Index-1, "UChar"))
return AOBPattern
}
; Method: modulePatternScan(module := "", aAOBPattern*)
; Scans the specified module for the specified array of bytes
; Parameters:
; module - The file name of the module/dll to search e.g. "calc.exe", "GDI32.dll", "Bass.dll" etc
; If no module (null) is specified, the executable file of the process will be used.
; e.g. for calc.exe it would be the same as calling modulePatternScan(, aAOBPattern*) or modulePatternScan("calc.exe", aAOBPattern*)
; aAOBPattern* A variadic list of byte values i.e. the array of bytes to find.
; Wild card bytes should be indicated by passing a non-numeric value eg "?".
; Return Values:
; Positive int Success. The memory address of the found pattern.
; Null Failed to find or retrieve the specified module. ErrorLevel is set to the returned error from getModuleBaseAddress()
; refer to that method for more information.
; 0 The pattern was not found inside the module
; -9 VirtualQueryEx() failed
; -10 The aAOBPattern* is invalid. No bytes were passed
modulePatternScan(module := "", aAOBPattern*)
{
MEM_COMMIT := 0x1000, MEM_MAPPED := 0x40000, MEM_PRIVATE := 0x20000
, PAGE_NOACCESS := 0x01, PAGE_GUARD := 0x100
if (result := this.getModuleBaseAddress(module, aModuleInfo)) <= 0
return "", ErrorLevel := result ; failed
if !patternSize := this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
return -10 ; no pattern
; Try to read the entire module in one RPM()
; If fails with access (-1) iterate the modules memory pages and search the ones which are readable
if (result := this.PatternScan(aModuleInfo.lpBaseOfDll, aModuleInfo.SizeOfImage, patternMask, AOBBuffer)) >= 0
return result ; Found / not found
; else RPM() failed lets iterate the pages
address := aModuleInfo.lpBaseOfDll
endAddress := address + aModuleInfo.SizeOfImage
loop
{
if !this.VirtualQueryEx(address, aRegion)
return -9
if (aRegion.State = MEM_COMMIT
&& !(aRegion.Protect & (PAGE_NOACCESS | PAGE_GUARD)) ; can't read these areas
;&& (aRegion.Type = MEM_MAPPED || aRegion.Type = MEM_PRIVATE) ;Might as well read Image sections as well
&& aRegion.RegionSize >= patternSize
&& (result := this.PatternScan(address, aRegion.RegionSize, patternMask, AOBBuffer)) > 0)
return result
} until (address += aRegion.RegionSize) >= endAddress
return 0
}
; Method: addressPatternScan(startAddress, sizeOfRegionBytes, aAOBPattern*)
; Scans a specified memory region for an array of bytes pattern.
; The memory entire area specified must be readable for this method to work,
; i.e. you must ensure the area is readable before calling this method.
; Parameters:
; startAddress The memory address from which to begin the search.
; sizeOfRegionBytes The numbers of bytes to scan in the memory region.
; aAOBPattern* A variadic list of byte values i.e. the array of bytes to find.
; Wild card bytes should be indicated by passing a non-numeric value eg "?".
; Return Values:
; Positive integer Success. The memory address of the found pattern.
; 0 Pattern not found
; -1 Failed to read the memory region.
; -10 An aAOBPattern pattern. No bytes were passed.
addressPatternScan(startAddress, sizeOfRegionBytes, aAOBPattern*)
{
if !this.getNeedleFromAOBPattern(patternMask, AOBBuffer, aAOBPattern*)
return -10
return this.PatternScan(startAddress, sizeOfRegionBytes, patternMask, AOBBuffer)
}
; Method: processPatternScan(aAOBPattern*)
; Scan the memory space of the current process for an array of bytes pattern.
; To use this in a loop (scanning for multiple occurrences of the same pattern),
; simply call it again passing the last found address + 1
; Parameters:
; startAddress - The memory address from which to begin the search.
; endAddress - The memory address at which the search ends. Defaults to 0x7FFFFFFF i.e. the maximum useful area for a 32 bit process
; Note: The entire pattern must be occur inside this range for a match to be found. The range is inclusive.
; aAOBPattern* - A variadic list of byte values i.e. the array of bytes to find.
; Wild card bytes should be indicated by passing a non-numeric value eg "?".
; Return Values:
; Positive integer - Success. The memory address of the found pattern.
; 0 The pattern was not found.
; -1 VirtualQueryEx() failed.
; -2 Failed to read a memory region.