NCPrePatcher 0.2.0-x64-mingw-ucrt
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/LICENSE.txt +674 -0
- data/README.md +66 -0
- data/example/README.md +3 -0
- data/example/disasm.rb +34 -0
- data/exe/ncpp +4 -0
- data/lib/ncpp/commands.rb +903 -0
- data/lib/ncpp/interpreter.rb +919 -0
- data/lib/ncpp/parser.rb +249 -0
- data/lib/ncpp/types.rb +68 -0
- data/lib/ncpp/utils.rb +700 -0
- data/lib/ncpp/version.rb +4 -0
- data/lib/ncpp.rb +478 -0
- data/lib/nitro/nitro.dll +0 -0
- data/lib/nitro/nitro.rb +440 -0
- data/lib/unarm/unarm.dll +0 -0
- data/lib/unarm/unarm.rb +836 -0
- metadata +91 -0
data/lib/unarm/unarm.rb
ADDED
|
@@ -0,0 +1,836 @@
|
|
|
1
|
+
require 'ffi'
|
|
2
|
+
|
|
3
|
+
module UnarmBind
|
|
4
|
+
extend FFI::Library
|
|
5
|
+
ffi_lib [
|
|
6
|
+
File.expand_path("unarm", __dir__),
|
|
7
|
+
File.expand_path("unarm.dylib", __dir__),
|
|
8
|
+
File.expand_path("unarm.so", __dir__),
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
typedef :pointer, :ins_handle
|
|
12
|
+
typedef :pointer, :cstr_handle
|
|
13
|
+
typedef :pointer, :parser_handle
|
|
14
|
+
typedef :pointer, :symbols_handle
|
|
15
|
+
typedef :pointer, :ins_args_handle
|
|
16
|
+
|
|
17
|
+
attach_function :arm9_new_arm_ins, [:uint32], :ins_handle
|
|
18
|
+
attach_function :arm9_new_thumb_ins, [:uint32], :ins_handle
|
|
19
|
+
attach_function :arm7_new_arm_ins, [:uint32], :ins_handle
|
|
20
|
+
attach_function :arm7_new_thumb_ins, [:uint32], :ins_handle
|
|
21
|
+
|
|
22
|
+
attach_function :arm9_new_parser, [:uint32], :parser_handle
|
|
23
|
+
attach_function :arm7_new_parser, [:uint32], :parser_handle
|
|
24
|
+
|
|
25
|
+
attach_function :arm9_arm_ins_to_str, [:ins_handle], :cstr_handle
|
|
26
|
+
attach_function :arm7_arm_ins_to_str, [:ins_handle], :cstr_handle
|
|
27
|
+
attach_function :arm9_thumb_ins_to_str, [:ins_handle], :cstr_handle
|
|
28
|
+
attach_function :arm7_thumb_ins_to_str, [:ins_handle], :cstr_handle
|
|
29
|
+
|
|
30
|
+
attach_function :arm9_arm_ins_to_str_with_syms,
|
|
31
|
+
[:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
|
|
32
|
+
attach_function :arm7_arm_ins_to_str_with_syms,
|
|
33
|
+
[:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
|
|
34
|
+
attach_function :arm9_thumb_ins_to_str_with_syms,
|
|
35
|
+
[:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
|
|
36
|
+
attach_function :arm7_thumb_ins_to_str_with_syms,
|
|
37
|
+
[:ins_handle, :symbols_handle, :uint32, :uint32, :int32], :cstr_handle
|
|
38
|
+
|
|
39
|
+
attach_function :arm9_arm_ins_get_args, [:ins_handle], :ins_args_handle
|
|
40
|
+
attach_function :arm7_arm_ins_get_args, [:ins_handle], :ins_args_handle
|
|
41
|
+
attach_function :arm9_thumb_ins_get_args, [:ins_handle], :ins_args_handle
|
|
42
|
+
attach_function :arm7_thumb_ins_get_args, [:ins_handle], :ins_args_handle
|
|
43
|
+
|
|
44
|
+
attach_function :arm9_arm_ins_get_opcode_id, [:ins_handle], :uint16
|
|
45
|
+
attach_function :arm7_arm_ins_get_opcode_id, [:ins_handle], :uint16
|
|
46
|
+
attach_function :arm9_thumb_ins_get_opcode_id, [:ins_handle], :uint16
|
|
47
|
+
attach_function :arm7_thumb_ins_get_opcode_id, [:ins_handle], :uint16
|
|
48
|
+
|
|
49
|
+
attach_function :arm_ins_is_conditional, [:ins_handle], :bool
|
|
50
|
+
attach_function :thumb_ins_is_conditional, [:ins_handle], :bool
|
|
51
|
+
|
|
52
|
+
attach_function :arm_ins_updates_condition_flags, [:ins_handle], :bool
|
|
53
|
+
attach_function :thumb_ins_updates_condition_flags, [:ins_handle], :bool
|
|
54
|
+
|
|
55
|
+
attach_function :arm_ins_is_data_operation, [:ins_handle], :bool
|
|
56
|
+
attach_function :thumb_ins_is_data_operation, [:ins_handle], :bool
|
|
57
|
+
|
|
58
|
+
attach_function :get_sym_for_addr, [:uint32, :symbols_handle, :uint32], :cstr_handle
|
|
59
|
+
|
|
60
|
+
attach_function :free_arm_ins, [:ins_handle], :void
|
|
61
|
+
attach_function :free_thumb_ins, [:ins_handle], :void
|
|
62
|
+
attach_function :free_ins_args, [:ins_args_handle, :uint32], :void
|
|
63
|
+
attach_function :arm9_free_parser, [:parser_handle], :void
|
|
64
|
+
attach_function :arm7_free_parser, [:parser_handle], :void
|
|
65
|
+
attach_function :free_c_str, [:cstr_handle], :void
|
|
66
|
+
|
|
67
|
+
# NOTE: some of the following instructions are not valid in ARMv5TE/v4T
|
|
68
|
+
OPCODE = [
|
|
69
|
+
:illegal, :adc, :add, :and, :asr, :b, :bl, :bic, :bkpt, :blxi,
|
|
70
|
+
:blxr, :bx, :bxj, :cdp, :cdp2, :clrex, :clz, :cmn, :cmp, :cps,
|
|
71
|
+
:csdb, :dbg, :eor, :ldc, :ldc2, :ldmw, :ldm, :ldmp, :ldmpw, :ldmpcw,
|
|
72
|
+
:ldmpc, :ldr, :ldrb, :ldrbt, :ldrd, :ldrex, :ldrexb, :ldrexd, :ldrexh, :ldrh,
|
|
73
|
+
:ldrsb, :ldrsh, :ldrt, :lsl, :lsr, :mcr, :mcr2, :mcrr, :mcrr2, :mla,
|
|
74
|
+
:mov, :movimm, :movreg, :mrc, :mrc2, :mrrc, :mrrc2, :mrs, :msri, :msr,
|
|
75
|
+
:mul, :mvn, :nop, :orr, :pkhbt, :pkhtb, :pld, :popm, :popr, :pushm,
|
|
76
|
+
:pushr, :qadd, :qadd16, :qadd8, :qasx, :qdadd, :qdsub, :qsax, :qsub, :qsub16,
|
|
77
|
+
:qsub8, :rev, :rev16, :revsh, :rfe, :ror, :rrx, :rsb, :rsc, :sadd16,
|
|
78
|
+
:sadd8, :sasx, :sbc, :sel, :setend, :sev, :shadd16, :shadd8, :shasx, :shsax,
|
|
79
|
+
:shsub16, :shsub8, :smla, :smlad, :smlal, :smlalxy, :smlald, :smlaw, :smlsd, :smlsld,
|
|
80
|
+
:smmla, :smmls, :smmul, :smuad, :smul, :smull, :smulw, :smusd, :srs, :ssat,
|
|
81
|
+
:ssat16, :ssax, :ssub16, :ssub8, :stc, :stc2, :stm, :stmw, :stmp, :stmpw,
|
|
82
|
+
:str, :strb, :strbt, :strd, :strex, :strexb, :strexd, :strexh, :strh, :strt,
|
|
83
|
+
:sub, :svc, :swi, :swp, :swpb, :sxtab, :sxtab16, :sxtah, :sxtb, :sxtb16,
|
|
84
|
+
:sxth, :teq, :tst, :uadd16, :uadd8, :uasx, :udf, :uhadd16, :uhadd8, :uhasx,
|
|
85
|
+
:uhsax, :uhsub16, :uhsub8, :umaal, :umlal, :umull, :uqadd16, :uqadd8, :uqasx, :uqsax,
|
|
86
|
+
:uqsub16, :uqsub8, :usad8, :usada8, :usat, :usat16, :usax, :usub16, :usub8, :uxtab,
|
|
87
|
+
:uxtab16, :uxtah, :uxtb, :uxtb16, :uxth, :wfe, :wfi, :yield
|
|
88
|
+
].freeze
|
|
89
|
+
|
|
90
|
+
OPCODE_MNEMONIC = [
|
|
91
|
+
'<illegal>', 'adc', 'add', 'and', 'asr', 'b', 'bl', 'bic', 'bkpt', 'blx',
|
|
92
|
+
'blx', 'bx', 'bxj', 'cdp', 'cdp2', 'clrex', 'clz', 'cmn', 'cmp', 'cps',
|
|
93
|
+
'csdb', 'dbg', 'eor', 'ldc', 'ldc2', 'ldm', 'ldm', 'ldm', 'ldm', 'ldm',
|
|
94
|
+
'ldm', 'ldr', 'ldrb', 'ldrbt', 'ldrd', 'ldrex', 'ldrexb', 'ldrexd', 'ldrexh', 'ldrh',
|
|
95
|
+
'ldrsb', 'ldrsh', 'ldrt', 'lsl', 'lsr', 'mcr', 'mcr2', 'mcrr', 'mcrr2', 'mla',
|
|
96
|
+
'mov', 'mov', 'mov', 'mrc', 'mrc2', 'mrrc', 'mrrc2', 'mrs', 'msr', 'msr',
|
|
97
|
+
'mul', 'mvn', 'nop', 'orr', 'pkhbt', 'pkhtb', 'pld', 'pop', 'pop', 'push',
|
|
98
|
+
'push', 'qadd', 'qadd16', 'qadd8', 'qasx', 'qdadd', 'qdsub', 'qsax', 'qsub', 'qsub16',
|
|
99
|
+
'qsub8', 'rev', 'rev16', 'revsh', 'rfe', 'ror', 'rrx', 'rsb', 'rsc', 'sadd16',
|
|
100
|
+
'sadd8', 'sasx', 'sbc', 'sel', 'setend', 'sev', 'shadd16', 'shadd8', 'shasx', 'shsax',
|
|
101
|
+
'shsub16', 'shsub8', 'smla', 'smlad', 'smlal', 'smlal', 'smlald', 'smlaw', 'smlsd', 'smlsld',
|
|
102
|
+
'smmla', 'smmls', 'smmul', 'smuad', 'smul', 'smull', 'smulw', 'smusd', 'srs', 'ssat',
|
|
103
|
+
'ssat16', 'ssax', 'ssub16', 'ssub8', 'stc', 'stc2', 'stm', 'stm', 'stm', 'stm',
|
|
104
|
+
'str', 'strb', 'strbt', 'strd', 'strex', 'strexb', 'strexd', 'strexh', 'strh', 'strt',
|
|
105
|
+
'sub', 'svc', 'swi', 'swp', 'swpb', 'sxtab', 'sxtab16', 'sxtah', 'sxtb', 'sxtb16',
|
|
106
|
+
'sxth', 'teq', 'tst', 'uadd16', 'uadd8', 'uasx', 'udf', 'uhadd16', 'uhadd8', 'uhasx',
|
|
107
|
+
'uhsax', 'uhsub16', 'uhsub8', 'umaal', 'umlal', 'umull', 'uqadd16', 'uqadd8', 'uqasx', 'uqsax',
|
|
108
|
+
'uqsub16', 'uqsub8', 'usad8', 'usada8', 'usat', 'usat16', 'usax', 'usub16', 'usub8', 'uxtab',
|
|
109
|
+
'uxtab16', 'uxtah', 'uxtb', 'uxtb16', 'uxth', 'wfe', 'wfi', 'yield'
|
|
110
|
+
].freeze
|
|
111
|
+
|
|
112
|
+
CONDITION = [:illegal, :eq, :ne, :hs, :lo, :mi, :pl, :vs, :vc, :hi, :ls, :ge, :lt, :gt, :le, :al].freeze
|
|
113
|
+
|
|
114
|
+
REGISTER = [:r0, :r1, :r2, :r3, :r4, :r5, :r6, :r7, :r8, :r9, :r10, :r11, :r12, :sp, :lr, :pc].freeze
|
|
115
|
+
|
|
116
|
+
SHIFT = [:lsl, :lsr, :asr, :ror, :rrx].freeze
|
|
117
|
+
|
|
118
|
+
CO_REG = [:c0, :c1, :c2, :c3, :c4, :c5, :c6, :c7, :c8, :c9, :c10, :c11, :c12, :c13, :c14, :c15].freeze
|
|
119
|
+
|
|
120
|
+
STATUS_REG = [:cpsr, :spsr].freeze
|
|
121
|
+
|
|
122
|
+
ARGUMENT_KIND = [
|
|
123
|
+
:none, :reg, :reg_list, :co_reg, :status_reg, :status_mask, :shift, :shift_imm, :shift_reg,
|
|
124
|
+
:u_imm, :sat_imm, :s_imm, :offset_imm, :offset_reg, :branch_dest, :co_option, :co_opcode,
|
|
125
|
+
:coproc_num, :cpsr_mode, :cpsr_flags, :endian
|
|
126
|
+
].freeze
|
|
127
|
+
|
|
128
|
+
ENDIAN = [:le, :be].freeze # illegal=255
|
|
129
|
+
|
|
130
|
+
CONDITION_MAP = CONDITION.each_with_index.to_h
|
|
131
|
+
REGISTER_MAP = REGISTER.each_with_index.to_h
|
|
132
|
+
SHIFT_MAP = SHIFT.each_with_index.to_h
|
|
133
|
+
CO_REG_MAP = CO_REG.each_with_index.to_h
|
|
134
|
+
STATUS_REG_MAP = STATUS_REG.each_with_index.to_h
|
|
135
|
+
ARGUMENT_KIND_MAP = ARGUMENT_KIND.each_with_index.to_h
|
|
136
|
+
ENDIAN_MAP = ENDIAN.each_with_index.to_h
|
|
137
|
+
|
|
138
|
+
class RegList < FFI::Struct
|
|
139
|
+
layout :regs, :uint32, # bitfield of registers
|
|
140
|
+
:user_mode, :bool # access user-mode registers from elevated mode
|
|
141
|
+
|
|
142
|
+
def contains?(register)
|
|
143
|
+
register = REGISTER_MAP[register] if register.is_a? Symbol
|
|
144
|
+
raise 'Invalid register' if register.nil? || (register.is_a?(Integer) && register >= REGISTER_MAP.length)
|
|
145
|
+
self[:regs] & (1 << register) != 0
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def user_mode?
|
|
149
|
+
self[:user_mode]
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Reg < FFI::Struct
|
|
154
|
+
layout :deref, :bool, # use as a base register
|
|
155
|
+
:reg, :uint8, # Register
|
|
156
|
+
:writeback, :bool # when used as a base register, update this register's value
|
|
157
|
+
|
|
158
|
+
def deref?
|
|
159
|
+
self[:deref]
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def reg
|
|
163
|
+
return :illegal if self[:reg] == 255
|
|
164
|
+
REGISTER[self[:reg]]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def writeback?
|
|
168
|
+
self[:writeback]
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class StatusMask < FFI::Struct
|
|
173
|
+
layout :control, :bool, # control field mask (c)
|
|
174
|
+
:extension, :bool, # extension field mask (x)
|
|
175
|
+
:flags, :bool, # flags field mask (f)
|
|
176
|
+
:reg, :uint8, # StatusReg
|
|
177
|
+
:status, :bool # status field mask (s)
|
|
178
|
+
|
|
179
|
+
def control?
|
|
180
|
+
self[:control]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def extension?
|
|
184
|
+
self[:extension]
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def flags?
|
|
188
|
+
self[:flags]
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def reg
|
|
192
|
+
return :illegal if self[:reg] == 255
|
|
193
|
+
REGISTER[self[:reg]]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def status?
|
|
197
|
+
self[:status]
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class ShiftImm < FFI::Struct
|
|
202
|
+
layout :imm, :uint32, # immediate shift offset
|
|
203
|
+
:op, :uint8 # Shift
|
|
204
|
+
|
|
205
|
+
def imm
|
|
206
|
+
self[:imm]
|
|
207
|
+
end
|
|
208
|
+
alias_method :value, :imm
|
|
209
|
+
|
|
210
|
+
def op
|
|
211
|
+
SHIFT[self[:op]]
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
class ShiftReg < FFI::Struct
|
|
216
|
+
layout :op, :uint8, # Shift
|
|
217
|
+
:reg, :uint8 # Register
|
|
218
|
+
|
|
219
|
+
def op
|
|
220
|
+
return :illegal if self[:op] == 255
|
|
221
|
+
SHIFT[self[:op]]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def reg
|
|
225
|
+
return :illegal if self[:reg] == 255
|
|
226
|
+
REGISTER[self[:reg]]
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
class OffsetImm < FFI::Struct
|
|
231
|
+
layout :post_indexed, :bool, # if true, add offset to base register and write-back AFTER derefencing base register
|
|
232
|
+
:value, :int32 # offset value
|
|
233
|
+
|
|
234
|
+
def post_indexed?
|
|
235
|
+
self[:post_indexed]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def value
|
|
239
|
+
self[:value]
|
|
240
|
+
end
|
|
241
|
+
alias_method :offset, :value
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
class OffsetReg < FFI::Struct
|
|
245
|
+
layout :add, :bool, # if true, add offset to base register, otherwise subtract
|
|
246
|
+
:post_indexed, :bool, # if true, add offset to base register and write-back AFTER derefencing base register
|
|
247
|
+
:reg, :uint8 # Register
|
|
248
|
+
|
|
249
|
+
def add?
|
|
250
|
+
self[:add]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def post_indexed?
|
|
254
|
+
self[:post_indexed]
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def reg
|
|
258
|
+
return :illegal if self[:reg] == 255
|
|
259
|
+
REGISTER[self[:reg]]
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
class CpsrMode < FFI::Struct
|
|
264
|
+
layout :mode, :uint32, # mode bits
|
|
265
|
+
:writeback, :bool # writeback to base register
|
|
266
|
+
|
|
267
|
+
def mode
|
|
268
|
+
self[:mode]
|
|
269
|
+
end
|
|
270
|
+
alias_method :bits, :mode
|
|
271
|
+
|
|
272
|
+
def writeback?
|
|
273
|
+
self[:writeback]
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
class CpsrFlags < FFI::Struct
|
|
278
|
+
layout :a, :bool, # imprecise data abort
|
|
279
|
+
:enable, :bool, # enable the A/I/F flags if true otherwise disable
|
|
280
|
+
:f, :bool, # FIQ interrupt
|
|
281
|
+
:i, :bool # IRQ interrupt
|
|
282
|
+
|
|
283
|
+
def abort?
|
|
284
|
+
self[:a]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def enable?
|
|
288
|
+
self[:enable]
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def fiq_interrupt?
|
|
292
|
+
self[:f]
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def irq_interrupt?
|
|
296
|
+
self[:i]
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
class ArgumentValue < FFI::Union
|
|
301
|
+
layout :reg, Reg,
|
|
302
|
+
:reg_list, RegList,
|
|
303
|
+
:co_reg, :uint8,
|
|
304
|
+
:status_reg, :uint8,
|
|
305
|
+
:status_mask, StatusMask,
|
|
306
|
+
:shift, :uint8,
|
|
307
|
+
:shift_imm, ShiftImm,
|
|
308
|
+
:shift_reg, ShiftReg,
|
|
309
|
+
:u_imm, :uint32,
|
|
310
|
+
:sat_imm, :uint32,
|
|
311
|
+
:s_imm, :int32,
|
|
312
|
+
:offset_imm, OffsetImm,
|
|
313
|
+
:offset_reg, OffsetReg,
|
|
314
|
+
:branch_dest, :int32,
|
|
315
|
+
:co_option, :uint32,
|
|
316
|
+
:co_opcode, :uint32,
|
|
317
|
+
:coproc_num, :uint32,
|
|
318
|
+
:cpsr_mode, CpsrMode,
|
|
319
|
+
:cpsr_flags, CpsrFlags,
|
|
320
|
+
:endian, :uint8
|
|
321
|
+
|
|
322
|
+
def co_reg
|
|
323
|
+
CO_REG[self[:co_reg]]
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
def status_reg
|
|
327
|
+
STATUS_REG[self[:status_reg]]
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
def shift
|
|
331
|
+
SHIFT[self[:shift]]
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def endian
|
|
335
|
+
return :illegal if self[:endian] == 255
|
|
336
|
+
ENDIAN[self[:endian]]
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
class Argument < FFI::Struct
|
|
341
|
+
layout :kind, :uint8,
|
|
342
|
+
:value, ArgumentValue
|
|
343
|
+
|
|
344
|
+
def kind
|
|
345
|
+
ARGUMENT_KIND[self[:kind]]
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def value
|
|
349
|
+
raise "No value for argument of kind 'none'" if kind == :none
|
|
350
|
+
self[:value][kind]
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
class Arguments < FFI::AutoPointer
|
|
355
|
+
include Enumerable
|
|
356
|
+
|
|
357
|
+
def self.release(ptr)
|
|
358
|
+
UnarmBind.free_ins_args(ptr, 6)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def [](index)
|
|
362
|
+
raise IndexError, 'there are 6 args' if index < 0 || index >= 6
|
|
363
|
+
Argument.new(self + index * Argument.size)
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def each
|
|
367
|
+
return enum_for(:each) unless block_given?
|
|
368
|
+
6.times { |i| yield self[i] }
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
Arg = Argument
|
|
374
|
+
Args = Arguments
|
|
375
|
+
|
|
376
|
+
class CStr < FFI::AutoPointer
|
|
377
|
+
def self.release(ptr)
|
|
378
|
+
UnarmBind.free_c_str(ptr)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def to_s
|
|
382
|
+
self.read_string
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
module Unarm
|
|
390
|
+
extend UnarmBind
|
|
391
|
+
|
|
392
|
+
module CPU
|
|
393
|
+
ARM9 = :arm9 # ARMv5Te
|
|
394
|
+
ARM7 = :arm7 # ARMv4T
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
@cpu = CPU::ARM9
|
|
398
|
+
@symbols9 = nil
|
|
399
|
+
@symbols7 = nil
|
|
400
|
+
@raw_syms = {}
|
|
401
|
+
|
|
402
|
+
def self.use_arm9
|
|
403
|
+
@cpu = CPU::ARM9
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def self.use_arm7
|
|
407
|
+
@cpu = CPU::ARM7
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def self.cpu = @cpu
|
|
411
|
+
|
|
412
|
+
def self.symbols9 = @symbols9
|
|
413
|
+
def self.symbols7 = @symbols7
|
|
414
|
+
|
|
415
|
+
def self.symbols
|
|
416
|
+
@cpu == CPU::ARM9 ? symbols9 : symbols7
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
def self.raw_syms = @raw_syms
|
|
420
|
+
|
|
421
|
+
def self.shitty_demangle(sym)
|
|
422
|
+
return sym unless sym.start_with?('_Z')
|
|
423
|
+
|
|
424
|
+
sym = sym[2..]
|
|
425
|
+
|
|
426
|
+
is_vtable = false
|
|
427
|
+
|
|
428
|
+
if sym.start_with? 'NK'
|
|
429
|
+
sym = sym[2..]
|
|
430
|
+
|
|
431
|
+
elsif sym.start_with? 'N'
|
|
432
|
+
sym = sym[1..]
|
|
433
|
+
|
|
434
|
+
elsif sym.start_with? 'TV'
|
|
435
|
+
sym = sym[(sym[2] == 'N' ? 3 : 2)..]
|
|
436
|
+
is_vtable = true
|
|
437
|
+
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
names = []
|
|
441
|
+
loop do
|
|
442
|
+
break if sym.nil? || sym.empty? || !sym[0].match?(/\d/)
|
|
443
|
+
len_end = sym.index(/\D/)
|
|
444
|
+
n_len = Integer(sym[...len_end], exception: false)
|
|
445
|
+
n = sym[len_end, n_len]
|
|
446
|
+
names << n
|
|
447
|
+
sym = sym[len_end + n_len..]
|
|
448
|
+
end
|
|
449
|
+
|
|
450
|
+
unless sym.nil?
|
|
451
|
+
if sym.start_with? 'nw'
|
|
452
|
+
names << 'new'
|
|
453
|
+
|
|
454
|
+
elsif sym.start_with? 'na'
|
|
455
|
+
names << 'new[]'
|
|
456
|
+
|
|
457
|
+
elsif sym.start_with? 'dl'
|
|
458
|
+
names << 'delete'
|
|
459
|
+
|
|
460
|
+
elsif sym.start_with? 'da'
|
|
461
|
+
names << 'delete[]'
|
|
462
|
+
|
|
463
|
+
elsif sym.start_with? 'eq'
|
|
464
|
+
names << '=='
|
|
465
|
+
|
|
466
|
+
elsif sym.start_with? 'ne'
|
|
467
|
+
names << '!='
|
|
468
|
+
|
|
469
|
+
elsif sym.match? /[DC]\d/
|
|
470
|
+
names << sym[..1]
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
names.join('::') + (is_vtable ? '::vtable' : '')
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
class Symbol < FFI::Struct
|
|
478
|
+
layout :name, :pointer,
|
|
479
|
+
:addr, :uint32
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
class Symbols
|
|
483
|
+
attr_reader :map, :locs, :count, :demangled_map, :ambig_demangled
|
|
484
|
+
|
|
485
|
+
def self.load(file_path)
|
|
486
|
+
syms = {} # maps symbol names to their addresses
|
|
487
|
+
locs = {} # maps symbol names to their code locations (e.g. arm9, ov0, ov10)
|
|
488
|
+
dest = nil # current symbol location
|
|
489
|
+
File.open(file_path) do |f|
|
|
490
|
+
f.each_line do |line|
|
|
491
|
+
parts = line.split
|
|
492
|
+
|
|
493
|
+
next if parts.length < 3
|
|
494
|
+
|
|
495
|
+
is_comment = line.strip.start_with?('/')
|
|
496
|
+
if is_comment
|
|
497
|
+
new_dest = parts[1].split('_')
|
|
498
|
+
dest = new_dest[new_dest.length == 1 ? 0 : 1] if new_dest[0].include?('arm')
|
|
499
|
+
next
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
next if is_comment || (line.length < 4)
|
|
503
|
+
|
|
504
|
+
parts.delete_at(1) # removes '='
|
|
505
|
+
parts = parts[0..1] # keep symbol and name
|
|
506
|
+
|
|
507
|
+
parts[1] = parts[1].chomp(';') if parts[1].end_with?(';')
|
|
508
|
+
|
|
509
|
+
addr = parts[1].hex
|
|
510
|
+
next if addr <= 0
|
|
511
|
+
parts[1] = addr # - (addr & 1)
|
|
512
|
+
syms[parts[0]] = parts[1]
|
|
513
|
+
if dest
|
|
514
|
+
locs[dest] = Array.new unless locs[dest]
|
|
515
|
+
locs[dest] << parts[0]
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
end
|
|
519
|
+
end
|
|
520
|
+
return syms, locs
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
def initialize(args = {})
|
|
524
|
+
if args.has_key? :file_path
|
|
525
|
+
@map, @locs = Symbols.load(args[:file_path])
|
|
526
|
+
elsif args.has_key? :syms
|
|
527
|
+
@map, @locs = args[:syms].map, args[:syms].locs
|
|
528
|
+
else
|
|
529
|
+
raise ArgumentError, 'Symbols must be initialized through a file or a hash'
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
if args.has_key? :locs
|
|
533
|
+
all_syms = @map
|
|
534
|
+
@map = {}
|
|
535
|
+
args[:locs].each do |loc|
|
|
536
|
+
@locs[loc].each { |sym| @map[sym] = all_syms[sym] }
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
@count = @map.length
|
|
541
|
+
|
|
542
|
+
@demangled_map = {}
|
|
543
|
+
@ambig_demangled = []
|
|
544
|
+
|
|
545
|
+
@map.each do |sym, addr|
|
|
546
|
+
demangled = Unarm.shitty_demangle(sym)
|
|
547
|
+
if @demangled_map[demangled].nil?
|
|
548
|
+
@demangled_map[demangled] = sym
|
|
549
|
+
else
|
|
550
|
+
@ambig_demangled << [demangled, addr]
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
class RawSymbols # symbols that can be passed to Rust
|
|
558
|
+
attr_reader :ptr, :count
|
|
559
|
+
|
|
560
|
+
def initialize(syms)
|
|
561
|
+
@count = syms.count
|
|
562
|
+
@ptr = FFI::MemoryPointer.new(Symbol, @count)
|
|
563
|
+
sym_arr = @count.times.map do |i|
|
|
564
|
+
Symbol.new(@ptr + i*Symbol.size)
|
|
565
|
+
end
|
|
566
|
+
@name_ptrs = [] # keeps memory for symbol names alive (?)
|
|
567
|
+
syms.map.each_with_index do |(name, addr), i|
|
|
568
|
+
name_ptr = FFI::MemoryPointer.from_string(name)
|
|
569
|
+
@name_ptrs << name_ptr
|
|
570
|
+
sym_arr[i][:name] = name_ptr
|
|
571
|
+
sym_arr[i][:addr] = addr
|
|
572
|
+
end
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
def self.load_symbols9(file_path)
|
|
578
|
+
@symbols9 = Symbols.new(file_path: file_path)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
def self.load_symbols7(file_path)
|
|
582
|
+
@symbols7 = Symbols.new(file_path: file_path)
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
def self.symbol_map
|
|
586
|
+
if @cpu == CPU::ARM9
|
|
587
|
+
raise 'Symbols9 not loaded' if !@symbols9
|
|
588
|
+
@symbols9.map
|
|
589
|
+
else
|
|
590
|
+
raise 'Symbols7 not loaded' if !@symbols7
|
|
591
|
+
@symbols7.map
|
|
592
|
+
end
|
|
593
|
+
end
|
|
594
|
+
|
|
595
|
+
def self.get_raw_symbols(loc)
|
|
596
|
+
syms = loc == 'arm7' ? @symbols7 : @symbols9
|
|
597
|
+
loc = 'arm9' if !syms.locs.has_key?(loc)
|
|
598
|
+
locs = %w[arm7 arm9].include?(loc) ? [loc] : ['arm9', loc]
|
|
599
|
+
@raw_syms[loc] ||= RawSymbols.new(Symbols.new(syms: syms, locs: locs))
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
class << self
|
|
603
|
+
alias_method :sym_map, :symbol_map
|
|
604
|
+
alias_method :get_raw_syms, :get_raw_symbols
|
|
605
|
+
end
|
|
606
|
+
|
|
607
|
+
class Ins
|
|
608
|
+
include UnarmBind
|
|
609
|
+
|
|
610
|
+
class << self
|
|
611
|
+
alias_method :disasm, :new
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
attr_reader :raw, :arguments, :address
|
|
615
|
+
|
|
616
|
+
alias_method :args, :arguments
|
|
617
|
+
alias_method :addr, :address
|
|
618
|
+
|
|
619
|
+
def size
|
|
620
|
+
@@size
|
|
621
|
+
end
|
|
622
|
+
alias_method :ins_size, :size
|
|
623
|
+
|
|
624
|
+
def string
|
|
625
|
+
@str.to_s
|
|
626
|
+
end
|
|
627
|
+
alias_method :str, :string
|
|
628
|
+
|
|
629
|
+
def eql?(other)
|
|
630
|
+
@raw == other.raw
|
|
631
|
+
end
|
|
632
|
+
alias_method :==, :eql?
|
|
633
|
+
|
|
634
|
+
def opcode
|
|
635
|
+
UnarmBind::OPCODE[@opcode_id]
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
def mnemonic
|
|
639
|
+
UnarmBind::OPCODE_MNEMONIC[@opcode_id]
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
def is_conditional?
|
|
643
|
+
@conditional
|
|
644
|
+
end
|
|
645
|
+
alias_method :conditional?, :is_conditional?
|
|
646
|
+
|
|
647
|
+
def is_unconditional?
|
|
648
|
+
!@conditional
|
|
649
|
+
end
|
|
650
|
+
alias_method :unconditional?, :is_unconditional?
|
|
651
|
+
|
|
652
|
+
def is_data_operation?
|
|
653
|
+
@data_op
|
|
654
|
+
end
|
|
655
|
+
alias_method :is_data_op?, :is_data_operation?
|
|
656
|
+
|
|
657
|
+
def is_illegal?
|
|
658
|
+
opcode == :illegal
|
|
659
|
+
end
|
|
660
|
+
alias_method :illegal?, :is_illegal?
|
|
661
|
+
|
|
662
|
+
def sets_flags?
|
|
663
|
+
@sets_flags
|
|
664
|
+
end
|
|
665
|
+
alias_method :updates_condition_flags?, :sets_flags?
|
|
666
|
+
|
|
667
|
+
def has_imod? # does instruction modify interrupt flags?
|
|
668
|
+
opcode == :cps
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
def branch_destination
|
|
672
|
+
arg = @arguments.find {|a| a.kind == :branch_dest}
|
|
673
|
+
return nil if !arg
|
|
674
|
+
arg.value + @address
|
|
675
|
+
end
|
|
676
|
+
alias_method :branch_dest, :branch_destination
|
|
677
|
+
|
|
678
|
+
def target_address
|
|
679
|
+
if opcode == :ldr && args[1].kind == :reg && args[1].value.reg == :pc && args[2].kind == :offset_imm
|
|
680
|
+
address + args[2].value.value + 8
|
|
681
|
+
else
|
|
682
|
+
nil
|
|
683
|
+
end
|
|
684
|
+
end
|
|
685
|
+
alias_method :target_addr, :target_address
|
|
686
|
+
|
|
687
|
+
def branch_to_register?
|
|
688
|
+
opcode == :bx ||
|
|
689
|
+
(mnemonic == 'mov' && args[0].value.reg == :pc) || (mnemonic == 'ldr' && args[0].value.reg == :pc)
|
|
690
|
+
end
|
|
691
|
+
alias_method :branch_to_reg?, :branch_to_register?
|
|
692
|
+
|
|
693
|
+
def function_end?
|
|
694
|
+
return false if conditional?
|
|
695
|
+
|
|
696
|
+
branch_to_register? ||
|
|
697
|
+
(mnemonic == 'pop' && args[0].value.contains?(:pc)) ||
|
|
698
|
+
(mnemonic == 'ldm' && args[0].value.reg == :sp && args[1].value.contains?(:pc))
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
class ArmIns < Ins
|
|
704
|
+
@@size = 4
|
|
705
|
+
|
|
706
|
+
def initialize(ins, addr = 0, loc = Unarm.cpu.to_s)
|
|
707
|
+
@raw = ins
|
|
708
|
+
@address = addr ? addr : nil
|
|
709
|
+
@ptr = FFI::AutoPointer.new(send(:"#{Unarm.cpu.to_s}_new_arm_ins", ins), method(:free_arm_ins))
|
|
710
|
+
|
|
711
|
+
if Unarm.symbols
|
|
712
|
+
syms = Unarm.get_raw_syms(loc)
|
|
713
|
+
@str = CStr.new(send(:"#{Unarm.cpu.to_s}_arm_ins_to_str_with_syms", @ptr, syms.ptr, syms.count, addr, 0))
|
|
714
|
+
else
|
|
715
|
+
@str = CStr.new(send(:"#{Unarm.cpu.to_s}_arm_ins_to_str", @ptr))
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
@arguments = Arguments.new(send(:"#{Unarm.cpu.to_s}_arm_ins_get_args", @ptr))
|
|
719
|
+
@opcode_id = send(:"#{Unarm.cpu.to_s}_arm_ins_get_opcode_id", @ptr)
|
|
720
|
+
|
|
721
|
+
@conditional = arm_ins_is_conditional(@ptr)
|
|
722
|
+
@data_op = arm_ins_is_data_operation(@ptr)
|
|
723
|
+
@sets_flags = arm_ins_updates_condition_flags(@ptr)
|
|
724
|
+
end
|
|
725
|
+
|
|
726
|
+
def is_compare_operation? # does opcode compare a register with another value?
|
|
727
|
+
[:cmn, :cmp, :teq, :tst].include? opcode
|
|
728
|
+
end
|
|
729
|
+
alias_method :is_compare_op?, :is_compare_operation?
|
|
730
|
+
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
class ThumbIns < Ins
|
|
734
|
+
@@size = 2
|
|
735
|
+
|
|
736
|
+
def initialize(ins, addr = 0, loc = Unarm.cpu.to_s)
|
|
737
|
+
@raw = ins
|
|
738
|
+
@address = addr ? addr : nil
|
|
739
|
+
@ptr = FFI::AutoPointer.new(send(:"#{Unarm.cpu.to_s}_new_thumb_ins", ins), method(:free_thumb_ins))
|
|
740
|
+
|
|
741
|
+
if Unarm.symbols
|
|
742
|
+
syms = Unarm.get_raw_syms(loc)
|
|
743
|
+
@str = CStr.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_to_str_with_syms", @ptr, syms.ptr, syms.count, addr, 0))
|
|
744
|
+
else
|
|
745
|
+
@str = CStr.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_to_str", @ptr))
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
@arguments = Arguments.new(send(:"#{Unarm.cpu.to_s}_thumb_ins_get_args", @ptr))
|
|
749
|
+
@opcode_id = send(:"#{Unarm.cpu.to_s}_thumb_ins_get_opcode_id", @ptr)
|
|
750
|
+
|
|
751
|
+
@conditional = thumb_ins_is_conditional(@ptr)
|
|
752
|
+
@data_op = thumb_ins_is_data_operation(@ptr)
|
|
753
|
+
@sets_flags = thumb_ins_updates_condition_flags(@ptr)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
class Parser
|
|
759
|
+
include UnarmBind
|
|
760
|
+
|
|
761
|
+
attr_reader :mode
|
|
762
|
+
|
|
763
|
+
module Mode
|
|
764
|
+
ARM = 0
|
|
765
|
+
THUMB = 1
|
|
766
|
+
DATA = 2
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
module Endian
|
|
770
|
+
LITTLE = 0
|
|
771
|
+
BIG = 1
|
|
772
|
+
end
|
|
773
|
+
|
|
774
|
+
def set_parse_mode(mode)
|
|
775
|
+
raise ArgumentError, 'mode must be ARM, THUMB, or DATA' unless (Mode::ARM..Mode::DATA).include? mode
|
|
776
|
+
@mode = mode
|
|
777
|
+
end
|
|
778
|
+
|
|
779
|
+
def initialize(data_ptr, data_size, addr, mode = Mode::ARM, endian = Endian::LITTLE)
|
|
780
|
+
set_parse_mode(mode)
|
|
781
|
+
# TODO!!!!!
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
end
|
|
785
|
+
|
|
786
|
+
class Data
|
|
787
|
+
include UnarmBind
|
|
788
|
+
|
|
789
|
+
attr_reader :raw, :size, :address, :location, :string, :value
|
|
790
|
+
|
|
791
|
+
alias_method :addr, :address
|
|
792
|
+
alias_method :loc, :location
|
|
793
|
+
alias_method :str, :string
|
|
794
|
+
|
|
795
|
+
def get_directive(size)
|
|
796
|
+
case size
|
|
797
|
+
when 4
|
|
798
|
+
'.word'
|
|
799
|
+
when 2
|
|
800
|
+
'.hword'
|
|
801
|
+
when 1
|
|
802
|
+
'.byte'
|
|
803
|
+
else
|
|
804
|
+
raise "Could not determine directive from data size: #{size}"
|
|
805
|
+
end
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
def initialize(raw, size: 4, addr: 0, loc: Unarm.cpu.to_s, might_be_ptr: true)
|
|
809
|
+
@raw = raw
|
|
810
|
+
@address = addr
|
|
811
|
+
@location = loc
|
|
812
|
+
if raw.is_a? String
|
|
813
|
+
@size = raw.length + 1
|
|
814
|
+
@string = ".asciiz \"#{raw}\""
|
|
815
|
+
else
|
|
816
|
+
@size = size
|
|
817
|
+
if size == 4 && might_be_ptr
|
|
818
|
+
syms = Unarm.get_raw_syms(loc)
|
|
819
|
+
raw_str = CStr.new(get_sym_for_addr(raw, syms.ptr, syms.count))
|
|
820
|
+
value = raw_str.null? ? raw.to_hex : raw_str.to_s
|
|
821
|
+
else
|
|
822
|
+
value = raw.to_hex
|
|
823
|
+
end
|
|
824
|
+
@string = "#{get_directive(size)} #{value}"
|
|
825
|
+
@value = value
|
|
826
|
+
end
|
|
827
|
+
end
|
|
828
|
+
|
|
829
|
+
def eql?(other)
|
|
830
|
+
@raw == other.raw
|
|
831
|
+
end
|
|
832
|
+
alias_method :==, :eql?
|
|
833
|
+
|
|
834
|
+
end
|
|
835
|
+
|
|
836
|
+
end
|