-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmain.asm
More file actions
1879 lines (1561 loc) · 77.7 KB
/
main.asm
File metadata and controls
1879 lines (1561 loc) · 77.7 KB
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
; Declare the 'main' procedure as public, making it the entry point for the linker.
public main
option casemap:none
include \masm64\include64\win64.inc
include \masm64\include64\advapi32.inc
;include \masm64\include64\winmm.inc
includelib \masm64\lib64\advapi32.lib
;includelib \masm64\lib64\winmm.lib
; Declare external procedures that reside in Windows DLLs (like kernel32.dll, user32.dll)
; or the C runtime library (for strcmp). The linker will resolve these addresses.
; ': proc' specifies that these are procedure labels.
extern MessageBoxA : proc ; USER32.DLL: Displays a message box.
extern OpenProcess : proc ; KERNEL32.DLL: Opens an existing process object.
extern VirtualAllocEx : proc ; KERNEL32.DLL: Reserves or commits memory in another process.
extern VirtualProtect : proc ; KERNEL32.DLL: Changes the protection on a region of committed pages in the virtual address space of the calling process.
extern CreateRemoteThread : proc ; KERNEL32.DLL: Creates a thread that runs in another process.
extern CloseHandle : proc ; KERNEL32.DLL: Closes an open object handle.
extern WriteProcessMemory : proc ; KERNEL32.DLL: Writes data to memory in another process.
extern GetModuleHandleA : proc ; KERNEL32.DLL: Retrieves a module handle for a specified module (if loaded).
extern GetProcAddress : proc ; KERNEL32.DLL: Retrieves the address of an exported function in a DLL.
extern CreateToolhelp32Snapshot : proc ; KERNEL32.DLL: Takes a snapshot of specified processes, heaps, modules, threads.
extern Process32First : proc ; KERNEL32.DLL: Retrieves information about the first process in a snapshot.
extern Process32Next : proc ; KERNEL32.DLL: Retrieves information about the next process in a snapshot.
extern strcmp : proc ; MSVCRT.DLL (usually): Compares two C strings.
; === From Kernel32.dll ===
extern LoadLibraryA : proc ; Loads the specified DLL into the address space of the calling process.
extern GetModuleHandleA : proc ; Retrieves a module handle for a specified module.
extern GetProcAddress : proc ; Retrieves the address of an exported function or variable from the specified DLL.
extern ExitThread : proc ; Ends the calling thread and optionally returns an exit code.
; === KERNEL32.DLL Functions === ;ForDebugPrivFunction
extern GetStdHandle : proc ; Retrieves a handle to the specified standard device (input, output, or error).
extern WriteConsoleA : proc ; Writes a character string to a console screen buffer.
extern GetCurrentProcess : proc ; Retrieves a pseudo handle for the current process.
extern GetLastError : proc ; Retrieves the calling thread's last-error code value.
extern ExitProcess : proc ; Ends the current process and returns an exit code.
; === Standard Handles ===
STD_OUTPUT_HANDLE equ -11 ; Standard output handle for console
; === From Winmm.dll ===
extern timeGetTime : proc ; Retrieves the system time, in milliseconds, since Windows was started.
; === Additional useful APIs you might be calling ===
extern QueryPerformanceCounter : proc ; Retrieves the current value of the high-resolution performance counter.
extern GetTickCount : proc ; Retrieves the number of milliseconds that have elapsed since the system was started.
.const
Acceleration dq 5 ; Acceleration factor for time functions
; Section for initialized data.
.data
; Define null-terminated strings for message box titles and content.
msg_title db "x64 dll injector", 0; Window title for message boxes.
msg_not_found db "Process is not running.", 0 ; Error message if target process isn't found.
msg_cant_inject db "Injection failed.", 0 ; Error message if injection steps fail.
msg_success db "Injection succeded.", 0 ; Success message.
kernel32 db "kernel32.dll", 0 ; Name of the core Windows library.
load_library db "LoadLibraryA", 0 ; Name of the function used to load DLLs.
szGetProcAddress db 'GetProcAddress',0 ; GetProcAddress function name
szGetModuleHandleA db 'GetModuleHandleA',0 ; GetProcAddress function name
szFailed db 'Failed', 0 ; Generic failure message
; Time function names
szGetTickCount db 'GetTickCount',0
sztimeGetTime db 'timeGetTime',0
szQueryPerformanceCounter db 'QueryPerformanceCounter',0
szWinmm db 'winmm.dll',0
; Debug privilege constant
SEDEBUGNAME db 'SeDebugPrivilege',0
; DLL names
szAdvapi32 db "Advapi32.dll", 0
szShell32 db "Shell32.dll", 0
; Function names
szOpenProcessToken db "OpenProcessToken", 0
szLookupPrivilegeValue db "LookupPrivilegeValueA", 0
szAdjustTokenPrivileges db "AdjustTokenPrivileges", 0
szIsUserAnAdmin db "IsUserAnAdmin", 0
; Messages
szThreadCreated db 'Thread has been created', 0
szSuccesser db 'Success', 0
szElevationRequired db 'This application requires administrator privileges',0
szWin11Detected db 'Windows 11 detected, using enhanced security bypass',0
szProcessNotFound db 'Failed to find target process',0
szOpenProcessFailed db 'Failed to open process handle',0
szMemoryAllocationFailed db 'Failed to allocate memory in target process',0
szMemoryWriteFailed db 'Failed to write code to target process',0
szGetProcAddressFailed db 'Failed to get GetProcAddress function',0
szThreadCreationFailed db 'Failed to create remote thread',0
szSuccess db 'Time acceleration hooks installed successfully',0
szWaitFailed db 'Failed to wait for thread completion',0
szOSVersionError db 'Failed to get OS version',0
szPlatformError db 'Operating system detection failed',0
szGetProcessMsg db "Getting current process...", 0
szOpenTokenMsg db "Opening process token with rights: ", 0
szLookupPrivMsg db "Looking up privilege value...", 0
szAdjustTokenMsg db "Adjusting token privileges...", 0
szErrorOpenToken db "Error: Failed to open process token. Error code: ", 0
szErrorLookupPriv db "Error: Failed to lookup privilege value. Error code: ", 0
szErrorAdjustToken db "Error: Failed to adjust token privileges. Error code: ", 0
szNumBuffer db 32 dup(0) ; Buffer for number conversion (increased size for 64-bit)
; Windows 11 detection variables
dwMajorVersion dd ?
dwMinorVersion dd ?
dwBuildNumber dd ?
align 8 ; 64-bit alignment
;target_process db "Notepad.exe", 0 ; The executable name of the process to inject into. CAP SENSITIVE PROCESS
target_process db "Exodus-Win64-Shipping.exe", 0 ; "TenMilesToSafety-Win64-Shipping.exe", 0
library_name db "C:\\DllToInject.dll", 0 ; --- IMPORTANT: Full path to the DLL that will be injected. ---
; Calculate the length of the library_name string *excluding* the null terminator.
; '$' represents the current address, so '$ - library_name' gives the length.
library_len equ $ - library_name
.data?
hProcess dq ? ; Handle to target process
lpInjected dq ? ; Address of injected code in target process
lenInjected equ EndInjected - StartInjected ; Length of injected code
hThread dq ? ; Handle to created remote thread
JmpBuffer db 14 dup(?) ; Buffer for jump instruction (increased for x64)
; Original function pointers (64-bit)
dGetTickCount dq ?
dtimeGetTime dq ?
dQueryPerformanceCounter dq ?
dPerfCounterResult dq ?
; Base time values
BaseTickCount dq ?
BaseGetTime dq ?
BasePerformanceCount dq ?
hStdOut dq ?
bytesWritten dq ?
; For FindProcessByName
hSnapshot dq ?
align 16 ; Ensure proper alignment for 64-bit
; Section for executable code.
.code
; Definition for IMAGE_NT_HEADERS64
IMAGE_NT_HEADERS64 STRUCT
Signature DWORD ? ; Should be IMAGE_NT_SIGNATURE ('PE\0\0')
FileHeader IMAGE_FILE_HEADER <> ; Nested structure
OptionalHeader IMAGE_OPTIONAL_HEADER64 <> ; Nested structure (64-bit version)
IMAGE_NT_HEADERS64 ENDS
IMAGE_IMPORT_DESCRIPTOR STRUCT
UNION
Characteristics DWORD ?
OriginalFirstThunk DWORD ? ; RVA to original unbound IAT (PIMAGE_THUNK_DATA)
ENDS
TimeDateStamp DWORD ?
ForwarderChain DWORD ?
Name DWORD ? ; RVA to the null-terminated string name of the DLL
FirstThunk DWORD ? ; RVA to IAT (if bound this IAT has actual addresses)
IMAGE_IMPORT_DESCRIPTOR ENDS
;Start of Image_function
StartInjected:
Injected PROC
;int 3 ;Breakpoint if needed
jmp start_code ; Jump over the strings
; Embedded strings section
; The processor will never execute these as they're jumped over
GetProcAddrStr db 'GetProcAddress', 0
GetModuleHandleStr db 'GetModuleHandleA', 0
GetTickCountStr db 'GetTickCount', 0
QueryPerformanceCounterStr db 'QueryPerformanceCounter', 0
LoadLibraryStr db 'LoadLibraryA', 0
WinmmStr db 'winmm.dll', 0
TimeGetTimeStr db 'timeGetTime', 0
VirtualProtectStr db 'VirtualProtect', 0
; Add padding to ensure proper alignment if needed
;align 8
pdGetTickCount dq 0
pdQueryPerformanceCounter dq 0
pdtimeGetTime dq 0
pBasePerformanceCount dq 0, 0
pAcceleration dq 5
pBaseGetTime dq 0
pBaseTickCount dq 0
start_code:
; ... (prologue remains the same) ...
push rbx
push rsi
push rdi
push r12 ; <-- We will use R12 to store the EXE base address
push r13
push r14
push r15
sub rsp, 50h
; Save GetProcAddress (assuming it was passed in RCX)
; --- Initial Setup ---
; Save GetProcAddress (assuming it was passed in RCX by the loader/caller)
; This is a placeholder/assumption based on the comment. The subsequent code
; dynamically FINDS GetProcAddress, making this initial save potentially redundant
; or part of a larger context not shown here.
; R15 will eventually hold the dynamically found GetProcAddress VA.
mov r15, rcx
; --- Find Kernel32 Base Address (Keep existing PEB method if it works) ---
; (Assuming the PEB walk to get Kernel32 base into RBX is working correctly)
; --- Find Kernel32 Base Address via PEB LDR ---
; This section walks the Process Environment Block (PEB) and its Loader Data (LDR)
; to find the base address of kernel32.dll in memory. This is a standard technique
; for dynamically finding loaded modules without relying on potentially hooked APIs.
; Access the PEB via the Thread Environment Block (TEB) using the GS segment register.
; GS:[60h] -> PEB address
xor rax, rax
mov rax, gs:[60h] ; PEB
;
; GS Segment Base TEB PEB
; +-----------------+ +-----------------+ +-----------------+
; | ... | --> | ... | --> | ... |
; | 0x60: PEB Ptr | ----+ | | 0x18: Ldr Ptr | ----+
; | ... | | ... | | ... | |
; +-----------------+ +-----------------+ +-----------------+ |
; V
; Get the pointer to the PEB_LDR_DATA structure.
; PEB+18h -> LDR_DATA address
mov rax, [rax+18h] ; LDR_DATA
;
; PEB PEB_LDR_DATA
; +-----------------+ +-------------------------------------+
; | ... | | ... |
; | 0x18: Ldr Ptr | --> | 0x30: InInitializationOrderModuleList | ----+
; | ... | | ... | |
; +-----------------+ +-------------------------------------+ |
; V
; Get the Flink (forward link) of the InInitializationOrderModuleList.
; This list contains modules in the order they were initialized.
; The first entry usually points back to the head, the second is the executable,
; the third is ntdll.dll, and the fourth is typically kernel32.dll.
; LDR_DATA+30h -> LIST_ENTRY.Flink (points to the first entry's Flink)
mov rsi, [rax+30h] ; InInitializationOrderModuleList.Flink
;
; LDR_DATA LIST_ENTRY (Head) LIST_ENTRY (Ntdll) LIST_ENTRY (Kernel32)
; +------------------------+ +-----------------+ +-----------------+ +-----------------+
; ... | 0x30: InitOrderFlink | ---> | Flink | ---> | Flink | ---> | Flink | -> ...
; | 0x38: InitOrderBlink | -- | Blink | <--- | Blink | <--- | Blink | <- ...
; ... +------------------------+ | | ... | | ... | | ... |
; | | ModuleEntry Ptr | | ModuleEntry Ptr | | ModuleEntry Ptr |
; | +-----------------+ +-----------------+ +-----------------+
; +------------------------------------------------------------^ (Points to list head entry itself)
;
; Traverse the linked list to get to the kernel32.dll entry.
; Current RSI -> List Head Entry -> Executable Entry -> Ntdll Entry -> Kernel32 Entry
mov rsi, [rsi] ; -> ntdll entry
mov rsi, [rsi] ; -> kernel32 entry
;
; ... -> LIST_ENTRY (Ntdll) LIST_ENTRY (Kernel32) LDR_DATA_TABLE_ENTRY (Kernel32)
; +-----------------+ +-----------------+ +-----------------+
; -> | Flink | ---> | Flink | --+ | ... |
; | Blink | <--- | Blink | | | 0x10: DllBase | ----> Kernel32 Base Address
; | ... | | ... | +--> | ... |
; | ModuleEntry Ptr | ---> | ModuleEntry Ptr | | FullDllName |
; +-----------------+ +-----------------+ | ... |
; +-----------------+
;
; Get the base address (DllBase) of kernel32.dll from its LDR_DATA_TABLE_ENTRY.
; LDR_DATA_TABLE_ENTRY+10h -> DllBase
mov rbx, [rsi+10h] ; RBX = Kernel32 Base Address
; --- Find GetProcAddress within kernel32.dll export table ---
; 1. Find the export directory
; --- Find GetProcAddress within kernel32.dll export table ---
; Now that we have the base address of Kernel32.dll (in RBX), we need to parse
; its PE (Portable Executable) header to find the Export Address Table (EAT),
; and then search that table for the address of the 'GetProcAddress' function.
; 1. Find the PE header and the Export Directory.
; Get the offset to the PE header ('PE\0\0' signature) from the DOS header.
; Kernel32Base + 3Ch -> IMAGE_DOS_HEADER.e_lfanew (Offset to PE Header)
mov eax, [rbx+3Ch] ; Get PE header offset
;
; Kernel32.dll Base (RBX)
; +-------------------------+
; | MZ Header ('MZ'...) |
; | ... |
; | 0x3C: e_lfanew (Offset) | ---> Value 'X'
; | ... |
; +-------------------------+
; |
; +-----> (RBX + 'X') = PE Header Address
;
; Calculate the virtual address (VA) of the PE header.
; R13 = Kernel32 Base + PE Header Offset
lea r13, [rbx+rax] ; R13 = PE header address
; Get export directory VA - different offset for 64-bit PE
;
; Kernel32.dll Memory Layout
; +-------------------------+ <-- RBX (Base Address)
; | DOS Header |
; | ... |
; | DOS Stub |
; | ... |
; +-------------------------+ <-- R13 (PE Header Address)
; | PE Signature ('PE\0\0') |
; | IMAGE_FILE_HEADER |
; | IMAGE_OPTIONAL_HEADER64 |
; | ... |
; | Data Directories | ----+
; | ... | |
; | Section Headers | |
; | ... | V
; +-------------------------+
;
; Get the Relative Virtual Address (RVA) of the Export Directory Table.
; This is located within the Data Directories array in the Optional Header.
; For 64-bit PE files, the Export Table entry is at offset 88h from the PE header start.
; PE Header + 88h -> IMAGE_OPTIONAL_HEADER64.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
mov eax, [r13+88h] ; RAX = export directory RVA
;
; PE Header (R13)
; +-----------------------------+
; | PE Signature |
; | File Header |
; | Optional Header |
; | ... |
; | 0x88: Export Dir RVA ------ | ---> Value 'Y' (Export Directory RVA)
; | 0x90: Export Dir Size |
; | 0x98: Import Dir RVA |
; | ... |
; +-----------------------------+
;
; Calculate the virtual address (VA) of the Export Directory.
; R14 = Kernel32 Base + Export Directory RVA
lea r14, [rbx+rax] ; R14 = export directory VA
; Get number of functions
;
; Kernel32.dll Memory Layout
; +-------------------------+ <-- RBX (Base Address)
; | ... |
; | PE Header |
; | ... |
; +-------------------------+ <-- R14 (Export Directory VA)
; | Export Directory Data |
; | ... |
; +-------------------------+
;
; 2. Get pointers to the necessary arrays within the Export Directory.
; Get the number of function names in the export table.
; Export Directory + 18h -> NumberOfNames
mov ecx, [r14+18h] ; ECX = number of functions (NumberOfNames)
; Get address arrays
;
; Export Directory (R14)
; +--------------------------------+
; | ... |
; | 0x18: NumberOfNames | ---> Value 'N' (Count of names) stored in ECX
; | 0x1C: AddressOfFunctions RVA |
; | 0x20: AddressOfNames RVA |
; | 0x24: AddressOfNameOrdinals RVA|
; | ... |
; +--------------------------------+
;
; Get the RVA of the Export Address Table (EAT) - an array of function RVAs.
; Export Directory + 1Ch -> AddressOfFunctions RVA
;
; Kernel32.dll Memory Layout Export Directory (R14) AddressOfFunctions Array (R8)
; +-------------------------+ +-------------------------+ +-------------------------+
; | ... | | ... | | RVA Func 0 | <-- Indexed by Ordinal
; | Export Directory | ---------> | 0x1C: AddrOfFunc RVA -> | --> | RVA Func 1 |
; | ... | | ... | | ... |
; +-------------------------+ +-------------------------+ | RVA Func N-1 |
; +-------------------------+
;
; Get the RVA of the Export Name Pointer Table (ENPT) - an array of name RVAs.
; Export Directory + 20h -> AddressOfNames RVA
mov eax, [r14+1Ch] ; EAX = AddressOfFunctions RVA
; Calculate the VA of the Export Address Table (EAT).
; R8 = Kernel32 Base + AddressOfFunctions RVA
lea r8, [rbx+rax] ; R8 = AddressOfFunctions VA
;
; Kernel32.dll Memory Layout Export Directory (R14) AddressOfFunctions Array (R8)
; +-------------------------+ +-------------------------+ +-------------------------+
; | ... | | ... | | RVA Func 0 | <-- Indexed by Ordinal
; | Export Directory | ---------> | 0x1C: AddrOfFunc RVA -> | --> | RVA Func 1 |
; | ... | | ... | | ... |
; +-------------------------+ +-------------------------+ | RVA Func N-1 |
; +-------------------------+
;
; Get the RVA of the Export Name Pointer Table (ENPT) - an array of name RVAs.
; Export Directory + 20h -> AddressOfNames RVA
mov eax, [r14+20h] ; EAX = AddressOfNames RVA
; Calculate the VA of the Export Name Pointer Table (ENPT).
; R9 = Kernel32 Base + AddressOfNames RVA
lea r9, [rbx+rax] ; R9 = AddressOfNames VA
;
; Kernel32.dll Memory Layout Export Directory (R14) AddressOfNames Array (R9)
; +-------------------------+ +-------------------------+ +-------------------------+
; | ... | | ... | | RVA Name 0 -------------+---> "FunctionNameA"
; | Export Directory | ---------> | 0x20: AddrOfNames RVA ->| --> | RVA Name 1 -------------+---> "FunctionNameB"
; | ... | | ... | | ... |
; +-------------------------+ +-------------------------+ | RVA Name N-1 -----------+---> "GetProcAddress"
; +-------------------------+ ^--- (Example, if found)
;
; Get the RVA of the Export Ordinal Table (EOT) - an array of WORD ordinals.
; Export Directory + 24h -> AddressOfNameOrdinals RVA
mov eax, [r14+24h] ; EAX = AddressOfNameOrdinals RVA
; Calculate the VA of the Export Ordinal Table (EOT).
; R10 = Kernel32 Base + AddressOfNameOrdinals RVA
lea r10, [rbx+rax] ; R10 = AddressOfNameOrdinals VA
; Now start loop to find GetProcAddress
;
; Kernel32.dll Memory Layout Export Directory (R14) AddressOfNameOrdinals Array (R10)
; +-------------------------+ +-------------------------+ +-------------------------+
; | ... | | ... | | Ordinal 0 (WORD) | <-- Maps Name Index to Function Index (Ordinal)
; | Export Directory | ---------> | 0x24: AddrOfOrd RVA --->| --> | Ordinal 1 (WORD) |
; | ... | | ... | | ... |
; +-------------------------+ +-------------------------+ | Ordinal N-1 (WORD) |
; +-------------------------+
; ^--- Indexed by RSI*2
; 3. Loop through the exported names to find "GetProcAddress".
; Initialize index counter for the names array.
xor rsi, rsi ; RSI = index counter
find_getprocaddr_loop:
; Check if we've checked all functions
; Check if we have iterated through all the names in the AddressOfNames array.
; Compare current index (RSI) with the total number of names (ECX).
cmp rsi, rcx
jae SkipHooks ; If we've checked all functions and not found it
; Get the name RVA for this function
; Get the RVA of the current function name from the AddressOfNames array (R9).
; Each entry is a 4-byte RVA.
; EAX = RVA of function name at index RSI
mov eax, [r9+rsi*4] ; EAX = RVA of current function name
;
; AddressOfNames Array (R9) Name String Memory Location
; +-------------------------+ +------------------+
; | RVA Name 0 | | FunctionNameA \0 |
; | ... | +------------------+
; | RVA Name RSI -----------|-----------------> | Current Func Name\0| <-- RDI will point here
; | ... | +------------------+
; +-------------------------+ ^ -- (RBX + EAX)
; ^-- R9 + RSI*4
;
; Calculate the VA of the current function name string.
; RDI = Kernel32 Base + Name RVA
lea rdi, [rbx+rax] ; RDI = VA of current function name
; Pointer to our string to compare
; Get the VA of our target string "GetProcAddress".
; Assume GetProcAddrStr is defined elsewhere in the data section.
lea r11, [GetProcAddrStr]
;
; Current Func Name (RDI) Target String (R11)
; +---------------------+ +---------------------+
; | 'C' 'u' 'r' 'r' ... | | 'G' 'e' 't' 'P' ... |
; +---------------------+ +---------------------+
; ^ ^
; | |
; RDI R11
;
; Compare the current function name (pointed to by RDI) with "GetProcAddress" (pointed to by R11).
compare_str:
movzx eax, byte ptr [rdi] ; Get byte from function name
movzx edx, byte ptr [r11] ; Get byte from our string
; Check if we reached the null terminator of the current function name.
test eax, eax
jz check_end
; Compare the bytes.
cmp eax, edx
jne next_function
; Bytes match, advance pointers to the next character.
inc rdi
inc r11
jmp compare_str
check_end:
; We reached the end of the function name string ([RDI] == 0).
; Now check if we also reached the end of our target string "GetProcAddress"
test edx, edx ; If our string also ended, we have a match
jnz next_function ; If not, strings don't match
; Found GetProcAddress, get its ordinal
; 4. Get the function address using the matched index (RSI).
; Get the ordinal associated with this name index (RSI).
; The ordinal is stored in the AddressOfNameOrdinals array (R10).
; Each entry is a 2-byte WORD.
; EAX = Ordinal value (zero-extended to 32 bits)
movzx eax, word ptr [r10+rsi*2] ; EAX = ordinal (zero extended to 32-bit)
; Use the ordinal to get the function address
;
; AddressOfNameOrdinals Array (R10)
; +-------------------------+
; | Ordinal 0 (WORD) |
; | ... |
; | Ordinal RSI (WORD) -----| ---> Value 'Ord' (The ordinal for the matched name)
; | ... |
; +-------------------------+
; ^-- R10 + RSI*2
;
; Use the ordinal (EAX) as an index into the AddressOfFunctions array (R8)
; to get the RVA of the actual GetProcAddress function.
; Note: The ordinal is the direct index into the AddressOfFunctions array.
; EAX = RVA of the function
mov eax, [r8+rax*4] ; EAX = RVA of function
;
; AddressOfFunctions Array (R8) Function Code Location
; +-------------------------+ +----------------------+
; | RVA Func 0 | | GetProcAddress Code | <-- R15 will point here
; | ... | | ... |
; | RVA Func 'Ord' ---------|--------------->| |
; | ... | +----------------------+
; +-------------------------+ ^ -- (RBX + EAX)
; ^-- R8 + Ordinal*4
;
; Calculate the final Virtual Address (VA) of GetProcAddress.
; R15 = Kernel32 Base + Function RVA
lea r15, [rbx+rax] ; R15 = VA of GetProcAddress
; Jump to the rest of the code, R15 now holds the address of GetProcAddress.
jmp continue_with_getprocaddr
; Label for moving to the next function name if the current one doesn't match.
next_function:
inc rsi
jmp find_getprocaddr_loop
; continue_with_getprocaddr: Label to jump to after successfully finding GetProcAddress.
continue_with_getprocaddr:
; R15 now contains the valid Virtual Address of the GetProcAddress function.
; The rest of the shellcode/program can now use R15 to call GetProcAddress.
; Example: call r15
; --- Get EXE Base Address using GetModuleHandleA(NULL) ---
; 1. Get address of GetModuleHandleA itself
; r15 is expected to have <kernel32.GetProcAddress>
lea rdx, [GetModuleHandleStr] ; szGetModuleHandleA ; RDX = "GetModuleHandleA"
mov rcx, rbx ; RCX = Kernel32 Base Address
call r15 ; Call GetProcAddress(hKernel32, "GetModuleHandleA")
test rax, rax ; Check if GetProcAddress succeeded
jz SkipHooks ; Or some other error handling if GetModuleHandleA not found
; RAX now holds the address of GetModuleHandleA
; 2. Call GetModuleHandleA(NULL) to get EXE base
xor rcx, rcx ; RCX = NULL (Argument for GetModuleHandleA)
call rax ; Call GetModuleHandleA(NULL)
;RAX now holds the injected modules main address
test rax, rax ; Check if GetModuleHandleA(NULL) succeeded
jz SkipHooks ; Or some other error handling if base address is NULL
; RAX now holds the EXE Base Address
mov r12, rax ; R12 = EXE Base Address
; ... (GetProcAddress for GetTickCount, QPC, LoadLibraryA as before, using RBX for kernel32 base) ...
lea rdx, [GetTickCountStr] ; szGetTickCount
mov rcx, rbx ; KERNEL32 base
call r15 ; Call GetProcAddress
mov [pdGetTickCount], rax
lea rdx, [QueryPerformanceCounterStr] ; szQueryPerformanceCounter
mov rcx, rbx ; KERNEL32 base
call r15 ; Call GetProcAddress
mov [pdQueryPerformanceCounter], rax
lea rdx, [LoadLibraryStr] ; load_library
mov rcx, rbx ; kernel32 base
call r15 ; GetProcAddress for LoadLibraryA
; Now RAX has LoadLibraryA address
lea rcx, [WinmmStr] ; szWinmm
call rax ; Call LoadLibraryA(szWinmm)
mov r14, rax ; R14 = winmm.dll handle (check for NULL)
test r14, r14
jz SkipWinmmHooking
; Get timeGetTime function from winmm.dll
lea rdx, [TimeGetTimeStr] ; sztimeGetTime
mov rcx, r14 ; winmm.dll handle
call r15 ; Call GetProcAddress
mov [pdtimeGetTime], rax
test rax, rax
jz SkipWinmmHooking
;Get VirtualProtect for HookFunction
lea rdx, [VirtualProtectStr] ; VirtualProtect
mov rcx, rbx ; kernel32 base
call r15
mov r13, rax
; ... (Initialize base time values as before) ...
; r13d = VirtualProtectAddr for hook function
; r14 = winmm.dll addr <-- 12,13,14 get overwritten by hook function
; rax = timeGetTime addr
; [dtimeGetTime]
; [pdGetTickCount]
; [pdQueryPerformanceCounter]
; [pdGetTickCount]
SkipWinmmHooking:
mov rax, [pdQueryPerformanceCounter]
test rax, rax
jz SkipHooks
lea rcx, pBasePerformanceCount
call rax ; init QueryPerformanceCounter
; === Set up hooks using SetHook function ===
; Hook GetTickCount (Targeting EXE's IAT)
mov rax, [pdGetTickCount]
test rax, rax
jz SkipHook1
mov r8, rax ; R8 = Original Address (GetTickCount)
lea rdx, GetTickCountHook ; RDX = Hook Address
mov rcx, r12 ; RCX = Module Base (EXE Base Address) <--- Correction
call SetHook ; SetHook(hExe, GetTickCountHook, pGetTickCount)
SkipHook1:
; Hook timeGetTime (Targeting WinMM's IAT - *ASSUMPTION*)
; This assumes you want to hook calls *originating* from winmm.dll or
; modules importing directly from it *after* it's loaded here.
; If you want to hook the EXE's calls to timeGetTime, use R12 for RCX here too.
mov rax, [pdtimeGetTime]
test rax, rax
jz SkipHook2
mov r8, rax ; R8 = Original Address (timeGetTime)
lea rdx, timeGetTimeHook ; RDX = Hook Address
mov rcx, r12 ; RCX = Module Base, R12 is base of EXE, R14 is (Winmm.dll handle)
call SetHook ; SetHook(hWinmm, timeGetTimeHook, pTimeGetTime)
SkipHook2:
; Hook QueryPerformanceCounter (Targeting EXE's IAT)
mov rax, [pdQueryPerformanceCounter]
test rax, rax
jz SkipHook3
mov r8, rax ; R8 = Original Address (QueryPerformanceCounter)
lea rdx, QueryPerformanceCounterHook ; RDX = Hook Address
mov rcx, r12 ; RCX = Module Base (EXE Base Address) <--- Correction
call SetHook ; SetHook(hExe, QueryPerformanceCounterHook, pQPC)
SkipHook3:
SkipHooks:
; ... (epilogue remains the same) ...
add rsp, 50h
pop r15
pop r14
pop r13
pop r12 ; <-- Restore R12
pop rdi
pop rsi
pop rbx
ret
Injected ENDP
; =====================================================================
; Hooks the specified function via IAT patching (Manual Stack Frame).
; RCX = inModule (Base address of module containing the IAT)
; RDX = inHookProc (Address of the hook function)
; R8 = inOriginalFunction (Address of the original function to find in IAT)
; RAX = Returns 1 on success, 0 on failure
; =====================================================================
SetHook proc
jmp start_hook_code ; Jump over the strings
pdVirtualProtect dq 0
align 8
start_hook_code:
; --- Manual Prologue ---
; Save non-volatile registers used by the function
push rsi
push rdi
push rbx
push r12
push r13
push r14
; Pushed 6 registers = 6 * 8 = 48 (30h) bytes. RSP is currently 16-byte aligned.
; Allocate stack space:
; Need 8 bytes for dwOldProtect
; Need 32 bytes (20h) shadow space for the upcoming 'call'
; Total needed = 40 bytes (28h)
; To maintain 16-byte alignment, must subtract a multiple of 16.
; Smallest multiple >= 40 bytes is 48 bytes (30h).
sub rsp, 30h
; Stack layout: [rsp+20h] = dwOldProtect, [rsp+0]..[rsp+1Fh] = shadow space
mov [pdVirtualProtect], r13 ; Save register
; --- Function Body ---
; Move input parameters to non-volatile registers for safekeeping
mov r12, rcx ; r12 = inModule
mov r13, rdx ; r13 = inHookProc
mov r14, r8 ; r14 = inOriginalFunction
; --- PE Header Parsing ---
mov rdi, r12 ; RDI = Module base address
; Check DOS Header signature
cmp word ptr [rdi + IMAGE_DOS_HEADER.e_magic], IMAGE_DOS_SIGNATURE
jne CodeFail
; Get offset to NT Headers (e_lfanew is a DWORD)
mov ebx, dword ptr [rdi + IMAGE_DOS_HEADER.e_lfanew]
add rdi, rbx ; RDI now points to IMAGE_NT_HEADERS64
; Check NT Header signature
cmp dword ptr [rdi + IMAGE_NT_HEADERS64.Signature], IMAGE_NT_SIGNATURE
jne CodeFail
; Get RVA of the Import Directory Table (DataDirectory index 1)
mov edi, dword ptr [rdi + IMAGE_NT_HEADERS64.OptionalHeader.DataDirectory + (IMAGE_DIRECTORY_ENTRY_IMPORT * SIZEOF IMAGE_DATA_DIRECTORY) + IMAGE_DATA_DIRECTORY.VirtualAddress]
test edi, edi ; Check if RVA is zero (no import table)
jz CodeFail
; rdi is automatically zero-extended
add rdi, r12 ; RDI = VA of the first IMAGE_IMPORT_DESCRIPTOR
ImportLoop:
; Check for the end of the Import Directory Table (terminator descriptor)
mov eax, dword ptr [rdi + 12] ; Use numeric offset for Name (0Ch)
or eax, dword ptr [rdi + 16] ; Use numeric offset for FirstThunk (10h)
jz DoneCheckingImports
; Get RVA of the Import Address Table (IAT) - using FirstThunk
mov esi, dword ptr [rdi + 16] ; Use numeric offset for FirstThunk (10h)
test esi, esi
jz NextImportDescriptor
; rsi is automatically zero-extended
add rsi, r12 ; RSI = VA of the IAT
ThunkLoop:
; Get the current function pointer from the IAT
mov rax, qword ptr [rsi]
test rax, rax ; Check for the null terminator of this IAT list
jz NextImportDescriptor
; Compare the IAT entry with the function we want to hook
cmp rax, r14 ; r14 holds inOriginalFunction
jne ContinueThunks
; --- Match Found ---
; Make the memory page containing the IAT entry writable
mov rcx, rsi ; arg 1: lpAddress = Address of the IAT entry
mov rdx, 8 ; arg 2: dwSize = sizeof(QWORD)
mov r8, PAGE_EXECUTE_READWRITE ; arg 3: flNewProtect = New protection flags
; Get address for dwOldProtect on stack (rsp + shadow space size)
lea r9, [rsp + 20h] ; arg 4: lpflOldProtect = Address on stack
call [pdVirtualProtect] ;VirtualProtect ; Call the API function
; Check if VirtualProtect succeeded
test rax, rax
jz CodeFail ; Failed to change protection, abort
; Overwrite the IAT entry with the address of our hook function
mov rbx, r13 ; r13 holds inHookProc
mov qword ptr [rsi], rbx ; Write the hook address
; Hook successful
mov eax, 1 ; Set return value to 1 (success)
jmp HookEnd ; Skip further searching
ContinueThunks:
add rsi, 8 ; Move to next IAT entry
jmp ThunkLoop
NextImportDescriptor:
add rdi, 20 ;SIZEOF IMAGE_IMPORT_DESCRIPTOR ; Move to next import descriptor
jmp ImportLoop
DoneCheckingImports:
; Target function not found in any import descriptor's IAT
CodeFail:
xor eax, eax ; Set return value to 0 (failure)
HookEnd:
; --- Manual Epilogue ---
; Deallocate stack space (must match the 'sub rsp' value)
add rsp, 30h
; Restore non-volatile registers in reverse order
pop r14
pop r13
pop r12
pop rbx
pop rdi
pop rsi
ret ; Return (RAX has the result)
SetHook endp
; =====================================================================
; GetTickCountHook - Replacement for GetTickCount that accelerates time
; =====================================================================
GetTickCountHook PROC
;int 3
sub rsp, 40 ; Shadow space + alignment
call qword ptr [pdGetTickCount] ; Call original GetTickCount, this cannot work with Inline Injection
mov rdx, rax ; RDX = currentTickCount
sub rdx, qword ptr [pBaseTickCount] ; RDX = currentTickCount - BaseTickCount
imul rdx, qword ptr [pAcceleration] ; RDX = (currentTickCount - BaseTickCount) * Acceleration
add rax, rdx ; RAX = BaseTickCount + ((currentTickCount - BaseTickCount) * Acceleration)
add rsp, 40 ; Restore stack
ret ; Return with accelerated tick count in RAX
GetTickCountHook ENDP
; =====================================================================
; timeGetTimeHook - Replacement for timeGetTime that accelerates time
; =====================================================================
timeGetTimeHook PROC
;int 3
sub rsp, 40 ; Shadow space + alignment
call qword ptr [pdtimeGetTime] ; Call original timeGetTime
mov rdx, rax ; RDX = currentGetTime
sub rdx, qword ptr [pBaseGetTime] ; RDX = currentGetTime - BaseGetTime
imul rdx, qword ptr [pAcceleration] ; RDX = (currentGetTime - BaseGetTime) * Acceleration
add rax, rdx ; RAX = BaseGetTime + ((currentGetTime - BaseGetTime) * Acceleration)
add rsp, 40 ; Restore stack
ret ; Return with accelerated time in RAX
timeGetTimeHook ENDP
QueryPerformanceCounterHook PROC
; int 3
; RCX = lpPerformanceCount (original caller's pointer)
; --- Prologue ---
push rcx ; Save original lpPerformanceCount pointer onto the stack. RSP is now RSP_entry - 8 (misaligned).
sub rsp, 28h ; Allocate 8 bytes for local LARGE_INTEGER + 32 bytes shadow space = 40 bytes (0x28).
; RSP is now RSP_entry - 8 - 0x28 = RSP_entry - 0x30 (16-byte aligned).
; Stack layout:
; [rsp+28h] = Saved original RCX
; [rsp+20h] = Local LARGE_INTEGER variable (8 bytes)
; [rsp+00h] = Shadow space (32 bytes)
; --- Call Original Function ---
lea rcx, [rsp+20h] ; RCX = Address of the local variable (Correct argument register)
call qword ptr [pdQueryPerformanceCounter]
test rax, rax ; Check if original call succeeded
jz QueryFailed_RestoreStack ; Jump to failure path
; --- Process Result (Original Call Succeeded) ---
mov rax, qword ptr [rsp+20h] ; Load the counter value from the local variable
sub rax, qword ptr [pBasePerformanceCount] ; Calculate delta
imul rax, qword ptr [pAcceleration] ; Apply acceleration
add rax, qword ptr [pBasePerformanceCount] ; Add base back
; --- Store Final Result ---
mov rdx, [rsp+28h] ; Restore the original caller's pointer from the stack into RDX
mov [rdx], rax ; Write the final result to the original caller's address. (Should be safe now)
; --- Success Path ---
mov rax, 1 ; Set return value to TRUE
jmp QueryEnd_RestoreStack
QueryFailed_RestoreStack:
; --- Failure Path ---
xor rax, rax ; Set return value to FALSE
QueryEnd_RestoreStack:
; --- Epilogue ---
add rsp, 28h ; Deallocate shadow space and local variable
pop rcx ; Restore original RCX
ret ; Return (RAX holds TRUE or FALSE)
QueryPerformanceCounterHook ENDP
EndInjected:
;End of Inject_function
; =====================================================================
; Inline Injection - Problem, overwrites primary function so cannot call within hook
; =====================================================================
InlineInjected PROC
; RCX = lpGetProcAddress
; Save non-volatile registers
push rbx
push rsi
push rdi
push r12
push r13
push r14
push r15
sub rsp, 80 ; Shadow space + locals
; Save GetProcAddress
mov r15, rcx ; R15 = GetProcAddress
; Find KERNEL32 base address
xor rax, rax
mov rax, gs:[60h] ; PEB
mov rax, [rax+18h] ; LDR_DATA
mov rsi, [rax+30h] ; InInitializationOrderModuleList.Flink
mov rsi, [rsi] ; first entry is NTdll next entry in the list (2nd module)
mov rbx, [rsi+30h] ; DllBase (base of kernel32.dll)
mov rbx, [rsi+10h] ; Base address of KERNEL32
; Get kernel32 functions
lea rdx, szGetTickCount
mov rcx, rbx ; KERNEL32 base
call r15 ; Call GetProcAddress
mov [dGetTickCount], rax
; Load winmm.dll
lea rcx, szWinmm
call LoadLibraryA
mov r14, rax ; R14 = winmm.dll handle
; Get timeGetTime function
lea rdx, sztimeGetTime
mov rcx, r14 ; Wimm.dll base
call r15 ; Call GetProcAddress
mov [dtimeGetTime], rax
; Get QueryPerformanceCounter function
lea rdx, szQueryPerformanceCounter
mov rcx, rbx ; KERNEL32 base
call r15 ; Call GetProcAddress
mov [dQueryPerformanceCounter], rax
; Initialize base time values
call qword ptr [dGetTickCount]
mov [BaseTickCount], rax
call qword ptr [dtimeGetTime]
mov [BaseGetTime], rax
lea rcx, BasePerformanceCount
call qword ptr [dQueryPerformanceCounter]
; Set up hooks using helper function (defined below)
mov rcx, qword ptr [dGetTickCount]
lea rdx, GetTickCountHook
call SetHook
mov rcx, qword ptr [dtimeGetTime]
lea rdx, timeGetTimeHook