
- Loading ELF Files
- Using Symbols
- Changing the Base Address
- Reading ELF Files
- Patching ELF Files
- Searching ELF Files
- Building ELF Files
- Running and Debugging ELF Files
ELF 파일들은 경로에 의해 로드된다. 로드되면, 파일의 몇몇 보안 속성이 출력된다.
ELF(’file’) 로 파일 오브젝트를 얻을 수 있다.
from pwn import *
e = ELF('/bin/bash')
# [*] '/bin/bash'
# Arch: amd64-64-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: No PIE
# FORTIFY: Enabled
ELF 파일에는 몇가지 사용 가능한 심볼 세트가 있으며, 각각은 딕셔너리 형태로 포함된다.
from pwn import *
e = ELF('/bin/bash')
print "%#x -> license" % e.symbols['bash_license']
print "%#x -> execve" % e.symbols['execve']
print "%#x -> got.execve" % e.got['execve']
print "%#x -> plt.execve" % e.plt['execve']
print "%#x -> list_all_jobs" % e.functions['list_all_jobs'].address
위 예제의 결과는 다음과 같다 :
0x4ba738 -> license
0x41db60 -> execve
0x6f0318 -> got.execve
0x41db60 -> plt.execve
0x446420 -> list_all_jobs
ELF의 Base Address를 변경할 수 있다. ( ASLR 조정 등 )
from pwn import *
e = ELF('/bin/bash')
print "%#x -> base address" % e.address
print "%#x -> entry point" % e.entry
print "%#x -> execve" % e.symbols['execve']
print "---"
e.address = 0x12340000
print "%#x -> base address" % e.address
print "%#x -> entry point" % e.entry
print "%#x -> execve" % e.symbols['execve']
위 예제의 결과는 다음과 같다 :
0x400000 -> base address
0x42020b -> entry point
0x41db60 -> execve
---
0x12340000 -> base address
0x1236020b -> entry point
0x1235db60 -> execve
우리는 read, write, 그리고 packing 모듈 등을 활용해 ELF 파일에 직접적으로 작용을 할 수 있다. 추가적으로, disasm method 를 활용해 디스어셈블리 또한 볼 수 있다.
from pwn import *
e = ELF('/bin/bash')
print repr(e.read(e.address, 4))
p_license = e.symbols['bash_license']
license = e.unpack(p_license)
print "%#x -> %#x" % (p_license, license)
print e.read(license, 14)
print e.disasm(e.symbols['main'], 12)
위 예제의 결과는 다음과 같다 :
'\x7fELF'
0x4ba738 -> 0x4ba640
License GPLv3+
41eab0: 41 57 push r15
41eab2: 41 56 push r14
41eab4: 41 55 push r13
ELF 파일들을 패치하는 것은 단순하다.
from pwn import *
e = ELF('/bin/bash')
# Cause a debug break on the 'exit' command
e.asm(e.symbols['exit_builtin'], 'int3')
# Disable chdir and just print it out instead
e.pack(e.got['chdir'], e.plt['puts'])
# Change the license
p_license = e.symbols['bash_license']
license = e.unpack(p_license)
e.write(license, 'Hello, world!\n\x00')
e.save('./bash-modified')
우리는 이제 우리의 수정된 버전의 배시를 실행할 수 있다.
$ chmod +x ./bash-modified
$ ./bash-modified -c 'exit'
Trace/breakpoint trap (core dumped)
$ ./bash-modified --version | grep "Hello"
Hello, world!
$ ./bash-modified -c 'cd "No chdir for you!"'
/home/user/No chdir for you!
No chdir for you!
./bash-modified: line 0: cd: No chdir for you!: No such file or directory
search를 활용해 바이트 시퀀스를 검색하고, 바이트 시퀀스의 주소를 얻을 수 있다.
from pwn import *
e = ELF('/bin/bash')
for address in e.search('/bin/sh\x00'):
print hex(address)
위 예제의 결과는 다음과 같다 :
0x420b82
0x420c5e
ELF 파일들은 쉽게(?) 생성될 수 있다. 모든 함수들은 context를 참조한다. 관련 함수는 from_bytes 와 from_assembly 가 있다. 각각은 ELF 오브젝트를 반환하며, 이것은 쉽게 파일로 저장될 수 있다.
이 기능은 잘 모르겠어서 추가적인 서술은 안함. 밑은 공식 예제
from pwn import *
ELF.from_bytes('\xcc').save('int3-1')
ELF.from_assembly('int3').save('int3-2')
ELF.from_assembly('nop', arch='powerpc').save('powerpc-nop')
만약 ELF object를 보유하고 있다면, 실행이나 디버깅을 직접 할 수 있다.
# process 실행
>>> io = elf.process()
# vs
>>> io = process(elf.path)
# process 디버깅
>>> io = elf.debug()
# vs
>>> io = gdb.debug(elf.path)