pwntools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+ require 'pwnlib/constants/constants'
5
+ require 'pwnlib/context'
6
+
7
+ class ConstantsTest < MiniTest::Test
8
+ include ::Pwnlib::Context
9
+ Constants = ::Pwnlib::Constants
10
+
11
+ def test_amd64
12
+ context.local(arch: 'amd64') do
13
+ assert_equal('Constant("SYS_read", 0x0)', Constants.SYS_read.inspect)
14
+ assert_equal('__NR_arch_prctl', Constants.__NR_arch_prctl.to_s)
15
+ assert_equal('Constant("(O_CREAT)", 0x40)', Constants.eval('O_CREAT').inspect)
16
+ # TODO(david942j): implement 'real' Constants.eval
17
+ # assert_equal('Constant("(O_CREAT | O_WRONLY)", 0x41)', Constants.eval('O_CREAT | O_WRONLY').inspect)
18
+ end
19
+ end
20
+
21
+ def test_i386
22
+ context.local(arch: 'i386') do
23
+ assert_equal('Constant("SYS_read", 0x3)', Constants.SYS_read.inspect)
24
+ assert_equal('__NR_prctl', Constants.__NR_prctl.to_s)
25
+ assert_equal('Constant("(O_CREAT)", 0x40)', Constants.eval('O_CREAT').inspect)
26
+ assert_equal(0x40, Constants.method(:O_CREAT).call.to_i)
27
+ # 2 < 3
28
+ assert_operator(2, :<, Constants.SYS_read)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,131 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+ require 'pwnlib/context'
5
+
6
+ class ContextTest < MiniTest::Test
7
+ include ::Pwnlib::Context
8
+
9
+ def test_update
10
+ context.update(arch: 'arm', os: 'windows')
11
+ assert_equal('arm', context.arch)
12
+ assert_equal('windows', context.os)
13
+ end
14
+
15
+ def test_local
16
+ context.timeout = 1
17
+ assert_equal(1, context.timeout)
18
+
19
+ context.local(timeout: 2) do
20
+ assert_equal(2, context.timeout)
21
+ context.timeout = 3
22
+ assert_equal(3, context.timeout)
23
+ end
24
+
25
+ assert_equal(1, context.timeout)
26
+
27
+ assert_raises(RuntimeError) do
28
+ context.local(timeout: 3) { raise 'QQ failed in block' }
29
+ end
30
+
31
+ assert_equal(1, context.timeout)
32
+ end
33
+
34
+ def test_clear
35
+ default_arch = context.arch
36
+ context.arch = 'arm'
37
+ context.clear
38
+ assert_equal(default_arch, context.arch)
39
+ end
40
+
41
+ def test_arch
42
+ context.arch = 'mips'
43
+ assert_equal('mips', context.arch)
44
+
45
+ err = assert_raises(ArgumentError) { context.arch = 'shik' }
46
+ assert_match(/arch must be one of/, err.message)
47
+ assert_equal('mips', context.arch)
48
+
49
+ context.clear
50
+ assert_equal(32, context.bits)
51
+ context.arch = 'powerpc64'
52
+ assert_equal(64, context.bits)
53
+ assert_equal('big', context.endian)
54
+ end
55
+
56
+ def test_bits
57
+ context.bits = 64
58
+ assert_equal(64, context.bits)
59
+
60
+ err = assert_raises(ArgumentError) { context.bits = 0 }
61
+ assert_match(/bits must be > 0/, err.message)
62
+ end
63
+
64
+ def test_bytes
65
+ context.bytes = 8
66
+ assert_equal(64, context.bits)
67
+ assert_equal(8, context.bytes)
68
+
69
+ context.bits = 32
70
+ assert_equal(4, context.bytes)
71
+ end
72
+
73
+ def test_endian
74
+ context.endian = 'le'
75
+ assert_equal('little', context.endian)
76
+
77
+ context.endian = 'big'
78
+ assert_equal('big', context.endian)
79
+
80
+ err = assert_raises(ArgumentError) { context.endian = 'SUPERBIG' }
81
+ assert_match(/endian must be one of/, err.message)
82
+ end
83
+
84
+ def test_log_level
85
+ context.log_level = 'error'
86
+ assert_equal(Logger::ERROR, context.log_level)
87
+
88
+ context.log_level = 514
89
+ assert_equal(514, context.log_level)
90
+
91
+ err = assert_raises(ArgumentError) { context.log_level = 'BOOM' }
92
+ assert_match(/log_level must be an integer or one of/, err.message)
93
+ end
94
+
95
+ def test_os
96
+ context.os = 'windows'
97
+ assert_equal('windows', context.os)
98
+
99
+ err = assert_raises(ArgumentError) { context.os = 'deepblue' }
100
+ assert_match(/os must be one of/, err.message)
101
+ end
102
+
103
+ def test_signed
104
+ context.signed = true
105
+ assert_equal(true, context.signed)
106
+
107
+ context.signed = 'unsigned'
108
+ assert_equal(false, context.signed)
109
+
110
+ err = assert_raises(ArgumentError) { context.signed = 'partial' }
111
+ assert_match(/signed must be boolean or one of/, err.message)
112
+ end
113
+
114
+ def test_timeout
115
+ context.timeout = 123
116
+ assert_equal(123, context.timeout)
117
+ end
118
+
119
+ def test_newline
120
+ context.newline = "\r\n"
121
+ assert_equal("\r\n", context.newline)
122
+ end
123
+
124
+ def test_to_s
125
+ assert_match(/\APwnlib::Context::ContextType\(.+\)\Z/, context.to_s)
126
+ end
127
+
128
+ def teardown
129
+ context.clear
130
+ end
131
+ end
@@ -0,0 +1,8 @@
1
+ #include <stdio.h>
2
+
3
+ int main() {
4
+ setvbuf(stdout, NULL, _IONBF, 0);
5
+ printf("%p\n", __builtin_return_address(0));
6
+ scanf("%c");
7
+ return 0;
8
+ }
Binary file
Binary file
@@ -0,0 +1,48 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'open3'
4
+
5
+ require 'tty-platform'
6
+
7
+ require 'test_helper'
8
+ require 'pwnlib/dynelf'
9
+
10
+ class DynELFTest < MiniTest::Test
11
+ def test_lookup
12
+ skip 'Only tested on linux' unless TTY::Platform.new.linux?
13
+ [32, 64].each do |b|
14
+ # TODO(hh): Use process instead of popen2
15
+ Open3.popen2(File.expand_path("../data/victim#{b}", __FILE__)) do |i, o, t|
16
+ main_ra = Integer(o.readline)
17
+ libc_path = nil
18
+ IO.readlines("/proc/#{t.pid}/maps").map(&:split).each do |s|
19
+ st, ed = s[0].split('-').map { |x| x.to_i(16) }
20
+ next unless main_ra.between?(st, ed)
21
+ libc_path = s[-1]
22
+ break
23
+ end
24
+ refute_nil(libc_path)
25
+
26
+ # TODO(hh): Use ELF instead of objdump
27
+ # Methods in libc might have multi-versions, so we record and check if
28
+ # we can find one of them.
29
+ h = Hash.new { |hsh, key| hsh[key] = [] }
30
+ symbols = `objdump -T #{libc_path}`.lines.map(&:split).select { |a| a[2] == 'DF' }
31
+ symbols.map { |a| h[a[-1]] << a[0].to_i(16) }
32
+
33
+ mem = open("/proc/#{t.pid}/mem", 'rb')
34
+ d = ::Pwnlib::DynELF.new(main_ra) do |addr|
35
+ mem.seek(addr)
36
+ mem.getc
37
+ end
38
+
39
+ assert_nil(d.lookup('pipi_hao_wei!'))
40
+ h.each do |sym, off|
41
+ assert_includes(off, d.lookup(sym) - d.libbase)
42
+ end
43
+
44
+ i.write('bye')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'test_helper'
4
+ require 'pwnlib/ext/array'
5
+ require 'pwnlib/ext/integer'
6
+ require 'pwnlib/ext/string'
7
+
8
+ class ExtTest < MiniTest::Test
9
+ # Thought that test one method in each module for each type is enough, since it's quite
10
+ # stupid (and meaningless) to copy the list of proxied functions to here...
11
+ def test_ext_string
12
+ assert_equal(0x4142, 'AB'.u16(endian: 'be'))
13
+ assert_equal([1, 1, 0, 0, 0, 1, 0, 0], "\xC4".bits)
14
+ end
15
+
16
+ def test_ext_integer
17
+ assert_equal('AB', 0x4241.p16)
18
+ assert_equal([0, 0, 1, 1, 0, 1, 0, 0], 0x34.bits)
19
+ assert_equal(2**31, 1.bitswap)
20
+ end
21
+
22
+ def test_ext_array
23
+ assert_equal("\xfe", [1, 1, 1, 1, 1, 1, 1, 0].unbits)
24
+ assert_equal("XX\xef\xbe\xad\xdeXX", ['XX', 0xdeadbeef, 'XX'].flat)
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ # Make sure we're using local copy for local testing.
4
+ $LOAD_PATH.unshift File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
5
+
6
+ require 'pwn'
7
+
8
+ context[arch: 'amd64']
9
+
10
+ raise 'pack fail' unless pack(1) == "\x01\0\0\0\0\0\0\0"
11
+ unless ::Pwnlib::Util::Fiddling.__send__(:context).object_id == context.object_id
12
+ raise 'not unique context'
13
+ end
14
+ unless ::Pwnlib::Context.context.object_id == context.object_id
15
+ raise 'not unique context'
16
+ end
17
+
18
+ # Make sure things aren't polluting Object
19
+ begin
20
+ 1.__send__(:context)
21
+ raise 'context polluting Object.'
22
+ rescue NoMethodError
23
+ puts 'good'
24
+ end
25
+
26
+ begin
27
+ '1'.__send__(:context)
28
+ raise 'context polluting Object.'
29
+ rescue NoMethodError
30
+ puts 'good'
31
+ end
32
+
33
+ # Make sure we can use Util::xxx::yyy directly
34
+ raise 'pack fail' unless Util::Packing.pack(1) == "\x01\0\0\0\0\0\0\0"
@@ -0,0 +1,19 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ # Make sure we're using local copy for local testing.
4
+ $LOAD_PATH.unshift File.expand_path(File.join(__FILE__, '..', '..', '..', 'lib'))
5
+
6
+ # TODO(Darkpi): Should we make sure ALL module works? (maybe we should).
7
+ require 'pwnlib/util/packing'
8
+
9
+ raise 'call from module fail' unless ::Pwnlib::Util::Packing.p8(0x61) == 'a'
10
+
11
+ include ::Pwnlib::Util::Packing::ClassMethods
12
+ raise 'include module and call fail' unless p8(0x61) == 'a'
13
+
14
+ begin
15
+ ::Pwnlib::Util::Packing.context
16
+ raise 'context public in Pwnlib module'
17
+ rescue NoMethodError
18
+ puts 'good'
19
+ end
@@ -0,0 +1,16 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'open3'
4
+
5
+ require 'test_helper'
6
+
7
+ class FullFileTest < MiniTest::Test
8
+ parallelize_me!
9
+ Dir['test/files/*.rb'].each do |f|
10
+ fn = File.basename(f, '.rb')
11
+ define_method("test_#{fn}") do
12
+ _, stderr, status = Open3.capture3('ruby', f, binmode: true)
13
+ assert(status.success?, stderr)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,72 @@
1
+ # encoding: ASCII-8BIT
2
+
3
+ require 'open3'
4
+
5
+ require 'tty-platform'
6
+
7
+ require 'test_helper'
8
+ require 'pwnlib/memleak'
9
+
10
+ class MemLeakTest < MiniTest::Test
11
+ def setup
12
+ @victim = IO.binread(File.expand_path('../data/victim32', __FILE__))
13
+ @leak = ::Pwnlib::MemLeak.new { |addr| @victim[addr] }
14
+ end
15
+
16
+ def test_find_elf_base_basic
17
+ assert_equal(0, @leak.find_elf_base(@victim.length * 2 / 3))
18
+ end
19
+
20
+ def test_find_elf_base_running
21
+ skip 'Only tested on linux' unless TTY::Platform.new.linux?
22
+ [32, 64].each do |b|
23
+ # TODO(hh): Use process instead of popen2
24
+ Open3.popen2(File.expand_path("../data/victim#{b}", __FILE__)) do |i, o, t|
25
+ main_ra = o.readline[2...-1].to_i(16)
26
+ realbase = nil
27
+ IO.readlines("/proc/#{t.pid}/maps").map(&:split).each do |s|
28
+ st, ed = s[0].split('-').map { |x| x.to_i(16) }
29
+ next unless main_ra.between?(st, ed)
30
+ realbase = st
31
+ break
32
+ end
33
+ refute_nil(realbase)
34
+ mem = open("/proc/#{t.pid}/mem", 'rb')
35
+ l2 = ::Pwnlib::MemLeak.new do |addr|
36
+ mem.seek(addr)
37
+ mem.getc
38
+ end
39
+ assert_equal(realbase, l2.find_elf_base(main_ra))
40
+ mem.close
41
+ i.write('bye')
42
+ end
43
+ end
44
+ end
45
+
46
+ def test_n
47
+ assert_equal("\x7fELF", @leak.n(0, 4))
48
+ assert_equal(@victim[0xf0, 0x20], @leak.n(0xf0, 0x20))
49
+ assert_equal(@victim[514, 0x20], @leak.n(514, 0x20))
50
+ end
51
+
52
+ def test_b
53
+ assert_equal(@victim[0x100], @leak.b(0x100))
54
+ assert_equal(@victim[514], @leak.b(514))
55
+ end
56
+
57
+ def test_w
58
+ assert_equal(::Pwnlib::Util::Packing.u16(@victim[0x100, 2]), @leak.w(0x100))
59
+ assert_equal(::Pwnlib::Util::Packing.u16(@victim[514, 2]), @leak.w(514))
60
+ end
61
+
62
+ def test_d
63
+ assert_equal(::Pwnlib::Util::Packing.u32(@victim[0, 4]), @leak.d(0))
64
+ assert_equal(::Pwnlib::Util::Packing.u32(@victim[0x100, 4]), @leak.d(0x100))
65
+ assert_equal(::Pwnlib::Util::Packing.u32(@victim[514, 4]), @leak.d(514))
66
+ end
67
+
68
+ def test_q
69
+ assert_equal(::Pwnlib::Util::Packing.u64(@victim[0x100, 8]), @leak.q(0x100))
70
+ assert_equal(::Pwnlib::Util::Packing.u64(@victim[514, 8]), @leak.q(514))
71
+ end
72
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: ASCII-8BIT
2
+ require 'test_helper'
3
+ require 'pwnlib/reg_sort'
4
+
5
+ class RegSortTest < MiniTest::Test
6
+ include ::Pwnlib::RegSort::ClassMethods
7
+
8
+ def setup
9
+ @regs = %w(a b c d x y z)
10
+ end
11
+
12
+ def test_normal
13
+ assert_equal([['mov', 'a', 1], ['mov', 'b', 2]], regsort({ a: 1, b: 2 }, @regs))
14
+ end
15
+
16
+ def test_post_mov
17
+ assert_equal([['mov', 'a', 1], %w(mov b a)], regsort({ a: 1, b: 1 }, @regs))
18
+ assert_equal([%w(mov c a), ['mov', 'a', 1], %w(mov b a)], regsort({ a: 1, b: 1, c: 'a' }, @regs))
19
+ end
20
+
21
+ def test_pseudoforest
22
+ # only one connected component
23
+ assert_equal([%w(mov b a), ['mov', 'a', 1]], regsort({ a: 1, b: 'a' }, @regs))
24
+ assert_equal([['mov', 'c', 3], %w(xchg a b)], regsort({ a: 'b', b: 'a', c: 3 }, @regs))
25
+ assert_equal([%w(mov c b), %w(xchg a b)], regsort({ a: 'b', b: 'a', c: 'b' }, @regs))
26
+ assert_equal([%w(mov x 1), %w(mov y z), %w(mov z c), %w(xchg a b), %w(xchg b c)],
27
+ regsort({ a: 'b', b: 'c', c: 'a', x: '1', y: 'z', z: 'c' }, @regs))
28
+
29
+ # more than one connected components
30
+ assert_equal([%w(xchg a b), %w(xchg c d)], regsort({ a: 'b', b: 'a', c: 'd', d: 'c' }, @regs))
31
+ assert_equal([%w(mov c b), %w(mov d b), %w(mov z x), %w(xchg a b), %w(xchg x y)],
32
+ regsort({ a: 'b', b: 'a', c: 'b', d: 'b', x: 'y', y: 'x', z: 'x' }, @regs))
33
+ end
34
+
35
+ def test_raise
36
+ err = assert_raises(ArgumentError) do
37
+ regsort({ a: 1 }, ['b'])
38
+ end
39
+ assert_match(/Unknown register!/, err.message)
40
+ end
41
+ end
@@ -0,0 +1,13 @@
1
+ require 'codeclimate-test-reporter'
2
+
3
+ require 'simplecov'
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new(
5
+ [SimpleCov::Formatter::HTMLFormatter, CodeClimate::TestReporter::Formatter]
6
+ )
7
+ SimpleCov.start do
8
+ add_filter '/test/'
9
+ end
10
+
11
+ require 'minitest/autorun'
12
+ require 'minitest/unit'
13
+ require 'minitest/hell'