Can a program make itself immune to an interposer? - Unix
This is a discussion on Can a program make itself immune to an interposer? - Unix ; Note: The question is posed in that way, suggesting that I am looking
for a way to make my program immune. However, in fact, I'm looking at
it the other way - I have written an interposer, but somehow it ...
-
Can a program make itself immune to an interposer?
Note: The question is posed in that way, suggesting that I am looking
for a way to make my program immune. However, in fact, I'm looking at
it the other way - I have written an interposer, but somehow it is not
working (in one case).
Longer version: I have written an interposer for the POSIX open()
function. This is not at all the first interposer I've written, and it
works fine with a little test program that I have written. However, it
does not work (that is, I can tell that my interposer is not called),
when used with GAWK.
I am invoking gawk as:
LD_PRELOAD=./libopen_fix.so gawk '{print FILENAME,$0}' "$@"
and yet the interposer never gets called. GAWK *is* dynamically linked:
(output of "ldd gawk")
linux-gate.so.1 => (0xb7ee8000)
libdl.so.2 => /lib/libdl.so.2 (0xb7ed2000)
libm.so.6 => /lib/libm.so.6 (0xb7ead000)
libc.so.6 => /lib/libc.so.6 (0xb7d74000)
/lib/ld-linux.so.2 (0xb7ee9000)
Any ideas?
-
Re: Can a program make itself immune to an interposer?
>Note: The question is posed in that way, suggesting that I am looking
>for a way to make my program immune. However, in fact, I'm looking at
>it the other way - I have written an interposer, but somehow it is not
>working (in one case).
>
>Longer version: I have written an interposer for the POSIX open()
>function. This is not at all the first interposer I've written, and it
>works fine with a little test program that I have written. However, it
>does not work (that is, I can tell that my interposer is not called),
>when used with GAWK.
Theory 1: What happens with your interposer if the program never
calls open() directly but only calls fopen(), which then calls
open() *from libc.a*? Can you interpose calls from a library to
that same library? You could interpose fopen(), probably.
Theory 2: What happens with your interposer if the program never
calls open() directly, but only calls fopen(), which then calls
some internal synonym for open like _open()? This one I'm fairly
sure will result in the interposer never getting called.
This is one of the problems with interposers: the whole thing seems
pretty fragile when applied to system libraries.
-
Re: Can a program make itself immune to an interposer?
In article ,
Gordon Burditt wrote:
>>Note: The question is posed in that way, suggesting that I am looking
>>for a way to make my program immune. However, in fact, I'm looking at
>>it the other way - I have written an interposer, but somehow it is not
>>working (in one case).
>>
>>Longer version: I have written an interposer for the POSIX open()
>>function. This is not at all the first interposer I've written, and it
>>works fine with a little test program that I have written. However, it
>>does not work (that is, I can tell that my interposer is not called),
>>when used with GAWK.
>
>Theory 1: What happens with your interposer if the program never
>calls open() directly but only calls fopen(), which then calls
>open() *from libc.a*? Can you interpose calls from a library to
>that same library? You could interpose fopen(), probably.
>
>Theory 2: What happens with your interposer if the program never
>calls open() directly, but only calls fopen(), which then calls
>some internal synonym for open like _open()? This one I'm fairly
>sure will result in the interposer never getting called.
Well, I am pretty sure it is something like that. However, a key piece
of information that I had meant to include in my OP, but forgot to
include is that I ran "strace" on GAWK and strace does show GAWK calling
open() - with the parameters that I expect. So, it seems clear that it
is calling some version of open() - but not mine. Note further that I
first tried interposing "fopen()" and convinced myself that GAWK does
*not* call fopen().
Incidentally, is that how it works - that if the app calls libc.foo()
and libc.foo() calls bar() and bar is also in libc, then any bar()
interposer will be left out of the loop?
>This is one of the problems with interposers: the whole thing seems
>pretty fragile when applied to system libraries.
It is a hack. A useful and fun hack...
When it works, great; when it doesn't - well, you tried.
-
Re: Can a program make itself immune to an interposer?
>>>Note: The question is posed in that way, suggesting that I am looking
>>>for a way to make my program immune. However, in fact, I'm looking at
>>>it the other way - I have written an interposer, but somehow it is not
>>>working (in one case).
>>>
>>>Longer version: I have written an interposer for the POSIX open()
>>>function. This is not at all the first interposer I've written, and it
>>>works fine with a little test program that I have written. However, it
>>>does not work (that is, I can tell that my interposer is not called),
>>>when used with GAWK.
>>
>>Theory 1: What happens with your interposer if the program never
>>calls open() directly but only calls fopen(), which then calls
>>open() *from libc.a*? Can you interpose calls from a library to
>>that same library? You could interpose fopen(), probably.
>>
>>Theory 2: What happens with your interposer if the program never
>>calls open() directly, but only calls fopen(), which then calls
>>some internal synonym for open like _open()? This one I'm fairly
>>sure will result in the interposer never getting called.
>
>Well, I am pretty sure it is something like that. However, a key piece
>of information that I had meant to include in my OP, but forgot to
>include is that I ran "strace" on GAWK and strace does show GAWK calling
>open() - with the parameters that I expect.
Doesn't strace operate on actual kernel calls? It wouldn't matter
what the name of the libc routine is. If the %qzx_dammit register
[1] is set to SYS_open and the kernel gets a syscall trap, that's
an open even if the code being executed is buffer overflow trash,
and that's what strace will call it. And it definitely won't
distinguish between a call to _open() and a call to open() which
calls _open(). There's also the possibility that it's using
syscall(), which seems pretty wierd for a program trying to be
portable.
[1] Your platform probably doesn't have a %qzx_dammit register, but
the kernel trap indicates *somehow* which call it is, and the details
aren't that important here.
Another question: if gdb (or whatever debugger) is tracing
instructions, and a particular function has an address associated
with *5* names: open, _open, _QZX_gerbil_teeth, _QZX_rat_burger,
and _QZX_eat_madagascar_hissing_****roach, which name will it display
in a traceback? Interposing works on the *name*, not the address,
as I understand it, so interposing open() won't interpose _open(),
even if they have the same address (and, obviously, the same code).
Interposing works on global symbols. If _open() is a *non-global*
symbol in libc, and open() calls it from libc, and you try interposing
_open(), I'd expect interposing to fail ("symbol not found"). Does
your interposing code check for this happening, and report it?
>So, it seems clear that it
>is calling some version of open() - but not mine. Note further that I
>first tried interposing "fopen()" and convinced myself that GAWK does
>*not* call fopen().
>
>Incidentally, is that how it works - that if the app calls libc.foo()
>and libc.foo() calls bar() and bar is also in libc, then any bar()
>interposer will be left out of the loop?
That's definitely the case if bar() is not a global symbol. It may
be implementation-dependent if bar() is a global symbol resolved
by the runtime linker. What does the linker do: resolve
call-to-own-library global-symbol calls at *link* time or *run*
time? That could be up to the implementation, and it affects
interposing.
>>This is one of the problems with interposers: the whole thing seems
>>pretty fragile when applied to system libraries.
>
>It is a hack. A useful and fun hack...
>When it works, great; when it doesn't - well, you tried.
Some descriptions of what people do/did with Windows/DOS suggest that
the whole OS is based on a couple dozen layers of interposing between
the user-level Windows/DOS program and the base viruses that comprise
the core OS. Naturally, the issue of what registers need to be
saved/restored when interposing is not documented.
-
Re: Can a program make itself immune to an interposer?
On Aug 19, 7:56 pm, gordonb.eo...@burditt.org (Gordon Burditt) wrote:
....
> Theory 2: What happens with your interposer if the program never
> calls open() directly, but only calls fopen(), which then calls
> some internal synonym for open like _open()? This one I'm fairly
> sure will result in the interposer never getting called.
This is the normal situation. Indeed, it's practically required by
the C standard! In particular, 'open' is not reserved by the C
standard, so the fopen() implementation can't be affected by an
application defining a function (or data member!) with that name.
Making that true for the application but not an interposer seems like
a waste of effort: I don't know of any implementations that bother.
So everyone has a symbol in the reserved namespace, like '_open', as
the real name, and then fopen() uses that and 'open' is provided as a
weak symbol for POSIX applications, but overrideable/ignorable for
pure C applications.
(rule: no function defined by the C standard can depend on a symbol
that isn't defined or reserved by the C standard.)
Philip Guenther
-
Re: Can a program make itself immune to an interposer?
gazelle@shell.xmission.com (Kenny McCormack) writes:
> Note: The question is posed in that way, suggesting that I am looking
> for a way to make my program immune. However, in fact, I'm looking at
> it the other way - I have written an interposer, but somehow it is not
> working (in one case).
Short answer: sure, just link it statically.
Or don't call any libc functions directly; instead do this:
void *h = dlopen("/lib/libc.so.6", RTLD_LAZY);
int (*p_open)() = dlsym(h, "open");
int fd = p_open("/etc/passwd", O_RDONLY); // foil interposers!
> Longer version: I have written an interposer for the POSIX open()
> function. This is not at all the first interposer I've written, and it
> works fine with a little test program that I have written. However, it
> does not work (that is, I can tell that my interposer is not called),
> when used with GAWK.
That's because Linux gawk doesn't call open. It was compiled with
large file support, and calls open64:
$ readelf -s /usr/bin/gawk | grep ' open'
296: 00000000 189 FUNC GLOBAL DEFAULT UND open64@GLIBC_2.1 (3)
Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
-
Re: Can a program make itself immune to an interposer?
gazelle@shell.xmission.com (Kenny McCormack) writes:
> Well, I am pretty sure it is something like that. However, a key piece
> of information that I had meant to include in my OP, but forgot to
> include is that I ran "strace" on GAWK and strace does show GAWK calling
> open() - with the parameters that I expect. So, it seems clear that it
> is calling some version of open() - but not mine.
The 'strace' only proves that the open system call was executed,
not that some version of libc open() was called. Also, if you look
*really* close, you'll likely discover that strace shows open with
slightly different parameters than what you expect.
> Note further that I
> first tried interposing "fopen()" and convinced myself that GAWK does
> *not* call fopen().
>
> Incidentally, is that how it works - that if the app calls libc.foo()
> and libc.foo() calls bar() and bar is also in libc, then any bar()
> interposer will be left out of the loop?
Not necessarily. Many glibc symbols have several aliases, some of
which are marked "internal". A call to "internal" alias (such as
a call to open from fopen) will not be interposable, but a call
to non-internal one (such as a call to malloc from strdup) will be
fully interposable.
Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
-
Re: Can a program make itself immune to an interposer?
In article ,
Paul Pluzhnikov wrote:
....
>That's because Linux gawk doesn't call open. It was compiled with
>large file support, and calls open64:
>
> $ readelf -s /usr/bin/gawk | grep ' open'
> 296: 00000000 189 FUNC GLOBAL DEFAULT UND open64@GLIBC_2.1 (3)
Short summary: That's it! I changed it to interpose open64() and it works.
Side note: I had thought (before posting) that that was it - I knew that
gawk was using the large file stuff and thus open64(). I just hadn't
gotten around to testing it before I posted.
Thanks for the tip on readelf - that's useful, too.
P.S. To all those who posted in this thread about "The C standard,
blah, blah, blah" - stuff it! And go back to clc, where they like that
sort of thing.
-
Re: Can a program make itself immune to an interposer?
In article ,
Gordon Burditt wrote:
....
>Some descriptions of what people do/did with Windows/DOS suggest that
>the whole OS is based on a couple dozen layers of interposing between
>the user-level Windows/DOS program and the base viruses that comprise
>the core OS. Naturally, the issue of what registers need to be
>saved/restored when interposing is not documented.
Heh - good quote. Belongs on my cork board.
Anyway, I got it sorted. Thanks for the commentary.
-
Re: Can a program make itself immune to an interposer?
gazelle@shell.xmission.com (Kenny McCormack) writes:
>In article ,
>Gordon Burditt wrote:
>>>Note: The question is posed in that way, suggesting that I am looking
>>>for a way to make my program immune. However, in fact, I'm looking at
>>>it the other way - I have written an interposer, but somehow it is not
>>>working (in one case).
>>>
>>>Longer version: I have written an interposer for the POSIX open()
>>>function. This is not at all the first interposer I've written, and it
>>>works fine with a little test program that I have written. However, it
>>>does not work (that is, I can tell that my interposer is not called),
>>>when used with GAWK.
>>
>>Theory 1: What happens with your interposer if the program never
>>calls open() directly but only calls fopen(), which then calls
>>open() *from libc.a*? Can you interpose calls from a library to
>>that same library? You could interpose fopen(), probably.
>>
>>Theory 2: What happens with your interposer if the program never
>>calls open() directly, but only calls fopen(), which then calls
>>some internal synonym for open like _open()? This one I'm fairly
>>sure will result in the interposer never getting called.
>
>Well, I am pretty sure it is something like that. However, a key piece
>of information that I had meant to include in my OP, but forgot to
>include is that I ran "strace" on GAWK and strace does show GAWK calling
>open() - with the parameters that I expect. So, it seems clear that it
>is calling some version of open() - but not mine. Note further that I
>first tried interposing "fopen()" and convinced myself that GAWK does
>*not* call fopen().
ltrace may be more useful in this case then strace. All strace shows
is that the system call has been invoked (via int $80 or syscall insn),
not that any entry point named 'open' was called.
scott
-
Re: Can a program make itself immune to an interposer?
Paul Pluzhnikov writes:
> gazelle@shell.xmission.com (Kenny McCormack) writes:
>
>> Note: The question is posed in that way, suggesting that I am looking
>> for a way to make my program immune. However, in fact, I'm looking at
>> it the other way - I have written an interposer, but somehow it is not
>> working (in one case).
>
> Short answer: sure, just link it statically.
> Or don't call any libc functions directly; instead do this:
>
> void *h = dlopen("/lib/libc.so.6", RTLD_LAZY);
> int (*p_open)() = dlsym(h, "open");
> int fd = p_open("/etc/passwd", O_RDONLY); // foil interposers!
It's easy enough to interpose dlopen() and dlsym(), and fake anything
you want. If an executable needs something from another file, there
is always a way to get in between.
--
Måns Rullgård
mans@mansr.com
-
Re: Can a program make itself immune to an interposer?
On Aug 20, 6:26 am, gaze...@shell.xmission.com (Kenny McCormack)
wrote:
> P.S. To all those who posted in this thread about "The C standard,
> blah, blah, blah" - stuff it! And go back to clc, where they like that
> sort of thing.
Heh. Don't worry, that information wasn't for you. It was for the
other people in this newsgroup who in the past have wondered why
fopen() doesn't call (their) open(). Someone people actually prefer
explanations to bare claims.
Philip Guenther
-
Re: Can a program make itself immune to an interposer?
In article <6vYqk.37176$ZE5.16164@nlpi061.nbdc.sbc.com>,
Scott Lurndal wrote:
....
>
>ltrace may be more useful in this case then strace. All strace shows
>is that the system call has been invoked (via int $80 or syscall insn),
>not that any entry point named 'open' was called.
Thanks. I knew there was a way to do it at the lib level, not (just)
the system level - but I couldn't find it. I thought it was an option
to strace, not a separate (different) program.
-
Re: Can a program make itself immune to an interposer?
Paul Pluzhnikov writes:
> Måns Rullgård writes:
>
>> It's easy enough to interpose dlopen() and dlsym(), and fake anything
>> you want.
>
> Actually, interposing dlsym() is non-trivial: how are you going to
> find the original to call?
I can include my own libdl with renamed functions. I can even make
them inaccessible outside the library if I choose.
>> If an executable needs something from another file, there
>> is always a way to get in between.
>
> In this particular case, everything is already in memory, so with
> sufficient understanding of ELF, all interposers could be avoided.
How did it get into memory? ld.so put it there. If I ptrace the
loading, I can replace anything it tries to access with other data.
I didn't say it would be easy, only that it can be done.
If all else fails, modifying the original executable is always an
option. That is, however, a somewhat different game.
--
Måns Rullgård
mans@mansr.com