Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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 examples/src/linux/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ patch_test.bin: patch_test.bin.c libpatch_test.so
x8664_onestraw_server: x8664_linux_onestraw.c
$(CC) $(CPPFLAGS) $(CFLAGS) -m64 -o $@ $<

x8664_linux_utime: x8664_linux_utime.c
$(CC) $(CPPFLAGS) $(CFLAGS) -m64 -o $@ $<

$(OBJS):%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@

Expand Down
76 changes: 76 additions & 0 deletions examples/src/linux/x8664_linux_utime.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <fcntl.h> /* Definition of AT_* constants */
#include <sys/stat.h>
#include <utime.h>
#include <sys/syscall.h> /* Definition of SYS_* constants */
#include <unistd.h>

int main(int argc, char **argv){
/*
struct utimbuf {
time_t atime;
time_t modtime;
*/
srand(0x1337);
struct timespec utimensat_times[2];
struct timespec atime;
struct timespec mtime;
atime.tv_sec = rand();
atime.tv_nsec = rand();
mtime.tv_sec = rand();
mtime.tv_nsec = rand() & 0xffff; // avoid illegal arg error
utimensat_times[0] = atime;
utimensat_times[1] = mtime;
int res = utimensat(AT_FDCWD, "./utimensat-test", utimensat_times,0);
if (!res){
perror("utimensat");
}


struct utimbuf utime_time[1];
struct utimbuf actime;
actime.actime = rand();
actime.modtime = rand();
utime_time[0] = actime;

res = syscall(SYS_utime,"./utime-test", utime_time);
if (!res){
perror("utime failed");
}

struct timeval utimes_times[2];
struct timeval utimes_actime;
struct timeval utimes_modtime;
utimes_actime.tv_sec = rand() & 0xff;
utimes_actime.tv_usec = rand() & 0xff;
utimes_modtime.tv_sec = rand() & 0xff;
utimes_modtime.tv_usec = rand() & 0xffff;
utimes_times[0] = utimes_actime;
utimes_times[1] = utimes_modtime;
res = syscall(SYS_utimes,"./utimes-test", utimes_times);
if (!res){
perror("utimes");
}

utimes_actime.tv_sec = rand() & 0xff;
utimes_actime.tv_usec = rand() & 0xff;
utimes_modtime.tv_sec = rand() & 0xff;
utimes_modtime.tv_usec = rand() & 0xffff;
utimes_times[0] = utimes_actime;
utimes_times[1] = utimes_modtime;
res = syscall(SYS_futimesat,AT_FDCWD, "./futimesat-test", utimes_times);
if (!res){
perror("futimesat");
}







}

198 changes: 184 additions & 14 deletions qiling/os/linux/syscall.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@
from qiling import Qiling
from qiling.arch.x86_const import *
from qiling.const import QL_ARCH

from qiling.os.posix.const import AT_FDCWD, AT_SYMLINK_NOFOLLOW
from qiling.os.posix.structs import *
from datetime import datetime
from math import floor
import os
import ctypes


def __get_timespec_struct(archbits: int):
long = getattr(ctypes, f'c_int{archbits}')
ulong = getattr(ctypes, f'c_uint{archbits}')
long = getattr(ctypes, f"c_int{archbits}")
ulong = getattr(ctypes, f"c_uint{archbits}")

class timespec(ctypes.Structure):
_pack_ = archbits // 8

_fields_ = (
('tv_sec', ulong),
('tv_nsec', long)
)
_fields_ = (("tv_sec", ulong), ("tv_nsec", long))

return timespec


def __get_timespec_obj(archbits: int):
now = datetime.now().timestamp()

Expand All @@ -38,17 +39,24 @@ def __get_timespec_obj(archbits: int):
def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
if ql.arch.type == QL_ARCH.X86:
u_info = ql.mem.read(u_info_addr, 4 * 4)
index = ql.unpack32s(u_info[0 : 4])
base = ql.unpack32(u_info[4 : 8])
limit = ql.unpack32(u_info[8 : 12])
index = ql.unpack32s(u_info[0:4])
base = ql.unpack32(u_info[4:8])
limit = ql.unpack32(u_info[8:12])

