cfg2asm 0.1
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.
- checksums.yaml +7 -0
- data/bin/cfg2asm +20 -0
- data/lib/cfg2asm/cfg/cfg_parser.rb +93 -0
- data/lib/cfg2asm/cfg/disassembler.rb +70 -0
- data/lib/cfg2asm/commands.rb +62 -0
- data/lib/cfg2asm/version.rb +5 -0
- data/lib/cfg2asm.rb +4 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9fe9c7d9795d226dbb31a45071c764468a29e04d416c060145660acd79c1ccf0
|
4
|
+
data.tar.gz: dcac08b911bc46c252629bf150079d81c648d02f0c6be93b36612a5b344907d5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b1c86cc939e77ae3ee16fb0ba796dcc2f6a0bae19287f88f2a121cb125bed6a1fcac8e7030a04aa212cfcea2bb6b07b8f45dbfdbbe398af18b877eff5cfdf24
|
7
|
+
data.tar.gz: 5b3433186dbf48aab5d00b817f4ba3151ee663394dfac98c22cee9ac57e1e8bcfd456abdd381f05693b8a429751c16856eb3b2dde9a4ae8e725cf938e26d9bc3
|
data/bin/cfg2asm
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'cfg2asm'
|
4
|
+
|
5
|
+
# This is the 'cfg2asm' command line entry point.
|
6
|
+
|
7
|
+
begin
|
8
|
+
# Run the command line.
|
9
|
+
commands = Cfg2asm::Commands.new($stdout)
|
10
|
+
commands.cfg2asm(*ARGV)
|
11
|
+
rescue StandardError => e
|
12
|
+
if $DEBUG
|
13
|
+
# Re-raise the exception so the user sees it, if debugging is
|
14
|
+
# enabled (ruby -d).
|
15
|
+
raise e
|
16
|
+
else
|
17
|
+
# Otherwise, just print the message.
|
18
|
+
warn "seafoam: #{e.message}"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'zlib'
|
3
|
+
|
4
|
+
module Cfg2asm
|
5
|
+
module CFG
|
6
|
+
Code = Struct.new(:arch, :arch_width, :base, :code)
|
7
|
+
Comment = Struct.new(:offset, :comment)
|
8
|
+
NMethod = Struct.new(:code, :comments)
|
9
|
+
|
10
|
+
# A parser for CFG files.
|
11
|
+
class CFGParser
|
12
|
+
def initialize(out, file)
|
13
|
+
@out = out
|
14
|
+
data = File.read(file, encoding: Encoding::ASCII_8BIT)
|
15
|
+
if data[0..1].bytes == [0x1f, 0x8b]
|
16
|
+
data = Zlib.gunzip(data)
|
17
|
+
end
|
18
|
+
@reader = StringIO.new(data)
|
19
|
+
@state = :any
|
20
|
+
@cfg_name = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def skip_over_cfg(name)
|
24
|
+
loop do
|
25
|
+
line = @reader.readline("\n")
|
26
|
+
case line
|
27
|
+
when "begin_cfg\n"
|
28
|
+
@state = :cfg
|
29
|
+
@cfg_name = nil
|
30
|
+
when / name "(.*)"\n/
|
31
|
+
if @state == :cfg
|
32
|
+
@cfg_name = Regexp.last_match(1)
|
33
|
+
end
|
34
|
+
when "end_cfg\n"
|
35
|
+
raise unless @state == :cfg
|
36
|
+
|
37
|
+
@state = :any
|
38
|
+
break if @cfg_name == name
|
39
|
+
else
|
40
|
+
next
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def read_nmethod
|
46
|
+
raise unless @state == :any
|
47
|
+
|
48
|
+
arch = nil
|
49
|
+
arch_width = nil
|
50
|
+
code = nil
|
51
|
+
comments = []
|
52
|
+
raise unless @reader.readline == "begin_nmethod\n"
|
53
|
+
|
54
|
+
loop do
|
55
|
+
line = @reader.readline("\n")
|
56
|
+
case line
|
57
|
+
when / Platform (.*) (.*) <\|\|@\n/
|
58
|
+
arch = Regexp.last_match(1)
|
59
|
+
arch_width = Regexp.last_match(2)
|
60
|
+
when / HexCode (.*) (.*) <\|\|@\n/
|
61
|
+
base = Regexp.last_match(1).to_i(16)
|
62
|
+
code = [Regexp.last_match(2)].pack('H*')
|
63
|
+
raise if arch.nil? || arch_width.nil?
|
64
|
+
|
65
|
+
code = Code.new(arch, arch_width, base, code)
|
66
|
+
when / Comment (\d*) (.*) <\|\|@\n/
|
67
|
+
offset = Regexp.last_match(1).to_i
|
68
|
+
comment = Regexp.last_match(2)
|
69
|
+
comments.push Comment.new(offset, comment)
|
70
|
+
when " <<<HexCodeFile\n"
|
71
|
+
next
|
72
|
+
when " HexCodeFile>>> <|@\n"
|
73
|
+
next
|
74
|
+
when "end_nmethod\n"
|
75
|
+
break
|
76
|
+
when / (.*) <\|\|@\n/
|
77
|
+
offset = -1
|
78
|
+
comment = Regexp.last_match(1)
|
79
|
+
comments.push Comment.new(offset, comment)
|
80
|
+
when / (.*)\n/
|
81
|
+
offset = -1
|
82
|
+
comment = Regexp.last_match(1)
|
83
|
+
comments.push Comment.new(offset, comment)
|
84
|
+
else
|
85
|
+
# In case anything was missed
|
86
|
+
raise 'There is currently no case for this line. Please open an issue so it can be addressed.'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
NMethod.new(code, comments)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module Cfg2asm
|
4
|
+
module CFG
|
5
|
+
# Disassemble and print comments from cfg files
|
6
|
+
class Disassembler
|
7
|
+
def initialize(out)
|
8
|
+
@out = out
|
9
|
+
end
|
10
|
+
|
11
|
+
def disassemble(nmethod, print_comments)
|
12
|
+
require_crabstone
|
13
|
+
|
14
|
+
comments = nmethod.comments
|
15
|
+
comments_n = 0
|
16
|
+
|
17
|
+
case [nmethod.code.arch, nmethod.code.arch_width]
|
18
|
+
when %w[AMD64 64]
|
19
|
+
crabstone_arch = [Crabstone::ARCH_X86, Crabstone::MODE_64]
|
20
|
+
else
|
21
|
+
raise "Unknown architecture #{nmethod.code.arch} and bit width #{nmethod.code.arch_width}"
|
22
|
+
end
|
23
|
+
|
24
|
+
cs = Crabstone::Disassembler.new(*crabstone_arch)
|
25
|
+
begin
|
26
|
+
cs.disasm(nmethod.code.code, nmethod.code.base).each do |i|
|
27
|
+
if print_comments
|
28
|
+
# Print comments associated to the instruction.
|
29
|
+
last_comment = i.address + i.bytes.length - nmethod.code.base
|
30
|
+
while comments_n < comments.length && comments[comments_n].offset < last_comment
|
31
|
+
if comments[comments_n].offset == -1
|
32
|
+
@out.printf("\t\t\t\t;%<comment>s\n", comment: comments[comments_n].comment)
|
33
|
+
else
|
34
|
+
@out.printf(
|
35
|
+
"\t\t\t\t;Comment %<loc>i:\t%<comment>s\n",
|
36
|
+
loc: comments[comments_n].offset,
|
37
|
+
comment: comments[comments_n].comment
|
38
|
+
)
|
39
|
+
end
|
40
|
+
comments_n += 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Print the instruction.
|
45
|
+
@out.printf(
|
46
|
+
"\t0x%<address>x:\t%<instruction>s\t%<details>s\n",
|
47
|
+
address: i.address,
|
48
|
+
instruction: i.mnemonic,
|
49
|
+
details: i.op_str
|
50
|
+
)
|
51
|
+
end
|
52
|
+
rescue StandardError => e
|
53
|
+
raise "Disassembly error: #{e.message}"
|
54
|
+
ensure
|
55
|
+
cs.close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def require_crabstone
|
60
|
+
require 'crabstone'
|
61
|
+
rescue LoadError => e
|
62
|
+
if $DEBUG
|
63
|
+
raise e
|
64
|
+
else
|
65
|
+
raise 'Could not load Capstone - is it installed?'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Cfg2asm
|
5
|
+
# Implementations of the command-line commands that you can run in Seafoam.
|
6
|
+
class Commands
|
7
|
+
def initialize(out)
|
8
|
+
@out = out
|
9
|
+
end
|
10
|
+
|
11
|
+
def cfg2asm(*args)
|
12
|
+
case args.first
|
13
|
+
when nil, 'help', '-h', '--help', '-help'
|
14
|
+
args = args.drop(1)
|
15
|
+
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
16
|
+
|
17
|
+
@out.puts 'cfg2asm file.bgv...'
|
18
|
+
@out.puts ' --no-comments'
|
19
|
+
@out.puts ' --help'
|
20
|
+
@out.puts ' --version'
|
21
|
+
when 'version', '-v', '-version', '--version'
|
22
|
+
args = args.drop(1)
|
23
|
+
version(*args)
|
24
|
+
else
|
25
|
+
comments = true
|
26
|
+
files = []
|
27
|
+
|
28
|
+
until args.empty?
|
29
|
+
arg = args.shift
|
30
|
+
if arg.start_with?('-')
|
31
|
+
case arg
|
32
|
+
when '--no-comments'
|
33
|
+
comments = false
|
34
|
+
else
|
35
|
+
raise ArgumentError, "unknown option #{arg}"
|
36
|
+
end
|
37
|
+
else
|
38
|
+
files.push arg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
files.each_with_index do |file, n|
|
43
|
+
parser = Cfg2asm::CFG::CFGParser.new(@out, file)
|
44
|
+
parser.skip_over_cfg 'After code installation'
|
45
|
+
nmethod = parser.read_nmethod
|
46
|
+
|
47
|
+
disassembler = Cfg2asm::CFG::Disassembler.new(@out)
|
48
|
+
@out.puts if n.positive?
|
49
|
+
@out.puts "[#{file}]"
|
50
|
+
disassembler.disassemble(nmethod, comments)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Prints the version.
|
56
|
+
def version(*args)
|
57
|
+
raise ArgumentError, "unexpected arguments #{args.join(' ')}" unless args.empty?
|
58
|
+
|
59
|
+
@out.puts "cfg2asm #{VERSION}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/lib/cfg2asm.rb
ADDED
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cfg2asm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Seaton
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-02-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: crabstone
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '4.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 13.0.6
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 13.0.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.81'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.81'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
executables:
|
72
|
+
- cfg2asm
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- bin/cfg2asm
|
77
|
+
- lib/cfg2asm.rb
|
78
|
+
- lib/cfg2asm/cfg/cfg_parser.rb
|
79
|
+
- lib/cfg2asm/cfg/disassembler.rb
|
80
|
+
- lib/cfg2asm/commands.rb
|
81
|
+
- lib/cfg2asm/version.rb
|
82
|
+
homepage: https://github.com/Shopify/cfg2asm
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.3.7
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.3.3
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: A tool for disassembling Graal CFG files
|
105
|
+
test_files: []
|