Sending signals on syscalls

Seraphime Kirkovski kirkseraph at gmail.com
Sat Dec 24 12:29:24 UTC 2016


Hello straces devs !

Recently, I had to do some reverse engineering on a malware for 
a somewhat exotic platform. As the malware had its .text encrypted my 
only possibility was strace. As always, it helped me to
understand the binary, but after I knew what it did, I couldn't do much 
more because I couldn't see the decrypted code section. What I would 
have liked to do is send a coredump-ing signal when I think the code is 
completely decrypted, i.e. before a call to munmap, after an open call 
or something like this, or simply stop the process in order to attach 
gdb. (This isn't always possible: often, malware writers fork() before
the main routine, which makes it more difficult to attach a debugger, as 
the pid changes, furthermore, if the text section is not decrypted the 
debugger would mess up checksums/keys/whatever.)

So I thought of extending strace like this:

	strace -e sigonsys=<before|after>:<SYSCALL>:<SIG> ./a.out

sigonsys: specifies the signal SIG to be sent before or after a syscall 
SYSCALL is done.

Example:

	strace -f -e sigonsys=after:open:SIGSEGV ./a.out

This sends a SIGSEGV after a call to open(2).

I've already taken a shot at it. And I've identified some limitations
that
	1) probably cannot be overcome from userpace
	2) are due to the racy nature of what I'm trying to do
	3) show some flaws in the kernel

First, the before parameter doesn't change anything in practice. In most 
cases the offending syscall will be executed, checking at the very end 
of the kernel procedure whether there are any pending signals. This 
yields some strange results. For instance,

	int main(void)
	{
		puts("hello");
	}

run with strace -e sigonsys=before:write:SIGSEGV, gives the following 
result:

	...
	write(1, "hello\n", 6) hello

	= -ERESTARTSYS

That it is, the syscall succeeds, it writes "hello" to stdout and before
returning to userspace it checks for pending signals, there is one, so 
it returns ERESTARTSYS, which is apparently stupid.

Another problem I found is related to the fact that signals are not 
delivered immediately. Consider the following program

	int main(void)
	{
		puts("aaaa");
		puts("bbbb");
	}

Strace outputs:

	..
	write(1, "aaaa\n", 5) aaaa = -ERESTARTSYS
	bbbb
	--- SIGNAL SIGSEGV ---

Or even worse,

	int main(void)
	{
		puts("aaaa");
		_exit(0);
		puts("bbbb");
	}

when run with

	strace -e sigonsys=before:write:SIGSEGV ./a.out

yields as expected:

	write(1, "aaaa\n", 5) aaaa = -RESTARTSYS
	--- SIGNAL SIGSEGV ---

But when piped like so

	strace -e sigonsys=before:write:SIGSEGV ./a.out | less

gives:
	group_exit(0)  = ??? (no write at all)


( I ran those examples on x86_64 and 4.7.0-1 kernel )

That being said, I think this option may help kernel developers as well.

What are your thoughts on extending strace like this ? Is it worth it ?
Do you have any ideas how I may overcome some of these difficulties ?
Currently, I modified the sources so the signal is send through 
ptrace(<GET|SET>SIGINFO... and ptrace_restart afterwards. I tried adding
an additional kill(2), but that didn't change anything.

Have a good Christams Eve,
Seraphime Kirkovski




More information about the Strace-devel mailing list