Thread: Calling exit() in hex assembly

  1. #1
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657

    Calling exit() in hex assembly

    I'm either not understanding these instructions correctly or I'm doing something else wrong to get a segmentation fault, here's what I got for the assembly (I included asm/unistd_64.h)
    Code:
    typedef struct {
    	u8 mov0_edi_to_rsp[3];
    	u8 lea0_rsi_to_8rsp[5];
    	u8 call0_func[5];
    	u8 mov1_edi_to_edx[2];
    	u8 mov2_x0000003C[5];
    	u8 syscall_exit[2];
    } start_t;
    start_t begin__start = {
    	/* mov edi,DWORD PTR [rsp] #Move argc into param1 register */
    	{ 0x8B, 0x3C, 0x24 },
    	/* lea rsi,[rsp+0x8] #Move argv into param2 register */
    	{ 0x48, 0x80, 0x74, 0x24, 0x08 },
    	/* call 0x12345678 #Call main() */
    	{ 0xE8, 0x78, 0x56, 0x34, 0x12 },
    	/* mov edi,eax #Move result of main() into param1 register */
    	{ 0x89, 0xC7 },
    	/* mov eax,0x3C #Designate exit() for syscall instruction */
    	{ 0xB8, __NR_exit, 0x00, 0x00, 0x00 },
    	/* syscall */
    	{ 0x0F, 0x05 }
    };
    typedef struct {
    	u8 rex_rx[1];
    	u8 xor_eax_eax[2];
    	u8 ret[1];
    } main_t;
    main_t begin_main = {
    	{0x48}, /* treat following dst & src as qword */
    	{0x31, 0xC0}, /* xor rax,rax */
    	{0xC3} /* ret */
    };
    And here's the output of both my test.elf & readelf when I run it on test.elf:
    Code:
    ./test.elf
    Segmentation fault
    readelf -all test.elf
    ELF Header:
      Magic:   7f 45 4c 46 02 01 01 ff 00 00 00 00 00 00 00 00 
      Class:                             ELF64
      Data:                              2's complement, little endian
      Version:                           1 (current)
      OS/ABI:                            <unknown: ff>
      ABI Version:                       0
      Type:                              EXEC (Executable file)
      Machine:                           Advanced Micro Devices X86-64
      Version:                           0x1
      Entry point address:               0x2d0
      Start of program headers:          64 (bytes into file)
      Start of section headers:          232 (bytes into file)
      Flags:                             0x0
      Size of this header:               64 (bytes)
      Size of program headers:           56 (bytes)
      Number of program headers:         3
      Size of section headers:           64 (bytes)
      Number of section headers:         4
      Section header string table index: 1
    readelf: Warning: Section 2 has an out of range sh_info value of 7
    
    Section Headers:
      [Nr] Name              Type             Address           Offset
           Size              EntSize          Flags  Link  Info  Align
      [ 0]                   NULL             0000000000000000  00000000
           0000000000000000  0000000000000000           0     0     0
      [ 1] .shstrtab         STRTAB           00000000000001e8  000001e8
           0000000000000040  0000000000000001  AS       0     0     1
      [ 2] .symtab           SYMTAB           0000000000000228  00000228
           00000000000000a8  0000000000000018 WAI       1     7     8
      [ 3] .text             PROGBITS         00000000000002d0  000002d0
           000000000000001a  0000000000000001 WAXlp       0     0     1
    Key to Flags:
      W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
      L (link order), O (extra OS processing required), G (group), T (TLS),
      C (compressed), x (unknown), o (OS specific), E (exclude),
      l (large), p (processor specific)
    
    There are no section groups in this file.
    
    Program Headers:
      Type           Offset             VirtAddr           PhysAddr
                     FileSiz            MemSiz              Flags  Align
      NULL           0x0000000000000000 0x0000000000000000 0x0000000000000000
                     0x0000000000000000 0x0000000000000000         0x0
      PHDR           0x0000000000000000 0x0000000000000040 0x0000000000000040
                     0x00000000000000a8 0x00000000000000a8  R      0x0
      LOAD           0x0000000000000000 0x00000000000002d0 0x00000000000002d0
                     0x000000000000001a 0x000000000000001a  RWE    0x0
    
     Section to Segment mapping:
      Segment Sections...
       00     
       01     
       02     
    
    There is no dynamic section in this file.
    
    There are no relocations in this file.
    
    The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
    
    Symbol table '.symtab' contains 7 entries:
       Num:    Value          Size Type    Bind   Vis      Ndx Name
         0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
         1: 00000000000001e8    64 SECTION LOCAL  DEFAULT    1 .shstrtab
         2: 0000000000000228   168 SECTION LOCAL  DEFAULT    2 .symtab
         3: 00000000000002d0    26 SECTION LOCAL  DEFAULT    3 .text
         4: 00000000000002d0    26 FILE    LOCAL  DEFAULT  ABS mitsy.c
         5: 00000000000002d0    22 NOTYPE  GLOBAL DEFAULT    3 _start
         6: 00000000000002e6     4 FUNC    GLOBAL DEFAULT    3 main
    Gotta go to work now so won't be able to try any suggestions until late tonight, might respond though if I have something to say

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    556
    Line 14-15: Pretty obvious why you got a segfault.

    PS: AND Your LEA RSI.[RSP+8] is wrong and you don't need to use the REX prefix on main.
    Why the structures?

  3. #3
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    Finally realised why my post are being deleted, my browser don't understand I'm tapping the text box when editing,
    anways the structs are because I need fixed assembly while learning the ins and outs of calling functions, the line your refering to (call main() I believe) is modified later before being written, I just needed something to initialise those bytes with and 0x12345678 seemed pretty easy to spot if I didn't modify.

  4. #4
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    Your point about the lea instruction makes me think the seg fault is occurring there, I'll try modifying that when I get home

  5. #5
    Registered User
    Join Date
    Feb 2019
    Posts
    556
    Quote Originally Posted by awsdert View Post
    Your point about the lea instruction makes me think the seg fault is occurring there, I'll try modifying that when I get home
    Probably... opcode 80 is XOR rm8,imm8, and address RSP+8 is, in theory, correctly mapped and it is R/W... Maybe it is a typo, since changing to 8D you'll get your LEA RSI,[RSP+8]...

    The way your _start code is, it translates to:
    Code:
    0000000000000000 <_start>:
       0: 8b 3c 24              mov    edi,DWORD PTR [rsp]
       3: 48 80 74 24 08 e8     rex.W xor BYTE PTR [rsp+0x8],0xe8
       9: 78 56                 js     61 <main+0x4b>
       b: 34 12                 xor    al,0x12
       d: 89 c7                 mov    edi,eax
       f: b8 3c 00 00 00        mov    eax,0x3c
      14: 0f 05                 syscall
    And, remember that 0xE8 opcode need a relative offset... when you fix this.
    Last edited by flp1969; 04-12-2019 at 07:15 AM.

  6. #6
    Registered User
    Join Date
    Feb 2019
    Posts
    556
    Quote Originally Posted by awsdert View Post
    Finally realised why my post are being deleted, my browser don't understand I'm tapping the text box when editing,
    anways the structs are because I need fixed assembly while learning the ins and outs of calling functions, the line your refering to (call main() I believe) is modified later before being written, I just needed something to initialise those bytes with and 0x12345678 seemed pretty easy to spot if I didn't modify.
    I think is cleaner this way:
    Code:
    char main[] =
      "\x31\xc0"   // xor eax,eax
      "\xc3";      // ret
    And, you don't need the REX prefix to zero RAX... modifying EAX will zero all upper bits of RAX automatically...
    Last edited by flp1969; 04-12-2019 at 07:23 AM.

  7. #7
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    Okay I tried what you mentioned but still got a segfault, initially I assumed it was because I forgot to remove the override that forces an absolute pointer, so I then wrapped that in an #ifdef 0 and still got the segfault, here's what it now looks like:
    Code:
    start_t begin__start = {
    	/* mov edi,DWORD PTR [rsp] #Move argc into param1 register */
    	{ 0x8B, 0x3C, 0x24 },
    	/* lea rsi,[rsp+0x8] #Move argv into param2 register */
    	{ 0x48, 0x8D, 0x74, 0x24, 0x08 },
    	/* call 0x12345678 #Call main() */
    	{ 0xE8, 0x09, 0x00, 0x00, 0x00 },
    	/* mov edi,eax #Move result of main() into param1 register */
    	{ 0x89, 0xC7 },
    	/* mov eax,0x3C #Designate exit() for syscall instruction */
    	{ 0xB8, __NR_exit, 0x00, 0x00, 0x00 },
    	/* syscall */
    	{ 0x0F, 0x05 }
    };

  8. #8
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,728
    0xE8 is "CALL rel32". Are you sure that "main" is placed right after "_start"?
    Devoted my life to programming...

  9. #9
    Registered User
    Join Date
    Feb 2019
    Posts
    556
    You are assuming the linker puts the "main" array just after "start" array... This call to RIP+9, maybe, is jumping to the wrong place.

  10. #10
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    Well it should be right after it because all my symbols and sections say not to duplicate anything and just load/execute the code as is, which was written to be right by each other:
    Code:
    memcpy( seg_coded->mem.buff, &begin__start, sizeof(start_t) );
    memcpy( &(((char*)seg_coded->mem.buff)[sizeof(start_t)]),
    	&begin_main, sizeof(main_t) );
    Which was then written to file via a loop of all segments I was giving the executable, I even checked the contents after and it was indeed where I expected it to be (at the end of the file)

  11. #11
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    I finally found some documentation that breaks down those opcodes to some extent which will help me understand the byte code I originally copied from a gcc generated executable and modified
    X86-64 Instruction Encoding - OSDev Wiki
    but I'm failing to understand what that last 4 bits is used for, probably not reading the docs correctly

  12. #12
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    This is the best I was able to understand the opcode to be:
    Code:
    typedef struct ATTR_PACKED {
    	u8 register_to_use : 4;
    	u8 register_value_32bit : 1;
    	u8 execute_another_byte : 1;
    	u8 protected_mode : 2;
    } opcode_t;

  13. #13
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    Still struggling with the relative opcodes but for now I gonna try absolute addressing which is easier by looks of it:
    Code:
    /* mov address of main into eax */
    { 0xC7, 0x3C000000 | sizeof(start_t) }, // seg_coded->pos added to this before writing to file
    /* call eax */
    { 0xE8, 0x37 },
    I'm sure I did that wrong because I'm still getting segfaults but without an example of how to set r,r/m or m types of expected data or how to switch between imm16 and imm32 etc I will probably continue to struggle to understand instruction usage

  14. #14
    Registered User
    Join Date
    Feb 2019
    Posts
    556
    @awsdert,

    Relative addressing is relative to the contents of RIP register. Is an offset...
    The only way to garantee if the relative address is correct is to put all the encoded "code" in a single array to avoid compiler alignment issues and linker symbols reordering, in your example:

    Code:
    char _start[] = {
                /* mov edi,DWORD PTR [rsp] #Move argc into param1 register */
                0x8B, 0x3C, 0x24,
    
                /* lea rsi,[rsp+0x8] #Move argv into param2 register */
                0x48, 0x8D, 0x74, 0x24, 0x08,
    
                /* call RIP+9 #Call main() */
                0xE8, 0x09, 0x00, 0x00, 0x00,
    
                /* mov edi,eax #Move result of main() into param1 register */
    /* RIP */   0x89, 0xC7,
    
                /* mov eax,0x3C #Designate exit() for syscall instruction */
                0xB8, __NR_exit, 0x00, 0x00, 0x00,
    
                /* syscall */
                0x0F, 0x05
    
    /* main @ RIP+9 */
                /* xor eax,eax */
                0x31, 0xC0,
    
                /* ret */
                0xC3
    };
    
    char *main = _start + 22; // Puts main on symbol table.
    
    // Auxiliary no exported symbols
    // In "compile time", you can do:
    //   *call_rel32 = (unsigned int)(main - rip_after_call);
    static char *rip_after_call = _start + 13;
    static unsigned int *call_rel32 = _start + 9;
    Last edited by flp1969; 04-13-2019 at 05:12 AM.

  15. #15
    Registered User awsdert's Avatar
    Join Date
    Jan 2015
    Posts
    657
    I wouldn't worry about the compiler putting them separately because it never makes the executable to begin with, my own code creates it and controls where everything is put, as I said before I'm learning how the execution environment works before I try to write my own compiler

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Calling assembly from c
    By Kuluzuva in forum C Programming
    Replies: 9
    Last Post: 05-16-2011, 03:54 AM
  2. Calling exit()... without exiting
    By JordyD in forum C Programming
    Replies: 3
    Last Post: 11-21-2009, 04:06 PM
  3. calling functions: exit and return
    By 911help in forum C Programming
    Replies: 3
    Last Post: 12-28-2007, 01:24 PM
  4. calling exit() crashes
    By ichijoji in forum C++ Programming
    Replies: 4
    Last Post: 12-19-2005, 12:26 AM
  5. Calling exit() with dynamic memory
    By miclus in forum C Programming
    Replies: 11
    Last Post: 10-05-2004, 11:49 AM

Tags for this Thread