Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Syscalls linux32 #1170

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions miasm/os_dep/linux/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ def __init__(self, number, family, type_, protocol):
self.type_ = type_
self.protocol = protocol

def read(self, count):
return b""

Comment on lines +186 to +188
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's not a good idea to have a default read here. Maybe we can raise an error with pure abstract function, in order to for the user subclass this in order to implements it's own read.
See for example

raise NotImplementedError("Abstract method")

The user can subclass its own LinuxEnvironement and set a brand new self.network

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea, but it seems hard to subclass, it means you have to implement a subclass of FileDescriptorSocket, Network and LinuxEnvironment, and make all this work together, right ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of. It will give something like:

class CustomFileDescriptorSocket(FileDescriptorSocket):
    def read(self, count):
        print("Turlututu")

class CustomNetworking(Networking):
    def socket(self, family, type_, protocol):
        fd = self.linux_env.next_fd()
        fdesc = CustomFileDescriptorSocket(fd, family, type_, protocol)
        self.linux_env.file_descriptors[fd] = fdesc
        return fd


class CustomLinuxEnvironment(LinuxEnvironment):
    def __init__(self):
        super(CustomLinuxEnvironment, self).__init__()
        self.network = CustomNetworking(self)

But maybe there is better: we could modify those classes to have a class variable which embed their needs. For example, for Networking:

class Networking(object):
    """Network abstraction"""

    fd_generator = FileDescriptorSocket
    def __init__(self, linux_env):
        self.linux_env = linux_env

    def socket(self, family, type_, protocol):
        fd = self.linux_env.next_fd()
        fdesc = self.fd_generator(fd, family, type_, protocol)
        self.linux_env.file_descriptors[fd] = fdesc
        return fd

So the "overhead" may just be:

class CustomNetworking(Networking):
    fd_generator = CustomFileDescriptorSocket

But I am not really sure if this is a suitable python pattern.
Or maybe Networking should take it's generator as init argument ?
It's a problem we already face in the SandBox object, which depends on os, arch, ...

@commial @p-l- I am interested if you have some feed on this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is the same problem than the multiple inheritance of Sandbox. IMHO, it looks like more what we've done in Jitcore with Cgen or SymbExecClass, which reflects your last proposal.

In my opinion, the question is "what we want to provides, and what customization should be reasonably easy to implements?".
I agree with the fact that it should be easy to modify what the socket returns, its state, etc. i'm not sure that the more global Networking part needs that kind of customization possibility.

A pattern we can use would be to provides a kind of "socket factory" (sorry for this word, but it is what it is) that the Network would use to creates its sockets.
It could be a function, taking as input the socket parameters and returning an instance with the socket "interface", ie. a subclass of the socket fd.
It could also be a class, taking as __init__ these parameters, and asked just after for successful creation or not (to keep the possibility to easiliy deny socket creation). I rather prefer the function solution, as it could be easier to return default implementation or several socket families implementation.

This "factory function" is then an attribute of the Networking class, and could be replaced with a dedicated function / property.

If this pattern become more frequent for the Linux kernel stub implementation, we could have a "config-like" class containing several factories functions, or hooks.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the socket factory would be an attribute of Network ?

Using it would be something like :

class CustomFileDescriptorSocket(FileDescriptorSocket):
    def read(self, count):
        print("Chapeau pointu")

env = environment.LinuxEnvironment_x86_32()
env.network.socket_class = CustomFileDescriptorSocket

Is that correct ?


