似乎没有直接的运行时方法来修补特征检测。此检测发生在动态链接器(ld.so)的早期。
目前,对链接程序进行二进制修补似乎是最简单的方法。@osgx描述了一种覆盖跳转的方法。另一种方法是伪造cpuid结果。通常,在寄存器ebx,ecx和edx
中返回制造商ID的同时,
cpuid(eax=0)返回支持的最高功能。我们在glibc
2.25中有以下代码片段:
eax
sysdeps/x86/cpu-features.c
__cpuid (0, cpu_features->max_cpuid, ebx, ecx, edx);if (ebx == 0x756e6547 && ecx == 0x6c65746e && edx == 0x49656e69) { }else { kind = arch_kind_other; get_common_indeces (cpu_features, NULL, NULL, NULL, NULL); }该
__cpuid行将转换为
/lib/ld-linux-x86-64.so.2(
/lib/ld-2.25.so)中的这些说明:
172a8: 31 c0 xor eax,eax172aa: c7 44 24 38 00 00 00 mov DWORD PTR [rsp+0x38],0x0172b1: 00 172b2: c7 44 24 3c 00 00 00 mov DWORD PTR [rsp+0x3c],0x0172b9: 00 172ba: 0f a2 cpuid
因此,除了修补分支之外,我们还可以将更
cpuid改为一条
nop指令,该指令将导致最后一个
else分支的调用(因为寄存器将不包含“
GenuineIntel”)。由于最初的
eax=0,
cpu_features->max_cpuid也将为0,
if(cpu_features->max_cpuid >= 7)也将被绕过。
二进制补丁
cpuid(eax=0)通过
nop这个可以用这个工具来完成(x86和x86-64的作品):
#!/usr/bin/env pythonimport reimport sysinfile, outfile = sys.argv[1:]d = open(infile, 'rb').read()# Match CPUID(eax=0), "xor eax,eax" followed closely by "cpuid"o = re.sub(b'(x31xc0.{0,32}?)x0fxa2', b'\1x66x90', d)assert d != oopen(outfile, 'wb').write(o)等效的Perl变体
-0777可确保立即读取文件,而不是在换行符处分开记录:
perl -0777 -pe 's/x31xc0.{0,32}?Kx0fxa2/x66x90/' < /lib/ld-linux-x86-64.so.2 > ld-linux-x86-64-patched.so.2# Verify result, should display "Success"cmp -s /lib/ld-linux-x86-64.so.2 ld-linux-x86-64-patched.so.2 && echo 'Not patched' || echo Success那是容易的部分。现在,我不想替换系统范围内的动态链接器,而是仅使用此链接器执行一个特定程序。当然,可以使用来完成
./ld-linux-x86-64-patched.so.2 ./a,但是朴素的gdb调用无法设置断点:
$ gdb -q -ex "set exec-wrapper ./ld-linux-x86-64-patched.so.2" -ex start ./aReading symbols from ./a...done.Temporary breakpoint 1 at 0x400502: file a.c, line 5.Starting program: /tmp/a During startup program exited normally.(gdb) quit$ gdb -q -ex start --args ./ld-linux-x86-64-patched.so.2 ./aReading symbols from ./ld-linux-x86-64-patched.so.2...(no debugging symbols found)...done.Function "main" not defined.Temporary breakpoint 1 (main) pending.Starting program: /tmp/ld-linux-x86-64-patched.so.2 ./a[Inferior 1 (process 27418) exited normally](gdb) quit
如何使用自定义elf解释器调试程序中介绍了手动解决方法。它可以工作,但是很遗憾,它是使用的手动操作
add-symbol-file。不过,应该可以使用GDB捕捉点将其自动化。
一点不二进制链接是一种替代方法
LD_PRELOAD荷兰国际集团,它定义了自定义程序库
memcpy,
memove等等。这一操作将优先于glibc的程序。功能的完整列表在中提供
sysdeps/x86_64/multiarch/ifunc-impl-list.c。与glibc 2.25发行版相比,当前的HEAD具有更多的符号,总计(
grep -Po 'IFUNC_IMPL(i, name, K[^,]+' sysdeps/x86_64/multiarch/ifunc-impl-list.c):
memchr,memcmp, memmove_chk,memmove,memrchr,
memset_chk,memset,rawmemchr,strlen,strnlen,stpncpy,stpcpy,strcasecmp,strcasecmp_l,strcat,strchr,strchpyuln,strrchr,strcmp,strcmpn,strcatnc,
strpbrk,strspn,strstr,wcschr,wcsrchr,wcscpy,wcslen,wcsnlen,wmemchr,wmemcmp,wmemset,
memcpy_chk,memcpy, mempcpy_chk,mempcpy,strncmp,__ wmemset_chk,



