在 Linux 中检测跟踪

逆向工程 linux 反调试
2021-07-06 02:11:06

在 Linux 下,可以使用strace. ltrace还可用于跟踪库调用。我想知道是否可以检测我的可执行文件是否在strace下运行ltrace

下面是输出的一个例子strace,并ltracediff可执行文件。

斯特拉斯

$ strace diff
execve("/usr/bin/diff", ["diff"], [/* 43 vars */]) = 0
brk(0)                                  = 0x110a000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc13f6000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=122500, ...}) = 0
mmap(NULL, 122500, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcbc13d8000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340!\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=31784, ...}) = 0
mmap(NULL, 2129016, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcbc0fce000
mprotect(0x7fcbc0fd5000, 2093056, PROT_NONE) = 0
mmap(0x7fcbc11d4000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x6000) = 0x7fcbc11d4000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1811160, ...}) = 0
mmap(NULL, 3925240, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcbc0c0f000
mprotect(0x7fcbc0dc4000, 2093056, PROT_NONE) = 0
mmap(0x7fcbc0fc3000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7fcbc0fc3000
mmap(0x7fcbc0fc9000, 17656, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcbc0fc9000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200l\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=135398, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc13d7000
mmap(NULL, 2212936, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcbc09f2000
mprotect(0x7fcbc0a0a000, 2093056, PROT_NONE) = 0
mmap(0x7fcbc0c09000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x17000) = 0x7fcbc0c09000
mmap(0x7fcbc0c0b000, 13384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcbc0c0b000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc09f1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc09f0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc09ef000
arch_prctl(ARCH_SET_FS, 0x7fcbc09f0700) = 0
mprotect(0x7fcbc0fc3000, 16384, PROT_READ) = 0
mprotect(0x7fcbc0c09000, 4096, PROT_READ) = 0
mprotect(0x7fcbc11d4000, 4096, PROT_READ) = 0
mprotect(0x61b000, 4096, PROT_READ)     = 0
mprotect(0x7fcbc13f8000, 4096, PROT_READ) = 0
munmap(0x7fcbc13d8000, 122500)          = 0
set_tid_address(0x7fcbc09f09d0)         = 32425
set_robust_list(0x7fcbc09f09e0, 0x18)   = 0
futex(0x7fff27e5992c, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 1, NULL, 7fcbc09f0700) = -1 EAGAIN (Resource temporarily unavailable)
rt_sigaction(SIGRTMIN, {0x7fcbc09f8750, [], SA_RESTORER|SA_SIGINFO, 0x7fcbc0a01cb0}, NULL, 8) = 0
rt_sigaction(SIGRT_1, {0x7fcbc09f87e0, [], SA_RESTORER|SA_RESTART|SA_SIGINFO, 0x7fcbc0a01cb0}, NULL, 8) = 0
rt_sigprocmask(SIG_UNBLOCK, [RTMIN RT_1], NULL, 8) = 0
getrlimit(RLIMIT_STACK, {rlim_cur=8192*1024, rlim_max=RLIM_INFINITY}) = 0
brk(0)                                  = 0x110a000
brk(0x112b000)                          = 0x112b000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=7216624, ...}) = 0
mmap(NULL, 7216624, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcbc030d000
close(3)                                = 0
sigaltstack({ss_sp=0x61c5e0, ss_flags=0, ss_size=8192}, NULL) = 0
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=2570, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcbc13f5000
read(3, "# Locale name alias data base.\n#"..., 4096) = 2570
read(3, "", 4096)                       = 0
close(3)                                = 0
munmap(0x7fcbc13f5000, 4096)            = 0
open("/usr/share/locale/en_US/LC_MESSAGES/diffutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/diffutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en_US/LC_MESSAGES/diffutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale-langpack/en/LC_MESSAGES/diffutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
rt_sigaction(SIGSEGV, {0x40b3c0, [], SA_RESTORER|SA_STACK|SA_NODEFER|SA_RESETHAND|SA_SIGINFO, 0x7fcbc0c454a0}, NULL, 8) = 0
write(2, "diff: ", 6diff: )                   = 6
write(2, "missing operand after `diff'", 28missing operand after `diff') = 28
write(2, "\n", 1
)                       = 1
write(2, "diff: ", 6diff: )                   = 6
write(2, "Try `diff --help' for more infor"..., 39Try `diff --help' for more information.) = 39
write(2, "\n", 1
)                       = 1
exit_group(2)                           = ?

跟踪

$ ltrace diff
__libc_start_main(0x402310, 1, 0x7fff876fcf28, 0x4151d0, 0x415260 <unfinished ...>
strrchr("diff", '/')                                             = NULL
setlocale(6, "")                                                 = "en_US.UTF-8"
bindtextdomain("diffutils", "/usr/share/locale")                 = "/usr/share/locale"
textdomain("diffutils")                                          = "diffutils"
sigaltstack(0x7fff876fccd0, 0, 1, 0x736c6974756666, 3)           = 0
dcgettext(0, 0x4183d7, 5, -1, 3)                                 = 0x4183d7
dcgettext(0, 0x4183e5, 5, 0, 1)                                  = 0x4183e5
sigemptyset(0x7fff876fccf8)                                      = 0
sigaction(11, 0x7fff876fccf0, NULL)                              = 0
re_set_syntax(264966, 0x7fff876fcb88, 0, -1, 0)                  = 0
malloc(16)                                                       = 0x016e1160
memset(0x016e1160, '\000', 16)                                   = 0x016e1160
getopt_long(1, 0x7fff876fcf28, "0123456789abBcC:dD:eEfF:hHiI:lL:"..., 0x00417360, NULL) = -1
malloc(1)                                                        = 0x016e1180
dcgettext(0, 0x415598, 5, 8, 3)                                  = 0x415598
error(0, 0, 0x415598, 0x7fff876fd469, 1diff: missing operand after `diff'
)                         = 0
dcgettext(0, 0x415878, 5, 0, 0x7fad1793c700)                     = 0x415878
error(2, 0, 0x415878, 0x7fff876fd469, 1diff: Try `diff --help' for more information.
 <unfinished ...>
+++ exited (status 2) +++
3个回答

ptrace可以通过一个进程只能调用ptrace一次的事实来检测如果ptrace()已经被strace可执行文件或调试器调用,我们可以在运行时检测到它。

#include <stdio.h>
#include <sys/ptrace.h>

int main()
{
    if (ptrace(PTRACE_TRACEME, 0, 1, 0) == -1) 
    {
        printf("don't trace me !!\n");
        return 1;
    }
    // normal execution
    return 0;
}

然而,破解这段代码并不难。第一,它可能只是NOPptrace()电话。其次,可以ptrace()使用我们自己的ptrace()呼叫替换呼叫LD_PRELOAD

除了 ptrace 技巧之外,您还可以检查 /proc/PID/cmdline、引发 SIGTRAP、使用 getppid、...

你可能想检查盘古(免责声明:我是作者)。

Pangu 是一个小工具集,用于处理来自 GNU 项目的调试相关工具,尤其是在 GNU/Linux x86 上。

其他评论者建议的方式是可能的。但是,它们都是可拦截的,如果你读一个文件,它可以被拦截,你的程序会得到不同的结果,如果你检查是否设置了 LD_PRELOAD,它可以在你接触它之前取消设置,等等。 suid不过,这是另一回事,但也可能有不同的利用方式。