class FileSystem(object):
"""File system abstraction
Expand Down
186 changes: 185 additions & 1 deletion miasm/os_dep/linux/syscall.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,157 @@ def _dump_struct_stat_arml(info):
return data


def sys_x86_32_execve(jitter, linux_env):
# int execve(const char *pathname, char *const argv[],
# char *const envp[]);
pathname_addr, argv_ptr, envp_addr = jitter.syscall_args_systemv(3)
pathname = jitter.get_c_str(pathname_addr)
argv = []
i = 0
argv_addr = jitter.vm.get_u32(argv_ptr)
while argv_addr != 0:
argv.append(jitter.get_c_str(argv_addr))
argv_ptr += 4
argv_addr = jitter.vm.get_u32(argv_ptr)
envp = []
i = 0
while envp_addr != 0:
argv.append(jitter.get_c_str(envp_addr))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's envp instead of argv here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also yes

i += 4
argv_addr = jitter.vm.get_u32(jitter.cpu.EDX+i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe it's envp_addr here instead of argv_addr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

log.debug("sys_execve(%s, [%s], [%s])", pathname,
", ".join(argv), ", ".join(envp))
jitter.syscall_ret_systemv(0)


def sys_x86_64_execve(jitter, linux_env):
# int execve(const char *pathname, char *const argv[],
# char *const envp[]);
# TODO : merge that into a generic execve
pathname_addr, argv_ptr, envp_addr = jitter.syscall_args_systemv(3)
pathname = jitter.get_c_str(pathname_addr)
argv = []
i = 0
argv_addr = jitter.vm.get_u64(argv_ptr)
while argv_addr != 0:
argv.append(jitter.get_c_str(argv_addr))
argv_ptr += 8
argv_addr = jitter.vm.get_u64(argv_ptr)
envp = []
i = 0
while envp_addr != 0:
argv.append(jitter.get_c_str(envp_addr))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same remarks here for argv

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn copy paste

i += 8
argv_addr = jitter.vm.get_u64(jitter.cpu.EDX+i)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same remarks here for argv_addr

log.debug("sys_execve(%s, [%s], [%s])", pathname,
", ".join(argv), ", ".join(envp))
jitter.syscall_ret_systemv(0)


def sys_x86_32_socket(jitter, linux_env):
# int socketcall(int call, unsigned long *args)
# Redirect to several other socket syscalls
SOCKET_DOMAINS = {
0: "AF_UNSPEC",
1: "AF_UNIX",
2: "AF_INET",
3: "AF_AX25",
4: "AF_IPX",
5: "AF_APPLETALK",
6: "AF_NETROM",
7: "AF_BRIDGE",
8: "AF_AAL5",
9: "AF_X25", #Who cares ?
10: "AF_INET6",
11: "AF_MAX"
}

SOCKET_TYPE = {
1: "SOCK_STREAM",
2: "SOCK_DGRAM",
3: "SOCK_RAW"
}

if jitter.cpu.EBX == 1:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could use constant instead of == 1 ?

# int socket(int domain, int type, int protocol);
domain = jitter.vm.get_u32(jitter.cpu.ESP)
stype = jitter.vm.get_u32(jitter.cpu.ESP+4)
proto = jitter.vm.get_u32(jitter.cpu.ESP+8)
fd = linux_env.socket(domain, stype, proto)
log.debug("socket(%s, %s, %s)", SOCKET_DOMAINS[domain],
SOCKET_TYPE[stype], proto)
jitter.syscall_ret_systemv(fd)
elif jitter.cpu.EBX == 2:
# int bind(int sockfd, const struct sockaddr *addr,
# socklen_t addrlen);
fd = jitter.vm.get_u32(jitter.cpu.ESP)
socklen = jitter.vm.get_u32(jitter.cpu.ESP+8)
sockaddr = jitter.vm.get_mem(jitter.cpu.ESP+4, socklen)
family = struct.unpack("H", sockaddr[0:2])[0]
if family == 2:
# IPv4
port = struct.unpack(">H", sockaddr[2:4])[0]
ip = ".".join([str(i) for i in struct.unpack("BBBB", sockaddr[4:8])])
log.debug("socket_bind(fd, [%s, %i, %s], %i)", SOCKET_DOMAINS[family],
port, ip, socklen)
else:
log.debug("socket_bind(fd, sockaddr, socklen_t)")
jitter.syscall_ret_systemv(0)
elif jitter.cpu.EBX == 3:
# int connect(int sockfd, const struct sockaddr *addr,
# socklen_t addrlen);
fd = jitter.vm.get_u32(jitter.cpu.ESP)
socklen = jitter.vm.get_u32(jitter.cpu.ESP+8)
# Not the exact size because shellcodes won't provide the full struct
sockaddr = jitter.vm.get_mem(jitter.vm.get_u32(jitter.cpu.ESP+4), 8)
family = struct.unpack("H", sockaddr[0:2])[0]
if family == 2:
# IPv4
port = struct.unpack(">H", sockaddr[2:4])[0]
ip = ".".join([str(i) for i in struct.unpack("BBBB", sockaddr[4:8])])
log.debug("socket_connect(fd, [%s, %i, %s], %i)", SOCKET_DOMAINS[family],
port, ip, socklen)
else:
log.debug("socket_connect(fd, sockaddr, socklen)")
jitter.syscall_ret_systemv(0)
Te-k marked this conversation as resolved.
Show resolved Hide resolved
elif jitter.cpu.EBX == 4:
# int listen(int sockfd, int backlog);
sockfd = jitter.vm.get_u32(jitter.cpu.ESP)
backlog = jitter.vm.get_u32(jitter.cpu.ESP+4)
log.debug("socket_listen(%x, %x)", sockfd, backlog)
jitter.syscall_ret_systemv(0)
elif jitter.cpu.EBX == 5:
# int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd = jitter.vm.get_u32(jitter.cpu.ESP)
sockaddr = jitter.vm.get_u32(jitter.cpu.ESP+4)
addrlen = jitter.vm.get_u32(jitter.cpu.ESP+8)
log.debug("socket_accept(%x, %x, %x)", sockfd, sockaddr, addrlen)
jitter.syscall_ret_systemv(0)
elif jitter.cpu.EBX == 14:
# SYS_SETSOCKOPT
# int setsockopt(int sockfd, int level, int optname,
# const void *optval, socklen_t optlen);
sockfd = jitter.vm.get_u32(jitter.cpu.ESP)
level = jitter.vm.get_u32(jitter.cpu.ESP+4)
optname = jitter.vm.get_u32(jitter.cpu.ESP+8)
optval_addr = jitter.vm.get_u32(jitter.cpu.ESP+12)
optlen = jitter.vm.get_u32(jitter.cpu.ESP+16)
log.debug("socket_setsockopt(%x, %x, %x, %x, %x)", sockfd, level, optname,
optval_addr, optlen)
jitter.syscall_ret_systemv(0)
else:
print(jitter.cpu.EBX)
raise NotImplemented()


def sys_generic_chmod(jitter, linux_env):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could really apply the chmod on the file located in the file sandbox file_sb ?

# int chmod(const char *pathname, mode_t mode);
path_addr, mode = jitter.syscall_args_systemv(2)
pathname = jitter.get_c_str(path_addr)
log.debug("sys_chmod(%s, %x)", pathname, mode)
jitter.syscall_ret_systemv(0)


def sys_x86_64_rt_sigaction(jitter, linux_env):
# Parse arguments
sig, act, oact, sigsetsize = jitter.syscall_args_systemv(4)
Expand Down Expand Up @@ -531,7 +682,12 @@ def sys_generic_write(jitter, linux_env):

# Stub
data = jitter.vm.get_mem(buf, count)
jitter.syscall_ret_systemv(linux_env.write(fd, data))
r = linux_env.write(fd, data)
if r is None:
log.debug("-> write : failed")
jitter.syscall_ret_systemv(-1)
else:
jitter.syscall_ret_systemv(r)


def sys_x86_64_getdents(jitter, linux_env):
Expand Down Expand Up @@ -614,6 +770,14 @@ def sys_x86_64_newlstat(jitter, linux_env):
jitter.cpu.RAX = 0


def sys_generic_exit(jitter, linux_env):
# void exit(int status);
status, = jitter.syscall_args_systemv(1)
log.debug("sys_exit(%i)", status)
jitter.run = False
jitter.pc = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you don't need to set pc to 0 here



def sys_arml_lstat64(jitter, linux_env):
# Parse arguments
filename = jitter.cpu.R0
Expand Down Expand Up @@ -821,6 +985,14 @@ def sys_generic_setgid(jitter, linux_env):
jitter.syscall_ret_systemv(0)


def sys_generic_setreuid(jitter, linux_env):
# Parse arguments
ruid, euid = jitter.syscall_args_systemv(2)
log.debug("sys_setreuid(%x, %x)", ruid, euid)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could use the current Linux env uid/euid/gid of the linux env here?


jitter.syscall_ret_systemv(0)


def sys_generic_setuid(jitter, linux_env):
# Parse arguments
uid, = jitter.syscall_args_systemv(1)
Expand Down Expand Up @@ -899,7 +1071,16 @@ def sys_arml_gettimeofday(jitter, linux_env):


syscall_callbacks_x86_32 = {
0x1: sys_generic_exit,
0x3: sys_generic_read,
0x4: sys_generic_write,
0x5: sys_generic_open,
0xB: sys_x86_32_execve,
0xF: sys_generic_chmod,
0x46: sys_generic_setreuid,
0x66: sys_x86_32_socket,
0x7A: sys_x86_32_newuname,
0x7D: sys_generic_mprotect,
}


Expand All @@ -925,15 +1106,18 @@ def sys_arml_gettimeofday(jitter, linux_env):
0x27: sys_x86_64_getpid,
0x29: sys_x86_64_socket,
0x2A: sys_x86_64_connect,
0x3B: sys_x86_64_execve,
0x3F: sys_x86_64_newuname,
0x48: sys_generic_fcntl64,
0x4E: sys_x86_64_getdents,
0x59: sys_x86_64_readlink,
0x5A: sys_generic_chmod,
0x63: sys_x86_64_sysinfo,
0x66: sys_generic_getuid,
0x68: sys_generic_getgid,
0x6B: sys_generic_geteuid,
0x6C: sys_generic_getegid,
0x71: sys_generic_setreuid,
0xE4: sys_x86_64_clock_gettime,
0x89: sys_x86_64_statfs,
0x9E: sys_x86_64_arch_prctl,
Expand Down