Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Running (ELF) executable stored in memory (not in a file)

806 views
Skip to first unread message

Noob

unread,
Dec 18, 2013, 10:50:59 AM12/18/13
to
Hello,

Typically, executables are stored in files, and one runs them using
one of the exec* functions.

http://pubs.opengroup.org/onlinepubs/9699919799/functions/execlp.html

(I do note that fexecve expects a fd, instead of a file or path.)

I'll reduce the scope of my question by noting:

- I am running Linux (2.6.28)
- By "executable", I mean "ELF executable with shared object dependencies"

One such "executable" of mine is stored encrypted.

I have a program that decrypts it, and stores the clear-text
version to a tmpfs. Then I run the program from the tmpfs.

But I was wondering: since I'm decrypting the program to memory
in my decryption program, why not...

1) decrypt the program to a large-enough buffer
2) exec the program "directly from memory"

But, of course, none of the exec functions expect a memory buffer
as input. So I'd have "fake" the file.

Could I use shm_open and mmap to get a file descriptor pointing
to my buffer, and then use fexecve to execute that?

http://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html

More importantly, is there any advantage to doing things this way?
(One difference is that, right now, the tmpfs cannot be marked noexec,
whereas it could be if the executable lived elsewhere.)

Regards.

Casper H.S. Dik

unread,
Dec 18, 2013, 10:59:41 AM12/18/13
to
Noob <ro...@127.0.0.1> writes:

>I have a program that decrypts it, and stores the clear-text
>version to a tmpfs. Then I run the program from the tmpfs.

You can also create it mode 0, unlink it and then safe it
there, possible fchmod it 755 and then run the executable
as if it is in memory and no way to get access to it
anymrore.

>But I was wondering: since I'm decrypting the program to memory
>in my decryption program, why not...

>1) decrypt the program to a large-enough buffer
>2) exec the program "directly from memory"

>But, of course, none of the exec functions expect a memory buffer
>as input. So I'd have "fake" the file.

>Could I use shm_open and mmap to get a file descriptor pointing
>to my buffer, and then use fexecve to execute that?

That's not much different from what I used above.

> http://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html

>More importantly, is there any advantage to doing things this way?
>(One difference is that, right now, the tmpfs cannot be marked noexec,
>whereas it could be if the executable lived elsewhere.)

Depends on the implementation of shm_open(); e.g., on Solaris it opens
a file in /tmp and you get the same as you had before.

You could also compile the program as a PIC executable, map it
and then transfer control to that memory image. (The trick is to
get rid of the rest of your program)

Casper

Rainer Weikusat

unread,
Dec 18, 2013, 11:27:44 AM12/18/13
to
Noob <ro...@127.0.0.1> writes:
> Typically, executables are stored in files, and one runs them using
> one of the exec* functions.
>
> http://pubs.opengroup.org/onlinepubs/9699919799/functions/execlp.html
>
> (I do note that fexecve expects a fd, instead of a file or path.)
>
> I'll reduce the scope of my question by noting:
>
> - I am running Linux (2.6.28)
> - By "executable", I mean "ELF executable with shared object dependencies"
>
> One such "executable" of mine is stored encrypted.
>
> I have a program that decrypts it, and stores the clear-text
> version to a tmpfs. Then I run the program from the tmpfs.
>
> But I was wondering: since I'm decrypting the program to memory
> in my decryption program, why not...
>
> 1) decrypt the program to a large-enough buffer
> 2) exec the program "directly from memory"

Hmm ... why not "create a sufficiently large file on the tmpfs
(ftruncate is sufficient here), mmap that and decrypt into the mmap'ed
area"?

[...]

> Could I use shm_open and mmap to get a file descriptor pointing
> to my buffer, and then use fexecve to execute that?
>
> http://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html

Assuming that you're also using Linux for this, a shared memory object
created via shm_open is really nothing but a file residing on a tmpfs
mounted on /dev/shm. At least on Linux 3.2.9, the shared memory object
must neither be open for writing nor mapped with PROT_WRITE for the
fexecve to suceed:

-----------
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

static char *args[] = {
"hic et nunc",
"-l",
"/dev/shm",
NULL
};

extern char **environ;

int main(void)
{
struct stat st;
void *p;
int fd, shm_fd, rc;

shm_fd = shm_open("wurstverschwendung", O_RDWR | O_CREAT, 0777);
if (shm_fd == -1) {
perror("shm_open");
exit(1);
}

rc = stat("/bin/ls", &st);
if (rc == -1) {
perror("stat");
exit(1);
}

rc = ftruncate(shm_fd, st.st_size);
if (rc == -1) {
perror("ftruncate");
exit(1);
}

p = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
shm_fd, 0);
if (p == MAP_FAILED) {
perror("mmap");
exit(1);
}

fd = open("/bin/ls", O_RDONLY, 0);
if (fd == -1) {
perror("openls");
exit(1);
}

rc = read(fd, p, st.st_size);
if (rc == -1) {
perror("read");
exit(1);
}
if (rc != st.st_size) {
fputs("Strange situation!\n", stderr);
exit(1);
}

munmap(p, st.st_size);
close(shm_fd);

shm_fd = shm_open("wurstverschwendung", O_RDONLY, 0);
fexecve(shm_fd, args, environ);
perror("fexecve");
return 0;
}

Rainer Weikusat

unread,
Dec 18, 2013, 11:31:23 AM12/18/13
to
Casper H.S. Dik <Caspe...@OrSPaMcle.COM> writes:
> Noob <ro...@127.0.0.1> writes:
>
>>I have a program that decrypts it, and stores the clear-text
>>version to a tmpfs. Then I run the program from the tmpfs.
>
> You can also create it mode 0, unlink it and then safe it
> there, possible fchmod it 755 and then run the executable
> as if it is in memory and no way to get access to it
> anymrore.

Depending on the OS, this might not be true: On Linux, a new descriptor
referring to the 'invisible' file could be created by opening the
/proc/<pid>/fd name referring to the other descriptor.
0 new messages