ql.log.debug("set_thread_area base : 0x%x limit is : 0x%x" % (base, limit))

if index == -1:
index = ql.os.gdtm.get_free_idx(12)

if index in (12, 13, 14):
access = QL_X86_A_PRESENT | QL_X86_A_PRIV_3 | QL_X86_A_DESC_DATA | QL_X86_A_DATA | QL_X86_A_DATA_E | QL_X86_A_DATA_W
access = (
QL_X86_A_PRESENT
| QL_X86_A_PRIV_3
| QL_X86_A_DESC_DATA
| QL_X86_A_DATA
| QL_X86_A_DATA_E
| QL_X86_A_DATA_W
)

ql.os.gdtm.register_gdt_segment(index, base, limit, access)
ql.mem.write_ptr(u_info_addr, index, 4)
Expand All @@ -57,12 +65,12 @@ def ql_syscall_set_thread_area(ql: Qiling, u_info_addr: int):
return -1

elif ql.arch.type == QL_ARCH.MIPS:
CONFIG3_ULR = (1 << 13)
CONFIG3_ULR = 1 << 13
ql.arch.regs.cp0_config3 = CONFIG3_ULR
ql.arch.regs.cp0_userlocal = u_info_addr
ql.arch.regs.v0 = 0
ql.arch.regs.a3 = 0
ql.log.debug ("set_thread_area(0x%x)" % u_info_addr)
ql.log.debug("set_thread_area(0x%x)" % u_info_addr)

return 0

Expand All @@ -75,18 +83,180 @@ def ql_syscall_set_tls(ql: Qiling, address: int):

ql.log.debug("settls(%#x)", address)


def ql_syscall_clock_gettime(ql: Qiling, clock_id: int, tp: int):
ts_obj = __get_timespec_obj(ql.arch.bits)
ql.mem.write(tp, bytes(ts_obj))

return 0


def ql_syscall_gettimeofday(ql: Qiling, tv: int, tz: int):
if tv:
ts_obj = __get_timespec_obj(ql.arch.bits)
ql.mem.write(tv, bytes(ts_obj))

if tz:
ql.mem.write(tz, b'\x00' * 8)
ql.mem.write(tz, b"\x00" * 8)

return 0


# Handle seconds conversions 'in house'
def microseconds_to_nanoseconds(s):
return s * 1000


def seconds_to_nanoseconds(s):
return s * 1000000000


"""
Actual implmentation of utime(s)
Rather than repeat work based on different
precision requirements, just convert seconds/microseconds
to ns and pass to os.utime()
"""


def do_utime(ql: Qiling, filename: ctypes.POINTER, times: ctypes.POINTER, s):
real_file = ""
try:
# get path inside of qiling rootfs
real_file = ql.os.path.transform_to_real_path(ql.mem.string(filename))
except Exception as ex: # return errors appropriately, don't try to handle
# everything ourselves
return -ex.errno
actime = modtime = 0
"""
times[0] specifies the new access time, and times[1] specifies the new modification time.
If times is NULL, then analogously to utime(), the access and modification times of the file are set to the
current time.
"""
if s: # utimes, times[0] == new access time, times[1] == modification
data = make_timeval_buf(ql.arch.bits, ql.arch.endian)
with data.ref(ql.mem, times) as ref_atime: # times[0]
actime = seconds_to_nanoseconds(ref_atime.tv_sec)
actime += microseconds_to_nanoseconds(ref_atime.tv_usec)
with data.ref(
ql.mem, times + ctypes.sizeof(data)
) as ref_mtime: # increment by ctypes.sizeof() to get times[1]
modtime = seconds_to_nanoseconds(ref_mtime.tv_sec)
modtime += microseconds_to_nanoseconds(ref_mtime.tv_usec)

