From 3869a67180ba642955e8a06c8572bf898cdc9e5d Mon Sep 17 00:00:00 2001 From: John Reiser Date: Fri, 2 Aug 2024 12:56:09 -0700 Subject: [PATCH] Intel CET (Control-flow Enforcement Technology) {endbr64, endbr32} at target of computed jmp/call; 'notrack' at source modified: stub/src/amd64-linux.elf-entry.S modified: stub/src/amd64-linux.elf-fold.S modified: stub/src/amd64-linux.elf-main2.c modified: stub/src/arch/amd64/macros.S modified: stub/src/arch/i386/macros.S modified: stub/src/i386-expand.S modified: stub/src/i386-linux.elf-entry.S modified: stub/src/i386-linux.elf-fold.S modified: stub/src/i386-linux.elf-main2.c --- src/stub/src/amd64-linux.elf-entry.S | 2 ++ src/stub/src/amd64-linux.elf-fold.S | 3 ++- src/stub/src/amd64-linux.elf-main2.c | 16 +++++++++++----- src/stub/src/arch/amd64/macros.S | 4 ++++ src/stub/src/arch/i386/macros.S | 4 ++++ src/stub/src/i386-expand.S | 4 ++++ src/stub/src/i386-linux.elf-entry.S | 3 ++- src/stub/src/i386-linux.elf-fold.S | 4 +++- src/stub/src/i386-linux.elf-main2.c | 22 +++++++++++++--------- 9 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/stub/src/amd64-linux.elf-entry.S b/src/stub/src/amd64-linux.elf-entry.S index 2cd3a5eb..e9f0b194 100644 --- a/src/stub/src/amd64-linux.elf-entry.S +++ b/src/stub/src/amd64-linux.elf-entry.S @@ -86,6 +86,7 @@ M_NRV2E_LE32=8 section ELFMAINX sz_pack2= .-4 _start: .globl _start + endbr64 //// nop; int3 # uncomment for debugging pop %rcx // argc @@ -304,6 +305,7 @@ here: // OUT: %rdi= (2+ retaddrj) without disturbing shadow stack ret getbit: + .byte 0xf3,0x0f,0x1e,0xfa // endbr64 // from "call *%rdx" addl bits,bits; jz refill // Carry= next bit rep; ret refill: diff --git a/src/stub/src/amd64-linux.elf-fold.S b/src/stub/src/amd64-linux.elf-fold.S index 20be68e2..2e11fcc6 100644 --- a/src/stub/src/amd64-linux.elf-fold.S +++ b/src/stub/src/amd64-linux.elf-fold.S @@ -86,6 +86,7 @@ PAGE_MASK: .quad -1<<12 // default // no 'section', thus '.text'; also loaded first in amd64-linux.elf-fold.bin. // Code from amd64-linux.elf-main.c is also .text, and is next. fold_begin: + endbr64 //// int3 # DEBUG mov %r13,F_UNMAPA(%rbp) mov %r13,%rax; and $is_ptinterp,%eax; or %eax,F_ELFA(%rbp) @@ -202,7 +203,7 @@ no_pse_map: pop %arg1 # ADRU: unfolded upx_main etc. pop %arg2 # LENU push $__NR_munmap; pop %rax - jmp *(%r14) # goto: syscall; pop %rdx; ret + /*notrack*/ jmp *(%r14) # goto: syscall; pop %rdx; ret get_page_mask: .globl get_page_mask mov PAGE_MASK(%rip),%rax diff --git a/src/stub/src/amd64-linux.elf-main2.c b/src/stub/src/amd64-linux.elf-main2.c index 056bf49e..a7b9c239 100644 --- a/src/stub/src/amd64-linux.elf-main2.c +++ b/src/stub/src/amd64-linux.elf-main2.c @@ -253,16 +253,22 @@ make_hatch_x86_64( ) { char *hatch = next_unc; - unsigned const sz_code = 4; + int code[3] = { + 0xfa1e0ff3, // endbr64 + 0x585a050f, // syscall; pop %arg3{%rdx}; pop %rax + 0x90e0ff3e, // notrack jmp *%rax; nop + }; DPRINTF("make_hatch %%p %%p %%x\\n", phdr, next_unc, frag_mask); if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { - if (sz_code <= (frag_mask & -(long)hatch)) { - ((long *)hatch)[0] = 0xc35a050f; // syscall; pop %arg3{%rdx); ret + if (sizeof(code) <= (unsigned)(frag_mask & -(long)hatch)) { + ((int *)hatch)[0] = code[0]; // endbr64 + ((int *)hatch)[1] = code[1]; // syscall; pop %arg3{%rdx}; pop %rax + ((int *)hatch)[2] = code[2]; // notrack jmp *%rax; nop } else { // Does not fit at hi end of .text, so must use a new page "permanently" int mfd = upxfd_create(addr_string("upx"), MFD_EXEC); // the directory entry - write(mfd, addr_string("\x0f\x05\x5a\xc3"), sz_code); - hatch = mmap(0, sz_code, PROT_READ|PROT_EXEC, MAP_SHARED, mfd, 0); + write(mfd, &code, sizeof(code)); + hatch = mmap(0, sizeof(code), PROT_READ|PROT_EXEC, MAP_SHARED, mfd, 0); close(mfd); } } diff --git a/src/stub/src/arch/amd64/macros.S b/src/stub/src/arch/amd64/macros.S index 6a8ecd14..04bc9069 100644 --- a/src/stub/src/arch/amd64/macros.S +++ b/src/stub/src/arch/amd64/macros.S @@ -41,4 +41,8 @@ .code64 .endm +.macro endbr64 + .byte 0xf3,0x0f,0x1e,0xfa +.endm + // vi:ts=8:et:nowrap diff --git a/src/stub/src/arch/i386/macros.S b/src/stub/src/arch/i386/macros.S index ecc45a86..d6a5dc3c 100644 --- a/src/stub/src/arch/i386/macros.S +++ b/src/stub/src/arch/i386/macros.S @@ -717,4 +717,8 @@ section LXUNF035 ret .endm +.macro endbr32 + .byte 0xf3,0x0f,0x1e,0xfb +.endm + // vi:ts=8:et:nowrap diff --git a/src/stub/src/i386-expand.S b/src/stub/src/i386-expand.S index 99fec069..efe19028 100644 --- a/src/stub/src/i386-expand.S +++ b/src/stub/src/i386-expand.S @@ -77,6 +77,9 @@ sz_binfo= 3*4 f_unfilter: // (*f_unf)(xo->buf, out_len, h.b_cto8, h.b_ftid); #include "arch/i386/bxx.S" +.macro endbr32 + .byte 0xf3,0x0f,0x1e,0xfb +.endm // int f_expand(nrv_byte const *src, nrv_byte *dst, size_t *dstlen) // C-callable // Includes unfilter and cache flush. @@ -128,6 +131,7 @@ refill: movzbl (%esi),%edx // pre-fetch: literal, or bottom 8 bits of offset rep; ret getbit: + endbr32 // from "call *%edx" addl bits,bits; jz refill // Carry= next bit rep; ret diff --git a/src/stub/src/i386-linux.elf-entry.S b/src/stub/src/i386-linux.elf-entry.S index e3f82811..bb0533e9 100644 --- a/src/stub/src/i386-linux.elf-entry.S +++ b/src/stub/src/i386-linux.elf-entry.S @@ -104,6 +104,7 @@ M_NRV2B_LE32= 2 section ELFMAINX sz_pack2 = -NBPW+ _start _start: .globl _start + endbr32 /// nop; int3 // DEBUG i386 entry.S call L70 // MATCH_08 push $&getbit L70ret: @@ -322,7 +323,7 @@ eof_n2b: mov F_ADRU(%ebp),%eax add $D_FOLD,%eax // PAGE_MASK, upxfd_path, mflg_data - jmp *%eax + /*notrack*/ jmp *%eax // %esp: // MATCH_13 ptr unfolded_code; for escape hatch // MATCH_12 len unfolded code; for escape hatch diff --git a/src/stub/src/i386-linux.elf-fold.S b/src/stub/src/i386-linux.elf-fold.S index dbade8c8..f76fd0f5 100644 --- a/src/stub/src/i386-linux.elf-fold.S +++ b/src/stub/src/i386-linux.elf-fold.S @@ -101,7 +101,9 @@ upxfn_path= . - 2*NBPW // displacement to filename string mflg_data= . - 1*NBPW // QNZ vs Linux fold_begin: + endbr32 //// int3 // DEBUG + endbr32 pop ebx; push ebx // F_ADRX: elfaddr + (O_BINFO | is_ptinterp | unmap_all_pages) jmp L10 @@ -285,7 +287,7 @@ L60: push eax // 32 bytes of zeroes now on stack, ready for 'popa' mov al, __NR_munmap // eax was 0 from L60 - jmp [edi] // unmap ourselves via escape hatch, then goto entry + /*notrack*/ jmp [edi] // unmap ourselves via escape hatch, then goto entry section SYSCALLS // Sometimes linux enforces page-aligned address diff --git a/src/stub/src/i386-linux.elf-main2.c b/src/stub/src/i386-linux.elf-main2.c index 83dc4ff2..54bbaa5f 100644 --- a/src/stub/src/i386-linux.elf-main2.c +++ b/src/stub/src/i386-linux.elf-main2.c @@ -289,27 +289,31 @@ make_hatch_i386( unsigned frag_mask ) { - char *hatch = 0; + unsigned *hatch = 0; + unsigned code[2] = { + 0x586180cd, // int #0x80; popa; pop %eax + 0x90e0ff3e, // notrack jmp *%eax; nop + } DPRINTF("make_hatch %%p %%p %%x\\n", phdr, next_unc, frag_mask); if (phdr->p_type==PT_LOAD && phdr->p_flags & PF_X) { next_unc += phdr->p_memsz - phdr->p_filesz; // Skip over local .bss frag_mask &= -(long)next_unc; // bytes left on page - unsigned /*const*/ escape = 0xc36180cd; // "int $0x80; popa; ret" - if (4 <= frag_mask) { - hatch = next_unc; - *(long *)&hatch[0] = escape; + if (sizeof(code) <= frag_mask) { + hatch = (unsigned *)next_unc; + hatch[0] = code[0]; + hatch[1] = code[1]; } else { // Does not fit at hi end of .text, so must use a new page "permanently" - unsigned long fdmap = upx_mmap_and_fd((void *)0, sizeof(escape), nullptr); + unsigned long fdmap = upx_mmap_and_fd((void *)0, sizeof(code), nullptr); unsigned mfd = -1+ (0xfff& fdmap); - write(mfd, &escape, sizeof(escape)); - hatch = mmap((void *)(fdmap & ~0xffful), sizeof(escape), + write(mfd, &code, sizeof(code)); + hatch = mmap((void *)(fdmap & ~0xffful), sizeof(code), PROT_READ|PROT_EXEC, MAP_PRIVATE, mfd, 0); close(mfd); } } DPRINTF("hatch=%%p\\n", hatch); - return hatch; + return (char *)hatch; } #elif defined(__arm__) /*}{*/ extern unsigned get_sys_munmap(void);