For Programmers: Free Programming Magazines  


Home > Archive > Unix Programming > July 2007 > Can't undate running process binary in Solaris?









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author Can't undate running process binary in Solaris?
Boltar

2007-07-05, 8:06 am

Hi

I'm trying to get a running process to update its own binary but for
some reason under Solaris it doesn't work , the binary remains
unchanged even though no errors are returned from any function. Does
anyone know why this might happen? My test code is below:

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

char *tag = "TEST";

int main(int argc, char **argv)
{
struct stat fs;
char *mptr;
char *start;
char *end;
int fd;

if ((fd = open(argv[0],O_RDWR)) == -1)
{
perror("open()"); exit(1);
}

if (fstat(fd,&fs) == -1)
{
perror("fstat()"); exit(1);
}

mptr = (char *)mmap(
NULL,fs.st_size,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,
0);
if (mptr == MAP_FAILED)
{
perror("mmap()"); exit(1);
}

end = mptr + fs.st_size - strlen(tag) - 1;
for(start = mptr;start != end;++start)
{
if (start[0] == 'T' &&
start[1] == 'E' &&
start[2] == 'S' &&
start[3] == 'T')
{
printf("Found tag at address 0x%08X\n",start);
memcpy(start,"DONE",4);
if (munmap(mptr,fs.st_size) == -1)
{
perror("munmap()"); exit(1);
}
if (close(fd) == -1)
{
perror("close()"); exit(1);
}
return 0;
}
}

puts("Tag not found.");
return 0;
}

Thanks for any help

B2003

David Schwartz

2007-07-05, 8:06 am


Boltar wrote:

> I'm trying to get a running process to update its own binary but for
> some reason under Solaris it doesn't work , the binary remains
> unchanged even though no errors are returned from any function. Does
> anyone know why this might happen? My test code is below:


When a process begins running, it gets its own logical copy of the
program to run from. You have the logical copy of the program that is
in the file and you have the logical copy of the program that is
running. But they are two separate logical copies and any attempt to
modify one of them will result in splitting the physical copies.

Actually, that's almost true. I think on Solaris you simply cannot
modify the file. You can modify the logical copy that is running. (You
do not need to 'mmap', simply 'mprotect' and then write -- it's
already mapped in.)

It would be truly horrible if updating or modifying an executable
resulted in running programs getting their execution binary changed
out from under them.

Note that if you have a program that's always running, like 'init',
and want to put up a new 'init' binary, you can do it several ways:

1) You can unlink the running 'init' and then rename the new one.

2) You can rename the new 'init' on top of the old one.

3) You can rename the running 'init' and then put the new one in
place.

Most operating systems implement this as follows:

1) When a file is executing, it is prohibited from having its data
contents modified.

2) When a program starts executing, the file is mapped into memory.
The pages fault in as they are loaded.

3) The pages are kept write protected. If the mapping is changed to be
writable, it is still kept as write protected internally.

4) If a page is modified, since it was write protected internally, a
fault will occur. The fault handle will see that the mapping was made
writable and go through the following sequence of steps:

A) If the page is not resident in memory, it will be loaded.
B) The page's status will be changed from 'clean copy of data on disk'
to 'anonymous'.
C) The page will be made writable internally.

Note that if the page was already in memory and in use by another
process, a new page will be allocated as anonymous, the data copied,
and that new page will be mapped in. (So when the write does take
place, the process will be writing to its own anonymous page that
starts as a copy of that part of the original executable image.)

This means that if the page is no longer going to be kept in physical
memory, it will be swapped rather than being discarded. (Before it
could be discarded because the OS can just load it back in from the
executable.)

DS

Boltar

2007-07-05, 8:06 am

On 5 Jul, 12:05, David Schwartz <dav...@webmaster.com> wrote:
> modify the file. You can modify the logical copy that is running. (You
> do not need to 'mmap', simply 'mprotect' and then write -- it's
> already mapped in.)


What address would I pass to mprotect to do this?

> It would be truly horrible if updating or modifying an executable
> resulted in running programs getting their execution binary changed
> out from under them.


Not really. Self modifying code can be damn useful. Its only in the
last 20 years or so that this idea that somehow its wrong has come
about.

B2003



Eric Sosman

2007-07-05, 7:08 pm

Boltar wrote On 07/05/07 06:40,:
> Hi
>
> I'm trying to get a running process to update its own binary but for
> some reason under Solaris it doesn't work , the binary remains
> unchanged even though no errors are returned from any function. Does
> anyone know why this might happen? My test code is below:
> [...]
>
> mptr = (char *)mmap(
> NULL,fs.st_size,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd, 0);

