plympton 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.document +5 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +24 -0
- data/Rakefile +48 -0
- data/VERSION +1 -0
- data/bin/func-auto.py +431 -0
- data/bin/func.py +435 -0
- data/bin/func.py.new +440 -0
- data/bin/idascript.py +21 -0
- data/lib/plympton.rb +26 -0
- data/lib/plympton/Solver.tokens +25 -0
- data/lib/plympton/SolverLexer.rb +704 -0
- data/lib/plympton/SolverParser.rb +550 -0
- data/lib/plympton/block.rb +19 -0
- data/lib/plympton/chunk.rb +20 -0
- data/lib/plympton/disassembly.rb +105 -0
- data/lib/plympton/function.rb +31 -0
- data/lib/plympton/matrix.rb +59 -0
- data/lib/plympton/object.rb +153 -0
- data/lib/plympton/solver.g +118 -0
- data/plympton.gemspec +95 -0
- data/spec/libFontParser.64.dylib.fz +152170 -0
- data/spec/libauto.dylib.fz +127001 -0
- data/spec/plympton_spec.rb +220 -0
- data/spec/rufus-test.32bit.trace.xml +53 -0
- data/spec/rufus-test.64bit.trace.xml +50 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/steady-state.64bit.trace.xml +12070 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c809128838470cc20b4ae9b7bac312bd736b33b
|
4
|
+
data.tar.gz: a770fb56e21f7c3f0bf1abda87a6873629f87094
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 57781fabc6343ce5c5e3fb35dea0ad55525694026d2bbfd2da24302276f2ae9f3af4696c97f45235046d84c0586304097912a3536cfe68c5a39509b52d386386
|
7
|
+
data.tar.gz: 6970e51b07c92beead632679de4b653ac6d0d4b82e1f690bb59a38600791049e5e6c6904036c07c2e1fc87038c967096c82c00da5ac1ebbcda70e01572a75eac
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://gemcutter.org"
|
2
|
+
|
3
|
+
# Add dependencies to develop your gem here.
|
4
|
+
# Include everything needed to run rake, tests, features, etc.
|
5
|
+
group :development do
|
6
|
+
gem "jeweler", "~> 2.0"
|
7
|
+
gem "yard", "~> 0.8"
|
8
|
+
gem "rspec", "~> 3.1"
|
9
|
+
gem "bundler", ">= 1.0"
|
10
|
+
end
|
11
|
+
|
12
|
+
gem 'nokogiri', '~> 1.6'
|
13
|
+
gem 'antlr3', '~> 1.10'
|
14
|
+
gem 'narray', '~> 0.6'
|
15
|
+
gem 'coveralls', require: false
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 rogwfu
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# plympton
|
2
|
+
|
3
|
+
A gem to read program disassembly from a YAML dump. The YAML dump is generated from an IDA Pro python script. This script is included along with this Gem (func.py)
|
4
|
+
|
5
|
+
## Status
|
6
|
+
[![Build Status](https://travis-ci.org/rogwfu/plympton.png)](https://travis-ci.org/rogwfu/plympton)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/rogwfu/plympton/badge.png)](https://coveralls.io/r/rogwfu/plympton)
|
8
|
+
[![Dependency Status](https://www.versioneye.com/user/projects/543603aab2a9c5dd3d000092/badge.svg?style=flat)](https://www.versioneye.com/user/projects/543603aab2a9c5dd3d000092)
|
9
|
+
|
10
|
+
## Contributing to plympton
|
11
|
+
|
12
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
13
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
14
|
+
* Fork the project
|
15
|
+
* Start a feature/bugfix branch
|
16
|
+
* Commit and push until you are happy with your contribution
|
17
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
18
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
19
|
+
|
20
|
+
## Copyright
|
21
|
+
|
22
|
+
Copyright (c) 2014 Roger Seagle. See LICENSE.txt for
|
23
|
+
further details.
|
24
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
require 'jeweler'
|
4
|
+
|
5
|
+
Jeweler::Tasks.new do |gem|
|
6
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
7
|
+
gem.name = "plympton"
|
8
|
+
gem.homepage = "http://github.com/rogwfu/plympton"
|
9
|
+
gem.license = "MIT"
|
10
|
+
gem.summary = %Q{Reads a YAML dump of a program's disassembly from IDA Pro}
|
11
|
+
gem.description = %Q{A Gem to read program disassembly from a YAML dump. The YAML dump is generated from an ida pro python script. This script is included along with this Gem (func.py)}
|
12
|
+
gem.email = "roger.seagle@gmail.com"
|
13
|
+
gem.authors = ["Roger Seagle"]
|
14
|
+
gem.required_ruby_version = '>= 1.9.3'
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
Jeweler::RubygemsDotOrgTasks.new
|
19
|
+
|
20
|
+
require 'rspec/core'
|
21
|
+
require 'rspec/core/rake_task'
|
22
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
23
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
27
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
28
|
+
spec.rcov = true
|
29
|
+
end
|
30
|
+
|
31
|
+
task :default => :spec
|
32
|
+
|
33
|
+
#require 'rake/rdoctask'
|
34
|
+
require 'rdoc/task'
|
35
|
+
Rake::RDocTask.new do |rdoc|
|
36
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
37
|
+
|
38
|
+
rdoc.rdoc_dir = 'rdoc'
|
39
|
+
rdoc.title = "plympton #{version}"
|
40
|
+
rdoc.rdoc_files.include('README*')
|
41
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
42
|
+
end
|
43
|
+
|
44
|
+
# Rake Task for building yard doc
|
45
|
+
require 'yard'
|
46
|
+
YARD::Rake::YardocTask.new do |t|
|
47
|
+
t.options = ['--exclude', 'bin/*']
|
48
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.0
|
data/bin/func-auto.py
ADDED
@@ -0,0 +1,431 @@
|
|
1
|
+
import yaml
|
2
|
+
import idascript
|
3
|
+
import idc
|
4
|
+
|
5
|
+
class Object(yaml.YAMLObject):
|
6
|
+
yaml_tag = u'!fuzz.io,2011/Object'
|
7
|
+
def __init__(self, textSegmentStart, textSegmentEnd):
|
8
|
+
self.name = GetInputFilePath()
|
9
|
+
self.functionList = []
|
10
|
+
self.importList = []
|
11
|
+
self.textSegmentStart = textSegmentStart
|
12
|
+
self.textSegmentEnd = textSegmentEnd
|
13
|
+
|
14
|
+
#
|
15
|
+
# Pull out all information about functions
|
16
|
+
#
|
17
|
+
self.initialize_functions()
|
18
|
+
|
19
|
+
self.numFunctions = len(self.functionList)
|
20
|
+
self.numImports = len(self.importList)
|
21
|
+
self.numBlocks = self.iter_functions()
|
22
|
+
self.textSegmentStart = hex(textSegmentStart)
|
23
|
+
self.textSegmentEnd = hex(textSegmentEnd)
|
24
|
+
|
25
|
+
def initialize_functions(self):
|
26
|
+
#
|
27
|
+
# Iterate through all of the functions
|
28
|
+
#
|
29
|
+
for fn in Functions(self.textSegmentStart, self.textSegmentEnd):
|
30
|
+
tmp = Function(fn)
|
31
|
+
if not tmp.isImport:
|
32
|
+
self.functionList.append(tmp)
|
33
|
+
else:
|
34
|
+
self.importList.append(tmp)
|
35
|
+
|
36
|
+
def iter_functions(self):
|
37
|
+
blockCount = 0
|
38
|
+
for fn in self.functionList:
|
39
|
+
blockCount = blockCount + fn.iter_chunks()
|
40
|
+
|
41
|
+
return(blockCount)
|
42
|
+
|
43
|
+
#
|
44
|
+
# Create a class for a function
|
45
|
+
#
|
46
|
+
class Function(yaml.YAMLObject):
|
47
|
+
yaml_tag = u'!fuzz.io,2011/Function'
|
48
|
+
def __init__(self, effectiveAddress):
|
49
|
+
self.name = Name(effectiveAddress)
|
50
|
+
self.argSize = 0
|
51
|
+
self.numArgs = 0
|
52
|
+
self.numLocalVars = 0
|
53
|
+
self.isImport = False
|
54
|
+
self.chunkList = []
|
55
|
+
self.numChunks = 0
|
56
|
+
self.cyclomaticComplexity = 0
|
57
|
+
|
58
|
+
#
|
59
|
+
# Get the function flags, structure, and frame info
|
60
|
+
#
|
61
|
+
flags = GetFunctionFlags(effectiveAddress)
|
62
|
+
funcStruct = idaapi.get_func(effectiveAddress)
|
63
|
+
frameStruct = idaapi.get_frame(funcStruct)
|
64
|
+
|
65
|
+
# if we're not in a "real" function. set the id and ea_start manually and stop analyzing.
|
66
|
+
if not funcStruct or flags & FUNC_LIB or flags & FUNC_STATIC:
|
67
|
+
self.startAddress = hex(effectiveAddress)
|
68
|
+
self.endAddress = hex(effectiveAddress)
|
69
|
+
self.name = idaapi.get_name(effectiveAddress, effectiveAddress)
|
70
|
+
self.savedRegSize = 0
|
71
|
+
self.localVarSize = 0
|
72
|
+
self.frameSize = 0
|
73
|
+
self.retSize = 0
|
74
|
+
self.isImport = True
|
75
|
+
|
76
|
+
#
|
77
|
+
# Need to fix these if possible
|
78
|
+
#
|
79
|
+
self.argSize = 0
|
80
|
+
self.numArgs = 0
|
81
|
+
self.numLocalVars = 0
|
82
|
+
return
|
83
|
+
|
84
|
+
#
|
85
|
+
# So we know we're in a real function
|
86
|
+
#
|
87
|
+
self.startAddress = funcStruct.startEA
|
88
|
+
self.endAddress = hex(PrevAddr(funcStruct.endEA))
|
89
|
+
self.savedRegSize = funcStruct.frregs
|
90
|
+
self.localVarSize = funcStruct.frsize
|
91
|
+
self.frameSize = idaapi.get_frame_size(funcStruct)
|
92
|
+
self.retSize = idaapi.get_frame_retsize(funcStruct)
|
93
|
+
|
94
|
+
print("=========================================")
|
95
|
+
print("Frame Size %d" % self.frameSize)
|
96
|
+
print("EIP Return Size %d" % self.retSize)
|
97
|
+
print("Saved Registers Size %d" % self.savedRegSize)
|
98
|
+
print("Locals Size %d" % self.localVarSize)
|
99
|
+
print("=========================================")
|
100
|
+
print("Function name: %s" % self.name)
|
101
|
+
|
102
|
+
#
|
103
|
+
# Fixup numbers for arguments and local variables
|
104
|
+
#
|
105
|
+
self.__init_args_and_local_vars__(funcStruct, frameStruct)
|
106
|
+
|
107
|
+
#
|
108
|
+
# Initialize chunks
|
109
|
+
#
|
110
|
+
self.collect_function_chunks()
|
111
|
+
self.cyclomaticComplexity = self.calculate_cyclomatic_complexity(self.startAddress)
|
112
|
+
self.startAddress = hex(self.startAddress)
|
113
|
+
|
114
|
+
def calculate_cyclomatic_complexity (self, function_ea):
|
115
|
+
'''Calculate the cyclomatic complexity measure for a function.
|
116
|
+
|
117
|
+
Given the starting address of a function, it will find all
|
118
|
+
the basic block's boundaries and edges between them and will
|
119
|
+
return the cyclomatic complexity, defined as:
|
120
|
+
|
121
|
+
CC = Edges - Nodes + 2
|
122
|
+
http://www.openrce.org/articles/full_view/11
|
123
|
+
'''
|
124
|
+
|
125
|
+
f_start = function_ea
|
126
|
+
f_end = FindFuncEnd(function_ea)
|
127
|
+
|
128
|
+
edges = set([])
|
129
|
+
boundaries = set((f_start,))
|
130
|
+
|
131
|
+
# For each defined element in the function.
|
132
|
+
for head in Heads(f_start, f_end):
|
133
|
+
|
134
|
+
# If the element is an instruction
|
135
|
+
if isCode(GetFlags(head)):
|
136
|
+
|
137
|
+
# Get the references made from the current instruction
|
138
|
+
# and keep only the ones local to the function.
|
139
|
+
refs = CodeRefsFrom(head, 0)
|
140
|
+
refs = set(filter(lambda x: x>=f_start and x<=f_end, refs))
|
141
|
+
|
142
|
+
if refs:
|
143
|
+
# If the flow continues also to the next (address-wise)
|
144
|
+
# instruction, we add a reference to it.
|
145
|
+
# For instance, a conditional jump will not branch
|
146
|
+
# if the condition is not met, so we save that
|
147
|
+
# reference as well.
|
148
|
+
next_head = NextHead(head, f_end)
|
149
|
+
if isFlow(GetFlags(next_head)):
|
150
|
+
refs.add(next_head)
|
151
|
+
|
152
|
+
# Update the boundaries found so far.
|
153
|
+
boundaries.update(refs)
|
154
|
+
|
155
|
+
# For each of the references found, and edge is
|
156
|
+
# created.
|
157
|
+
for r in refs:
|
158
|
+
# If the flow could also come from the address
|
159
|
+
# previous to the destination of the branching
|
160
|
+
# an edge is created.
|
161
|
+
if isFlow(GetFlags(r)):
|
162
|
+
edges.add((PrevHead(r, f_start), r))
|
163
|
+
edges.add((head, r))
|
164
|
+
|
165
|
+
return len(edges) - len(boundaries) + 2
|
166
|
+
|
167
|
+
def __init_args_and_local_vars__ (self, funcStruct, frameStruct):
|
168
|
+
'''
|
169
|
+
Calculate the total size of arguments, # of arguments and # of local variables. Update the internal class member
|
170
|
+
variables appropriately. Taken directly from paimei
|
171
|
+
'''
|
172
|
+
# Initialize some local variables
|
173
|
+
args = {}
|
174
|
+
local_vars = {}
|
175
|
+
|
176
|
+
if not frameStruct:
|
177
|
+
return
|
178
|
+
|
179
|
+
argument_boundary = self.frameSize
|
180
|
+
frame_offset = frameStruct.get_member(0).soff
|
181
|
+
|
182
|
+
|
183
|
+
for i in xrange(0, frameStruct.memqty):
|
184
|
+
end_offset = frameStruct.get_member(i).soff
|
185
|
+
|
186
|
+
if i == frameStruct.memqty - 1:
|
187
|
+
begin_offset = frameStruct.get_member(i).eoff
|
188
|
+
else:
|
189
|
+
begin_offset = frameStruct.get_member(i+1).soff
|
190
|
+
|
191
|
+
frame_offset += (begin_offset - end_offset)
|
192
|
+
|
193
|
+
# grab the name of the current local variable or argument.
|
194
|
+
name = idaapi.get_member_name(frameStruct.get_member(i).id)
|
195
|
+
print("=============================")
|
196
|
+
print("Name: %s" % name)
|
197
|
+
# print "Agument Boundary: %d" % argument_boundary
|
198
|
+
# print "Frame offset: %d" % frame_offset
|
199
|
+
print("End offset: %d" % end_offset)
|
200
|
+
print("Begin Offset: %d" % begin_offset)
|
201
|
+
# print("Frame offset: %d\n" % frame_offset)
|
202
|
+
print("=============================")
|
203
|
+
|
204
|
+
if name == None:
|
205
|
+
continue
|
206
|
+
|
207
|
+
if frame_offset > argument_boundary:
|
208
|
+
args[end_offset] = name
|
209
|
+
# if name.startswith("arg_"):
|
210
|
+
# args[end_offset] = name
|
211
|
+
# self.argSize = self.argSize + (begin_offset - end_offset)
|
212
|
+
else:
|
213
|
+
# if the name starts with a space, then ignore it as it is either the stack saved ebp or eip.
|
214
|
+
# XXX - this is a pretty ghetto check.
|
215
|
+
if not name.startswith(" "):
|
216
|
+
local_vars[end_offset] = name
|
217
|
+
self.argSize = frame_offset - argument_boundary
|
218
|
+
self.numArgs = len(args)
|
219
|
+
self.numLocalVars = len(local_vars)
|
220
|
+
|
221
|
+
def iter_chunks(self):
|
222
|
+
chunkBlockCount = 0
|
223
|
+
for ch in self.chunkList:
|
224
|
+
chunkBlockCount = chunkBlockCount + len(ch.blockList)
|
225
|
+
|
226
|
+
return(chunkBlockCount)
|
227
|
+
|
228
|
+
def collect_function_chunks(self):
|
229
|
+
'''
|
230
|
+
Generate and return the list of function chunks (including the main one) for the current function. Ripped from idb2reml (Ero Carerra). Modified slightly by Roger Seagle.
|
231
|
+
|
232
|
+
@rtype: None
|
233
|
+
@return: None
|
234
|
+
'''
|
235
|
+
|
236
|
+
#
|
237
|
+
# Loop through all chunks for a function
|
238
|
+
#
|
239
|
+
iterator = idaapi.func_tail_iterator_t(idaapi.get_func(self.startAddress))
|
240
|
+
status = iterator.main()
|
241
|
+
|
242
|
+
while status:
|
243
|
+
chunk = iterator.chunk()
|
244
|
+
tmp = Chunk(chunk)
|
245
|
+
self.chunkList.append(tmp)
|
246
|
+
status = iterator.next()
|
247
|
+
|
248
|
+
#
|
249
|
+
# Create a class for a basic block
|
250
|
+
#
|
251
|
+
class Chunk(yaml.YAMLObject):
|
252
|
+
yaml_tag = u'!fuzz.io,2011/Chunk'
|
253
|
+
def __init__(self, chunk):
|
254
|
+
self.startEA = chunk.startEA
|
255
|
+
self.endEA = chunk.endEA
|
256
|
+
self.blockList = []
|
257
|
+
self.numBlocks = 0
|
258
|
+
|
259
|
+
#
|
260
|
+
# Just to get it started
|
261
|
+
#
|
262
|
+
block_start = self.startEA
|
263
|
+
|
264
|
+
#
|
265
|
+
# Might be a bug? (effective address that has code mixed in and no ret instruction
|
266
|
+
# Or effective address just calls exit (there is no return instruction!!!!)
|
267
|
+
#
|
268
|
+
|
269
|
+
#
|
270
|
+
# Break down the chunk into blocks
|
271
|
+
#
|
272
|
+
for effectiveAddress in Heads(self.startEA, self.endEA):
|
273
|
+
|
274
|
+
#
|
275
|
+
# Ignore Head if data
|
276
|
+
#
|
277
|
+
if not isCode(GetFlags(effectiveAddress)):
|
278
|
+
continue
|
279
|
+
|
280
|
+
prev_ea = PrevNotTail(effectiveAddress)
|
281
|
+
next_ea = NextNotTail(effectiveAddress)
|
282
|
+
|
283
|
+
#
|
284
|
+
# Get the list of places branched to and from
|
285
|
+
#
|
286
|
+
branchesTo = self._branches_to(effectiveAddress)
|
287
|
+
branchesFrom = self._branches_from(effectiveAddress)
|
288
|
+
|
289
|
+
|
290
|
+
# ensure that both prev_ea and next_ea reference code and not data.
|
291
|
+
while not isCode(GetFlags(prev_ea)):
|
292
|
+
prev_ea = PrevNotTail(prev_ea)
|
293
|
+
|
294
|
+
while not isCode(GetFlags(next_ea)):
|
295
|
+
next_ea = PrevNotTail(next_ea)
|
296
|
+
|
297
|
+
# if the current instruction is a ret instruction, end the current node at ea.
|
298
|
+
if idaapi.is_ret_insn(effectiveAddress):
|
299
|
+
tmp = Block(block_start, effectiveAddress, branchesTo, branchesFrom)
|
300
|
+
self.blockList.append(tmp)
|
301
|
+
block_start = next_ea
|
302
|
+
|
303
|
+
elif branchesTo and block_start != effectiveAddress:
|
304
|
+
tmp = Block(block_start, effectiveAddress, branchesTo, branchesFrom)
|
305
|
+
self.blockList.append(tmp)
|
306
|
+
|
307
|
+
# start a new block at ea.
|
308
|
+
block_start = effectiveAddress
|
309
|
+
|
310
|
+
# if there is a branch from the current instruction, end the current node at ea.
|
311
|
+
elif branchesFrom:
|
312
|
+
tmp = Block(block_start, effectiveAddress, branchesTo, branchesFrom)
|
313
|
+
self.blockList.append(tmp)
|
314
|
+
|
315
|
+
# start a new block at the next ea
|
316
|
+
block_start = next_ea
|
317
|
+
|
318
|
+
#
|
319
|
+
# Calculate the number of blocks
|
320
|
+
#
|
321
|
+
self.numBlocks = len(self.blockList)
|
322
|
+
|
323
|
+
#
|
324
|
+
# Covert addresses to hex
|
325
|
+
#
|
326
|
+
self.startEA = hex(self.startEA)
|
327
|
+
self.endEA = hex(self.endEA)
|
328
|
+
|
329
|
+
####################################################################################################################
|
330
|
+
def _branches_from (self, ea):
|
331
|
+
'''
|
332
|
+
Enumerate and return the list of branches from the supplied address, *including* the next logical instruction.
|
333
|
+
Part of the reason why we even need this function is that the "flow" argument to CodeRefsFrom does not appear
|
334
|
+
to be functional.
|
335
|
+
|
336
|
+
@type ea: DWORD
|
337
|
+
@param ea: Effective address of instruction to enumerate jumps from.
|
338
|
+
|
339
|
+
@rtype: List
|
340
|
+
@return: List of branches from the specified address.
|
341
|
+
'''
|
342
|
+
|
343
|
+
if idaapi.is_call_insn(ea):
|
344
|
+
return []
|
345
|
+
|
346
|
+
xrefs = list(CodeRefsFrom(ea, 1))
|
347
|
+
|
348
|
+
# if the only xref from ea is next ea, then return nothing.
|
349
|
+
if len(xrefs) == 1 and xrefs[0] == NextNotTail(ea):
|
350
|
+
xrefs = []
|
351
|
+
|
352
|
+
return xrefs
|
353
|
+
|
354
|
+
|
355
|
+
####################################################################################################################
|
356
|
+
def _branches_to (self, ea):
|
357
|
+
'''
|
358
|
+
Enumerate and return the list of branches to the supplied address, *excluding* the previous logical instruction.
|
359
|
+
Part of the reason why we even need this function is that the "flow" argument to CodeRefsTo does not appear to
|
360
|
+
be functional.
|
361
|
+
|
362
|
+
@type ea: DWORD
|
363
|
+
@param ea: Effective address of instruction to enumerate jumps to.
|
364
|
+
|
365
|
+
@rtype: List
|
366
|
+
@return: List of branches to the specified address.
|
367
|
+
'''
|
368
|
+
|
369
|
+
xrefs = []
|
370
|
+
prev_ea = PrevNotTail(ea)
|
371
|
+
prev_code_ea = prev_ea
|
372
|
+
|
373
|
+
while not isCode(GetFlags(prev_code_ea)):
|
374
|
+
prev_code_ea = PrevNotTail(prev_code_ea)
|
375
|
+
|
376
|
+
for xref in list(CodeRefsTo(ea, 1)):
|
377
|
+
if not idaapi.is_call_insn(xref) and xref not in [prev_ea, prev_code_ea]:
|
378
|
+
xrefs.append(hex(xref))
|
379
|
+
|
380
|
+
return xrefs
|
381
|
+
|
382
|
+
#
|
383
|
+
# Create a class for a basic block
|
384
|
+
#
|
385
|
+
class Block(yaml.YAMLObject):
|
386
|
+
yaml_tag = u'!fuzz.io,2011/Block'
|
387
|
+
def __init__(self, effectiveAddressStart, effectiveAddressEnd, branchesTo, branchesFrom):
|
388
|
+
self.startEA = hex(effectiveAddressStart)
|
389
|
+
self.endEA = hex(effectiveAddressEnd)
|
390
|
+
self.branchTo = branchesTo
|
391
|
+
branchFr = []
|
392
|
+
|
393
|
+
#
|
394
|
+
# Covert branches to hex addresses
|
395
|
+
#
|
396
|
+
for i in range(len(branchesFrom)):
|
397
|
+
branchFr.append(hex(branchesFrom[i]))
|
398
|
+
|
399
|
+
self.branchFrom = branchFr
|
400
|
+
|
401
|
+
#
|
402
|
+
# Get the number of instructions in the block
|
403
|
+
#
|
404
|
+
heads = [head for head in Heads(effectiveAddressStart, effectiveAddressEnd + 1) if isCode(GetFlags(head))]
|
405
|
+
self.numInstructions = len(heads)
|
406
|
+
|
407
|
+
# Wait for the analysis to stop
|
408
|
+
idaapi.autoWait()
|
409
|
+
|
410
|
+
# Create the filenames to dump and log
|
411
|
+
yamlFilename = os.environ['PWD'] + "/" + GetInputFile() + ".fz"
|
412
|
+
|
413
|
+
# Open the file
|
414
|
+
yamlFile = open(yamlFilename, 'w')
|
415
|
+
|
416
|
+
# Get the start and end of the text section
|
417
|
+
textSegmentSelector = SegByName("__text")
|
418
|
+
textSegmentStart = SegByBase(textSegmentSelector)
|
419
|
+
textSegmentEnd = SegEnd(textSegmentStart)
|
420
|
+
|
421
|
+
# Pull out all the information
|
422
|
+
disassembledObject = Object(textSegmentStart, textSegmentEnd)
|
423
|
+
|
424
|
+
# Dump the disassembled Object info in a portable format
|
425
|
+
yaml.dump(disassembledObject, yamlFile, default_flow_style=False)
|
426
|
+
|
427
|
+
# Be nice close the file
|
428
|
+
yamlFile.close()
|
429
|
+
|
430
|
+
# Exit IDA Pro
|
431
|
+
Exit(0)
|