cfg2asm 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|