-
Notifications
You must be signed in to change notification settings - Fork 56
/
Copy pathlinux.py
1692 lines (1450 loc) · 58.8 KB
/
linux.py
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
import weakref
import sys, os, fcntl, struct
import cpu
from smtlib import chr, ord
from elftools.elf.elffile import ELFFile
from contextlib import closing
import logging
logger = logging.getLogger("LINUX")
class SyscallNotImplemented(Exception):
''' Exception raised when you try to call a not implemented
system call. Go to linux.py and add it!
'''
def __init__(self, mode, number):
super(Exception, self).__init__("%s bit system call number %s Not Implemented" % (mode, number))
class ProcessExit(Exception):
def __init__(self, code):
super(Exception, self).__init__("Process exited correctly. Code: %s" % code)
class File(object):
def __init__(self, *args, **kwargs):
#Todo: assert file is seekable otherwise we should save wwhat was
#read/write to the state
self.file = file(*args,**kwargs)
def stat(self):
return os.fstat(self.fileno())
def ioctl(self, request, argp):
#argp ignored..
return fcntl.fcntl(self, request)
@property
def name(self):
return self.file.name
@property
def mode(self):
return self.file.mode
def tell(self, *args):
return self.file.tell(*args)
def seek(self, *args):
return self.file.seek(*args)
def write(self, *args):
return self.file.write(*args)
def read(self, *args):
return self.file.read(*args)
def close(self, *args):
return self.file.close(*args)
def fileno(self, *args):
return self.file.fileno(*args)
def __getstate__(self):
state = {}
state['name'] = self.name
state['mode'] = self.mode
state['pos'] = self.tell()
return state
def __setstate__(self, state):
name = state['name']
mode = state['mode']
pos = state['pos']
self.file = file(name, mode)
self.seek(pos)
class Linux(object):
EPERM = 1 # Operation not permitted
ENOENT = 2 # No such file or directory
ESRCH = 3 # No such process
EINTR = 4 # Interrupted system call
EIO = 5 # I/O error
ENXIO = 6 # No such device or address
E2BIG = 7 # Argument list too long
ENOEXEC = 8 # Exec format error
BADF = 9 # Bad file number
ECHILD = 10 # No child processes
EAGAIN = 11 # Try again
ENOMEM = 12 # Out of memory
EACCES = 13 # Permission denied
EFAULT = 14 # Bad address
ENOTBLK = 15 # Block device required
EBUSY = 16 # Device or resource busy
EEXIST = 17 # File exists
EXDEV = 18 # Cross-device link
ENODEV = 19 # No such device
ENOTDIR = 20 # Not a directory
EISDIR = 21 # Is a directory
EINVAL = 22 # Invalid argument
ENFILE = 23 # File table overflow
EMFILE = 24 # Too many open files
ENOTTY = 25 # Not a typewriter
ETXTBSY = 26 # Text file busy
EFBIG = 27 # File too large
ENOSPC = 28 # No space left on device
ESPIPE = 29 # Illegal seek
EROFS = 30 # Read-only file system
EMLINK = 31 # Too many links
EPIPE = 32 # Broken pipe
EDOM = 33 # Math argument out of domain of func
ERANGE = 34 # Math result not representable
'''
A simple Linux Operating System Model.
This class emulates the most common Linux system calls
'''
def __init__(self, cpus, mem):
'''
Builds a Linux OS model
@param cpus: CPU for this model.
@param mem: memory for this model.
@todo: generalize for more CPUs.
@todo: fix deps?
'''
self.files = []
self.cpu = cpus[0]
self.mem = mem
self.base = 0
self.elf_bss = 0
self.end_code = 0
self.end_data = 0
self.elf_brk = 0
@property
def current(self):
return self.cpu
def __getstate__(self):
state = {}
state['files'] = self.files
state['cpu'] = self.cpu
state['mem'] = self.mem
state['base'] = self.base
state['elf_bss'] = self.elf_bss
state['end_code'] = self.end_code
state['end_data'] = self.end_data
state['elf_brk'] = self.elf_brk
return state
def __setstate__(self, state):
"""
@todo: some asserts
@todo: fix deps? (last line)
"""
self.files = state['files']
self.cpu = state['cpu']
self.mem = state['mem']
self.base = state['base']
self.elf_bss = state['elf_bss']
self.end_code = state['end_code']
self.end_data = state['end_data']
self.elf_brk = state['elf_brk']
def _read_string(self, cpu, buf):
"""
Reads a null terminated concrete buffer form memory
@todo: FIX. move to cpu or memory
"""
filename = ""
for i in xrange(0,1024):
c = chr(cpu.load(buf+i,8))
if c == '\x00':
break
filename += c
return filename
def _read_buffer(self, cpu, p, length):
data = ''
for i in xrange(length):
data += chr(cpu.load(p+i,8))
return data
def _write_buffer(self, cpu, p, data):
for i in xrange(len(data)):
cpu.load(p+i, chr(data[i]), 8)
def _open(self, f):
if None in self.files:
fd = self.files.index(None)
self.files[fd]=f
else:
fd = len(self.files)
self.files.append(f)
return fd
def _close(self, fd):
self.files[fd] = None
def _dup(self, fd):
return self._open(self.files[fd])
def exe(self, filename, argv=[], envp=[]):
'''
Loads and an ELF program in memory and prepares the initial CPU state.
Creates the stack and loads the environment variables and the arguments in it.
@param filename: pathname of the file to be executed.
@param argv: list of parameters for the program to execute.
@param envp: list of environment variables for the program to execute.
@raise error:
- 'Not matching cpu': if the program is compiled for a different architecture
- 'Not matching memory': if the program is compiled for a different address size
@todo: define va_randomize and read_implies_exec personality
'''
#load elf See binfmt_elf.c
#read the ELF object file
elf = ELFFile(file(filename))
arch = {'x86':'i386','x64':'amd64'}[elf.get_machine_arch()]
addressbitsize = {'x86':32, 'x64':64}[elf.get_machine_arch()]
logger.info("Loading %s as a %s elf"%(filename,arch))
logger.info("\tArguments: %s"%repr(argv))
logger.debug("\tEnvironmen:")
for e in envp:
logger.debug("\t\t%s"%repr(e))
assert self.cpu.machine == arch, "Not matching cpu"
assert self.mem.addressbitsize == addressbitsize, "Not matching memory"
assert elf.header.e_type in ['ET_DYN', 'ET_EXEC']
cpu = self.cpu
#Get interpreter elf
interpreter = None
for elf_segment in elf.iter_segments():
if elf_segment.header.p_type != 'PT_INTERP':
continue
interpreter = ELFFile(file(elf_segment.data()[:-1]))
break
if not interpreter is None:
assert interpreter.get_machine_arch() == elf.get_machine_arch()
assert interpreter.header.e_type in ['ET_DYN', 'ET_EXEC']
#Stack Executability
executable_stack = False
for elf_segment in elf.iter_segments():
if elf_segment.header.p_type != 'PT_GNU_STACK':
continue
if elf_segment.header.p_flags & 0x01:
executable_stack = True
else:
executable_stack = False
break
base = 0
elf_bss = 0
end_code = 0
end_data = 0
elf_brk = 0
load_addr = 0
base = 0
for elf_segment in elf.iter_segments():
if elf_segment.header.p_type != 'PT_LOAD':
continue
align = 0x1000 #elf_segment.header.p_align
ELF_PAGEOFFSET = elf_segment.header.p_vaddr & (align-1)
flags = elf_segment.header.p_flags
memsz = elf_segment.header.p_memsz + ELF_PAGEOFFSET
offset = elf_segment.header.p_offset - ELF_PAGEOFFSET
filesz = elf_segment.header.p_filesz + ELF_PAGEOFFSET
vaddr = elf_segment.header.p_vaddr - ELF_PAGEOFFSET
memsz = self.mem._ceil(memsz+1) # (memsz + align ) & ~(align-1)
if base == 0 and elf.header.e_type == 'ET_DYN':
assert vaddr == 0
if addressbitsize == 32:
base = 0x56555000
else:
base = 0x555555554000
#PF_X 0x1 Execute
#PF_W 0x2 Write
#PF_R 0x4 Read
#base = cpu.mem.mmap(base+vaddr,memsz,flags&0x4,flags&0x2,flags&0x1,data) - vaddr
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags&7]
hint = base+vaddr
if hint == 0:
hint = None
base = self.mem.mmapFile(hint,memsz,perms,elf_segment.stream.name,offset) - vaddr
logger.debug("Loading elf offset: %08x addr:%08x %08x %s" %(offset, base+vaddr, base+vaddr+memsz, perms))
if load_addr == 0 :
load_addr = base + vaddr
k = base + vaddr + filesz;
if k > elf_bss :
elf_bss = k;
if (flags & 4) and end_code < k: #PF_X
end_code = k
if end_data < k:
end_data = k
k = base + vaddr + memsz
if k > elf_brk:
elf_brk = k
elf_entry = elf.header.e_entry
if elf.header.e_type == 'ET_DYN':
elf_entry += load_addr
entry = elf_entry
real_elf_brk = elf_brk
# We need to explicitly zero any fractional pages
# after the data section (i.e. bss). This would
# contain the junk from the file that should not
# be in memory
#TODO:
#cpu.write(elf_bss, '\x00'*((elf_bss | (align-1))-elf_bss))
logger.debug("Zeroing main elf fractional pages. From %x to %x.", elf_bss, elf_brk)
logger.debug("Main elf bss:%x"%elf_bss)
logger.debug("Main elf brk %x:"%elf_brk)
saved_perms = self.mem.getPermissions(elf_bss)
self.mem.mprotect(self.mem._floor(elf_bss), elf_brk-elf_bss, 'rw')
for i in xrange(elf_bss, elf_brk):
try:
self.mem.putchar(i, '\x00')
except Exception, e:
logger.debug("Exception zeroing main elf fractional pages: %s"%str(e))
self.mem.mprotect(self.mem._floor(elf_bss), elf_brk-elf_bss, saved_perms)
reserved = self.mem.mmap(base+vaddr+memsz,0x1000000,' ')
interpreter_base = 0
if not interpreter is None:
base = 0
elf_bss = 0
end_code = 0
end_data = 0
elf_brk = 0
entry = interpreter.header.e_entry
for elf_segment in interpreter.iter_segments():
if elf_segment.header.p_type != 'PT_LOAD':
continue
align = 0x1000#elf_segment.header.p_align
vaddr = elf_segment.header.p_vaddr
filesz = elf_segment.header.p_filesz
flags = elf_segment.header.p_flags
offset = elf_segment.header.p_offset
memsz = elf_segment.header.p_memsz
ELF_PAGEOFFSET = (vaddr & (align-1))
memsz = memsz + ELF_PAGEOFFSET
offset = offset - ELF_PAGEOFFSET
filesz = filesz + ELF_PAGEOFFSET
vaddr = vaddr - ELF_PAGEOFFSET
memsz = self.mem._ceil(memsz+1)
if base == 0 and elf.header.e_type == 'ET_DYN':
assert vaddr == 0
if addressbitsize == 32:
base = 0xf7fde000
else:
base = 0x7ffff7ddb000
if base == 0:
assert vaddr == 0
perms = [' ', ' x', ' w ', ' wx', 'r ', 'r x', 'rw ', 'rwx'][flags&7]
hint = base+vaddr
if hint == 0:
hint = None
base = self.mem.mmapFile(hint ,memsz,perms,elf_segment.stream.name,offset) - vaddr
logger.debug("Loading interpreter offset: %08x addr:%08x %08x %s%s%s" %(offset, base+vaddr, base+vaddr+memsz, (flags&1 and 'r' or ' '), (flags&2 and 'w' or ' '), (flags&4 and 'x' or ' ')))
k = base + vaddr+ filesz;
if k > elf_bss :
elf_bss = k;
if (flags & 4) and end_code < k: #PF_X
end_code = k
if end_data < k:
end_data = k
k = base + vaddr+ memsz
if k > elf_brk:
elf_brk = k
if interpreter.header.e_type == 'ET_DYN':
entry += base
interpreter_base = base
logger.debug("Zeroing interpreter elf fractional pages. From %x to %x.", elf_bss, elf_brk)
logger.debug("Interpreter bss:%x"%elf_bss)
logger.debug("Interpreter brk %x:"%elf_brk)
self.mem.mprotect(self.mem._floor(elf_bss), elf_brk-elf_bss, 'rw')
for i in xrange(elf_bss, elf_brk):
try:
self.mem.putchar(i, '\x00')
except Exception, e:
logger.debug("Exception zeroing Interpreter fractional pages: %s"%str(e))
#TODO FIX mprotect as it was before zeroing?
#free reserved brk space
self.mem.munmap(reserved,0x1000000)
#load vdso #TODO or #IGNORE
bsz = addressbitsize/8
if addressbitsize == 32:
stack_base = 0xbffdf000
else:
stack_base = 0x7ffffffde000
stack = self.mem.mmap(stack_base,0x21000,'rwx')+0x21000-1
logger.info("Setting argv, envp and auxv.")
#http://www.phrack.org/issues.html?issue=58&id=5#article
# position content size (bytes) + comment
# ----------------------------------------------------------------------
# stack pointer -> [ argc = number of args ] 4
# [ argv[0] (pointer) ] 4 (program name)
# [ argv[1] (pointer) ] 4
# [ argv[..] (pointer) ] 4 * x
# [ argv[n - 1] (pointer) ] 4
# [ argv[n] (pointer) ] 4 (= NULL)
#
# [ envp[0] (pointer) ] 4
# [ envp[1] (pointer) ] 4
# [ envp[..] (pointer) ] 4
# [ envp[term] (pointer) ] 4 (= NULL)
#
# [ auxv[0] (Elf32_auxv_t) ] 8
# [ auxv[1] (Elf32_auxv_t) ] 8
# [ auxv[..] (Elf32_auxv_t) ] 8
# [ auxv[term] (Elf32_auxv_t) ] 8 (= AT_NULL vector)
#
# [ padding ] 0 - 16
#
# [ argument ASCIIZ strings ] >= 0
# [ environment ASCIIZ str. ] >= 0
#
# (0xbffffffc) [ end marker ] 4 (= NULL)
#
# (0xc0000000) < top of stack > 0 (virtual)
# ----------------------------------------------------------------------
argvlst=[]
envplst=[]
stack-=16
cpu.write(stack, "A"*16)
AT_RANDOM = stack
#end envp marker empty string
stack-=1
cpu.write(stack,'\x00')
envplst.append(stack)
for e in envp:
stack-=(len(e)+1)
envplst.append(stack)
cpu.write(stack,e)
cpu.write(stack+len(e),'\x00')
for a in argv:
stack-=(len(a)+1)
argvlst.append(stack)
cpu.write(stack,a)
cpu.write(stack+len(a),'\x00')
stack = ((stack - bsz) /bsz )*bsz # [ padding ]
stack-=bsz
cpu.store(stack,0,addressbitsize)
stack-=bsz
cpu.store(stack,0,addressbitsize)
#The "secure execution" mode of secure_getenv() is controlled by the
#AT_SECURE flag contained in the auxiliary vector passed from the
#kernel to user space.
for i in reversed([ 3, load_addr+elf.header.e_phoff,
4, 0x0000000000000038, #64bits 0x38 | #32bits 0x20
5, 0x0000000000000007, #64bits 0x07 | #32bits 0x0a
6, 4096,
7, interpreter_base,
8, 0,
9, elf_entry,
11, 1000,
12, 1000,
13, 1000,
14, 1000,
17, 100,
23, 0,
25, AT_RANDOM,
0, 0]):
stack-=bsz
cpu.store(stack,i,addressbitsize)
stack-=bsz # NULL ENVP
cpu.store(stack,0,addressbitsize)
for e in reversed(envplst): # ENVP n
stack-=bsz
cpu.store(stack,e,addressbitsize)
stack-=bsz
cpu.store(stack,0,addressbitsize) # NULL ARGV
for a in reversed(argvlst): # Argv n
stack-=bsz
cpu.store(stack,a,addressbitsize)
stack-=bsz
cpu.store(stack,len(argvlst),addressbitsize) #ARGC
logger.info("Setting initial cpu state")
#set initial CPU state
cpu.setRegister('RAX', 0x0)
cpu.setRegister('RCX', 0x0)
cpu.setRegister('RDX', 0x0)
cpu.setRegister('RBX', 0x0)
cpu.setRegister('RSP', stack)
cpu.setRegister('RBP', 0x0)
cpu.setRegister('RSI', 0x0)
cpu.setRegister('RDI', 0x0)
cpu.setRegister('RIP', entry)
cpu.setRegister('RFLAGS', 0x202)
cpu.setRegister('CS', 0x23)
cpu.setRegister('SS', 0x2b)
cpu.setRegister('DS', 0x2b)
cpu.setRegister('ES', 0x2b)
cpu.setRegister('FS', 0x0)
cpu.setRegister('GS', 0x0)
logger.info("Entry point: %016x", entry)
logger.info("Stack start: %016x", stack)
logger.info("Brk: %016x", real_elf_brk)
logger.info("Mappings:")
for m in str(self.mem).split('\n'):
logger.info(" %s", m)
self.base = base
self.elf_bss = elf_bss
self.end_code = end_code
self.end_data = end_data
self.elf_brk = real_elf_brk
#Clean heap?
#for i in xrange(self.end_data, self.elf_brk-1):
# cpu.write(i, '\x00')
#dump initial mappings
#for m in self.mem.mappings():
# start =m[0]
# end = m[1]
# f = file('map%016x_%016x.img'%(start,end),'w+')
# for i in xrange(start,end):
# f.write(self.mem.getchar(i))
def sys_uname(self, cpu, old_utsname):
'''
Writes system information in the variable C{old_utsname}.
@rtype: int
@param cpu: current CPU.
@param old_utsname: the buffer to write the system info.
@return: C{0} on success
'''
uname = "Linux" + '\x00'*(65-5)
uname += "localhost" + '\x00'*(65-9)
uname += "3.9.2-gentoo" + '\x00'*(65-12)
uname += "#2 SMP Fri May 17 21:08:46 ART 2013"+ '\x00'*(65-35)
uname += "x86_64"+ '\x00'*(65-6)
uname += "(none)"+ '\x00'*(65-6)
cpu.write(old_utsname, uname)
return 0
def sys_brk(self, cpu, brk):
'''
Changes data segment size (moves the C{elf_brk} to the new address)
@rtype: int
@param cpu: current CPU.
@param brk: the new address for C{elf_brk}.
@return: the value of the new C{elf_brk}.
@raise error:
- "Error in brk!" if there is any error allocating the memory
'''
if brk != 0:
size = brk-self.elf_brk
perms = cpu.mem.getPermissions(self.elf_brk-1)
addr = cpu.mem.mmap(self.elf_brk, size, perms)
assert cpu.mem._ceil(self.elf_brk-1) == addr, "Error in brk!"
self.elf_brk += size
return self.elf_brk
def sys_arch_prctl(self, cpu, code, addr):
'''
Sets architecture-specific thread state
@rtype: int
@param cpu: current CPU.
@param code: must be C{ARCH_SET_FS}.
@param addr: the base address of the FS segment.
@return: C{0} on success
@raise error:
- if C{code} is different to C{ARCH_SET_FS}
'''
ARCH_SET_GS = 0x1001
ARCH_SET_FS = 0x1002
ARCH_GET_FS = 0x1003
ARCH_GET_GS = 0x1004
assert code == ARCH_SET_FS
cpu.FS = 0
cpu.segments['FS'][0] = addr
return 0
def sys_open(self, cpu, buf, flags, mode):
'''
Given a pathname for a file, it returns a file descriptor
@rtype: int
@param cpu: current CPU.
@param buf: buffer with the pathname of the file to open.
@param flags: file access bits.
@param mode: file permissions mode.
@return: a file description of the opened file.
@todo: flags and mode not used
'''
filename = self._read_string(cpu, buf)
return self._open(File(filename))
def sys_read(self, cpu, fd, buf, count):
'''
Reads from a file descriptor
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor to read.
@param buf: address of the buffer to put the read bytes.
@param count: maximum number of bytes to read.
@return: the amount of bytes read.
@todo: Out number of bytes actually read | EAGAIN | EBADF | EFAULT | EINTR | EINVAL | EIO | EISDIR
'''
data = self.files[fd].read(count)
cpu.write(buf, data)
logger.debug("READ %d %x %d -> %s",fd,buf,count,repr(data[:10]))
return len(data)
def sys_close(self, cpu, fd):
'''
Closes a file descriptor
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor to close.
@return: C{0} on success.
'''
self.files[fd]=None
return 0
def sys_fstat(self, cpu, fd, buf):
'''
Determines information about a file based on its file descriptor.
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor of the file that is being inquired.
@param buf: a buffer where data about the file will be stored.
@return: C{0} on success.
'''
'''
dev_t st_dev; /* ID of device containing file */
ino_t st_ino; /* inode number */
mode_t st_mode; /* protection */
nlink_t st_nlink; /* number of hard links */
uid_t st_uid; /* user ID of owner */
gid_t st_gid; /* group ID of owner */
dev_t st_rdev; /* device ID (if special file) */
off_t st_size; /* total size, in bytes */
blksize_t st_blksize; /* blocksize for file system I/O */
blkcnt_t st_blocks; /* number of 512B blocks allocated */
time_t st_atime; /* time of last access */
time_t st_mtime; /* time of last modification */
time_t st_ctime; /* time of last status change */
'''
stat = self.files[fd].stat()
bufstat = ''
bufstat += struct.pack('<L', stat.st_dev)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', stat.st_ino)
bufstat += struct.pack('<L', stat.st_mode)
bufstat += struct.pack('<L', stat.st_nlink)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', stat.st_size)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('<L', stat.st_blksize)
bufstat += struct.pack('<L', stat.st_blocks)
bufstat += struct.pack('<L', 0)
bufstat += struct.pack('d', stat.st_atime)
bufstat += struct.pack('d', stat.st_ctime)
bufstat += struct.pack('d', stat.st_mtime)
cpu.write(buf, bufstat)
return 0
def sys_fstat64(self, cpu, fd, buf):
'''
Determines information about a file based on its file descriptor (for Linux 64 bits).
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor of the file that is being inquired.
@param buf: a buffer where data about the file will be stored.
@return: C{0} on success.
@todo: Fix device number.
'''
''' unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
unsigned int st_mode; /* File mode. */
unsigned int st_nlink; /* Link count. */
unsigned int st_uid; /* User ID of the file's owner. */
unsigned int st_gid; /* Group ID of the file's group. */
unsigned long st_rdev; /* Device number, if device. */
unsigned long __pad1;
long st_size; /* Size of file, in bytes. */
int st_blksize; /* Optimal block size for I/O. */
int __pad2;
long st_blocks; /* Number 512-byte blocks allocated. */
long st_atime; /* Time of last access. */
unsigned long st_atime_nsec;
long st_mtime; /* Time of last modification. */
unsigned long st_mtime_nsec;
long st_ctime; /* Time of last status change. */
unsigned long st_ctime_nsec;
unsigned int __unused4;
unsigned int __unused5;'''
stat = self.files[fd].stat()
bufstat = ''
bufstat += struct.pack('<Q', stat.st_dev)
bufstat += struct.pack('<Q', stat.st_ino)
bufstat += struct.pack('<L', stat.st_mode)
bufstat += struct.pack('<L', stat.st_nlink)
bufstat += struct.pack('<L', stat.st_uid)
bufstat += struct.pack('<L', stat.st_gid)
bufstat += struct.pack('<Q', 0)
bufstat += struct.pack('<Q', 0) #pad
bufstat += struct.pack('<Q', stat.st_size)
bufstat += struct.pack('<L', 1000 )
bufstat += struct.pack('<L', 0) #pad
bufstat += struct.pack('<Q', stat.st_size/512)
bufstat += struct.pack('d', stat.st_atime)
bufstat += struct.pack('<Q', 0)
bufstat += struct.pack('d', stat.st_mtime)
bufstat += struct.pack('<Q', 0)
bufstat += struct.pack('d', stat.st_ctime)
bufstat += struct.pack('<Q', 0)
bufstat += struct.pack('<L', 0) #pad
bufstat += struct.pack('<L', 0) #pad
cpu.write(buf, bufstat)
return 0
def sys_stat64(self, cpu, path, buf):
'''
Determines information about a file based on its filename (for Linux 64 bits).
@rtype: int
@param cpu: current CPU.
@param path: the pathname of the file that is being inquired.
@param buf: a buffer where data about the file will be stored.
@return: C{0} on success.
'''
fd = self.sys_open(cpu, path, 0, 'r')
ret = self.sys_fstat64(cpu, fd, buf)
self.sys_close(cpu, fd)
return ret
def sys_mmap2(self, cpu, address, size, prot, flags, fd, offset):
'''
Creates a new mapping in the virtual address space of the calling process.
@rtype: int
@param cpu: current CPU.
@param address: the starting address for the new mapping. This address is used as hint unless the
flag contains C{MAP_FIXED}.
@param size: the length of the mapping.
@param prot: the desired memory protection of the mapping.
@param flags: determines whether updates to the mapping are visible to other
processes mapping the same region, and whether updates are carried
through to the underlying file.
@param fd: the contents of a file mapping are initialized using C{size} bytes starting at
offset C{offset} in the file referred to by the file descriptor C{fd}.
@param offset: the contents of a file mapping are initialized using C{size} bytes starting at
offset C{offset}*0x1000 in the file referred to by the file descriptor C{fd}.
@return:
- C{-1} In case you use C{MAP_FIXED} in the flags and the mapping can not be place at the desired address.
- the address of the new mapping.
'''
return self.sys_mmap(cpu, address, size, prot, flags, fd, offset*0x1000)
def sys_mmap(self, cpu, address, size, prot, flags, fd, offset):
'''
Creates a new mapping in the virtual address space of the calling process.
@rtype: int
@param cpu: current CPU.
@param address: the starting address for the new mapping. This address is used as hint unless the
flag contains C{MAP_FIXED}.
@param size: the length of the mapping.
@param prot: the desired memory protection of the mapping.
@param flags: determines whether updates to the mapping are visible to other
processes mapping the same region, and whether updates are carried
through to the underlying file.
@param fd: the contents of a file mapping are initialized using C{size} bytes starting at
offset C{offset} in the file referred to by the file descriptor C{fd}.
@param offset: the contents of a file mapping are initialized using C{size} bytes starting at
offset C{offset} in the file referred to by the file descriptor C{fd}.
@return:
- C{-1} in case you use C{MAP_FIXED} in the flags and the mapping can not be place at the desired address.
- the address of the new mapping (that must be the same as address in case you included C{MAP_FIXED} in flags).
@todo: handle exception.
'''
if address == 0:
address = None
if flags & 0x10 !=0 :
cpu.mem.munmap(address,size)
perms = [' ', 'r ',' w ','rw ',' x','r x', ' wx','rwx'][prot&7]
if fd == 0xffffffff or fd == 0xffffffffffffffff:
result = cpu.mem.mmap(address, size, perms)
else:
result = cpu.mem.mmapFile(address, size, perms, self.files[fd].name, offset)
if (flags & 0x10 !=0) and result != address:
cpu.mem.munmap(result, size)
result = -1
return result
def sys_write(self, cpu, fd, buf, size):
'''
Writes to a file descriptor
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor of the file to write.
@param buf: the buffer where the bytes to write are taken.
@param size: it writes up to C{size} bytes from the buffer C{buf}
to the file referred to by the file descriptor C{fd}.
@return: the amount of bytes written.
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | EINVAL | EIO | ENOSPC | EPIPE
'''
for i in xrange(0, size):
value = chr(cpu.load(buf+i,8))
if not isinstance(value, str):
logger.warning("Writing symbolic values to file %s", self.files[fd].name)
value = str(value)
self.files[fd].write(value)
return size
def sys_readlink(self, cpu, path, buf, bufsize):
'''
Read
@rtype: int
@param cpu: current CPU.
@param path: the "link path id"
@param buf: the buffer where the bytes will be putted.
@param bufsize: the max size for read the link.
@todo: Out eax number of bytes actually sent | EAGAIN | EBADF | EFAULT | EINTR | EINVAL | EIO | ENOSPC | EPIPE
'''
if bufsize <= 0:
return -EINVAL
filename = self._read_string(cpu, path)
data = os.readlink(filename)[:bufsize]
cpu.write(buf, data)
logger.debug("READLINK %d %x %d -> %s",path,buf,bufsize,repr(data[:10]))
return len(data[:bufsize])
def sys_exit_group(self, cpu, error_code):
'''
Exits all threads in a process
@param cpu: current CPU.
@param error_code: not used.
@raise Exception: 'Finished'
'''
raise ProcessExit(error_code)
def sys_access(self, cpu, buf, mode):
'''
Checks real user's permissions for a file
@rtype: int
@param cpu: current CPU.
@param buf: a buffer containing the pathname to the file to check its permissions.
@param mode: the access permissions to check.
@return:
- C{0} if the calling process can access the file in the desired mode.
- C{-1} if the calling process can not access the file in the desired mode.
'''
filename = ""
for i in xrange(0,255):
c = chr(cpu.load(buf+i,8))
if c == '\x00':
break
filename += c
#if path.isfile(PATH) and access(PATH, MODE):
# print "File exists and is readable"
#else:
# print "Either file is missing or is not readable"
if os.access(filename, mode):
return 0
else:
return -1
def sys_mprotect(self, cpu, start, size, prot):
'''
Sets protection on a region of memory. Changes protection for the calling process's
memory page(s) containing any part of the address range in the interval [C{start}, C{start}+C{size}-1].
@rtype: int
@param cpu: current CPU.
@param start: the starting address to change the permissions.
@param size: the size of the portion of memory to change the permissions.
@param prot: the new acces permission for the memory.
@return: C{0} on success.
'''
perms = [' ', 'r ',' w ','rw ',' x','r x', ' wx','rwx'][prot&7]
cpu.mem.mprotect(start, size, perms)
return 0
def sys_munmap(self, cpu, addr, size):
'''
Unmaps a file from memory. It deletes the mappings for the specified address range
@rtype: int
@param cpu: current CPU.
@param addr: the starting address to unmap.
@param size: the size of the portion to unmap.
@return: C{0} on success.
'''
cpu.mem.munmap(addr, size)
return 0
def sys_getuid(self, cpu):
'''
Gets user identity.
@rtype: int
@param cpu: current CPU.
@return: this call returns C{1000} for all the users.
'''
return 1000
def sys_getgid(self, cpu):
'''
Gets group identity.
@rtype: int
@param cpu: current CPU.
@return: this call returns C{1000} for all the groups.
'''
return 1000
def sys_geteuid(self, cpu):
'''
Gets user identity.
@rtype: int
@param cpu: current CPU.
@return: This call returns C{1000} for all the users.
'''
return 1000
def sys_getegid(self, cpu):
'''
Gets group identity.
@rtype: int
@param cpu: current CPU.
@return: this call returns C{1000} for all the groups.
'''
return 1000
def sys_writev(self, cpu, fd, iov, count):
'''
Works just like C{sys_write} except that multiple buffers are written out (for Linux 64 bits).
@rtype: int
@param cpu: current CPU.
@param fd: the file descriptor of the file to write.
@param iov: the buffer where the the bytes to write are taken.
@param count: amount of C{iov} buffers to write into the file.
@return: the amount of bytes written in total.
'''
total = 0
for i in xrange(0, count):
buf = cpu.load(iov+i*16,64)
size = cpu.load(iov+i*16+8,64)
data = ""
for i in xrange(0,size):
data += chr(cpu.load(buf+i,8))