else:
# utime uses utimbuf, so different data handling needs to be done
data = make_utimbuf(ql.arch.bits, ql.arch.endian)
with data.ref(ql.mem, times) as ref:
actime = seconds_to_nanoseconds(ref.actime)
modtime = seconds_to_nanoseconds(ref.modtime)
try:
os.utime(real_file, ns=(actime, modtime))
except Exception as ex:
return -ex.errno
return 0


"""
https://www.man7.org/linux/man-pages/man2/utimes.2.html
int utime(const char *filename,
const struct utimbuf *_Nullable times);
"""


def ql_syscall_utime(ql: Qiling, filename: ctypes.POINTER, times: ctypes.POINTER):
return do_utime(ql, filename, times, False) # False for 's' means
# do plain utime


"""
https://www.man7.org/linux/man-pages/man2/utimes.2.html
int utimes(const char *filename,
const struct timeval times[_Nullable 2]);
"""


def ql_syscall_utimes(ql: Qiling, filename: ctypes.POINTER, times: ctypes.POINTER):
return do_utime(ql, filename, times, True) # True for 's' means the
# we want 'utimes', which has a different prototype, and consequently,
# struct unpacking requirements, than utime


"""
Not re-using the do_utime implementation so we can handle
the dfd and timespec unpacking here
"""


def do_utime_fd_ns(
ql: Qiling,
dfd: int,
filename: ctypes.POINTER,
utimes: ctypes.POINTER,
flags: int,
symlinks,
):
# transform to real path, which ensures that we are
# operating inside of the qiling root
unpacked_filename = ql.os.path.transform_to_real_path(ql.mem.string(filename))
timespec_struct = make_timespec_buf(ql.arch.bits, ql.arch.endian)
atime_nsec = mtime_nsec = 0
if dfd is not None:
dfd = ql.os.fd[dfd].fileno
with timespec_struct.ref(ql.mem, utimes) as atime_ref:
atime_nsec = atime_ref.tv_nsec
atime_nsec += seconds_to_nanoseconds(atime_ref.tv_sec)
with timespec_struct.ref(
ql.mem, utimes + ctypes.sizeof(timespec_struct)
) as mtime_ref:
mtime_nsec = mtime_ref.tv_nsec
mtime_nsec += seconds_to_nanoseconds(mtime_ref.tv_sec)
ql.log.debug(f"Got filename {unpacked_filename} for utimensat syscall ")
try:
os.utime(
unpacked_filename,
ns=(atime_nsec, mtime_nsec),
dir_fd=dfd,
follow_symlinks=symlinks,
)
except Exception as ex:
return -ex.errno
return 0


"""
https://www.man7.org/linux/man-pages/man2/utimensat.2.html
sys_utimensat int dfd const char *filename struct timespec *utimes int flags
"""


def ql_syscall_utimensat(
ql: Qiling, dfd: int, filename: ctypes.POINTER, utimes: ctypes.POINTER, flags: int
):
if filename == 0:
return EACCES
if utimes == 0:
return EACCES
if dfd == AT_FDCWD:
dfd = None
if flags == AT_SYMLINK_NOFOLLOW:
follow_symlink = False
else:
follow_symlink = True
return do_utime_fd_ns(ql, dfd, filename, utimes, flags, follow_symlink)


"""
This is considered deprecated,
https://www.man7.org/linux/man-pages/man2/futimesat.2.html
but including here in case some legacy code needs it
int futimesat(int dirfd, const char *pathname,
const struct timeval times[2]);
"""


def ql_syscall_futimesat(
ql: Qiling, dfd: int, pathname: ctypes.POINTER, timeval: ctypes.POINTER
):

return ql_syscall_utimensat(ql, dfd, pathname, timeval, 0)
3 changes: 2 additions & 1 deletion qiling/os/posix/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,8 +751,9 @@ class qnx_mmap_prot_flags(QlPrettyFlag):

FD_CLOEXEC = 1

AT_FDCWD = -100
AT_FDCWD = 0xffffff9c # /usr/include/linux/fcntl.h
AT_EMPTY_PATH = 0x1000
AT_SYMLINK_NOFOLLOW = 0x100

# error code
EPERM = 1
Expand Down
Loading
Loading