Fröhliche x64 Hackerei (GCC)



  • Hallo,
    ich erkläre besser gar nicht warum ich das tue. 🙂

    /*
       0:	48 83 ec 10          	sub    $0x10,%rsp
       4:	c7 44 24 0c ef be ef 	movl   $0xbeefbeef,0xc(%rsp)
       b:	be 
       c:	c7 44 24 08 ad de ad 	movl   $0xdeaddead,0x8(%rsp)
      13:	de 
      14:	c7 44 24 04 fe af fe 	movl   $0xaffeaffe,0x4(%rsp)
      1b:	af 
      1c:	c7 04 24 ad de ad de 	movl   $0xdeaddead,(%rsp)
      23:	c3                   	retq   
      24:	48 83 c4 08          	add    $0x8,%rsp
      28:	c3                   	retq  
    */
    
    #define SUB_RSP_I8(i8) 0x48, 0x83, 0xEC, (i8)
    #define ADD_RSP_I8(i8) 0x48, 0x83, 0xC4, (i8)
    #define MOV_RSP_I8OFFSET_I32(i8offset, i32)  0xC7, 0x44, 0x24, Byte(i8offset), \
        Byte((i32) & 0xFF), \
        Byte(((i32) >> 8) & 0xFF), \
        Byte(((i32) >> 16) & 0xFF), \
        Byte(((i32) >> 24) & 0xFF)
    #define RET() 0xC3
    
    #include <iostream>
    #include <sys/mman.h>
    
    typedef unsigned char Byte;
    typedef unsigned int Int32;
    typedef unsigned long long Int64;
    
    int testFunc(int a, char b)
    {
        Int64 helper[0];
    
        std::cout << a << " " << b << " 0x" << std::hex << helper[0] << std::endl;
    
        asm("add $0x8,%rsp");
        return 123;
    }
    
    /*
    ; We need stackspace for 2 pointers.
    sub rsp, 16
    
    ; Push userdata.
    mov dword [rsp + 12], 0xBEEFBEEF
    mov dword [rsp + 8 ], 0xDEADDEAD
    
    ; Push function address.
    mov dword [rsp + 4], 0xAFFEAFFE
    mov dword [rsp + 0], 0xDEADDEAD
    
    ; Call the function.
    ret
    
    ; Remove userdata from stack and return.
    add rsp, 8
    ret
    */
    
    Byte hack[] =
    {
        // We need stackspace for 2 pointers.
        SUB_RSP_I8(16),
    
        // Push userdata.
        MOV_RSP_I8OFFSET_I32(12, 0xBEEFBEEF),
        MOV_RSP_I8OFFSET_I32(8, 0xDEADDEAD),
    
        // Push function address.
        MOV_RSP_I8OFFSET_I32(4, Int32(((Int64)&testFunc >> 32) & 0xFFFFFFFF)),
        MOV_RSP_I8OFFSET_I32(0, Int32(((Int64)&testFunc) & 0xFFFFFFFF)),
    
        // Call the function.
        RET(),
    
        // Remove userdata from stack and return.
        ADD_RSP_I8(8),
        RET()
    };
    
    int main()
    {
        Int64 tmp = (Int64)(&hack[0]);
        Byte* aligned = (Byte*)(Int64(tmp / 4096) * 4096);
        if(mprotect(aligned, 4096, PROT_EXEC | PROT_READ | PROT_WRITE) != 0)
        {
            std::cout << "Fuuuuuuuuuuuuuuuuuuu";
            return 1;
        }
    
        auto tocall = (int (*)(int, char))(&hack[0]);
        std::cout << "Result (Expected: 123): " << tocall(12, 'a') << std::endl;
        std::cout << "Done";
    }
    

    Das zugreifen auf das nullte Element im leeren Array helper in testFunc gibt mir leider nicht das erhoffte 0xbeefbeefdeaddead, das sich laut Debugger aber oben auf dem Stack befindet.

    Ein Blick auf den Disassembly von testFunc zeigt mir dass 0x18 Bytes auf dem Stack reserviert werden (obwohl ich keine weiteren temporären Variablen anlege), mein Zugriff landet also im aktuellen Stackframe und ich bekomme Müll.

    Jemand eine Idee wie ich hier den Compiler austricksen könnte um Zugriff auf den Zeiger zu ergattern? 😕

    Danke und Grüße,
    Ethon


Anmelden zum Antworten