^^^^^^^^^^^
You told the system "Make sure any changes are private
to this process," and it did what you told it to. If you
wanted something else to happen, you should have said so!

--
Eric.Sosman@sun.com
Frank Cusack

2007-07-05, 7:08 pm

On Thu, 05 Jul 2007 03:40:12 -0700 Boltar <boltar2003@yahoo.co.uk> wrote:
> I'm trying to get a running process to update its own binary but for
> some reason under Solaris it doesn't work , the binary remains
> unchanged even though no errors are returned from any function. Does
> anyone know why this might happen? My test code is below:

....
> mptr = (char *)mmap(
> NULL,fs.st_size,PROT_READ | PROT_WRITE,MAP_PRIVATE,fd,

^^^^^^^^^^^

-frank
Frank Cusack

2007-07-05, 7:08 pm

On Thu, 05 Jul 2007 13:34:23 -0700 Frank Cusack <fcusack@fcusack.com> wrote:
> On Thu, 05 Jul 2007 03:40:12 -0700 Boltar <boltar2003@yahoo.co.uk> wrote:
> ...
> ^^^^^^^^^^^


Actually, even when you fix that i don't think you'll be able to
update in-place. You will probably get ETXTBUSY or whatever the
error is, if not at mmap() time, than maybe you'll get SEGV or
a bus error when you actually write to memory.

If a program's text is busy (I mean, if a program is running),
Solaris will not let you, e.g., do something like "echo > program".

Or do I have it backwards and it's Linux that doesn't allow this?

-frank
Rainer Weikusat

2007-07-05, 7:08 pm

Frank Cusack <fcusack@fcusack.com> writes:
> On Thu, 05 Jul 2007 13:34:23 -0700 Frank Cusack <fcusack@fcusack.com> wrote:

[...]
[color=darkred]
>
> Actually, even when you fix that i don't think you'll be able to
> update in-place. You will probably get ETXTBUSY or whatever the
> error is, if not at mmap() time, than maybe you'll get SEGV or
> a bus error when you actually write to memory.
>
> If a program's text is busy (I mean, if a program is running),
> Solaris will not let you, e.g., do something like "echo > program".
>
> Or do I have it backwards and it's Linux that doesn't allow this?


Linux 2.6 returns an 'is busy' error on attempt to modify a file which
is currently being executed.
Eric Sosman

2007-07-05, 7:08 pm

Frank Cusack wrote On 07/05/07 16:41,:
> On Thu, 05 Jul 2007 13:34:23 -0700 Frank Cusack <fcusack@fcusack.com> wrote:
>
>
>
> Actually, even when you fix that i don't think you'll be able to
> update in-place. You will probably get ETXTBUSY or whatever the
> error is, if not at mmap() time, than maybe you'll get SEGV or
> a bus error when you actually write to memory.
>
> If a program's text is busy (I mean, if a program is running),
> Solaris will not let you, e.g., do something like "echo > program".
>
> Or do I have it backwards and it's Linux that doesn't allow this?


Solaris allows the update (I tried it).

I'm not sure what Boltar's goal in self-modifying the
executable is, though. He mentioned this thread in another
having to do with license-enforcement schemes, but I don't
see any way to use self-modification for enforcement (it's
too easy to protect the file against being written, or to
restore an unmodified backup copy). Boltar, what are you
trying to accomplish? Maybe there's an easier way ...

--
Eric.Sosman@sun.com
David Schwartz

2007-07-05, 7:08 pm

On Jul 5, 1:41 pm, Frank Cusack <fcus...@fcusack.com> wrote:

> If a program's text is busy (I mean, if a program is running),
> Solaris will not let you, e.g., do something like "echo > program".
>
> Or do I have it backwards and it's Linux that doesn't allow this?
>
> -frank


Neither of them will allow you to modify a running binary. There is a
difference in implementation though, which is probably what you are
remembering. I can't remember the exact details of the different
though. I believe Linux would let you do some things that Solaris
would not involving different ways to replace the executable with
another or rename/remove it.

If anyone remembers it precisely, please post the details.

DS

Alan Curry

2007-07-06, 4:15 am

In article <1183670102.491240.222190@m37g2000prh.googlegroups.com>,
David Schwartz <davids@webmaster.com> wrote:
>
>Neither of them will allow you to modify a running binary. There is a
>difference in implementation though, which is probably what you are
>remembering. I can't remember the exact details of the different
>though. I believe Linux would let you do some things that Solaris
>would not involving different ways to replace the executable with
>another or rename/remove it.
>
>If anyone remembers it precisely, please post the details.


Just guessing without knowing the Solaris half of the equation:

Linux doesn't give ETXTBSY errors for shared libraries loaded with
mmap(...PROT_EXEC...), only for the main program image. You can easily cause
running processes to crash by upgrading a shared library without using
rename-into-place.

In the good^H^H^H^H old days, the shared library loader gave in-use libraries
the same ETXTBSY protection as the main program image. Then we got ELF.

I doubt this behavior is useful for self-modifying code. Libraries are loaded
MAP_PRIVATE so modifications to the backing store on disk may or may not be
reflected in the process's memory space, depending on whether or not the
in-memory copy of the page has already been modified (and thus copied and
dissociated from the original on disk).

--
Alan Curry
pacman@world.std.com
Boltar

2007-07-06, 4:15 am

On 5 Jul, 16:39, Eric Sosman <Eric.Sos...@sun.com> wrote:
> Boltar wrote On 07/05/07 06:40,:> Hi
>
>
>
> ^^^^^^^^^^^
> You told the system "Make sure any changes are private
> to this process," and it did what you told it to. If you
> wanted something else to happen, you should have said so!


I tried MAP_SHARED , that didn't work either. What should it be?

B2003

Boltar

2007-07-06, 4:15 am

On 5 Jul, 22:14, Eric Sosman <Eric.Sos...@sun.com> wrote:
> Frank Cusack wrote On 07/05/07 16:41,:
>
>
>
>
>
>
>
>
>
>
>
>
> Solaris allows the update (I tried it).


How did you get it to work?

>
> I'm not sure what Boltar's goal in self-modifying the
> executable is, though. He mentioned this thread in another
> having to do with license-enforcement schemes, but I don't
> see any way to use self-modification for enforcement (it's
> too easy to protect the file against being written, or to
> restore an unmodified backup copy). Boltar, what are you
> trying to accomplish? Maybe there's an easier way ...


Well I could just use a hidden file or shared memory but they're much
easier for users to fiddle with. Not many people are going to whip out
a disassembler and figure out what the binary does then hack the
assembly code.

B2003
>
> --
> Eric.Sos...@sun.com



Boltar

2007-07-06, 8:06 am

On 6 Jul, 09:30, Boltar <boltar2...@yahoo.co.uk> wrote:
> On 5 Jul, 16:39, Eric Sosman <Eric.Sos...@sun.com> wrote:
>
>
>
>
>
> I tried MAP_SHARED , that didn't work either. What should it be?


Ignore me. Tried it again and it did work. Must've done something
wrong first time.

B2003


Paul Pluzhnikov

2007-07-06, 8:06 am

Boltar <boltar2003@yahoo.co.uk> writes:

> I'm trying to get a running process to update its own binary but for
> some reason under Solaris it doesn't work , the binary remains
> unchanged even though no errors are returned from any function.


It's not clear what you mean by "update its own binary" --
[1] update the running image of the current process, or
[2] update the disk image of binary (which will have effect only
on subsequent executions of that disk image) ?

> My test code is below:


Your test code opens a new mapping of the disk image file, updates
it, and discards the updates (see "man mmap", meaning of MAP_PRIVATE).

If you intended to do [2], then use MAP_SHARED.

If you intended to do [1], then new mapping is *not* what you want.
You want to mprotect the current running image:

size_t page_size = getpagesize();
char *tag_page_aligned = round_down(tag, page_size);
mprotect(tag_page_aligned, 2*page_size, PROT_READ|PROT_WRITE|PROT_EXEC); // [3]
mempcpy(tag, "DONE", 4);
mprotect(tag_page_aligned, 2*page_size, PROT_READ|PROT_EXEC); // [4]

You may wonder why PROT_EXEC in [3], [4].

There is no portable way to find out what current protections are,
and some compilers will put string literals into the (read-only)
..text section, since such literals can't be (legally) modified.

On Solaris, you can find current protections of the page(s) on which
"tag" resides via the proc(4) /proc/self/map interface.

Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
Eric Sosman

2007-07-06, 10:05 pm

Boltar wrote:
> On 5 Jul, 22:14, Eric Sosman <Eric.Sos...@sun.com> wrote:
>
> How did you get it to work?
>
>
> Well I could just use a hidden file or shared memory but they're much
> easier for users to fiddle with. Not many people are going to whip out
> a disassembler and figure out what the binary does then hack the
> assembly code.


You still didn't say what you're trying to do ...

However, here's an easy way to defeat an enforcement scheme
that relies on modifying the executable file: Make a backup
copy, and restore from backup whenever the executable stops
working. Or make the file owned by user1 with 755 permissions,
and run the program as user2. Or burn the file to a CD or
other WORM medium and run it from there. Or ...

You may also run afoul of a feature of present-day Solaris:
it can verify the digitally-signed checksum of an executable file
or library before permitting it to run. The verification is
optional, by default, but can be made mandatory for specific user
accounts if the symin is security-conscious. Usage of this
feature does not seem to be widespread as yet, but as malware
continues to spread it would not surprise me to find verification
enabled at more and more sites. And since your self-modifying
executable cannot maintain a constant checksum, you will find
no customers, nor even any trial users, at those sites ...

--
Eric Sosman
esosman@ieee-dot-org.invalid
Boltar

2007-07-06, 10:05 pm

On 6 Jul, 14:36, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
> However, here's an easy way to defeat an enforcement scheme
> that relies on modifying the executable file: Make a backup
> copy, and restore from backup whenever the executable stops
> working. Or make the file owned by user1 with 755 permissions,
> and run the program as user2. Or burn the file to a CD or
> other WORM medium and run it from there. Or ...


You could say the same for any system that has to have an offline data
store. As for running as a different user , you can force the program
to run under a given user simply by refusing to run if its not got
that uid, and also refuse if it doesn't have read/write access to the
binary (or wherever it needs to write to).

> You may also run afoul of a feature of present-day Solaris:
> it can verify the digitally-signed checksum of an executable file
> or library before permitting it to run. The verification is


Now that would be an issue though it perhaps wouldn't be impossible to
replace one tag with another that produces the same checksum , just
bloody hard.

B2003



Eric Sosman

2007-07-06, 10:05 pm

Boltar wrote On 07/06/07 10:04,:
> On 6 Jul, 14:36, Eric Sosman <esos...@ieee-dot-org.invalid> wrote:
>
>
>
> You could say the same for any system that has to have an offline data
> store. As for running as a different user , you can force the program
> to run under a given user simply by refusing to run if its not got
> that uid, and also refuse if it doesn't have read/write access to the
> binary (or wherever it needs to write to).


Right. You can make it arbitrarily difficult to run
the trial version of your program. And what does that do
to the population of potential customers, pray tell? What
do *you* do when you download somebody's try-and-buy program
and cannot get it to work?

If you're going to make it harder and harder to try the
program, you need to make the program more and more attractive
just to get people to try it. And if you make it really
attractive, you'll get more and more people looking harder
and harder for ways to crack your enforcement scheme. It's
not at all clear that this vicious cycle pays you any money
each time it passes "Go."

But hey: it's your party and you're entitled to all the
lead balloons you want. Enjoy!

--
Eric.Sosman@sun.com
Boltar

2007-07-06, 10:05 pm

On Jul 6, 4:28 pm, Eric Sosman <Eric.Sos...@sun.com> wrote:
> Right. You can make it arbitrarily difficult to run
> the trial version of your program. And what does that do
> to the population of potential customers, pray tell? What
> do *you* do when you download somebody's try-and-buy program
> and cannot get it to work?


I read the instructions. If it needs to be installed under a given
user (eg Oracle) then thats what I do. If someone gives you something
for free but you can't be bothered to spend 10 minutes getting it set
up then you obviously don't need it that much in the first place.

B2003


James Carlson

2007-07-27, 8:06 am

Eric Sosman <esosman@ieee-dot-org.invalid> writes:
> You may also run afoul of a feature of present-day Solaris:
> it can verify the digitally-signed checksum of an executable file
> or library before permitting it to run. The verification is
> optional, by default, but can be made mandatory for specific user
> accounts if the symin is security-conscious. Usage of this


In addition to that, there's pkgchk, bart, tripwire, and any number of
intrusion-detection systems. This modified binary will stand out like
a sore thumb if the administrator tries to verify that his system
hasn't been tampered with.

That's essentially what it sounds like the original poster will be
accomplishing -- making the system appear to have been tampered with.
Sane administrators will wipe the damaged binary from the system and
perhaps restore everything from backup at that point.

I'm sure the objection to this will be something like, "but our
documentation says that we do this!" So what? It doesn't scale.
Maybe I could put up with having one or two binaries on the system
that are known to mutate over time. I don't think I could stand it if
there were dozens or hundreds of them. Is this application really so
special that the user will never want to install another bit?

> feature does not seem to be widespread as yet, but as malware
> continues to spread it would not surprise me to find verification
> enabled at more and more sites. And since your self-modifying
> executable cannot maintain a constant checksum, you will find
> no customers, nor even any trial users, at those sites ...


As with many schemes that appear to assume customers are crooks, it
sounds like a self-solving problem to me. ;-}

--
James Carlson, Solaris Networking <james.d.carlson@sun.com>
Sun Microsystems / 1 Network Drive 71.232W Vox +1 781 442 2084
MS UBUR02-212 / Burlington MA 01803-2757 42.496N Fax +1 781 442 1677
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com