mirror of
https://github.com/qilingframework/qiling.git
synced 2026-10-12 02:21:40 +00:00
Fix docker.yaml and sync
This commit is contained in:
@@ -33,6 +33,7 @@ TARGETS = \
|
||||
x8664_hello_cpp \
|
||||
x8664_hello_cpp_static \
|
||||
x8664_cloexec_test \
|
||||
x8664_linux_onestraw \
|
||||
patch_test.bin
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -124,7 +125,10 @@ libpatch_test.so: patch_test.so.h patch_test.so.c
|
||||
$(CC) $(CPPFLAGS) -Wall -s -O0 -shared -fpic -o $@ patch_test.so.c
|
||||
|
||||
patch_test.bin: patch_test.bin.c libpatch_test.so
|
||||
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^
|
||||
x8664_onestraw_server: x8664_linux_onestraw.c
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -m64 -o $@ $<
|
||||
|
||||
$(OBJS):%.o:%.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#define MAX_EVENTS 5
|
||||
#define READ_SIZE 10
|
||||
#include <stdio.h> // for fprintf()
|
||||
#include <unistd.h> // for close(), read()
|
||||
#include <sys/epoll.h> // for epoll_create1(), epoll_ctl(), struct epoll_event
|
||||
#include <string.h> // for strncmp
|
||||
|
||||
int main()
|
||||
{
|
||||
//setvbuf(stdin, NULL, _IONBF, 0);
|
||||
int running = 1, event_count, i;
|
||||
size_t bytes_read;
|
||||
char read_buffer[READ_SIZE + 1];
|
||||
struct epoll_event event, events[MAX_EVENTS];
|
||||
int epoll_fd = epoll_create1(0);
|
||||
|
||||
if (epoll_fd == -1) {
|
||||
fprintf(stderr, "Failed to create epoll file descriptor\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
event.events = EPOLLIN;
|
||||
event.data.fd = 0;
|
||||
|
||||
if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, 0, &event))
|
||||
{
|
||||
fprintf(stderr, "Failed to add file descriptor to epoll\n");
|
||||
close(epoll_fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (running) {
|
||||
printf("\nPolling for input...\n");
|
||||
event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, 30000);
|
||||
printf("%d ready events\n", event_count);
|
||||
for (i = 0; i < event_count; i++) {
|
||||
printf("Reading file descriptor '%d' -- ", events[i].data.fd);
|
||||
bytes_read = read(events[i].data.fd, read_buffer, READ_SIZE);
|
||||
printf("%zd bytes read.\n", bytes_read);
|
||||
read_buffer[bytes_read] = '\0';
|
||||
printf("Read '%s'\n", read_buffer);
|
||||
|
||||
if(!strncmp(read_buffer, "stop\n", 5))
|
||||
running = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (close(epoll_fd)) {
|
||||
fprintf(stderr, "Failed to close epoll file descriptor\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Attention:
|
||||
* To keep things simple, do not handle socket/bind/listen/.../epoll_create/epoll_wait API error
|
||||
*/
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DEFAULT_PORT 8000
|
||||
#define MAX_CONN 16
|
||||
#define MAX_EVENTS 32
|
||||
#define BUF_SIZE 16
|
||||
#define MAX_LINE 256
|
||||
|
||||
void server_run();
|
||||
void client_run();
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
char role = 's';
|
||||
while ((opt = getopt(argc, argv, "cs")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
role = 'c';
|
||||
break;
|
||||
case 's':
|
||||
break;
|
||||
default:
|
||||
printf("usage: %s [-cs]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (role == 's') {
|
||||
server_run();
|
||||
} else {
|
||||
client_run();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* register events of fd to epfd
|
||||
*/
|
||||
static void epoll_ctl_add(int epfd, int fd, uint32_t events)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
ev.events = events;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
|
||||
perror("epoll_ctl()\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_sockaddr(struct sockaddr_in *addr)
|
||||
{
|
||||
bzero((char *)addr, sizeof(struct sockaddr_in));
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = INADDR_ANY;
|
||||
addr->sin_port = htons(DEFAULT_PORT);
|
||||
}
|
||||
|
||||
static int setnonblocking(int sockfd)
|
||||
{
|
||||
if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK) ==
|
||||
-1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* epoll echo server
|
||||
*/
|
||||
void server_run()
|
||||
{
|
||||
int i;
|
||||
int n;
|
||||
int epfd;
|
||||
int nfds;
|
||||
int listen_sock;
|
||||
int conn_sock;
|
||||
int socklen;
|
||||
char buf[BUF_SIZE];
|
||||
struct sockaddr_in srv_addr;
|
||||
struct sockaddr_in cli_addr;
|
||||
struct epoll_event events[MAX_EVENTS];
|
||||
|
||||
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
set_sockaddr(&srv_addr);
|
||||
bind(listen_sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
|
||||
|
||||
setnonblocking(listen_sock);
|
||||
listen(listen_sock, MAX_CONN);
|
||||
|
||||
epfd = epoll_create(1);
|
||||
epoll_ctl_add(epfd, listen_sock, EPOLLIN | EPOLLOUT | EPOLLET);
|
||||
|
||||
socklen = sizeof(cli_addr);
|
||||
int a;
|
||||
for (a = 0; a < 2; a++) {
|
||||
nfds = epoll_wait(epfd, events, MAX_EVENTS, -1);
|
||||
for (i = 0; i < nfds; i++) {
|
||||
if (events[i].data.fd == listen_sock) {
|
||||
/* handle new connection */
|
||||
conn_sock =
|
||||
accept(listen_sock,
|
||||
(struct sockaddr *)&cli_addr,
|
||||
&socklen);
|
||||
|
||||
inet_ntop(AF_INET, (char *)&(cli_addr.sin_addr),
|
||||
buf, sizeof(cli_addr));
|
||||
printf("[+] connected with %s:%d\n", buf,
|
||||
ntohs(cli_addr.sin_port));
|
||||
|
||||
setnonblocking(conn_sock);
|
||||
epoll_ctl_add(epfd, conn_sock,
|
||||
EPOLLIN | EPOLLET | EPOLLRDHUP |
|
||||
EPOLLHUP);
|
||||
} else if (events[i].events & EPOLLIN) {
|
||||
/* handle EPOLLIN event */
|
||||
for (;;) {
|
||||
bzero(buf, sizeof(buf));
|
||||
n = read(events[i].data.fd, buf,
|
||||
sizeof(buf));
|
||||
if (n <= 0 /* || errno == EAGAIN */ ) {
|
||||
break;
|
||||
} else {
|
||||
printf("[+] data: %s\n", buf);
|
||||
write(events[i].data.fd, buf,
|
||||
strlen(buf));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("[+] unexpected\n");
|
||||
}
|
||||
/* check if the connection is closing */
|
||||
if (events[i].events & (EPOLLRDHUP | EPOLLHUP)) {
|
||||
printf("[+] connection closed\n");
|
||||
epoll_ctl(epfd, EPOLL_CTL_DEL,
|
||||
events[i].data.fd, NULL);
|
||||
close(events[i].data.fd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test clinet
|
||||
*/
|
||||
void client_run()
|
||||
{
|
||||
int n;
|
||||
int c;
|
||||
int sockfd;
|
||||
char buf[MAX_LINE];
|
||||
struct sockaddr_in srv_addr;
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
set_sockaddr(&srv_addr);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr *)&srv_addr, sizeof(srv_addr)) < 0) {
|
||||
perror("connect()");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
printf("input: ");
|
||||
fgets(buf, sizeof(buf), stdin);
|
||||
c = strlen(buf) - 1;
|
||||
buf[c] = '\0';
|
||||
write(sockfd, buf, c + 1);
|
||||
|
||||
bzero(buf, sizeof(buf));
|
||||
while (errno != EAGAIN
|
||||
&& (n = read(sockfd, buf, sizeof(buf))) > 0) {
|
||||
printf("echo: %s\n", buf);
|
||||
bzero(buf, sizeof(buf));
|
||||
|
||||
c -= n;
|
||||
if (c <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
close(sockfd);
|
||||
}
|
||||
@@ -1064,3 +1064,29 @@ SHMAT = 21
|
||||
SHMDT = 22
|
||||
SHMGET = 23
|
||||
SHMCTL = 24
|
||||
|
||||
# see: https://elixir.bootlin.com/linux/v5.19.17/source/include/uapi/linux/eventpoll.h
|
||||
EPOLL_CTL_ADD = 1
|
||||
EPOLL_CTL_DEL = 2
|
||||
EPOLL_CTL_MOD = 3
|
||||
|
||||
EPOLLIN = 0x00000001
|
||||
EPOLLPRI = 0x00000002
|
||||
EPOLLOUT = 0x00000004
|
||||
EPOLLERR = 0x00000008
|
||||
EPOLLHUP = 0x00000010
|
||||
EPOLLNVAL = 0x00000020
|
||||
EPOLLRDNORM = 0x00000040
|
||||
EPOLLRDBAND = 0x00000080
|
||||
EPOLLWRNORM = 0x00000100
|
||||
EPOLLWRBAND = 0x00000200
|
||||
EPOLLMSG = 0x00000400
|
||||
EPOLLRDHUP = 0x00002000
|
||||
|
||||
# TODO: should be aligned to emulated system's close-on-exec value
|
||||
EPOLL_CLOEXEC = 0o2000000
|
||||
|
||||
EPOLLEXCLUSIVE = 0b1 << 28
|
||||
EPOLLWAKEUP = 0b1 << 29
|
||||
EPOLLONESHOT = 0b1 << 30
|
||||
EPOLLET = 0b1 << 31
|
||||
|
||||
@@ -31,3 +31,4 @@ from .uio import *
|
||||
from .unistd import *
|
||||
from .utsname import *
|
||||
from .wait import *
|
||||
from .epoll import *
|
||||
@@ -0,0 +1,294 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import ctypes
|
||||
import select
|
||||
|
||||
from typing import TYPE_CHECKING, Dict, KeysView, NamedTuple
|
||||
|
||||
from qiling.os import struct
|
||||
from qiling.os.posix.const import *
|
||||
from qiling.os.filestruct import PersistentQlFile, ql_file
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from qiling import Qiling
|
||||
from qiling.arch.arch import QlArch
|
||||
from qiling.os.posix.posix import QlFileDes
|
||||
|
||||
|
||||
class QlEpollEntry(NamedTuple):
|
||||
"""A named tuple to represent an epoll entry.
|
||||
|
||||
This is used to store the events mask and the data for each entry in
|
||||
the epoll instance.
|
||||
"""
|
||||
|
||||
events: int
|
||||
data: int
|
||||
|
||||
|
||||
@struct.cache
|
||||
def __make_epoll_event(arch: QlArch):
|
||||
"""Create a structure to represent an epoll event.
|
||||
"""
|
||||
|
||||
Struct = struct.get_packed_struct(arch.endian)
|
||||
|
||||
class epoll_event(Struct):
|
||||
_fields_ = (
|
||||
('events', ctypes.c_uint32),
|
||||
('data', ctypes.c_uint64)
|
||||
)
|
||||
|
||||
return epoll_event
|
||||
|
||||
|
||||
class QlEpollObj:
|
||||
def __init__(self, epoll_object: select.epoll):
|
||||
self._epoll_object = epoll_object
|
||||
|
||||
# maps fd to eventmask
|
||||
# keep track of which fds have what eventmasks,
|
||||
# since this isn't directly supported in select.epoll
|
||||
self._fds: Dict[int, QlEpollEntry] = {}
|
||||
|
||||
@property
|
||||
def fds(self) -> KeysView[int]:
|
||||
return self._fds.keys()
|
||||
|
||||
@property
|
||||
def epoll_instance(self) -> select.epoll:
|
||||
return self._epoll_object
|
||||
|
||||
def close(self) -> None:
|
||||
self._epoll_object.close()
|
||||
|
||||
def __getitem__(self, fd: int) -> QlEpollEntry:
|
||||
return self._fds[fd]
|
||||
|
||||
def __setitem__(self, fd: int, entry: QlEpollEntry) -> None:
|
||||
# if fd is already being watched, modify its eventmask.
|
||||
if fd in self:
|
||||
self._epoll_object.modify(fd, entry.events)
|
||||
|
||||
# otherwise, register it with the epoll object
|
||||
else:
|
||||
self._epoll_object.register(fd, entry.events)
|
||||
|
||||
self._fds[fd] = entry
|
||||
|
||||
def __delitem__(self, fd: int) -> None:
|
||||
"""Remove an fd from the epoll instance.
|
||||
"""
|
||||
|
||||
self._fds.pop(fd)
|
||||
self._epoll_object.unregister(fd)
|
||||
|
||||
def __contains__(self, fd: int) -> bool:
|
||||
"""Test whether a specific fd is already being watched by this epoll instance.
|
||||
"""
|
||||
|
||||
return fd in self.fds
|
||||
|
||||
|
||||
def check_epoll_depth(ql_fd_list: QlFileDes) -> None:
|
||||
"""Recursively check each epoll instance's 'watched' fds for an instance of
|
||||
epoll being watched. If a chain of over 5 levels is detected, raise an exception
|
||||
"""
|
||||
|
||||
def __visit_obj(obj: QlEpollObj, depth: int):
|
||||
if depth >= 5:
|
||||
raise RecursionError
|
||||
|
||||
for fd in obj.fds:
|
||||
if isinstance(ql_fd_list[fd], QlEpollObj):
|
||||
__visit_obj(obj, depth + 1)
|
||||
|
||||
for obj in ql_fd_list:
|
||||
if isinstance(obj, QlEpollObj):
|
||||
__visit_obj(obj, 1)
|
||||
|
||||
|
||||
|
||||
def ql_syscall_epoll_ctl(ql: Qiling, epfd: int, op: int, fd: int, event: int):
|
||||
"""Modify an existing epoll.
|
||||
"""
|
||||
|
||||
# not clear from man page, but to be safe don't support 'undefined' ops.
|
||||
if op not in (EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD):
|
||||
return -EINVAL
|
||||
|
||||
if epfd == fd:
|
||||
return -EINVAL
|
||||
|
||||
if epfd not in range(NR_OPEN):
|
||||
return -EBADF
|
||||
|
||||
epoll_parent_obj = ql.os.fd[epfd]
|
||||
|
||||
if not isinstance(epoll_parent_obj, QlEpollObj):
|
||||
return -EINVAL
|
||||
|
||||
epoll_obj = epoll_parent_obj.epoll_instance
|
||||
|
||||
if epoll_obj is None:
|
||||
return -EBADF
|
||||
|
||||
if epoll_obj.fileno() == fd:
|
||||
return -ELOOP
|
||||
|
||||
# Qiling doesn't check process capabilities right now, so this case isn't explicitly handled yet
|
||||
# EPOLLWAKEUP (since Linux 3.5)
|
||||
# If EPOLLONESHOT and EPOLLET are clear and the process has the CAP_BLOCK_SUSPEND capability
|
||||
|
||||
fd_obj = ql.os.fd[fd]
|
||||
|
||||
if fd_obj is None:
|
||||
return -EBADF
|
||||
|
||||
# TODO: not sure if qiling supports a way to determine if the target file descriptor is a
|
||||
# directory. Here, check against PersistentQlFile is to ensure that polling stdin, stdout,
|
||||
# stderr is supported
|
||||
|
||||
# The target file fd does not support epoll. This error can occur if fd refers to, for
|
||||
# example, a regular file or a directory.
|
||||
if isinstance(fd_obj, ql_file) and not isinstance(fd_obj, PersistentQlFile):
|
||||
return -EPERM
|
||||
|
||||
|
||||
|
||||
# EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance
|
||||
if isinstance(fd_obj, QlEpollObj) and (op & EPOLLEXCLUSIVE):
|
||||
return -EINVAL
|
||||
|
||||
# Necessary to iterate over all possible qiling fds to determine if we have a chain of more
|
||||
# than five epolls monitoring each other This may be removed in the future if the QlOsLinux
|
||||
# class had a separate field for reserved for tracking epoll objects.
|
||||
epolls_list = [fobj for fobj in ql.os.fd if isinstance(fobj, QlEpollObj)]
|
||||
|
||||
try:
|
||||
check_epoll_depth(ql.os.fd)
|
||||
except RecursionError:
|
||||
return -ELOOP
|
||||
|
||||
if op == EPOLL_CTL_ADD:
|
||||
# can't add an fd that's already being waited on
|
||||
if fd in epoll_parent_obj:
|
||||
return -EEXIST
|
||||
|
||||
if not event:
|
||||
return -EINVAL
|
||||
|
||||
# dereference the event pointer to get structure fields
|
||||
epoll_event_cls = __make_epoll_event(ql.arch)
|
||||
epoll_event = epoll_event_cls.load_from(ql.mem, event)
|
||||
|
||||
# EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance
|
||||
if isinstance(fd_obj, QlEpollObj) and (epoll_event.events & EPOLLEXCLUSIVE):
|
||||
return -EINVAL
|
||||
|
||||
epoll_parent_obj[fd] = QlEpollEntry(
|
||||
epoll_event.events,
|
||||
epoll_event.data
|
||||
)
|
||||
|
||||
elif op == EPOLL_CTL_DEL:
|
||||
if fd not in epoll_parent_obj:
|
||||
return -ENOENT
|
||||
|
||||
# remove from fds list and do so in the underlying epoll instance
|
||||
del epoll_parent_obj[fd]
|
||||
|
||||
elif op == EPOLL_CTL_MOD:
|
||||
if fd not in epoll_parent_obj:
|
||||
return -ENOENT
|
||||
|
||||
if not event:
|
||||
return -EINVAL
|
||||
|
||||
# dereference the event pointer to get structure fields
|
||||
epoll_event_cls = __make_epoll_event(ql.arch)
|
||||
epoll_event = epoll_event_cls.load_from(ql.mem, event)
|
||||
|
||||
# EPOLLEXCLUSIVE cannot be set on MOD operation, only on ADD
|
||||
if epoll_event.events & EPOLLEXCLUSIVE:
|
||||
return -EINVAL
|
||||
|
||||
epoll_parent_obj[fd] = QlEpollEntry(
|
||||
epoll_event.events,
|
||||
epoll_event.data
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def ql_syscall_epoll_wait(ql: Qiling, epfd: int, epoll_events: int, maxevents: int, timeout: int):
|
||||
"""Wait on an existing epoll for specific events.
|
||||
"""
|
||||
|
||||
if maxevents <= 0:
|
||||
return -EINVAL
|
||||
|
||||
# default value is 0xffffffff, but this fails when passing to epoll.poll()
|
||||
if timeout == 0xFFFFFFFF:
|
||||
timeout = None
|
||||
|
||||
if epfd not in range(NR_OPEN):
|
||||
return -EBADF
|
||||
|
||||
epoll_parent_obj = ql.os.fd[epfd]
|
||||
|
||||
if not isinstance(epoll_parent_obj, QlEpollObj):
|
||||
return -EINVAL
|
||||
|
||||
epoll_obj = epoll_parent_obj.epoll_instance
|
||||
|
||||
if epoll_obj is None:
|
||||
return -EBADF
|
||||
|
||||
ready_fds = epoll_obj.poll(timeout, maxevents)
|
||||
|
||||
epoll_event_cls = __make_epoll_event(ql.arch)
|
||||
|
||||
# Each tuple in ready_fds consists of (file descriptor, eventmask) so we iterate
|
||||
# through these to indicate which fds are ready and 'why'
|
||||
#
|
||||
# FIXME: emulated system fds are not the same as hosted system fds
|
||||
for i, (fd, events) in enumerate(ready_fds):
|
||||
entry = epoll_parent_obj[fd]
|
||||
epoll_event = epoll_event_cls(events, entry.data)
|
||||
|
||||
offset = epoll_event_cls.sizeof() * i
|
||||
ql.mem.write(epoll_events + offset, bytes(epoll_event))
|
||||
|
||||
# if no longer interested in this fd, remove from list
|
||||
if events & EPOLLONESHOT:
|
||||
del epoll_parent_obj[fd]
|
||||
|
||||
return len(ready_fds)
|
||||
|
||||
|
||||
def __epoll_create(ql: Qiling, sizehint: int, flags: int) -> int:
|
||||
# Use select.epoll for underlying implementation, just as select.poll is
|
||||
# used for emulating poll()
|
||||
|
||||
ret = select.epoll(sizehint, flags)
|
||||
|
||||
fd = ret.fileno()
|
||||
ql.os.fd[fd] = QlEpollObj(ret)
|
||||
|
||||
return fd
|
||||
|
||||
|
||||
def ql_syscall_epoll_create1(ql: Qiling, flags: int):
|
||||
if flags != select.EPOLL_CLOEXEC and flags != 0:
|
||||
return -EINVAL
|
||||
|
||||
return __epoll_create(ql, -1, flags)
|
||||
|
||||
|
||||
def ql_syscall_epoll_create(ql: Qiling, size: int):
|
||||
if size < 0:
|
||||
return -EINVAL
|
||||
|
||||
return __epoll_create(ql, size, 0)
|
||||
@@ -222,6 +222,26 @@ class BaseStructEB(BaseStruct, ctypes.BigEndianStructure):
|
||||
pass
|
||||
|
||||
|
||||
@cache
|
||||
def get_packed_struct(endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[BaseStruct]:
|
||||
"""Provide a packed version of BaseStruct based on the emulated
|
||||
architecture endianess.
|
||||
|
||||
Args:
|
||||
archbits: required alignment in bits
|
||||
"""
|
||||
|
||||
Struct = {
|
||||
QL_ENDIAN.EL: BaseStructEL,
|
||||
QL_ENDIAN.EB: BaseStructEB
|
||||
}[endian]
|
||||
|
||||
class PackedStruct(Struct):
|
||||
_pack_ = 1
|
||||
|
||||
return PackedStruct
|
||||
|
||||
|
||||
@cache
|
||||
def get_aligned_struct(archbits: int, endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[BaseStruct]:
|
||||
"""Provide an aligned version of BaseStruct based on the emulated
|
||||
@@ -229,6 +249,7 @@ def get_aligned_struct(archbits: int, endian: QL_ENDIAN = QL_ENDIAN.EL) -> Type[
|
||||
|
||||
Args:
|
||||
archbits: required alignment in bits
|
||||
endian: required endianness
|
||||
"""
|
||||
|
||||
Struct = {
|
||||
|
||||
+64
-3
@@ -9,8 +9,11 @@ import random
|
||||
import os
|
||||
import io
|
||||
import re
|
||||
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
from ctypes import *
|
||||
sys.path.append("..")
|
||||
|
||||
from typing import Any, Sequence
|
||||
@@ -214,13 +217,13 @@ class ELFTest(unittest.TestCase):
|
||||
ql = Qiling(["../examples/rootfs/x8664_linux/bin/x8664_hello_static"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
|
||||
ql.run()
|
||||
del ql
|
||||
|
||||
|
||||
def test_elf_linux_x86(self):
|
||||
filename = 'test.qlog'
|
||||
|
||||
ql = Qiling(["../examples/rootfs/x86_linux/bin/x86_hello"], "../examples/rootfs/x86_linux", verbose=QL_VERBOSE.DEBUG, log_devices=[filename])
|
||||
ql.run()
|
||||
|
||||
ql._log_file_fd.handlers[0].close() # prevent FD leak that causes downstream issues
|
||||
os.remove(filename)
|
||||
del ql
|
||||
|
||||
@@ -770,6 +773,64 @@ class ELFTest(unittest.TestCase):
|
||||
|
||||
del ql
|
||||
|
||||
@unittest.skip("stdin hijacking doesn't work as expected")
|
||||
def test_elf_linux_x8664_epoll_simple(self):
|
||||
# This tests a sample binary that (e)polls on stdin and echos back the output. Upon
|
||||
# receiving 'stop', it will exit.
|
||||
#
|
||||
# epoll-0 tkaen from: https://github.com/maxasm/epoll-c/blob/main/main.c
|
||||
|
||||
rootfs = "../examples/rootfs/x8664_linux"
|
||||
argv = r"../examples/rootfs/x8664_linux/bin/x8664_linux_epoll_0".split()
|
||||
ql = Qiling(argv, rootfs, verbose=QL_VERBOSE.DEBUG)
|
||||
ql.os.stdin = pipe.SimpleInStream(0)
|
||||
ql.os.stdin.write(b'echo\n')
|
||||
ql.os.stdin.write(b'stop\n') # signal to exit gracefully
|
||||
ql.run()
|
||||
|
||||
self.assertIn(b'echo\n', ql.os.stdout.read())
|
||||
|
||||
del ql
|
||||
|
||||
def test_elf_linux_x8664_epoll_server(self):
|
||||
# This tests a simple server that uses epoll to wait for data, then prints it out. It has
|
||||
# been modified to exit after data has been received; instead of a typical server operation
|
||||
# that reads requests indefinitely.
|
||||
#
|
||||
# It listens on port 8000, and a separate thread is spawned in order to test how the server
|
||||
# handles a 'hello world' input. The server prints out whatever it receives, so the assert
|
||||
# statement checks the input is present as expected.
|
||||
#
|
||||
# onestraw server taken from: https://github.com/onestraw/epoll-example
|
||||
|
||||
# Note: Without a hook for this syscall, this error fires:
|
||||
# TypeError: stat: path should be string, bytes, os.PathLike or integer, not NoneType
|
||||
def hook_newfstatat(ql: Qiling, dirfd: int, pathname: int, statbuf: int, flags: int):
|
||||
return 0
|
||||
|
||||
def client():
|
||||
# give time for the server to listen
|
||||
time.sleep(3)
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(("127.0.0.1", 8000))
|
||||
s.send(b"hello world")
|
||||
s.close()
|
||||
|
||||
rootfs = "../examples/rootfs/x8664_linux"
|
||||
argv = r"../examples/rootfs/x8664_linux/bin/x8664_onestraw_server s".split() # s means 'server mode'
|
||||
|
||||
ql = Qiling(argv, rootfs, verbose=QL_VERBOSE.DEBUG)
|
||||
ql.os.set_syscall("newfstatat", hook_newfstatat, QL_INTERCEPT.CALL)
|
||||
ql.os.stdout = pipe.SimpleOutStream(1) # server prints data received to stdout
|
||||
ql.filter = '^data:'
|
||||
|
||||
client_thread = threading.Thread(target=client, daemon=True)
|
||||
client_thread.start()
|
||||
ql.run()
|
||||
|
||||
self.assertIn(b'hello world', ql.os.stdout.read(200)) # 200 is arbitrary--"good enough" for this task
|
||||
del ql
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
import http.client
|
||||
import platform
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import socket
|
||||
from typing import List
|
||||
|
||||
sys.path.append("..")
|
||||
@@ -636,6 +635,5 @@ class ELFTest(unittest.TestCase):
|
||||
feedback = response.read()
|
||||
self.assertEqual('httpd_test_successful', feedback.decode())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user