Search code examples
assemblygnu-assembler

Using gas, how can I get the offset to a particular label?


I'm using pwnlib to write a small shellcode for a challenge. My shellcode needs to modify itself to pass application filters. I first wrote it with nasm, and did something like that:

        sub        edx, edx
        mov        dl, 0x82
        add        al, do_mov_rdi_rax
        sub        dword [rax], edx
        mov        dh, 0x82
        add        al, do_syscall - do_mov_rdi_rax
        sub        dword [rax], edx
        shr        edi, 31

    do_mov_rdi_rax:
        ; mov    rsi, rax
        ; (with 0x82 added to first byte to pass validation)
        db         0xca, 0x89, 0xc6
        sub        eax, eax

    do_syscall:
        ; syscall
        ; (with 0x82 added to both bytes to pass validation)
        db         0x91, 0x87

Pwnlib uses gas, however, so my assembly code has to conform to its syntax. Besides the obvious (// instead of ;, .byte instead of db), I'm stuck with one last problem: while nasm happily converted my labels to integers (for add al, do_mov_rdi_rax and add al, do_syscall - do_mov_rdi_rax), gas keeps telling me that it can't represent addressing type BFD_RELOC_8, or something like that (I somehow ended up with a French version of gas, sorry for the lacking error message).

How can I get the address of the labels as integers? My shellcode is based at address 0 (and gas is told with .org 0x0).


Solution

  • Since labels wouldn't work, I dug through the gas documentation and found that it's also possible to create expression symbols, and that one could use . to get the location's address. As it turns out, with the Mach-O output format, gas will accept this:

    .set main, .
        sub     edx, edx
        mov     dl, 0x82
        add     al, do_mov_rdi_rax - main
        sub     dword [rax], edx
        mov     dh, 0x82
        add     al, do_syscall - do_mov_rdi_rax
        sub     dword [rax], edx
        shr     edi, 31
    
    .set do_mov_rdi_rax, .
        // mov  rsi, rax
        // (with 0x82 added to first byte to pass validation)
        .byte   0xca, 0x89, 0xc6
        sub     eax, eax
    
    .set do_syscall, .
        // syscall
        // (with 0x82 added to both bytes to pass validation)
        .byte   0x91, 0x87
    

    On the first add, simply using do_mov_rdi_rax wouldn't work, but using the difference between it and main worked perfectly. (Replacing main with a literal zero, however, would not do it.)

    There are other problems with it, though: support for sub dword [rax], edx appears to be lacking. With gas 2.25 on my Mac, it got assembled as sub dword [rax+4], edx, which is very wrong. The version of as that ships with Xcode refuses to assemble it, citing absolute 32-bit addressing use; gas 2.24.90 for Debian also refused to assemble it because there would be too many memory references, somehow. Because this is all incorrect, I'll stick with a nasm-assembled binary version of the shellcode instead of using pwnlib's asm to compile it.