Document probing samples
This commit is contained in:
@@ -22,7 +22,13 @@
|
||||
- `docs/` contains various documents that describe the reverse engineered
|
||||
vmware APIs and resulting modules.
|
||||
- Use `docs/reverse_engineering_procedure.md` to best describe the steps that
|
||||
were undertaken to reverse engineer the Vmware APIs.
|
||||
were undertaken to reverse engineer the Vmware APIs. Make sure to cover the
|
||||
tools that were used (e.g. tcpdump, strace), when and how Python pickled objects
|
||||
were stored.
|
||||
- `docs/probing_samples` contains examples of scripts that were used for
|
||||
reverse engineering purposes. The goal is to provide a better insight over
|
||||
the reverse engineering procedure. Sanitize any sensitive information such
|
||||
as credentials and ips.
|
||||
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Probe NFC write open-flags / opcode. Not part of the library."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import struct
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, "/home/ubuntu/workspace/vmware_nbd_tests")
|
||||
os.environ.pop("LD_PRELOAD", None)
|
||||
|
||||
from openvixdisklib import nfc_open
|
||||
from tests.integration.base import TestBase
|
||||
|
||||
NFC_AIO_MSG_ERROR = 1
|
||||
NFC_AIO_MSG_IO = 7
|
||||
|
||||
|
||||
def recvn(sock, size):
|
||||
buf = bytearray()
|
||||
while len(buf) < size:
|
||||
chunk = sock.recv(size - len(buf))
|
||||
if not chunk:
|
||||
raise RuntimeError(f"closed, got {len(buf)}/{size}")
|
||||
buf.extend(chunk)
|
||||
return bytes(buf)
|
||||
|
||||
|
||||
def open_rw(session, disk_path, oflag):
|
||||
sock = nfc_open.takeover_authd_socket(session.authd_sock)
|
||||
nfc_open._handshake(sock, "vddk", "nbdmode", nfc_open.NFC_PROTOCOL_VERSION)
|
||||
disk = nfc_open.NfcDisk(
|
||||
sock, disk_path, handle=0, sector_size=nfc_open.NFC_SECTOR_SIZE)
|
||||
nfc_open._aio_prepare(disk)
|
||||
path_b = disk_path.encode("utf-8")
|
||||
open_body = struct.pack(
|
||||
"<IIIIII", len(path_b), 0, 0, 0, nfc_open.NFC_DISK, oflag)
|
||||
open_body = open_body.ljust(60, b"\x00")
|
||||
reply = disk._aio_roundtrip(
|
||||
nfc_open.NFC_AIO_MSG_OPEN_FILE, open_body, extra=path_b)
|
||||
handle, sector_size = nfc_open._parse_open_reply(reply)
|
||||
disk.handle = handle
|
||||
disk.sector_size = sector_size
|
||||
echoed = struct.unpack_from("<I", reply, 20)[0]
|
||||
print(f"OPEN sent=0x{oflag:x} echoed=0x{echoed:x} handle={handle}")
|
||||
return disk
|
||||
|
||||
|
||||
def try_io(disk, opcode, flags_field, data, send_extra=True):
|
||||
length = len(data)
|
||||
payload = struct.pack(
|
||||
"<QQQQIII",
|
||||
disk.handle, opcode, 0, length, length, length, flags_field)
|
||||
op_id = disk._next_op_id()
|
||||
disk._sock.sendall(
|
||||
nfc_open._pack_aio_hdr(NFC_AIO_MSG_IO, len(payload), op_id) + payload)
|
||||
if send_extra:
|
||||
disk._sock.sendall(data)
|
||||
rhdr = recvn(disk._sock, 16)
|
||||
magic, rtype, rsize, rop = struct.unpack_from("<IIII", rhdr)
|
||||
body = recvn(disk._sock, rsize) if rsize else b""
|
||||
extra = b""
|
||||
if rtype == NFC_AIO_MSG_IO and rsize >= 36:
|
||||
chunk_len = struct.unpack_from("<I", body, 32)[0]
|
||||
if 0 < chunk_len <= 65536:
|
||||
extra = recvn(disk._sock, chunk_len)
|
||||
return rtype, rsize, body, extra
|
||||
|
||||
|
||||
class Probe(TestBase):
|
||||
def test_probe(self):
|
||||
data = self.pattern_bytes(self.SECTOR_SIZE, b"PROBE-S0")
|
||||
open_flags = [
|
||||
0x1C, 0x00, 0x1F, 0x0E, 0x0C, 0x04, 0x06, 0x02,
|
||||
0x3E, 0x5E, 0x16, 0x1A, 0x1D, 0x0A, 0x08, 0x20,
|
||||
0x3C, 0x7E, 0xFE, 0x100, 0x1E, 0x0F, 0x18,
|
||||
]
|
||||
# First: opcode 2 (guessed write) across open flags
|
||||
for oflag in open_flags:
|
||||
with self.authenticate() as session:
|
||||
try:
|
||||
disk = open_rw(session, self.DISK_PATH, oflag)
|
||||
except Exception as exc:
|
||||
print(f"OPEN FAIL 0x{oflag:x} {exc}")
|
||||
continue
|
||||
rtype, rsize, body, extra = try_io(disk, 2, 0, data)
|
||||
print(
|
||||
f" IO op=2 flags=0x{oflag:x} rtype={rtype} "
|
||||
f"extra={len(extra)} body={body.hex()}")
|
||||
if rtype == NFC_AIO_MSG_IO:
|
||||
got = disk.read(0, 1)
|
||||
print(f" readback match={got==data} {got[:16]!r}")
|
||||
if got == data:
|
||||
print("SUCCESS opcode=2")
|
||||
disk.close()
|
||||
return
|
||||
try:
|
||||
disk.close()
|
||||
except Exception:
|
||||
disk._sock.close()
|
||||
|
||||
# Then: opcode 1 with ioflags as write bit, open 0x1C and 0x00
|
||||
for oflag in (0x1C, 0x00, 0x0E):
|
||||
for opcode, ioflags, send_extra in (
|
||||
(1, 1, True), (1, 2, True), (2, 1, True),
|
||||
(4, 0, True), (5, 0, True), (8, 0, True),
|
||||
(2, 0, True)):
|
||||
with self.authenticate() as session:
|
||||
disk = open_rw(session, self.DISK_PATH, oflag)
|
||||
rtype, rsize, body, extra = try_io(
|
||||
disk, opcode, ioflags, data, send_extra)
|
||||
print(
|
||||
f" IO op={opcode} iofl={ioflags} of=0x{oflag:x} "
|
||||
f"rtype={rtype} extra={len(extra)} body={body[:16].hex()}")
|
||||
if rtype == NFC_AIO_MSG_IO:
|
||||
try:
|
||||
got = disk.read(0, 1)
|
||||
print(f" readback match={got==data} {got[:16]!r}")
|
||||
if got == data:
|
||||
print("SUCCESS")
|
||||
disk.close()
|
||||
return
|
||||
except Exception as exc:
|
||||
print(f" readback exc {exc}")
|
||||
try:
|
||||
disk.close()
|
||||
except Exception:
|
||||
disk._sock.close()
|
||||
print("NO SUCCESS")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,173 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE *out;
|
||||
static pthread_mutex_t log_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int log_fd = -1;
|
||||
|
||||
static void init_out(void) {
|
||||
if (out) {
|
||||
return;
|
||||
}
|
||||
const char *path = getenv("SSLHOOK_LOG");
|
||||
if (!path) {
|
||||
path = "/tmp/sslhook-open.log";
|
||||
}
|
||||
out = fopen(path, "w");
|
||||
if (!out) {
|
||||
out = stderr;
|
||||
}
|
||||
log_fd = fileno(out);
|
||||
setvbuf(out, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
static int peer_port(int fd) {
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t len = sizeof(ss);
|
||||
if (getpeername(fd, (struct sockaddr *)&ss, &len) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ss.ss_family == AF_INET) {
|
||||
return ntohs(((struct sockaddr_in *)&ss)->sin_port);
|
||||
}
|
||||
if (ss.ss_family == AF_INET6) {
|
||||
return ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dump(const char *tag, const void *id, int fd, const void *buf, int num) {
|
||||
init_out();
|
||||
pthread_mutex_lock(&log_mu);
|
||||
fprintf(out, "\n==== %s %p fd=%d port=%d %d ====\n",
|
||||
tag, id, fd, fd >= 0 ? peer_port(fd) : -1, num);
|
||||
const unsigned char *p = buf;
|
||||
for (int i = 0; i < num; i++) {
|
||||
fprintf(out, "%02x", p[i]);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
pthread_mutex_unlock(&log_mu);
|
||||
}
|
||||
|
||||
typedef int (*ssl_write_fn)(void *, const void *, int);
|
||||
typedef int (*ssl_read_fn)(void *, void *, int);
|
||||
typedef int (*ssl_get_fd_fn)(const void *);
|
||||
typedef int (*ssl_write_ex_fn)(void *, const void *, size_t, size_t *);
|
||||
typedef int (*ssl_read_ex_fn)(void *, void *, size_t, size_t *);
|
||||
|
||||
static ssl_get_fd_fn real_ssl_get_fd(void) {
|
||||
static ssl_get_fd_fn fn;
|
||||
if (!fn) {
|
||||
fn = (ssl_get_fd_fn)dlsym(RTLD_NEXT, "SSL_get_fd");
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
int SSL_write(void *ssl, const void *buf, int num) {
|
||||
static ssl_write_fn real;
|
||||
if (!real) {
|
||||
real = (ssl_write_fn)dlsym(RTLD_NEXT, "SSL_write");
|
||||
}
|
||||
int fd = real_ssl_get_fd() ? real_ssl_get_fd()(ssl) : -1;
|
||||
dump("SSL_W", ssl, fd, buf, num);
|
||||
return real(ssl, buf, num);
|
||||
}
|
||||
|
||||
int SSL_read(void *ssl, void *buf, int num) {
|
||||
static ssl_read_fn real;
|
||||
if (!real) {
|
||||
real = (ssl_read_fn)dlsym(RTLD_NEXT, "SSL_read");
|
||||
}
|
||||
int n = real(ssl, buf, num);
|
||||
if (n > 0) {
|
||||
int fd = real_ssl_get_fd() ? real_ssl_get_fd()(ssl) : -1;
|
||||
dump("SSL_R", ssl, fd, buf, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int SSL_write_ex(void *ssl, const void *buf, size_t num, size_t *written) {
|
||||
static ssl_write_ex_fn real;
|
||||
if (!real) {
|
||||
real = (ssl_write_ex_fn)dlsym(RTLD_NEXT, "SSL_write_ex");
|
||||
}
|
||||
int fd = real_ssl_get_fd() ? real_ssl_get_fd()(ssl) : -1;
|
||||
dump("SSL_WEX", ssl, fd, buf, (int)num);
|
||||
return real(ssl, buf, num, written);
|
||||
}
|
||||
|
||||
int SSL_read_ex(void *ssl, void *buf, size_t num, size_t *readbytes) {
|
||||
static ssl_read_ex_fn real;
|
||||
if (!real) {
|
||||
real = (ssl_read_ex_fn)dlsym(RTLD_NEXT, "SSL_read_ex");
|
||||
}
|
||||
int rc = real(ssl, buf, num, readbytes);
|
||||
if (rc && readbytes && *readbytes > 0) {
|
||||
int fd = real_ssl_get_fd() ? real_ssl_get_fd()(ssl) : -1;
|
||||
dump("SSL_REX", ssl, fd, buf, (int)*readbytes);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int interesting_fd(int fd) {
|
||||
if (fd < 0 || fd == log_fd) {
|
||||
return 0;
|
||||
}
|
||||
int port = peer_port(fd);
|
||||
return port == 902 || port == 9020;
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count) {
|
||||
static ssize_t (*real)(int, const void *, size_t);
|
||||
if (!real) {
|
||||
real = (ssize_t (*)(int, const void *, size_t))dlsym(RTLD_NEXT, "write");
|
||||
}
|
||||
if (interesting_fd(fd) && count > 0 && buf) {
|
||||
dump("WRITE", NULL, fd, buf, (int)count);
|
||||
}
|
||||
return real(fd, buf, count);
|
||||
}
|
||||
|
||||
ssize_t send(int fd, const void *buf, size_t len, int flags) {
|
||||
static ssize_t (*real)(int, const void *, size_t, int);
|
||||
if (!real) {
|
||||
real = (ssize_t (*)(int, const void *, size_t, int))dlsym(RTLD_NEXT, "send");
|
||||
}
|
||||
if (interesting_fd(fd) && len > 0 && buf) {
|
||||
dump("SEND", NULL, fd, buf, (int)len);
|
||||
}
|
||||
return real(fd, buf, len, flags);
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t count) {
|
||||
static ssize_t (*real)(int, void *, size_t);
|
||||
if (!real) {
|
||||
real = (ssize_t (*)(int, void *, size_t))dlsym(RTLD_NEXT, "read");
|
||||
}
|
||||
ssize_t n = real(fd, buf, count);
|
||||
if (n > 0 && interesting_fd(fd)) {
|
||||
dump("READ", NULL, fd, buf, (int)n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t recv(int fd, void *buf, size_t len, int flags) {
|
||||
static ssize_t (*real)(int, void *, size_t, int);
|
||||
if (!real) {
|
||||
real = (ssize_t (*)(int, void *, size_t, int))dlsym(RTLD_NEXT, "recv");
|
||||
}
|
||||
ssize_t n = real(fd, buf, len, flags);
|
||||
if (n > 0 && interesting_fd(fd)) {
|
||||
dump("RECV", NULL, fd, buf, (int)n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE *out;
|
||||
static pthread_mutex_t log_mu = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int log_fd = -1;
|
||||
static int ready;
|
||||
|
||||
static ssize_t (*real_write)(int, const void *, size_t);
|
||||
static ssize_t (*real_read)(int, void *, size_t);
|
||||
static ssize_t (*real_send)(int, const void *, size_t, int);
|
||||
static ssize_t (*real_recv)(int, void *, size_t, int);
|
||||
static ssize_t (*real_writev)(int, const struct iovec *, int);
|
||||
static ssize_t (*real_readv)(int, const struct iovec *, int);
|
||||
static ssize_t (*real_sendmsg)(int, const struct msghdr *, int);
|
||||
static ssize_t (*real_recvmsg)(int, struct msghdr *, int);
|
||||
static int (*real_ssl_write)(void *, const void *, int);
|
||||
static int (*real_ssl_read)(void *, void *, int);
|
||||
static int (*real_ssl_get_fd)(const void *);
|
||||
|
||||
__attribute__((constructor))
|
||||
static void init_syms(void) {
|
||||
real_write = dlsym(RTLD_NEXT, "write");
|
||||
real_read = dlsym(RTLD_NEXT, "read");
|
||||
real_send = dlsym(RTLD_NEXT, "send");
|
||||
real_recv = dlsym(RTLD_NEXT, "recv");
|
||||
real_writev = dlsym(RTLD_NEXT, "writev");
|
||||
real_readv = dlsym(RTLD_NEXT, "readv");
|
||||
real_sendmsg = dlsym(RTLD_NEXT, "sendmsg");
|
||||
real_recvmsg = dlsym(RTLD_NEXT, "recvmsg");
|
||||
real_ssl_write = dlsym(RTLD_NEXT, "SSL_write");
|
||||
real_ssl_read = dlsym(RTLD_NEXT, "SSL_read");
|
||||
real_ssl_get_fd = dlsym(RTLD_NEXT, "SSL_get_fd");
|
||||
const char *path = getenv("SSLHOOK_LOG");
|
||||
if (!path) {
|
||||
path = "/tmp/sslhook-write.log";
|
||||
}
|
||||
out = fopen(path, "w");
|
||||
if (!out) {
|
||||
out = stderr;
|
||||
}
|
||||
log_fd = fileno(out);
|
||||
setvbuf(out, NULL, _IONBF, 0);
|
||||
ready = 1;
|
||||
fprintf(out, "hook loaded write=%p ssl_write=%p\n",
|
||||
(void *)real_write, (void *)real_ssl_write);
|
||||
}
|
||||
|
||||
static int peer_port(int fd) {
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t len = sizeof(ss);
|
||||
if (getpeername(fd, (struct sockaddr *)&ss, &len) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ss.ss_family == AF_INET) {
|
||||
return ntohs(((struct sockaddr_in *)&ss)->sin_port);
|
||||
}
|
||||
if (ss.ss_family == AF_INET6) {
|
||||
return ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void dump(const char *tag, int fd, const void *buf, int num) {
|
||||
if (!ready || !out || fd == log_fd || num <= 0 || num > 1024 * 1024) {
|
||||
return;
|
||||
}
|
||||
int port = peer_port(fd);
|
||||
if (port <= 0 || port == 443) {
|
||||
return;
|
||||
}
|
||||
const unsigned char *p = buf;
|
||||
/* Skip TLS records. */
|
||||
if (num >= 3 && p[0] == 0x16 && p[1] == 0x03) {
|
||||
return;
|
||||
}
|
||||
if (num >= 3 && p[0] == 0x17 && p[1] == 0x03) {
|
||||
return;
|
||||
}
|
||||
pthread_mutex_lock(&log_mu);
|
||||
fprintf(out, "\n==== %s fd=%d port=%d %d ====\n", tag, fd, port, num);
|
||||
for (int i = 0; i < num; i++) {
|
||||
fprintf(out, "%02x", p[i]);
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
pthread_mutex_unlock(&log_mu);
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count) {
|
||||
if (real_write == NULL) {
|
||||
return syscall(SYS_write, fd, buf, count);
|
||||
}
|
||||
if (ready && out && fd != log_fd) {
|
||||
static int nlogged;
|
||||
if (nlogged < 40) {
|
||||
fprintf(out, "write fd=%d port=%d n=%zu\n", fd, peer_port(fd), count);
|
||||
nlogged++;
|
||||
}
|
||||
}
|
||||
dump("WRITE", fd, buf, (int)count);
|
||||
return real_write(fd, buf, count);
|
||||
}
|
||||
|
||||
ssize_t read(int fd, void *buf, size_t count) {
|
||||
if (real_read == NULL) {
|
||||
return syscall(SYS_read, fd, buf, count);
|
||||
}
|
||||
ssize_t n = real_read(fd, buf, count);
|
||||
if (n > 0) {
|
||||
dump("READ", fd, buf, (int)n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t send(int fd, const void *buf, size_t len, int flags) {
|
||||
if (real_send == NULL) {
|
||||
return syscall(SYS_sendto, fd, buf, len, flags, NULL, 0);
|
||||
}
|
||||
dump("SEND", fd, buf, (int)len);
|
||||
return real_send(fd, buf, len, flags);
|
||||
}
|
||||
|
||||
ssize_t recv(int fd, void *buf, size_t len, int flags) {
|
||||
if (real_recv == NULL) {
|
||||
return syscall(SYS_recvfrom, fd, buf, len, flags, NULL, 0);
|
||||
}
|
||||
ssize_t n = real_recv(fd, buf, len, flags);
|
||||
if (n > 0) {
|
||||
dump("RECV", fd, buf, (int)n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
ssize_t writev(int fd, const struct iovec *iov, int iovcnt) {
|
||||
if (real_writev == NULL) {
|
||||
return syscall(SYS_writev, fd, iov, iovcnt);
|
||||
}
|
||||
size_t total = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
total += iov[i].iov_len;
|
||||
}
|
||||
if (total > 0 && total <= 1024 * 1024) {
|
||||
unsigned char *buf = malloc(total);
|
||||
if (buf) {
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
memcpy(buf + off, iov[i].iov_base, iov[i].iov_len);
|
||||
off += iov[i].iov_len;
|
||||
}
|
||||
dump("WRITEV", fd, buf, (int)total);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
return real_writev(fd, iov, iovcnt);
|
||||
}
|
||||
|
||||
int SSL_write(void *ssl, const void *buf, int num) {
|
||||
if (real_ssl_write == NULL) {
|
||||
real_ssl_write = dlsym(RTLD_NEXT, "SSL_write");
|
||||
}
|
||||
int fd = real_ssl_get_fd ? real_ssl_get_fd(ssl) : -1;
|
||||
dump("SSL_W", fd, buf, num);
|
||||
return real_ssl_write(ssl, buf, num);
|
||||
}
|
||||
|
||||
int SSL_read(void *ssl, void *buf, int num) {
|
||||
if (real_ssl_read == NULL) {
|
||||
real_ssl_read = dlsym(RTLD_NEXT, "SSL_read");
|
||||
}
|
||||
int n = real_ssl_read(ssl, buf, num);
|
||||
if (n > 0) {
|
||||
int fd = real_ssl_get_fd ? real_ssl_get_fd(ssl) : -1;
|
||||
dump("SSL_R", fd, buf, n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void dump_msghdr(const char *tag, int fd, const struct msghdr *msg) {
|
||||
if (!msg || !msg->msg_iov) {
|
||||
return;
|
||||
}
|
||||
size_t total = 0;
|
||||
for (size_t i = 0; i < (size_t)msg->msg_iovlen; i++) {
|
||||
total += msg->msg_iov[i].iov_len;
|
||||
}
|
||||
if (total == 0 || total > 1024 * 1024) {
|
||||
return;
|
||||
}
|
||||
unsigned char *buf = malloc(total);
|
||||
if (!buf) {
|
||||
return;
|
||||
}
|
||||
size_t off = 0;
|
||||
for (size_t i = 0; i < (size_t)msg->msg_iovlen; i++) {
|
||||
memcpy(buf + off, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
|
||||
off += msg->msg_iov[i].iov_len;
|
||||
}
|
||||
dump(tag, fd, buf, (int)total);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
|
||||
if (real_sendmsg == NULL) {
|
||||
real_sendmsg = dlsym(RTLD_NEXT, "sendmsg");
|
||||
}
|
||||
if (ready && out && fd != log_fd) {
|
||||
static int nlogged;
|
||||
if (nlogged < 20) {
|
||||
fprintf(out, "sendmsg fd=%d port=%d\n", fd, peer_port(fd));
|
||||
nlogged++;
|
||||
}
|
||||
}
|
||||
dump_msghdr("SENDMSG", fd, msg);
|
||||
return real_sendmsg(fd, msg, flags);
|
||||
}
|
||||
|
||||
ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
|
||||
if (real_recvmsg == NULL) {
|
||||
real_recvmsg = dlsym(RTLD_NEXT, "recvmsg");
|
||||
}
|
||||
ssize_t n = real_recvmsg(fd, msg, flags);
|
||||
if (n > 0) {
|
||||
dump_msghdr("RECVMSG", fd, msg);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Trace VDDK Open+Read for NFC protocol capture."""
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
|
||||
VDDK_DIR = "/home/ubuntu/workspace/vmware_nbd_tests/vddk"
|
||||
lib = ctypes.CDLL(os.path.join(VDDK_DIR, "libvixDiskLib.so"))
|
||||
|
||||
VIXDISKLIB_CRED_UID = 1
|
||||
VIXDISKLIB_FLAG_OPEN_READ_ONLY = 4
|
||||
|
||||
|
||||
class VixDiskLibUidPasswdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("userName", ctypes.c_char_p),
|
||||
("password", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibSessionIdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cookie", ctypes.c_char_p),
|
||||
("userName", ctypes.c_char_p),
|
||||
("key", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibCreds(ctypes.Union):
|
||||
_fields_ = [
|
||||
("uid", VixDiskLibUidPasswdCreds),
|
||||
("sessionId", VixDiskLibSessionIdCreds),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibConnectParams(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("vmxSpec", ctypes.c_char_p),
|
||||
("serverName", ctypes.c_char_p),
|
||||
("thumbPrint", ctypes.c_char_p),
|
||||
("privateUse", ctypes.c_longlong),
|
||||
("credType", ctypes.c_uint32),
|
||||
("creds", VixDiskLibCreds),
|
||||
("port", ctypes.c_uint32),
|
||||
("nfcHostPort", ctypes.c_uint32),
|
||||
("vimApiVer", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
def check(err, what):
|
||||
if err != 0:
|
||||
lib.VixDiskLib_GetErrorText.restype = ctypes.c_void_p
|
||||
lib.VixDiskLib_GetErrorText.argtypes = [ctypes.c_uint64, ctypes.c_char_p]
|
||||
msg = lib.VixDiskLib_GetErrorText(err, None)
|
||||
text = ctypes.cast(msg, ctypes.c_char_p).value
|
||||
lib.VixDiskLib_FreeErrorText.argtypes = [ctypes.c_char_p]
|
||||
lib.VixDiskLib_FreeErrorText(ctypes.cast(msg, ctypes.c_char_p))
|
||||
raise SystemExit(f"{what} failed: {err} {text}")
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs("/tmp/vddk-open-trace", exist_ok=True)
|
||||
config_path = "/tmp/vddk-open-trace/vddk.config"
|
||||
with open(config_path, "w") as f:
|
||||
f.write("tmpDirectory=/tmp/vddk-open-trace\n")
|
||||
f.write("log.fileName=/tmp/vddk-open-trace/vddk.log\n")
|
||||
f.write("log.fileLevel=verbose\n")
|
||||
f.write("vixDiskLib.nfc.LogLevel=4\n")
|
||||
f.write("vixDiskLib.transport.LogLevel=4\n")
|
||||
|
||||
lib.VixDiskLib_InitEx.argtypes = [
|
||||
ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p,
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
lib.VixDiskLib_InitEx.restype = ctypes.c_uint64
|
||||
check(lib.VixDiskLib_InitEx(
|
||||
8, 0, None, None, None, VDDK_DIR.encode(), config_path.encode()),
|
||||
"InitEx")
|
||||
|
||||
params = VixDiskLibConnectParams()
|
||||
params.vmxSpec = b"<sanitized>"
|
||||
params.serverName = b"<sanitized>"
|
||||
params.thumbPrint = b"<sanitized>"
|
||||
params.credType = VIXDISKLIB_CRED_UID
|
||||
params.creds.uid.userName = b"<sanitized>"
|
||||
params.creds.uid.password = b"<sanitized>"
|
||||
params.port = 443
|
||||
|
||||
lib.VixDiskLib_ConnectEx.argtypes = [
|
||||
ctypes.POINTER(VixDiskLibConnectParams), ctypes.c_char,
|
||||
ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
|
||||
lib.VixDiskLib_ConnectEx.restype = ctypes.c_uint64
|
||||
conn = ctypes.c_void_p()
|
||||
check(lib.VixDiskLib_ConnectEx(
|
||||
params, True, b"snapshot-13442", b"nbd", ctypes.byref(conn)),
|
||||
"ConnectEx")
|
||||
print("ConnectEx ok", flush=True)
|
||||
|
||||
lib.VixDiskLib_Open.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_void_p)]
|
||||
lib.VixDiskLib_Open.restype = ctypes.c_uint64
|
||||
disk = ctypes.c_void_p()
|
||||
path = b"[datastore0] test-coriolis-jenkins-rocky9/test-coriolis-jenkins-rocky9-000007.vmdk"
|
||||
check(lib.VixDiskLib_Open(conn, path, VIXDISKLIB_FLAG_OPEN_READ_ONLY, ctypes.byref(disk)),
|
||||
"Open")
|
||||
print("Open ok", flush=True)
|
||||
|
||||
buf = ctypes.create_string_buffer(512)
|
||||
lib.VixDiskLib_Read.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_char_p]
|
||||
lib.VixDiskLib_Read.restype = ctypes.c_uint64
|
||||
check(lib.VixDiskLib_Read(disk, 0, 1, buf), "Read")
|
||||
print("Read ok first16", buf.raw[:16].hex(), flush=True)
|
||||
|
||||
lib.VixDiskLib_Close.argtypes = [ctypes.c_void_p]
|
||||
lib.VixDiskLib_Close.restype = ctypes.c_uint64
|
||||
lib.VixDiskLib_Disconnect.argtypes = [ctypes.c_void_p]
|
||||
lib.VixDiskLib_Disconnect.restype = ctypes.c_uint64
|
||||
lib.VixDiskLib_Close(disk)
|
||||
lib.VixDiskLib_Disconnect(conn)
|
||||
lib.VixDiskLib_Exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Trace VDDK Read at several offsets/sizes for NFC IO capture."""
|
||||
|
||||
import ctypes
|
||||
import os
|
||||
|
||||
VDDK_DIR = "/home/ubuntu/workspace/vmware_nbd_tests/vddk"
|
||||
lib = ctypes.CDLL(os.path.join(VDDK_DIR, "libvixDiskLib.so"))
|
||||
|
||||
VIXDISKLIB_CRED_UID = 1
|
||||
VIXDISKLIB_FLAG_OPEN_READ_ONLY = 4
|
||||
SECTOR = 512
|
||||
|
||||
|
||||
class VixDiskLibUidPasswdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("userName", ctypes.c_char_p),
|
||||
("password", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibSessionIdCreds(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("cookie", ctypes.c_char_p),
|
||||
("userName", ctypes.c_char_p),
|
||||
("key", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibCreds(ctypes.Union):
|
||||
_fields_ = [
|
||||
("uid", VixDiskLibUidPasswdCreds),
|
||||
("sessionId", VixDiskLibSessionIdCreds),
|
||||
]
|
||||
|
||||
|
||||
class VixDiskLibConnectParams(ctypes.Structure):
|
||||
_fields_ = [
|
||||
("vmxSpec", ctypes.c_char_p),
|
||||
("serverName", ctypes.c_char_p),
|
||||
("thumbPrint", ctypes.c_char_p),
|
||||
("privateUse", ctypes.c_longlong),
|
||||
("credType", ctypes.c_uint32),
|
||||
("creds", VixDiskLibCreds),
|
||||
("port", ctypes.c_uint32),
|
||||
("nfcHostPort", ctypes.c_uint32),
|
||||
("vimApiVer", ctypes.c_char_p),
|
||||
]
|
||||
|
||||
|
||||
def check(err, what):
|
||||
if err != 0:
|
||||
lib.VixDiskLib_GetErrorText.restype = ctypes.c_void_p
|
||||
lib.VixDiskLib_GetErrorText.argtypes = [ctypes.c_uint64, ctypes.c_char_p]
|
||||
msg = lib.VixDiskLib_GetErrorText(err, None)
|
||||
text = ctypes.cast(msg, ctypes.c_char_p).value
|
||||
lib.VixDiskLib_FreeErrorText.argtypes = [ctypes.c_char_p]
|
||||
lib.VixDiskLib_FreeErrorText(ctypes.cast(msg, ctypes.c_char_p))
|
||||
raise SystemExit(f"{what} failed: {err} {text}")
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs("/tmp/vddk-read-trace", exist_ok=True)
|
||||
config_path = "/tmp/vddk-read-trace/vddk.config"
|
||||
with open(config_path, "w") as f:
|
||||
f.write("tmpDirectory=/tmp/vddk-read-trace\n")
|
||||
f.write("log.fileName=/tmp/vddk-read-trace/vddk.log\n")
|
||||
f.write("log.fileLevel=verbose\n")
|
||||
f.write("vixDiskLib.nfc.LogLevel=4\n")
|
||||
f.write("vixDiskLib.transport.LogLevel=4\n")
|
||||
|
||||
lib.VixDiskLib_InitEx.argtypes = [
|
||||
ctypes.c_uint32, ctypes.c_uint32, ctypes.c_void_p, ctypes.c_void_p,
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p]
|
||||
lib.VixDiskLib_InitEx.restype = ctypes.c_uint64
|
||||
check(lib.VixDiskLib_InitEx(
|
||||
8, 0, None, None, None, VDDK_DIR.encode(), config_path.encode()),
|
||||
"InitEx")
|
||||
|
||||
params = VixDiskLibConnectParams()
|
||||
params.vmxSpec = b"<sanitized>"
|
||||
params.serverName = b"<sanitized>"
|
||||
params.thumbPrint = b"<sanitized>"
|
||||
params.credType = VIXDISKLIB_CRED_UID
|
||||
params.creds.uid.userName = b"<sanitized>"
|
||||
params.creds.uid.password = b"<sanitized>"
|
||||
params.port = 443
|
||||
|
||||
lib.VixDiskLib_ConnectEx.argtypes = [
|
||||
ctypes.POINTER(VixDiskLibConnectParams), ctypes.c_char,
|
||||
ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(ctypes.c_void_p)]
|
||||
lib.VixDiskLib_ConnectEx.restype = ctypes.c_uint64
|
||||
conn = ctypes.c_void_p()
|
||||
check(lib.VixDiskLib_ConnectEx(
|
||||
params, True, b"snapshot-13442", b"nbd", ctypes.byref(conn)),
|
||||
"ConnectEx")
|
||||
print("ConnectEx ok", flush=True)
|
||||
|
||||
lib.VixDiskLib_Open.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint32,
|
||||
ctypes.POINTER(ctypes.c_void_p)]
|
||||
lib.VixDiskLib_Open.restype = ctypes.c_uint64
|
||||
disk = ctypes.c_void_p()
|
||||
path = b"[datastore0] test-coriolis-jenkins-rocky9/test-coriolis-jenkins-rocky9-000007.vmdk"
|
||||
check(lib.VixDiskLib_Open(conn, path, VIXDISKLIB_FLAG_OPEN_READ_ONLY, ctypes.byref(disk)),
|
||||
"Open")
|
||||
print("Open ok", flush=True)
|
||||
|
||||
lib.VixDiskLib_Read.argtypes = [
|
||||
ctypes.c_void_p, ctypes.c_uint64, ctypes.c_uint64, ctypes.c_char_p]
|
||||
lib.VixDiskLib_Read.restype = ctypes.c_uint64
|
||||
|
||||
reads = [
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(1, 1),
|
||||
(8, 8),
|
||||
(0, 128),
|
||||
(0, 129),
|
||||
(256, 64),
|
||||
]
|
||||
for start, n in reads:
|
||||
buf = ctypes.create_string_buffer(n * SECTOR)
|
||||
check(lib.VixDiskLib_Read(disk, start, n, buf), f"Read {start}+{n}")
|
||||
nz = sum(1 for b in buf.raw if b)
|
||||
print(f"Read start={start} n={n} first16={buf.raw[:16].hex()} nonzero={nz}",
|
||||
flush=True)
|
||||
|
||||
lib.VixDiskLib_Close.argtypes = [ctypes.c_void_p]
|
||||
lib.VixDiskLib_Close.restype = ctypes.c_uint64
|
||||
lib.VixDiskLib_Disconnect.argtypes = [ctypes.c_void_p]
|
||||
lib.VixDiskLib_Disconnect.restype = ctypes.c_uint64
|
||||
lib.VixDiskLib_Close(disk)
|
||||
lib.VixDiskLib_Disconnect(conn)
|
||||
lib.VixDiskLib_Exit()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
"""One-off: VDDK write with verbose NFC logs. Not part of the library."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, "/home/ubuntu/workspace/vmware_nbd_tests")
|
||||
os.environ.pop("LD_PRELOAD", None)
|
||||
os.environ["LD_LIBRARY_PATH"] = (
|
||||
"/home/ubuntu/workspace/vmware_nbd_tests/.vddk:"
|
||||
+ os.environ.get("LD_LIBRARY_PATH", ""))
|
||||
|
||||
from tests.integration.base import TestBase
|
||||
from tests.integration import vixdisklib
|
||||
|
||||
|
||||
class _T(TestBase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.require_vddk()
|
||||
super().setUpClass()
|
||||
|
||||
def test_write(self):
|
||||
handle = vixdisklib.VixDiskLibHandle(
|
||||
vixdisklib_compatibility_version="8.0",
|
||||
config_path="/tmp/vddk_nfc.conf")
|
||||
buf = vixdisklib.get_buffer(self.SECTOR_SIZE)
|
||||
expected = self.pattern_bytes(self.SECTOR_SIZE, b"TRACE-S0")
|
||||
buf[:self.SECTOR_SIZE] = expected
|
||||
with handle.connect(**self.vixdisklib_connect_kwargs()) as conn:
|
||||
with handle.open(conn, self.DISK_PATH, flags=0) as disk:
|
||||
handle.write(disk, 0, 1, buf)
|
||||
handle.read(disk, 0, 1, buf)
|
||||
print("DISK_PATH", self.DISK_PATH, "ok", buf.raw[:8])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user