forked from cseagle/blc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jumptable.hh
411 lines (389 loc) · 19.7 KB
/
jumptable.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Abstract jump table, we do not specify how addresses are encoded in table
#ifndef __CPUI_JUMPTABLE__
#define __CPUI_JUMPTABLE__
#include "emulateutil.hh"
#include "rangeutil.hh"
class EmulateFunction;
struct JumptableThunkError : public LowlevelError { // Thunk that looks like a jumptable
/// Initialize the error with an explanatory string
JumptableThunkError(const string &s) : LowlevelError(s) {}
};
struct JumptableNotReachableError : public LowlevelError { // There are no legal flows to the switch
JumptableNotReachableError(const string &s) : LowlevelError(s) {}
};
class LoadTable {
friend class EmulateFunction;
Address addr; // Starting address of table
int4 size; // Size of table entry
int4 num; // Number of entries in table;
public:
LoadTable(void) {} // For use with restoreXml
LoadTable(const Address &ad,int4 sz) { addr = ad, size = sz; num = 1; }
LoadTable(const Address &ad,int4 sz,int4 nm) { addr = ad; size = sz; num = nm; }
bool operator<(const LoadTable &op2) const { return (addr < op2.addr); }
void saveXml(ostream &s) const;
void restoreXml(const Element *el,Architecture *glb);
static void collapseTable(vector<LoadTable> &table);
};
class PathMeld {
struct RootedOp {
PcodeOp *op;
int4 rootVn;
RootedOp(PcodeOp *o,int4 root) { op = o; rootVn = root; }
};
vector<Varnode *> commonVn; // Varnodes in common with all paths
vector<RootedOp> opMeld; // All the ops for the melded paths
void internalIntersect(vector<int4> &parentMap);
int4 meldOps(const vector<PcodeOp *> &path,int4 cutOff,const vector<int4> &parentMap);
void truncatePaths(int4 cutPoint);
public:
void set(const PathMeld &op2);
void set(const vector<PcodeOp *> &path,const vector<int4> &slot);
void set(PcodeOp *op,Varnode *vn);
void append(const PathMeld &op2);
void clear(void);
void meld(vector<PcodeOp *> &path,vector<int4> &slot);
int4 numCommonVarnode(void) const { return commonVn.size(); }
int4 numOps(void) const { return opMeld.size(); }
Varnode *getVarnode(int4 i) const { return commonVn[i]; }
Varnode *getOpParent(int4 i) const { return commonVn[ opMeld[i].rootVn ]; }
PcodeOp *getOp(int4 i) const { return opMeld[i].op; }
PcodeOp *getEarliestOp(int4 pos) const;
bool empty(void) const { return commonVn.empty(); }
};
class EmulateFunction : public EmulatePcodeOp {
Funcdata *fd;
map<Varnode *,uintb> varnodeMap; // Lightweight memory state based on Varnodes
bool collectloads;
vector<LoadTable> loadpoints;
virtual void executeLoad(void);
virtual void executeBranch(void);
virtual void executeBranchind(void);
virtual void executeCall(void);
virtual void executeCallind(void);
virtual void executeCallother(void);
virtual void fallthruOp(void);
public:
EmulateFunction(Funcdata *f);
void setLoadCollect(bool val) { collectloads = val; }
virtual void setExecuteAddress(const Address &addr);
virtual uintb getVarnodeValue(Varnode *vn) const;
virtual void setVarnodeValue(Varnode *vn,uintb val);
uintb emulatePath(uintb val,const PathMeld &pathMeld,PcodeOp *startop,Varnode *startvn);
void collectLoadPoints(vector<LoadTable> &res) const;
};
class FlowInfo;
class JumpTable;
class GuardRecord {
PcodeOp *cbranch; // instruction branching around switch
int4 indpath; // branch going to switch
CircleRange range; // range of values which goto switch
Varnode *vn; // Varnode being restricted
Varnode *baseVn; // Value being (quasi)copied to vn
int4 bitsPreserved; // Number of bits copied (all other bits are zero)
public:
GuardRecord(PcodeOp *op,int4 path,const CircleRange &rng,Varnode *v);
PcodeOp *getBranch(void) const { return cbranch; }
int4 getPath(void) const { return indpath; }
const CircleRange &getRange(void) const { return range; }
bool isClear(void) const { return (cbranch == (PcodeOp *)0); }
void clear(void) { cbranch = (PcodeOp *)0; }
int4 valueMatch(Varnode *vn2,Varnode *baseVn2,int4 bitsPreserved2) const;
static int4 oneOffMatch(PcodeOp *op1,PcodeOp *op2);
static Varnode *quasiCopy(Varnode *vn,int4 &bitsPreserved,bool noWholeValue);
};
// This class represents a set of switch variables, and the values that they can take
class JumpValues {
public:
virtual ~JumpValues(void) {}
virtual void truncate(int4 nm)=0;
virtual uintb getSize(void) const=0;
virtual bool contains(uintb val) const=0;
virtual bool initializeForReading(void) const=0;
virtual bool next(void) const=0;
virtual uintb getValue(void) const=0;
virtual Varnode *getStartVarnode(void) const=0;
virtual PcodeOp *getStartOp(void) const=0;
virtual bool isReversible(void) const=0; // Can the current value be reversed to get a label
virtual JumpValues *clone(void) const=0;
};
// This class implements a single entry switch variable that can take a range of values
class JumpValuesRange : public JumpValues {
protected:
CircleRange range; // Acceptable range of values for normalvn
Varnode *normqvn;
PcodeOp *startop;
mutable uintb curval;
public:
void setRange(const CircleRange &rng) { range = rng; }
void setStartVn(Varnode *vn) { normqvn = vn; }
void setStartOp(PcodeOp *op) { startop = op; }
virtual void truncate(int4 nm); ///< Truncate the number of values to the given number
virtual uintb getSize(void) const;
virtual bool contains(uintb val) const;
virtual bool initializeForReading(void) const;
virtual bool next(void) const;
virtual uintb getValue(void) const;
virtual Varnode *getStartVarnode(void) const;
virtual PcodeOp *getStartOp(void) const;
virtual bool isReversible(void) const { return true; }
virtual JumpValues *clone(void) const;
};
// This class extends having a single entry switch variable with range and
// adds a second entry point that takes only a single value
class JumpValuesRangeDefault : public JumpValuesRange { // Range like model1, but with extra default value
uintb extravalue;
Varnode *extravn;
PcodeOp *extraop;
mutable bool lastvalue;
public:
void setExtraValue(uintb val) { extravalue = val; }
void setDefaultVn(Varnode *vn) { extravn = vn; }
void setDefaultOp(PcodeOp *op) { extraop = op; }
virtual uintb getSize(void) const;
virtual bool contains(uintb val) const;
virtual bool initializeForReading(void) const;
virtual bool next(void) const;
virtual Varnode *getStartVarnode(void) const;
virtual PcodeOp *getStartOp(void) const;
virtual bool isReversible(void) const { return !lastvalue; } // The -extravalue- is not reversible
virtual JumpValues *clone(void) const;
};
// This class represents the entire recovery process, recognizing the model, tracing
// from the switch entry to the address, and folding in guards
class JumpModel {
protected:
JumpTable *jumptable; // The jumptable that is building this model
public:
JumpModel(JumpTable *jt) { jumptable = jt; }
virtual ~JumpModel(void) {}
virtual bool isOverride(void) const=0;
virtual int4 getTableSize(void) const=0;
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize)=0;
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const=0;
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext)=0;
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const=0;
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop)=0;
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump)=0;
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable)=0;
virtual JumpModel *clone(JumpTable *jt) const=0;
virtual void clear(void) {}; // Clear any non-permanent aspects of the model
virtual void saveXml(ostream &s) const {} // For use with override models
virtual void restoreXml(const Element *el,Architecture *glb) {} // For use with override models
};
// This class treats the branch indirection variable as the switch variable, and recovers
// its possible values from the existing block structure
class JumpModelTrivial : public JumpModel {
uint4 size;
public:
JumpModelTrivial(JumpTable *jt) : JumpModel(jt) { size = 0; }
virtual bool isOverride(void) const { return false; }
virtual int4 getTableSize(void) const { return size; }
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop) {}
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
virtual JumpModel *clone(JumpTable *jt) const;
};
// This is the basic switch model. In brief
// 1) Straight-line calculation from switch variable to BRANCHIND
// 2) Switch variable is bounded by one or more "guards" that branch around the BRANCHIND
// 3) Recover unnormalized switch from bounded switch, through some basic transforms
class JumpBasic : public JumpModel {
protected:
JumpValuesRange *jrange;
PathMeld pathMeld; // Set of PcodeOps and Varnodes producing the final switch addresses
vector<GuardRecord> selectguards;
int4 varnodeIndex; // Position of the normalized switch varnode within PathMeld
Varnode *normalvn; // The normalized switch varnode
Varnode *switchvn; // The unnormalized switch varnode
static bool isprune(Varnode *vn);
static bool ispoint(Varnode *vn);
static int4 getStride(Varnode *vn); ///< Get the step/stride associated with the Varnode
static uintb backup2Switch(Funcdata *fd,uintb output,Varnode *outvn,Varnode *invn);
void findDeterminingVarnodes(PcodeOp *op,int4 slot);
void analyzeGuards(BlockBasic *bl,int4 pathout);
void calcRange(Varnode *vn,CircleRange &rng) const;
void findSmallestNormal(uint4 matchsize);
void findNormalized(Funcdata *fd,BlockBasic *rootbl,int4 pathout,uint4 matchsize,uint4 maxtablesize);
void markFoldableGuards();
virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
public:
JumpBasic(JumpTable *jt) : JumpModel(jt) { jrange = (JumpValuesRange *)0; }
const PathMeld &getPathMeld(void) const { return pathMeld; }
const JumpValuesRange *getValueRange(void) const { return jrange; }
virtual ~JumpBasic(void);
virtual bool isOverride(void) const { return false; }
virtual int4 getTableSize(void) const { return jrange->getSize(); }
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop);
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable);
virtual JumpModel *clone(JumpTable *jt) const;
virtual void clear(void);
};
// This model expects two paths to the switch, 1 from a default value, 1 from the other values that hit the switch
// If A is the guarding control-flow block, C is the block setting the default value, and S the switch block itself,
// We expect one of the following situations:
// A -> C or S and C -> S
// A -> C or D and C -> S D -> S
// C -> S and S -> A A -> S or "out of loop", i.e. S is in a loop, and the guard block doubles as the loop condition
class JumpBasic2 : public JumpBasic {
Varnode *extravn;
PathMeld origPathMeld;
bool checkNormalDominance(void) const;
virtual bool foldInOneGuard(Funcdata *fd,GuardRecord &guard,JumpTable *jump);
public:
JumpBasic2(JumpTable *jt) : JumpBasic(jt) {}
void initializeStart(const PathMeld &pathMeld);
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext);
virtual JumpModel *clone(JumpTable *jt) const;
virtual void clear(void);
};
// This is the basic model for manually specifying the list of addresses the switch goes to
// It tries to repurpose some of the analysis that JumpBasic does to recover what the switch variable
// is, but will revert to the trivial model if it can't find a suitable switch variable
class JumpBasicOverride : public JumpBasic {
set<Address> adset; // Absolute address table (manually specified)
vector<uintb> values; // Normalized switch variable values associated with addresses
vector<Address> addrtable; // Address associated with each value
uintb startingvalue; // Possible start for guessing values that match addresses
Address normaddress; // Dynamic info for recovering normalized switch variable
uint8 hash; // if (hash==0) there is no normalized switch (use trivial model)
bool istrivial; // true if we use a trivial value model
int4 findStartOp(Varnode *vn);
int4 trialNorm(Funcdata *fd,Varnode *trialvn,uint4 tolerance);
void setupTrivial(void);
Varnode *findLikelyNorm(void);
void clearCopySpecific(void);
public:
JumpBasicOverride(JumpTable *jt);
void setAddresses(const vector<Address> &adtable);
void setNorm(const Address &addr,uintb h) { normaddress = addr; hash = h; }
void setStartingValue(uintb val) { startingvalue = val; }
virtual bool isOverride(void) const { return true; }
virtual int4 getTableSize(void) const { return addrtable.size(); }
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
// findUnnormalized inherited from JumpBasic
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
// foldInNormalization inherited from JumpBasic
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump) { return false; }
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
virtual JumpModel *clone(JumpTable *jt) const;
virtual void clear(void);
virtual void saveXml(ostream &s) const;
virtual void restoreXml(const Element *el,Architecture *glb);
};
class JumpAssistOp;
// This model looks for a special "jumpassist" pseudo-op near the branch site, which contains
// p-code models describing how to parse a jump-table for case labels and addresses.
// It views the switch table calculation as a two-stage process:
// case2index: convert the switchvar to an index into a table
// index2address: convert the index to an address
// The pseudo-op holds:
// the table address, size (number of indices)
// exemplar p-code for inverting the case2index part of the calculation
// exemplar p-code for calculating index2address
class JumpAssisted : public JumpModel {
PcodeOp *assistOp;
JumpAssistOp *userop;
int4 sizeIndices; // Total number of indices in the table (not including the defaultaddress)
Varnode *switchvn; // The switch variable
public:
JumpAssisted(JumpTable *jt) : JumpModel(jt) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; sizeIndices=0; }
// virtual ~JumpAssisted(void);
virtual bool isOverride(void) const { return false; }
virtual int4 getTableSize(void) const { return sizeIndices+1; }
virtual bool recoverModel(Funcdata *fd,PcodeOp *indop,uint4 matchsize,uint4 maxtablesize);
virtual void buildAddresses(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable,vector<LoadTable> *loadpoints) const;
virtual void findUnnormalized(uint4 maxaddsub,uint4 maxleftright,uint4 maxext) {}
virtual void buildLabels(Funcdata *fd,vector<Address> &addresstable,vector<uintb> &label,const JumpModel *orig) const;
virtual void foldInNormalization(Funcdata *fd,PcodeOp *indop);
virtual bool foldInGuards(Funcdata *fd,JumpTable *jump);
virtual bool sanityCheck(Funcdata *fd,PcodeOp *indop,vector<Address> &addresstable) { return true; }
virtual JumpModel *clone(JumpTable *jt) const;
virtual void clear(void) { assistOp = (PcodeOp *)0; switchvn = (Varnode *)0; }
};
class JumpTable {
Architecture *glb; // Architecture under which this jumptable operates
JumpModel *jmodel,*origmodel;
vector<Address> addresstable; // Raw addresses in the jumptable
vector<uint4> blocktable; // Addresses converted to basic blocks
vector<uintb> label;
vector<LoadTable> loadpoints;
Address opaddress; // Absolute address of op
PcodeOp *indirect; // INDIRECT op referring to this jump table
uint4 mostcommon; // Most common position in table
uint4 maxtablesize; // Maximum table size we allow to be built (sanity check)
uint4 maxaddsub; // Maximum ADDs or SUBs to normalize
uint4 maxleftright; // Maximum shifts to normalize
uint4 maxext; // Maximum extensions to normalize
int4 recoverystage; // 0=no stages, 1=needs additional stage, 2=complete
bool collectloads;
void recoverModel(Funcdata *fd);
void trivialSwitchOver(void);
void sanityCheck(Funcdata *fd);
uint4 block2Position(const FlowBlock *bl) const;
static bool isReachable(PcodeOp *op);
public:
JumpTable(Architecture *g,Address ad=Address());
JumpTable(const JumpTable *op2);
~JumpTable(void);
bool isSwitchedOver(void) const { return !blocktable.empty(); }
bool isRecovered(void) const { return !addresstable.empty(); }
bool isLabelled(void) const { return !label.empty(); }
bool isOverride(void) const;
bool isPossibleMultistage(void) const { return (addresstable.size()==1); }
int4 getStage(void) const { return recoverystage; }
int4 numEntries(void) const { return addresstable.size(); }
int4 getMostCommon(void) const { return mostcommon; }
const Address &getOpAddress(void) const { return opaddress; }
PcodeOp *getIndirectOp(void) const { return indirect; }
void setIndirectOp(PcodeOp *ind) { opaddress = ind->getAddr(); indirect = ind; }
void setMaxTableSize(uint4 val) { maxtablesize = val; }
void setNormMax(uint4 maddsub,uint4 mleftright,uint4 mext) {
maxaddsub = maddsub; maxleftright = mleftright; maxext = mext; }
void setOverride(const vector<Address> &addrtable,const Address &naddr,uintb h,uintb sv);
int4 numIndicesByBlock(const FlowBlock *bl) const;
int4 getIndexByBlock(const FlowBlock *bl,int4 i) const;
Address getAddressByIndex(int4 index) const { return addresstable[index]; }
void setMostCommonIndex(uint4 tableind);
void setMostCommonBlock(uint4 bl) { mostcommon = bl; }
void setLoadCollect(bool val) { collectloads = val; }
void addBlockToSwitch(BlockBasic *bl,uintb lab);
void switchOver(const FlowInfo &flow);
uintb getLabelByIndex(int4 index) const { return label[index]; }
void foldInNormalization(Funcdata *fd) { jmodel->foldInNormalization(fd,indirect); }
bool foldInGuards(Funcdata *fd) { return jmodel->foldInGuards(fd,this); }
void recoverAddresses(Funcdata *fd);
void recoverMultistage(Funcdata *fd);
bool recoverLabels(Funcdata *fd);
bool checkForMultistage(Funcdata *fd);
void clear(void);
void saveXml(ostream &s) const;
void restoreXml(const Element *el);
};
#endif