6.1 interposition of the open() function call
whenever a file is opened, the control is transferred to the interposed open() from cache_oraus.so. the interposed open() checks to see if the file being opened is the oraus.msb (see step 3 in the following code example). if so, the file is opened, and memory mapped (step 5.1). the descriptor returned by open() is also cached. for all other opens, the control is transferred to the original libc.so (step 7).
int open(const char *path, int oflag, mode_t mode) {
static int(*fptr)() = 0;
static char* msb_path;
step 1
if (fptr == 0) {
fptr = (int (*)())dlsym(rtld_next, "open");
if (fptr == null) {
fprintf(stderr, "dlopen: %s \n", dlerror());
return 0;
}
step 1.1
msb_path = (char*)getenv("oraus_msb_file");
}
step 2
if (!msb_path) {
msb_path = "/oracle/app/oracle/product/8.1.7/rdbms/mesg/oraus.msb";
}
step 3
if (strcmp(path, msb_path) == 0) {
step 4
if (k_bm_fd == -1) {
k_bm_fd = ((*fptr)(path, oflag, mode));
if (k_bm_fd <= 0) {
perror(path);
exit(1);
}
step 5
fstat(k_bm_fd, &k_bm_stat_buf);
step 5.1
k_bm_buf = (char*)mmap((caddr_t) 0,
k_bm_stat_buf.st_size, (prot_read),
map_shared, k_bm_fd, 0);
assert(k_bm_buf != map_failed);
return k_bm_fd;
} else {
step 6
return k_bm_fd;
}
}
step 7
return ((*fptr)(path, oflag, mode));
}
| steps |
description |
| 1 |
use dlysym() to get a pointer to the original libc.so open() call, so that we can chain to it. |
| 1.1 |
we use an environment variable, oraus_msb_file, to find the location of the oraus.msb file. |
| 2 |
if this variable is not set, we use a default path. |
| 3 |
we make sure the open call is related to the oraus_msb_file. for all other open calls, we chain to the original open() call. |
| 4 |
we make sure this is the first time we are going through this code path as we want to map the file into memory only once. we open the file and cache the returned descriptor in k_bm_fd. |
| 5 |
we get some information about the file itself, such as size. |
| 5.1 |
the most important step: we map the file into memory. |
| 6 |
we have already opened the file, and we return the cache descriptor. |
| 7 |
for all other opens, the interpose gives control back to the original libc.so open() call. | |
table 1: open() call
6.2 interposition of the fcntl() function call
a fcntl() call is made to change the file control parameters. the first time fcntl() is executed to change oraus.msb control parameters, the control is first transferred to the fcntl() in libc.so. the return value is cached, as well as returned back to the oracle client (step 2). the next time fcntl() is executed, if the file descriptor matches the oraus.msb file descriptor, the cached return value is returned (step 3). the control is not transferred to fcntl() in libc.so.
int fcntl(int fildes, int cmd, int arg) {
static int ret;
static int(*fptr)() = 0;
char* path;
step 1
if (fptr == 0) {
fptr = (int (*)())dlsym(rtld_next, "fcntl");
if (fptr == null) {
fprintf(stderr, "dlopen: %s \n", dlerror());
return 0;
}
}
step 2
if (k_fcntl_bm_fd == -1) {
if (fildes == k_bm_fd) {
ret = ((*fptr)(fildes, cmd, arg));
k_fcntl_bm_fd = k_bm_fd;
return ret;;
}
step 3
} else if (k_fcntl_bm_fd == fildes) {
return ret;
}
step 4
return ((*fptr)(fildes, cmd, arg));
}
| steps |
description |
| 1 |
use dlysym() to get a pointer to the original libc.so fcntl() call, so that we can chain to it. |
| 2 |
we make sure this is the first time we are going through this code path as we want to execute fcntl() only once. we also make a copy of the open descriptor in k_fcntl_bm_fd. |
| 3 |
if the fildes is equal to k_fcntl_bm_fd, then we just return the cached return value. |
| 4 |
for all other opens, the interpose gives control back to the original libc.so fcntl() call. | |
table 2: fcntl() call
back to top
6.3 interposition of the lseek(), read(), and close() function calls
for the lseek() call, if the file descriptor matches the cached oraus.msb file descriptor, the file offset is stored instead of calling the lseek() in libc.so (step l2). on a read() call, if the file descriptor matches the cached oraus.msb file descriptor, the file offset stored from the lseek() is used to index into the memory mapped oraus.msb data. a memcpy() is then executed (step r2). so an i/o call is now transformed to a simple memcpy() call. a close() on the cached file descriptor is ignored so that the cached file descriptor can be reused.
off_t lseek(int fildes, off_t offset, int whence) {
static off_t (*fptr)() = 0;
step l1 if (fptr == 0) {
fptr = (int (*)())dlsym(rtld_next, "lseek");
if (fptr == null) {
fprintf(stderr, "dlopen: %s \n", dlerror());
return 0;
}
}
step l2 if (fildes == k_bm_fd) {
k_bm_offset = offset;
return offset;
}
step l3 return ((*fptr)(fildes, offset, whence));
}
| steps |
description |
| l1 |
use dlysym() to get a pointer to the original libc.so lseek() call, so that we can chain to it. |
| l2 |
if the fildes is equal to k_bm_fd, then we keep track of the k_bm_offset so that we access this memory location. |
| l3 |
for all other opens, the interpose gives control back to the original libc.so lseek() call. | |
table 3: lseek() call
ssize_t read(int fildes, void *buf, size_t nbyte) {
static ssize_t (*fptr)() = 0;
step r1 if (fptr == 0) {
fptr = (ssize_t (*)())dlsym(rtld_next, "read");
if (fptr == null) {
fprintf(stderr, "dlopen: %s\n", dlerror());
return (0);
}
}
step r2 if (fildes == k_bm_fd) {
memcpy(buf, k_bm_buf+k_bm_offset, nbyte);
return nbyte;
}
step r3 return ((*fptr)(fildes, buf, nbyte));
}
| steps |
description |
| r1 |
use dlysym() to get a pointer to the original libc.so read() call, so that we can chain to it. |
| r2 |
if the fildes is equal to k_bm_fd, then we use the stored k_bm_offset to access the right memory location, and do a memcpy(). |
| r3 |
for all other opens, the interpose gives control back to the original libc.so read() call. | |