Я пытался написать простой отладчик в python3 в 32-битной тестовой linux системе (Lubuntu), которая должна была бы быть способна перехватывать все системные вызовы программы-абитера (в данном случае: / bin / Ls ). Для этого я использовал системный вызов ptrace для пошагового выполнения этого процесса. После каждого шага я читаю регистры, чтобы найти указатель команды eip , чтобы прочитать 2 байта из следующей инструкции. Если эти 2 байта равны 0xcd и 0x80 , это указывает int 80 , который является системным вызовом. Я знаю, что для этой цели также существует PTRACE_SYSCALL, но я хотел сделать это без его использования.
В следующем я покажу вам код, и он, кажется, работает, НО там Это странное поведение:
Чтобы выяснить, работает ли это, я использовал strace , чтобы сравнить его вывод с моими собственными системными вызовами. И кажется, что моя программа показывает только первую часть системных вызовов, вторая часть просто отсутствует. Чтобы показать вам, я опубликовал вывод моей программы и strace в следующем. У кого-то есть идея, что здесь может быть не так?
import os # os interaction
from struct import pack # dealing with bytes (ptrace)
import ctypes # support c data structures
""" ========================================================== """
# 32 bit reg process structrue
class UserRegsStruct(ctypes.Structure):
_fields_ = [
("ebx", ctypes.c_ulong),
("ecx", ctypes.c_ulong),
("edx", ctypes.c_ulong),
("esi", ctypes.c_ulong),
("edi", ctypes.c_ulong),
("ebp", ctypes.c_ulong),
("eax", ctypes.c_ulong),
("xds", ctypes.c_ulong),
("xes", ctypes.c_ulong),
("xfs", ctypes.c_ulong),
("xgs", ctypes.c_ulong),
("orig_eax", ctypes.c_ulong),
("eip", ctypes.c_ulong),
("xcs", ctypes.c_ulong),
("eflags", ctypes.c_ulong),
("esp", ctypes.c_ulong),
("xss", ctypes.c_ulong),
]
# ptrace constants
PTRACE_TRACEME = 0
PTRACE_PEEKDATA = 2
PTRACE_SINGLESTEP = 9
PTRACE_GETREGS = 12
CPU_WORD_SIZE = 4 # size of cpu word size (32 bit = 4 bytes)
# for syscalls
libc = ctypes.CDLL('libc.so.6')
# check if child (tracee) is still running
def WIFSTOPPED(status):
return (status & 0xff) == 0x7f
# read from process memory by PTRACE_PEEKDATA
def ReadProcessMemory(pid, address, size):
# address must be aligned!!
offset = address % CPU_WORD_SIZE
if offset:
address -= offset
word = libc.ptrace(PTRACE_PEEKDATA, pid, address, 0)
wordbytes = pack("i", word)
subsize = min(CPU_WORD_SIZE - offset, size)
data = wordbytes[offset:offset + subsize]
size -= subsize
address += CPU_WORD_SIZE
else:
data = bytes(0)
while size:
word = libc.ptrace(PTRACE_PEEKDATA, pid, address, 0)
wordbytes = pack("i", word)
if size < CPU_WORD_SIZE:
data += wordbytes[:size]
break
data += wordbytes
size -= CPU_WORD_SIZE
address += CPU_WORD_SIZE
return data
""" ========================================================== """
# extract syscall names
fp = open("/usr/include/i386-linux-gnu/asm/unistd_32.h", "r")
syscalls = [0] * 400
for line in fp:
if "__NR_" in line:
a = line.rstrip().split(" ")
name = a[1].split("NR_")[1]
number = int(a[2])
syscalls[number] = name
# "int 80" asm instruction = (0xCD 0x80)
a0 = 0xcd
a1 = 0x80
# create child tracee
pid = os.fork()
if pid == 0: # in tracee
libc.ptrace(PTRACE_TRACEME, 0, 0, 0) # make child traceable
os.execv("/bin/ls", [":-P"]) # run test programm
else: # in tracer
pid, status = os.waitpid(pid, 0)
regs = UserRegsStruct()
# catch all syscalls
while True:
libc.ptrace(PTRACE_SINGLESTEP, pid, 0, 0) # execute next instruction
pid, status = os.waitpid(pid, 0) # wait for tracee
libc.ptrace(PTRACE_GETREGS, pid, 0, ctypes.byref(regs)) # get register values
data = ReadProcessMemory(pid, regs.eip, 2) # read 2 bytes from instruction pointer address
# now check if this is a syscall
if data[0] == a0 and data[1] == a1:
print("HEUREKA! SYSCALL at " + hex(regs.eip) + ": " + syscalls[regs.eax])
if WIFSTOPPED(status) == False: break # exit loop when tracee stopped
Это сгенерировало следующий вывод:
HEUREKA! SYSCALL at 0xb7fae2c5: brk
HEUREKA! SYSCALL at 0xb7fa3944: access
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf689: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faa758: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf57e: read
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faa758: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf57e: read
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faa758: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf57e: read
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faa758: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf57e: read
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faa758: access
HEUREKA! SYSCALL at 0xb7faf4b5: openat
HEUREKA! SYSCALL at 0xb7faf57e: read
HEUREKA! SYSCALL at 0xb7faf419: fstat64
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7faf755: close
HEUREKA! SYSCALL at 0xb7faf7ae: mmap2
HEUREKA! SYSCALL at 0xb7f95bd9: set_thread_area
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf822: mprotect
HEUREKA! SYSCALL at 0xb7faf7ff: munmap
test.py
А вот вывод strace:
execve("/bin/ls", ["/bin/ls"], 0xbfef5e40 /* 45 vars */) = 0
brk(NULL) = 0x220c000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7f00000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=89915, ...}) = 0
mmap2(NULL, 89915, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7eea000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\0L\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=169960, ...}) = 0
mmap2(NULL, 179612, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7ebe000
mmap2(0xb7ee7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0xb7ee7000
mmap2(0xb7ee9000, 3484, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7ee9000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\3\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\20\220\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1942840, ...}) = 0
mmap2(NULL, 1948188, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7ce2000
mprotect(0xb7eb7000, 4096, PROT_NONE) = 0
mmap2(0xb7eb8000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1d5000) = 0xb7eb8000
mmap2(0xb7ebb000, 10780, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7ebb000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libpcre.so.3", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\360\16\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=480564, ...}) = 0
mmap2(NULL, 483512, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7c6b000
mmap2(0xb7ce0000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x74000) = 0xb7ce0000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\320\n\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0644, st_size=13796, ...}) = 0
mmap2(NULL, 16500, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7c66000
mmap2(0xb7c69000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2000) = 0xb7c69000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/i386-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\300P\0\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=142820, ...}) = 0
mmap2(NULL, 123544, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb7c47000
mmap2(0xb7c62000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a000) = 0xb7c62000
mmap2(0xb7c64000, 4760, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7c64000
close(3) = 0
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7c45000
set_thread_area({entry_number=-1, base_addr=0xb7c45780, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=6)
mprotect(0xb7eb8000, 8192, PROT_READ) = 0
mprotect(0xb7c62000, 4096, PROT_READ) = 0
mprotect(0xb7c69000, 4096, PROT_READ) = 0
mprotect(0xb7ce0000, 4096, PROT_READ) = 0
mprotect(0xb7ee7000, 4096, PROT_READ) = 0
mprotect(0x469000, 4096, PROT_READ) = 0
mprotect(0xb7f2d000, 4096, PROT_READ) = 0
munmap(0xb7eea000, 89915) = 0
До тех пор, пока здесь нет полного соответствия с моим собственным выводом, но остальные системные вызовы никогда не появляются в моей программе. Так вот в чем вопрос. Я надеюсь, что кто-то знает ответ: P Если у вас есть какие-либо вопросы, пожалуйста, задавайте!
set_tid_address(0xb7c457e8) = 9767
set_robust_list(0xb7c457f0, 12) = 0
rt_sigaction(SIGRTMIN, {sa_handler=0xb7c4baf0, sa_mask=[], sa_flags=SA_SIGINFO}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {sa_handler=0xb7c4bb80, sa_mask=[], sa_flags=SA_RESTART|SA_SIGINFO}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
ugetrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
uname({sysname="Linux", nodename="p200300D053D7310F22107AFFFE01D58C", ...}) = 0
statfs("/sys/fs/selinux", 0xbffeddb4) = -1 ENOENT (No such file or directory)
statfs("/selinux", 0xbffeddb4) = -1 ENOENT (No such file or directory)
brk(NULL) = 0x220c000
brk(0x222d000) = 0x222d000
brk(0x222e000) = 0x222e000
openat(AT_FDCWD, "/proc/filesystems", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "nodev\tsysfs\nnodev\trootfs\nnodev\tr"..., 1024) = 401
read(3, "", 1024) = 0
close(3) = 0
brk(0x222d000) = 0x222d000
access("/etc/selinux/config", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=3365136, ...}) = 0
mmap2(NULL, 2097152, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7a45000
close(3) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=48, ws_col=198, ws_xpixel=0, ws_ypixel=0}) = 0
openat(AT_FDCWD, ".", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_CLOEXEC|O_DIRECTORY) = 3
fstat64(3, {st_mode=S_IFDIR|0775, st_size=4096, ...}) = 0
getdents64(3, /* 3 entries */, 32768) = 80
getdents64(3, /* 0 entries */, 32768) = 0
close(3) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
write(1, "test.py\n", 8test.py
) = 8
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++