Newer
Older
//
// SMPDataFlowAnalysis.cpp
//
// This module performs the fundamental data flow analyses needed for the
// SMP project (Software Memory Protection).
//
#include <vector>
#include <pro.h>
#include <ida.hpp>
#include <idp.hpp>
#include <allins.hpp>
#include <auto.hpp>
#include <bytes.hpp>
#include <funcs.hpp>
#include <intel.hpp>
#include <loader.hpp>
#include <lines.hpp>
#include <name.hpp>
#include "SMPDataFlowAnalysis.h"
#include "SMPStaticAnalyzer.h"
// Set to 1 for debugging output
#define SMP_DEBUG 1
#define SMP_DEBUG2 0 // verbose
#define SMP_DEBUG3 0 // verbose
#define SMP_DEBUG_CONTROLFLOW 0 // tells what processing stage is entered
#define SMP_DEBUG_XOR 0
#define SMP_DEBUG_CHUNKS 1 // tracking down tail chunks for functions
#define SMP_DEBUG_FRAMEFIXUP 0
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
// Used for binary search by function number in SMPStaticAnalyzer.cpp
// to trigger debugging output and find which instruction in which
// function is causing a crash.
bool SMPBinaryDebug = false;
// Define instruction categories for data flow analysis.
static SMPitype DFACategory[NN_last+1];
static char *RegNames[R_of + 1] =
{ "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI",
"R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15",
"AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH",
"SPL", "BPL", "SIL", "DIL", "EIP", "ES", "CS", "SS",
"DS", "FS", "GS", "CF", "ZF", "SF", "OF"
};
// Make the CF_CHG1 .. CF_CHG6 and CF_USE1..CF_USE6 macros more usable
// by allowing us to pick them up with an array index.
static ulong DefMacros[UA_MAXOP] = {CF_CHG1, CF_CHG2, CF_CHG3, CF_CHG4, CF_CHG5, CF_CHG6};
static ulong UseMacros[UA_MAXOP] = {CF_USE1, CF_USE2, CF_USE3, CF_USE4, CF_USE5, CF_USE6};
// Text to be printed in each optimizing annotation explaining why
// the annotation was emitted.
static char *OptExplanation[LAST_OPT_CATEGORY + 1] =
{ "NoOpt", "NoMetaUpdate", "AlwaysNUM", "NUMVia2ndSrcIMMEDNUM",
"Always1stSrc", "1stSrcVia2ndSrcIMMEDNUM", "AlwaysPtr",
"AlwaysNUM", "AlwaysNUM", "NUMViaFPRegDest"
};
// *****************************************************************
// Class DefOrUseList
// *****************************************************************
// Default constructor.
DefOrUseList::DefOrUseList(void) {
return;
}
// Set a Def or Use into the list, along with its type.
void DefOrUseList::SetRef(op_t Ref, SMPOperandType Type) {
this->Refs.push_back(Ref);
this->Types.push_back(Type);
return;
}
// Get a reference by index.
op_t DefOrUseList::GetRef(size_t index) const {
return Refs[index];
}
SMPOperandType DefOrUseList::GetRefType(size_t index) const {
return Types[index];
}
// *****************************************************************
// Class SMPInstr
// *****************************************************************
// Constructor for instruction.
SMPInstr::SMPInstr(ea_t addr) {
this->address = addr;
this->analyzed = false;
this->JumpTarget = false;
return;
}
// Is the instruction the type that terminates a basic block?
bool SMPInstr::IsBasicBlockTerminator() const {
return ((type == JUMP) || (type == COND_BRANCH)
|| (type == INDIR_JUMP) || (type == RETURN));
}
// Is the destination operand a memory reference?
bool SMPInstr::HasDestMemoryOperand(void) const {
bool MemDest = false;
for (size_t index = 0; index < Defs.GetSize(); ++index) {
MemDest = ((Defs.GetRef(index).type == o_mem)
|| (Defs.GetRef(index).type == o_phrase)
|| (Defs.GetRef(index).type == o_displ));
if (MemDest)
break;
}
return MemDest;
} // end of SMPInstr::HasDestMemoryOperand()
// Is the destination operand a memory reference?
bool SMPInstr::HasSourceMemoryOperand(void) const {
bool MemSrc = false;
for (size_t index = 0; index < Uses.GetSize(); ++index) {
MemSrc = ((Uses.GetRef(index).type == o_mem)
|| (Uses.GetRef(index).type == o_phrase)
|| (Uses.GetRef(index).type == o_displ));
if (MemSrc)
break;
}
return MemSrc;
} // end of SMPInstr::HasSourceMemoryOperand()
// Does the instruction whose flags are in F have a numeric type
// as the second source operand?
// NOTE: We can only analyze immediate values now, using a heuristic
// that values in the range +/- 8K are numeric and others are
// probably addresses. When data flow analyses are implemented,
// we will be able to analyze many non-immediate operands.
#define IMMEDNUM_LOWER -8191
#define IMMEDNUM_UPPER 8191
bool SMPInstr::IsSecondSrcOperandNumeric(flags_t F) const {
bool SecondOpImm = (SMPcmd.Operands[1].type == o_imm);
signed long TempImm;
if (SecondOpImm) {
TempImm = (signed long) SMPcmd.Operands[1].value;
}
#if SMP_DEBUG
if (SecondOpImm && (0 > TempImm)) {
#if 0
msg("Negative immediate: %d Hex: %x ASM: %s\n", TempImm,
SMPcmd.Operands[1].value, disasm);
#endif
}
else if ((!SecondOpImm) && (SMPcmd.Operands[1].type == o_imm)) {
msg("Problem with flags on immediate src operand: %s\n", disasm);
}
#endif
return (SecondOpImm && (TempImm > IMMEDNUM_LOWER)
&& (TempImm < IMMEDNUM_UPPER));
} // end of SMPInstr::IsSecondSrcOperandNumeric()
// DEBUG Print DEF and/or USE for an operand.
void PrintDefUse(ulong feature, int OpNum) {
// CF_ macros number the operands from 1 to 6, while OpNum
// is a 0 to 5 index into the insn_t.Operands[] array.
switch (OpNum) {
case 0:
if (feature & CF_CHG1)
msg(" DEF");
if (feature & CF_USE1)
msg(" USE");
break;
case 1:
if (feature & CF_CHG2)
msg(" DEF");
if (feature & CF_USE2)
msg(" USE");
break;
case 2:
if (feature & CF_CHG3)
msg(" DEF");
if (feature & CF_USE3)
msg(" USE");
break;
case 3:
if (feature & CF_CHG4)
msg(" DEF");
if (feature & CF_USE4)
msg(" USE");
break;
case 4:
if (feature & CF_CHG5)
msg(" DEF");
if (feature & CF_USE5)
msg(" USE");
break;
case 5:
if (feature & CF_CHG6)
msg(" DEF");
if (feature & CF_USE6)
msg(" USE");
break;
}
return;
} // end PrintDefUse()
// DEBUG print SIB info for an operand.
void PrintSIB(op_t Opnd) {
int BaseReg = sib_base(Opnd);
short IndexReg = sib_index(Opnd);
int ScaleFactor = sib_scale(Opnd);
#define NAME_LEN 5
char BaseName[NAME_LEN] = {'N', 'o', 'n', 'e', '\0'};
char IndexName[NAME_LEN] = {'N', 'o', 'n', 'e', '\0'};
#if 0
if (BaseReg != R_bp) // SIB code for NO BASE REG
#endif
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
if (IndexReg != R_sp) { // SIB code for NO INDEX REG
qstrncpy(IndexName, RegNames[IndexReg], NAME_LEN -1);
}
msg(" Base %s Index %s Scale %d", BaseName, IndexName, ScaleFactor);
} // end PrintSIB()
// DEBUG print operands for Inst.
void SMPInstr::PrintOperands() const {
op_t Opnd;
for (int i = 0; i < UA_MAXOP; ++i) {
Opnd = SMPcmd.Operands[i];
if (Opnd.type == o_void)
continue;
else if (Opnd.type == o_mem) {
msg(" Operand %d : memory : addr: %x", i, Opnd.addr);
PrintDefUse(features, i);
if (Opnd.hasSIB) { // has SIB info
PrintSIB(Opnd);
}
}
else if (Opnd.type == o_phrase) {
msg(" Operand %d : memory phrase :", i);
PrintDefUse(features, i);
if (Opnd.hasSIB) { // has SIB info
PrintSIB(Opnd);
}
else { // no SIB info
ushort BaseReg = Opnd.phrase;
msg(" reg %s", RegNames[BaseReg]);
}
if (Opnd.addr != 0) {
msg(" \n WARNING: addr for o_phrase type: %d\n", Opnd.addr);
}
}
else if (Opnd.type == o_displ) {
ea_t offset = Opnd.addr;
PrintDefUse(features, i);
if (Opnd.hasSIB) {
PrintSIB(Opnd);
msg(" displ %d", offset);
}
else {
ushort BaseReg = Opnd.reg;
msg(" Operand %d : memory displ : reg %s displ %d", i,
RegNames[BaseReg], offset);
}
}
else if (Opnd.type == o_reg) {
msg(" Operand %d : register", i);
msg(" regno: %d", Opnd.reg);
PrintDefUse(features, i);
}
else if (Opnd.type == o_imm) {
msg(" Operand %d : immed", i);
PrintDefUse(features, i);
}
else if (Opnd.type == o_far) {
msg(" Operand %d : FarPtrImmed", i);
msg(" addr: %x", Opnd.addr);
PrintDefUse(features, i);
}
else if (Opnd.type == o_near) {
msg(" Operand %d : NearPtrImmed", i);
msg(" addr: %x", Opnd.addr);
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
PrintDefUse(features, i);
}
else {
msg(" Operand %d : unknown", i);
PrintDefUse(features, i);
}
if (!(Opnd.showed()))
msg(" HIDDEN ");
}
msg(" \n");
return;
} // end of SMPInstr::PrintOperands()
// Print out the destination operand list for the instruction, given
// the OptCategory for the instruction as a hint.
char * SMPInstr::DestString(int OptType) {
static char DestList[MAXSTR];
if (OptType != 7) {
if (SMPcmd.Operands[0].type != o_reg) {
msg("Problem: destination operand not memory and not reg: %d %d %s \n",
SMPcmd.Operands[0].type, SMPcmd.Operands[1].type, disasm);
}
else {
ushort DestReg = SMPcmd.Operands[0].reg;
qstrncpy(DestList, RegNames[DestReg],
1 + strlen(RegNames[DestReg]));
#if 1
qstrncat(DestList, " ZZ ", MAXSTR);
#endif
return DestList;
}
}
else { // OptType 7 could have one or two destinations.
// NOTE: FIX later. Currently a clone of code above. **
#if SMP_DEBUG3
msg("OptType 7: %s\n", disasm);
PrintOperands();
#endif
if (SMPcmd.Operands[0].type != o_reg) {
msg("Problem: destination operand not memory and not reg: %d %d %s\n",
SMPcmd.Operands[0].type, SMPcmd.Operands[1].type, disasm);
}
else {
ushort DestReg = SMPcmd.Operands[0].reg;
qstrncpy(DestList, RegNames[DestReg],
1 + strlen(RegNames[DestReg]));
#if 1
qstrncat(DestList, " ZZ ", MAXSTR);
#endif
return DestList;
}
}
DestList[0] = '\0';
return DestList;
} // end of SMPInstr::DestString()
// Equality operator for SMPInstr. Key field is address.
int SMPInstr::operator==(const SMPInstr &rhs) const {
if (this->address != rhs.GetAddr())
return 0;
else
return 1;
}
// Inequality operator for SMPInstr. Key field is address.
int SMPInstr::operator!=(const SMPInstr &rhs) const {
return (this->address != rhs.GetAddr());
}
// Less than operator for sorting SMPInstr lists. Key field is address.
int SMPInstr::operator<(const SMPInstr &rhs) const {
return (this->address < rhs.GetAddr());
}
// Get optimization category for instruction
int SMPInstr::GetOptType(void) const {
return OptType;
}
// Is this instruction the one that allocates space on the
// stack for the local variables?
bool SMPInstr::MDIsFrameAllocInstr(void) const {
// The frame allocating instruction should look like:
// sub esp,48 or add esp,-64 etc.
if ((SMPcmd.itype == NN_sub) || (SMPcmd.itype == NN_add)) {
if (Defs.GetRef(0).is_reg(R_sp)) {
// We know that an addition or subtraction is being
// performed on the stack pointer. This should not be
// possible within the prologue except at the stack
// frame allocation instruction, so return true. We
// could be more robust in this analysis in the future. **!!**
// CAUTION: If a compiler allocates 64 bytes for locals
// and 16 bytes for outgoing arguments in a single
// instruction: sub esp,80
// you cannot insist on finding sub esp,LocSize
// To make this more robust, we are going to insist that
// an allocation of stack space is either performed by
// adding a negative immediate value, or by subtracting
// a positive immediate value. We will throw in, free of
// charge, a subtraction of a register, which is how alloca()
// usually allocates stack space.
if (o_imm == Uses.GetRef(0).type) {
signed long TempImm = (signed long) Uses.GetRef(0).value;
if (((0 > TempImm) && (SMPcmd.itype == NN_add))
|| ((0 < TempImm) && (SMPcmd.itype == NN_sub))) {
return true;
}
}
else if ((o_reg == Uses.GetRef(0).type)
&& (SMPcmd.itype == NN_sub)) { // alloca() ?
return true;
}
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
}
}
return false;
} // end of SMPInstr::MDIsFrameAllocInstr()
// Is this instruction in the epilogue the one that deallocates the local
// vars region of the stack frame?
bool SMPInstr::MDIsFrameDeallocInstr(bool UseFP, asize_t LocalVarsSize) const {
// The usual compiler idiom for the prologue on x86 is to
// deallocate the local var space with: mov esp,ebp
// It could be add esp,constant. We can be tricked by
// add esp,constant when the constant is just the stack
// adjustment after a call. We will have to insist that
// the immediate operand have at least the value of
// LocalVarsSize for this second form, and that UseFP be true
// for the first form.
if (UseFP && (this->SMPcmd.itype == NN_mov)
&& (this->Defs.GetRef(0).is_reg(R_sp))
&& (this->Uses.GetRef(0).is_reg(R_bp)))
return true;
else if ((this->SMPcmd.itype == NN_add)
&& (this->Defs.GetRef(0).is_reg(R_sp))
&& (this->Uses.GetRef(1).is_imm((uval_t) LocalVarsSize)))
return true;
else if ((this->SMPcmd.itype == NN_add)
&& (this->Defs.GetRef(0).is_reg(R_sp))
&& (this->Uses.GetRef(1).type == o_imm)) {
msg("Used imprecise LocalVarsSize to find dealloc instr.\n");
return true;
}
else if (NN_leave == this->SMPcmd.itype)
return true;
else
return false;
} // end of SMPInstr::MDIsFrameDeallocInstr()
// MACHINE DEPENDENT: Is instruction a return instruction?
bool SMPInstr::MDIsReturnInstr(void) const {
return ((SMPcmd.itype == NN_retn) || (SMPcmd.itype == NN_retf));
}
// MACHINE DEPENDENT: Is instruction a POP instruction?
#define FIRST_POP_INST NN_pop
#define LAST_POP_INST NN_popfq
bool SMPInstr::MDIsPopInstr(void) const {
return ((SMPcmd.itype >= FIRST_POP_INST)
&& (SMPcmd.itype <= LAST_POP_INST));
}
// MACHINE DEPENDENT: Is instruction a PUSH instruction?
#define FIRST_PUSH_INST NN_push
#define LAST_PUSH_INST NN_pushfq
bool SMPInstr::MDIsPushInstr(void) const {
return ((SMPcmd.itype >= FIRST_PUSH_INST)
&& (SMPcmd.itype <= LAST_PUSH_INST));
}
// MACHINE DEPENDENT: Does instruction use a callee-saved register?
bool SMPInstr::MDUsesCalleeSavedReg(void) const {
for (size_t index = 0; index < this->Uses.GetSize(); ++index) {
op_t CurrUse = this->GetUse(index);
if (CurrUse.is_reg(R_bp) || CurrUse.is_reg(R_si)
|| CurrUse.is_reg(R_di) || CurrUse.is_reg(R_bx)) {
return true;
}
}
return false;
} // end of SMPInstr::MDUsesCalleeSavedReg()
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
// Analyze the instruction and its operands.
void SMPInstr::Analyze(void) {
if (this->analyzed)
return;
// Fill cmd structure with disassembly of instr
ua_ana0(this->address);
// Get the instr disassembly text.
(void) generate_disasm_line(this->address, this->disasm, sizeof(this->disasm) - 1);
// Remove interactive color-coding tags.
tag_remove(this->disasm, this->disasm, 0);
// Copy cmd to member variable SMPcmd.
this->SMPcmd = cmd;
// Get the canonical features into member variables features.
this->features = cmd.get_canon_feature();
// Record what type of instruction this is, simplified for the needs
// of data flow and type analysis.
this->type = DFACategory[cmd.itype];
// Record optimization category.
this->OptType = OptCategory[cmd.itype];
// Build the DEF and USE lists for the instruction.
this->BuildSMPDefUseLists();
// Fix up machine dependent quirks in the def and use lists.
this->MDFixupDefUseLists();
// Determine whether the instruction is a jump target by looking
// at its cross references and seeing if it has "TO" code xrefs.
xrefblk_t xrefs;
for (bool ok = xrefs.first_to(this->address, XREF_FAR); ok; ok = xrefs.next_to()) {
if ((xrefs.from != 0) && (xrefs.iscode)) {
this->JumpTarget = true;
break;
}
}
this->analyzed = true;
return;
} // end of SMPInstr::Analyze()
// Fill the Defs and Uses private data members.
void SMPInstr::BuildSMPDefUseLists(void) {
size_t OpNum;
// Start with the Defs.
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
if (this->features & DefMacros[OpNum]) { // DEF
this->Defs.SetRef(this->SMPcmd.Operands[OpNum]);
}
} // end for (OpNum = 0; ...)
// Now, do the Uses. Uses have special case operations, because
// any memory operand could have register uses in the addressing
// expression, and we must create Uses for those registers. For
// example: mov eax,[ebx + esi*2 + 044Ch]
// This is a two-operand instruction with one def: eax. But
// there are three uses: [ebx + esi*2 + 044Ch], ebx, and esi.
// The first use is an op_t of type o_phrase (memory phrase),
// which can be copied from cmd.Operands[1]. Likewise, we just
// copy cmd.Operands[0] into the defs list. However, we must create
// op_t types for register ebx and register esi and append them
// to the Uses list. This is handled by the machine dependent
// method MDFixupDefUseLists().
for (OpNum = 0; OpNum < UA_MAXOP; ++OpNum) {
if (this->features & UseMacros[OpNum]) { // USE
this->Uses.SetRef(this->SMPcmd.Operands[OpNum]);
}
} // end for (OpNum = 0; ...)
return;
} // end of SMPInstr::BuildSMPDefUseLists()
// Perform machine dependent ad hoc fixes to the def and use lists.
// For example, some multiply and divide instructions in x86 implicitly
// use and/or define register EDX. For memory phrase examples, see comment
// in BuildSMPDefUseLists().
void SMPInstr::MDFixupDefUseLists(void) {
return;
}
// Emit annotations for constants used as ptr offsets from EBP or
// ESP into the stack frame. Only pay attention to EBP-relative
// offsets if EBP is being used as a frame pointer (UseFP == true).
void SMPInstr::AnnotateStackConstants(bool UseFP, FILE *AnnotFile) {
op_t Opnd;
#if 1
msg("PROBLEM INSTRUCTION: \n");
this->PrintOperands();
}
#endif
for (int i = 0; i < UA_MAXOP; ++i) {
Opnd = SMPcmd.Operands[i];
if (Opnd.type == o_displ) {
ea_t offset = Opnd.addr;
if (Opnd.hasSIB) {
int BaseReg = sib_base(Opnd);
short IndexReg = sib_index(Opnd);
if (BaseReg == R_none) {
msg("BaseReg of R_none at %x\n", this->address);
}
if (BaseReg == R_sp) { // ESP cannot be IndexReg
"%x %d PTRIMMEDESP STACK %d displ %s\n",
else if (UseFP && ((IndexReg == R_bp) || (BaseReg == R_bp))) {
"%x %d PTRIMMEDEBP STACK %d displ %s\n",
SMPcmd.ea, SMPcmd.size, offset, disasm);
}
}
else { // no SIB
ushort BaseReg = Opnd.reg;
if (BaseReg == R_sp) {
// ESP-relative constant offset
qfprintf(AnnotFile,
"%x %d PTRIMMEDESP STACK %d displ %s\n",
else if (UseFP && (BaseReg == R_bp)) {
"%x %d PTRIMMEDEBP STACK %d displ %s\n",
SMPcmd.ea, SMPcmd.size, offset, disasm);
}
} // end if (Opnd.hasSIB) ... else ...
} // end if (Opnd.type == o_displ)
else if (Opnd.type == o_phrase) {
ea_t offset = 0; // mmStrata thinks [esp] is [esp+0]
if (Opnd.hasSIB) {
int BaseReg = sib_base(Opnd);
short IndexReg = sib_index(Opnd);
if (BaseReg == R_sp) { // ESP cannot be IndexReg
"%x %d PTRIMMEDESP STACK %d displ %s\n",
else if (UseFP && ((BaseReg == R_bp) || (IndexReg == R_bp))) {
"%x %d PTRIMMEDEBP STACK %d displ %s\n",
SMPcmd.ea, SMPcmd.size, offset, disasm);
}
}
else { // Something like [ecx]
ushort BaseReg = Opnd.reg;
if (BaseReg == R_sp) {
// ESP-relative constant offset
qfprintf(AnnotFile,
"%x %d PTRIMMEDESP STACK %d displ %s\n",
else if (UseFP && (BaseReg == R_bp)) {
"%x %d PTRIMMEDEBP STACK %d displ %s\n",
SMPcmd.ea, SMPcmd.size, offset, disasm);
}
} // end if (Opnd.hasSIB) ... else ...
} // end else if (Opnd.type == o_phrase)
} // end for all operands
return;
} // end of SMPInstr::AnnotateStackConstants()
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
// Emit all annotations for the instruction.
void SMPInstr::EmitAnnotations(bool UseFP, bool AllocSeen, FILE *AnnotFile) {
ea_t addr = this->address;
flags_t InstrFlags = getFlags(addr);
bool MemDest = this->HasDestMemoryOperand();
bool MemSrc = this->HasSourceMemoryOperand();
bool SecondSrcOperandNum = this->IsSecondSrcOperandNumeric(InstrFlags);
++OptCount[OptType]; // keep count for debugging info
#if SMP_DEBUG_MEM
if (MemDest || MemSrc) {
msg("OptType: %d %s", OptType, disasm);
this->PrintOperands();
}
#endif
// Emit appropriate optimization annotations.
bool SDTInstrumentation = false;
switch (OptType) {
case 0: // SDT will have to handle these
{
#if SMP_DEBUG_TYPE0
msg("OptType 0: %x %s\n", addr, disasm);
#endif
// mmStrata wants to suppress warnings on the PUSH
// instructions that precede the LocalVarsAllocInstr
// (i.e. the PUSHes of callee-saved regs).
if (!AllocSeen && this->MDIsPushInstr()) {
qfprintf(AnnotFile, "%x %d INSTR LOCAL NoWarn %s \n",
addr, -3, disasm);
}
else {
SDTInstrumentation = true;
}
break;
}
case 1: // nothing for SDT to do
{ qfprintf(AnnotFile, "%x %d INSTR LOCAL NoMetaUpdate %s \n",
addr, -1, disasm);
++AnnotationCount[OptType];
break;
}
case 4: // INC, DEC, etc.: no SDT work unless MemDest
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%x %d INSTR LOCAL Always1stSrc %s \n",
addr, -1, disasm);
++AnnotationCount[OptType];
break;
}
case 5: // ADD, etc.: If numeric 2nd src operand, no SDT work.
{ if (MemDest || MemSrc) {
SDTInstrumentation = true;
break; // treat as category 0
}
if (SecondSrcOperandNum) { // treat as category 1
qfprintf(AnnotFile, "%x %d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
break;
}
case 6: // Only OS code should include these; problem for SDT
{ if (MemDest) {
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%x %d INSTR LOCAL AlwaysPTR %s \n",
addr, -OptType, disasm);
++AnnotationCount[OptType];
break;
}
case 8: // Implicitly writes to EDX:EAX, always numeric.
{ qfprintf(AnnotFile, "%x %d INSTR LOCAL n EDX EAX ZZ %s %s \n",
addr, -2, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
SDTInstrumentation = true;
break;
}
case 9: // Either writes to FP reg (cat. 1) or memory (cat. 0)
{ if (MemDest) {
#if SMP_DEBUG
// MemDest seems to happen too much.
msg("Floating point MemDest: %s \n", disasm);
#endif
SDTInstrumentation = true;
break; // treat as category 0
}
qfprintf(AnnotFile, "%x %d INSTR LOCAL %s %s \n",
addr, -1, OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
break;
}
default: // 2,3,7: Optimization possibilities depend on operands
{
#if SMP_DEBUG2
if (OptType == 3) { // MOV instr class
if (MemDest) {
msg("MemDest on MOV: %s\n", disasm);
}
else if (!SecondSrcOperandNum) {
msg("MOV: not 2nd op numeric: %s\n", disasm);
this->PrintOperands();
}
}
#endif
SDTInstrumentation = true;
if (MemDest) {
#if SMP_DEBUG_XOR
if (OptType == 2)
msg("MemDest on OptType 2: %s\n", disasm);
#endif
break; // treat as category 0
}
if ((OptType == 2) || (OptType == 7) || SecondSrcOperandNum) {
qfprintf(AnnotFile, "%x %d INSTR LOCAL n %s %s %s \n",
addr, -2, this->DestString(OptType),
OptExplanation[OptType], disasm);
++AnnotationCount[OptType];
}
break;
}
} // end switch (OptType)
// If mmStrata is going to have to deal with the
// instruction, then we can annotate EBP and ESP
// relative constant offsets. If we have emitted
// an annotation of type -1, there is no point
// in telling mmStrata about these constants.
if (SDTInstrumentation) {
this->AnnotateStackConstants(UseFP, AnnotFile);
}
return;
} // end of SMPInstr::EmitAnnotations()
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
// *****************************************************************
// Class SMPBasicBlock
// *****************************************************************
// Constructor
SMPBasicBlock::SMPBasicBlock(list<SMPInstr>::iterator First, list<SMPInstr>::iterator Last) {
this->FirstInstr = First;
this->LastInstr = Last;
this->IndirectJump = false;
this->Returns = false;
this->SharedTailChunk = false;
}
// Analyze basic block and fill data members.
void SMPBasicBlock::Analyze() {
if (LastInstr->GetDataFlowType() == INDIR_JUMP) {
this->IndirectJump = true;
}
else if (LastInstr->MDIsReturnInstr()) {
this->Returns = true;
}
} // end of SMPBasicBlock::Analyze()
// *****************************************************************
// Class SMPFunction
// *****************************************************************
// Constructor
SMPFunction::SMPFunction(func_t *Info) {
this->FuncInfo = Info;
IndirectCalls = false;
return;
}
// Figure out the different regions of the stack frame, and find the
// instructions that allocate and deallocate the local variables space
// on the stack frame.
// The stack frame info will be used to emit stack
// annotations when Analyze() reaches the stack allocation
// instruction that sets aside space for local vars.
// Set the address of the instruction at which these
// annotations should be emitted. This should normally
// be an instruction such as: sub esp,48
// However, for a function with no local variables at all,
// we will need to determine which instruction should be
// considered to be the final instruction of the function
// prologue and return its address.
// Likewise, we find the stack deallocating instruction in
// the function epilogue.
void SMPFunction::SetStackFrameInfo(void) {
bool FoundAllocInstr = false;
bool FoundDeallocInstr = false;
// The sizes of the three regions of the stack frame other than the
// return address are stored in the function structure.
this->LocalVarsSize = this->FuncInfo->frsize;
this->CalleeSavedRegsSize = this->FuncInfo->frregs;
this->IncomingArgsSize = this->FuncInfo->argsize;
// The return address size can be obtained in a machine independent
// way by calling get_frame_retsize().
this->RetAddrSize = get_frame_retsize(this->FuncInfo);
// IDA Pro has trouble with functions that do not have any local
// variables. Unfortunately, the C library has plenty of these
// functions. IDA usually claims that frregs is zero and frsize
// is N, when the values should have been reversed. We can attempt
// to detect this and fix it.
bool FrameInfoFixed = this->MDFixFrameInfo();
#if SMP_DEBUG_FRAMEFIXUP
if (FrameInfoFixed) {
msg("Fixed stack frame size info: %s\n", this->FuncName);
SMPBasicBlock CurrBlock = this->Blocks.front();
msg("First basic block:\n");
for (list<SMPInstr>::iterator CurrInstr = CurrBlock.GetFirstInstr();
CurrInstr != CurrBlock.GetLastInstr();
++CurrInstr) {
msg("%s\n", CurrInstr->GetDisasm());
}
msg("%s\n", CurrBlock.GetLastInstr()->GetDisasm());
}
#endif
// Now, if LocalVarsSize is not zero, we need to find the instruction
// in the function prologue that allocates space on the stack for
// local vars. This code could be made more robust in the future
// by matching LocalVarsSize to the immediate value in the allocation
// instruction. However, IDA Pro is sometimes a little off on this
// number. **!!**
if (0 < this->LocalVarsSize) {
for (list<SMPInstr>::iterator CurrInstr = this->Instrs.begin();
CurrInstr != this->Instrs.end();
++CurrInstr) {
ea_t addr = CurrInstr->GetAddr();
// Keep the most recent instruction in the DeallocInstr
// in case we reach the return without seeing a dealloc.
if (!FoundDeallocInstr) {
this->LocalVarsDeallocInstr = addr;
}
if (!FoundAllocInstr
&& CurrInstr->MDIsFrameAllocInstr()) {
this->LocalVarsAllocInstr = addr;
FoundAllocInstr = true;
// As soon as we have found the local vars allocation,
// we can try to fix incorrect sets of UseFP by IDA.
// NOTE: We might want to extend this in the future to
// handle functions that have no locals. **!!**
bool FixedUseFP = MDFixUseFP();
#if SMP_DEBUG_FRAMEFIXUP
if (FixedUseFP) {
msg("Fixed UseFP in %s\n", this->FuncName);
}
#endif
}
else if (FoundAllocInstr) {
// We can now start searching for the DeallocInstr.
if (CurrInstr->MDIsFrameDeallocInstr(UseFP, this->LocalVarsSize)) {
// Keep saving the most recent addr that looks
// like the DeallocInstr until we reach the
// end of the function. Last one to look like
// it is used as the DeallocInstr.
this->LocalVarsDeallocInstr = addr;
FoundDeallocInstr = true;
}
}
} // end for (list<SMPInstr>::iterator CurrInstr ... )
if (!FoundAllocInstr) {
// Could not find the frame allocating instruction. Bad.
// Emit diagnostic and use the first instruction in the
// function as a pseudo-allocation instruction to emit
// some stack frame info (return address, etc.)
this->LocalVarsAllocInstr = this->FindAllocPoint(this->FuncInfo->frsize);
#if SMP_DEBUG_FRAMEFIXUP
if (BADADDR == this->LocalVarsAllocInstr) {
msg("ERROR: Could not find stack frame allocation in %s\n",
FuncName);
msg("LocalVarsSize: %d SavedRegsSize: %d ArgsSize: %d\n",
LocalVarsSize, CalleeSavedRegsSize, IncomingArgsSize);
}
else {
msg("FindAllocPoint found %x for function %s\n",
this->LocalVarsAllocInstr, this->GetFuncName());
}
#endif
#if SMP_DEBUG_FIX_FRAMEINFO
if (!FoundDeallocInstr) {
// Could not find the frame deallocating instruction. Bad.
// Emit diagnostic and use the last instruction in the
// function.
msg("ERROR: Could not find stack frame deallocation in %s\n",
FuncName);
}
#endif
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
}
// else LocalVarsSize was zero, meaning that we need to search
// for the end of the function prologue code and emit stack frame
// annotations from that address (i.e. this method returns that
// address). We will approximate this by finding the end of the
// sequence of PUSH instructions at the beginning of the function.
// The last PUSH instruction should be the last callee-save-reg
// instruction. We can make this more robust in the future by
// making sure that we do not count a PUSH of anything other than
// a register. **!!**
// NOTE: 2nd prologue instr is usually mov ebp,esp
// THE ASSUMPTION THAT WE HAVE ONLY PUSH INSTRUCTIONS BEFORE
// THE ALLOCATING INSTR IS ONLY TRUE WHEN LOCALVARSSIZE == 0;
else {
ea_t SaveAddr = FuncInfo->startEA;
for (list<SMPInstr>::iterator CurrInstr = this->Instrs.begin();
CurrInstr != this->Instrs.end();
++CurrInstr) {
insn_t CurrCmd = CurrInstr->GetCmd();
ea_t addr = CurrInstr->GetAddr();
if (CurrCmd.itype == NN_push)
SaveAddr = addr;
else
break;
}
this->LocalVarsAllocInstr = SaveAddr;
this->LocalVarsDeallocInstr = 0;
} // end if (LocalVarsSize > 0) ... else ...
#if 0
// Now we need to do the corresponding operations from the
// end of the function to find the DeallocInstr in the
// function epilogue. Because there is no addition to the
// stack pointer to deallocate the local vars region, the
// function epilogue will consist of (optional) pops of
// callee-saved regs, followed by the return instruction.
// Working backwards, we should find a return and then
// stop when we do not find any more pops.
if (0 >= LocalVarsSize) {
this->LocalVarsDeallocInstr = NULL;
}
else {
SaveAddr = FuncInfo->endEA - 1;
bool FoundRet = false;
do {
ea_t addr = get_item_head(SaveAddr);
flags_t InstrFlags = getFlags(addr);
if (isCode(addr) && isHead(addr)) {
ua_ana0(addr);
if (!FoundRet) { // Just starting out.
if (MDIsReturnInstr(cmd)) {
FoundRet = true;
SaveAddr = addr - 1;
}
else {
msg("ERROR: Last instruction not a return.\n");
}
}
else { // Should be 0 or more POPs before the return.
if (MDIsPopInstr(cmd)) {
SaveAddr = addr - 1;
}
else if (FrameAllocInstr(cmd, this->LocalVarsSize)) {
this->LocalVarsDeallocInstr = addr;
}
else {
msg("ERROR: Frame deallocation not prior to POPs.\n");
this->LocalVarsDeallocInstr = SaveAddr + 1;
}
} // end if (!FoundRet) ... else ...
}
else {
--SaveAddr;
} // end if (isCode(addr) && isHead(addr))
} while (NULL == this->LocalVarsDeallocInstr);
} // end if (0 >= this->LocalVarsSize)
#endif // 0
return;
} // end of SMPFunction::SetStackFrameInfo()
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
// IDA Pro defines the sizes of regions in the stack frame in a way
// that suits its purposes but not ours. the frsize field of the func_info_t
// structure measures the distance between the stack pointer and the
// frame pointer (ESP and EBP in the x86). This region includes some
// of the callee-saved registers. So, the frregs field only includes
// the callee-saved registers that are above the frame pointer.
// x86 standard prologue on gcc/linux:
// push ebp ; save old frame pointer
// mov ebp,esp ; new frame pointer = current stack pointer
// push esi ; callee save reg
// push edi ; callee save reg
// sub esp,34h ; allocate 52 bytes for local variables
//
// Notice that EBP acquires its final frame pointer value AFTER the
// old EBP has been pushed. This means that, of the three callee saved
// registers, one is above where EBP points and two are below.
// IDA Pro is concerned with generating readable addressing expressions
// for items on the stack. None of the callee-saved regs will ever
// be addressed in the function; they will be dormant until they are popped
// off the stack in the function epilogue. In order to create readable
// disassembled code, IDA defines named constant offsets for locals. These
// offsets are negative values (x86 stack grows downward from EBP toward
// ESP). When ESP_relative addressing occurs, IDA converts a statement:
// mov eax,[esp+12]
// into the statement:
// mov eax,[esp+3Ch+var_30]
// Here, 3Ch == 60 decimal is the distance between ESP and EBP, and
// var_30 is defined to ahve the value -30h == -48 decimal. So, the
// "frame size" in IDA Pro is 60 bytes, and a certain local can be
// addressed in ESP-relative manner as shown, or as [ebp+var_30] for
// EBP-relative addressing. The interactive IDA user can then edit
// the name var_30 to something mnemonic, such as "virus_size", and IDA
// will replace all occurrences with the new name, so that code references
// automatically become [ebp+virus_size]. As the user proceeds
// interactively, he eventually produces very understandable code.
// This all makes sense for producing readable assembly text. However,
// our analyses have a compiler perspective as well as a memory access
// defense perspective. SMP distinguishes between callee saved regs,
// which should not be overwritten in the function body, and local
// variables, which can be written. We view the stack frame in logical
// pieces: here are the saved regs, here are the locals, here is the
// return address, etc. We don't care which direction from EBP the
// callee-saved registers lie; we don't want to lump them in with the
// local variables. We also don't like the fact that IDA Pro will take
// the function prologue code shown above and declare frregs=4 and
// frsize=60, because frsize no longer matches the stack allocation
// statement sub esp,34h == sub esp,52. We prefer frsize=52 and frregs=12.
// So, the task of this function is to fix these stack sizes in our
// private data members for the function, while leaving the IDA database
// alone because IDA needs to maintain its own definitions of these
// variables.
// Fixing means we will update the data members LocalVarsSize and
// CalleeSavedRegsSize.
// NOTE: This function is both machine dependent and platform dependent.
// The prologue and epilogue code generated by gcc-linux is as discussed
// above, while on Visual Studio and other Windows x86 compilers, the
// saving of registers other than EBP happens AFTER local stack allocation.
// A Windows version of the function would expect to see the pushing
// of ESI and EDI AFTER the sub esp,34h statement.
int SavedRegsSize = 0;
int OtherPushesSize = 0; // besides callee-saved regs
int NewLocalsSize = 0;
int OldFrameTotal = this->CalleeSavedRegsSize + this->LocalVarsSize;
bool Changed = false;
// Iterate through the first basic block in the function. If we find
// a frame allocating Instr in it, then we have local vars. If not,
// we don't, and LocalVarsSize should have been zero. Count the callee
// register saves leading up to the local allocation. Set data members
// according to what we found if the values of the data members would
// change.
SMPBasicBlock CurrBlock = this->Blocks.front();
for (list<SMPInstr>::iterator CurrInstr = CurrBlock.GetFirstInstr();
CurrInstr != CurrBlock.GetLastInstr(); // LastInstr is jump anyway
++CurrInstr) {
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
if (CurrInstr->MDIsPushInstr()) {
// We will make the gcc-linux assumption that a PUSH in
// the first basic block, prior to the stack allocating
// instruction, is a callee register save. To make this
// more robust, we should ensure that the register is from
// the callee saved group of registers, and that it has
// not been defined thus far in the function (else it might
// be a push of an outgoing argument to a call that happens
// in the first block when there are no locals). **!!!!**
if (CurrInstr->MDUsesCalleeSavedReg()
&& !CurrInstr->HasSourceMemoryOperand()) {
SavedRegsSize += 4; // **!!** should check the size
}
else {
// Pushes of outgoing args can be scheduled so that
// they are mixed with the pushes of callee saved regs.
OtherPushesSize += 4;
}
}
else if (CurrInstr->MDIsFrameAllocInstr()) {
SavedRegsSize += OtherPushesSize;
// Get the size being allocated.
for (size_t index = 0; index < CurrInstr->NumUses(); ++index) {
// Find the immediate operand.
if (o_imm == CurrInstr->GetUse(index).type) {
// Get its value into LocalVarsSize.
long AllocValue = (signed long) CurrInstr->GetUse(index).value;
// One compiler might have sub esp,24 and another
// might have add esp,-24. Take the absolute value.
if (0 > AllocValue)
AllocValue = -AllocValue;
if (AllocValue != (long) this->LocalVarsSize) {
Changed = true;
#if SMP_DEBUG_FRAMEFIXUP
if (AllocValue + SavedRegsSize != OldFrameTotal)
msg("Total frame size changed: %s\n", this->FuncName);
#endif
this->LocalVarsSize = (asize_t) AllocValue;
this->CalleeSavedRegsSize = (ushort) SavedRegsSize;
NewLocalsSize = this->LocalVarsSize;
}
else { // Old value was correct; no change.
NewLocalsSize = this->LocalVarsSize;
if (SavedRegsSize != this->CalleeSavedRegsSize) {
this->CalleeSavedRegsSize = (ushort) SavedRegsSize;
Changed = true;
#if SMP_DEBUG_FRAMEFIXUP
msg("Only callee regs size changed: %s\n", this->FuncName);
#endif
}
}
} // end if (o_imm == ...)
} // end for all uses
break; // After frame allocation instr, we are done
} // end if (push) .. elsif frame allocating instr
} // end for all instructions in the first basic block
// If we did not find an allocating instruction, see if it would keep
// the total size the same to set LocalVarsSize to 0 and to set
// CalleeSavedRegsSize to SavedRegsSize. If so, do it. If not, we
// might be better off to leave the numbers alone.
if (!Changed && (NewLocalsSize == 0)) {
if (OldFrameTotal == SavedRegsSize) {
this->CalleeSavedRegsSize = SavedRegsSize;
this->LocalVarsSize = 0;
Changed = true;
}
#if SMP_DEBUG_FRAMEFIXUP
else {
msg("Could not update frame sizes: %s\n", this->FuncName);
}
#endif
#if SMP_DEBUG_FRAMEFIXUP
if ((0 < OtherPushesSize) && (0 < NewLocalsSize))
msg("Extra pushes found of size %d in %s\n", OtherPushesSize,
this->FuncName);
#endif
return Changed;
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
// Some functions have difficult to find stack allocations. For example, in some
// version of glibc, strpbrk() zeroes out register ECX and then pushes it more than
// 100 times in order to allocate zero-ed out local vars space for a character translation
// table. We will use the stack pointer analysis of IDA to find out if there is a point
// in the first basic block at which the stack pointer reaches the allocation total
// that IDA is expecting for the local vars region.
// If so, we return the address of the instruction at which ESP reaches its value, else
// we return BADADDR.
ea_t SMPFunction::FindAllocPoint(asize_t OriginalLocSize) {
bool DebugFlag = (0 == strncmp("strpbrk", this->GetFuncName(), 7));
sval_t TargetSize = - ((sval_t) OriginalLocSize); // negate; stack grows down
#if SMP_DEBUG_FRAMEFIXUP
if (DebugFlag)
msg("strpbrk OriginalLocSize: %d\n", OriginalLocSize);
#endif
if (this->FuncInfo->analyzed_sp()) {
// Limit our analysis to the first basic block in the function.
ea_t AddrLimit = this->Blocks.front().GetLastInstr()->GetAddr();
for (list<SMPInstr>::iterator CurrInstr = this->Blocks.front().GetFirstInstr();
CurrInstr != this->Blocks.front().GetLastInstr();
++CurrInstr) {
ea_t addr = CurrInstr->GetAddr();
// get_spd() returns a cumulative delta of ESP
sval_t sp_delta = get_spd(this->FuncInfo, addr);
#if SMP_DEBUG_FRAMEFIXUP
if (DebugFlag)
msg("strpbrk delta: %d at %x\n", sp_delta, addr);
#endif
if (sp_delta == TargetSize) {
// Previous instruction hit the frame size.
if (CurrInstr == this->Blocks.front().GetFirstInstr()) {
return BADADDR; // cannot back up from first instruction
}
else {
return (--CurrInstr)->GetAddr();
}
}
}
// SP delta is marked at the beginning of an instruction to show the SP
// after the effects of the previous instruction. Maybe the last instruction
// is the first time the SP shows its desired value.
list<SMPInstr>::iterator FinalInstr = this->Blocks.front().GetLastInstr();
ea_t FinalAddr = FinalInstr->GetAddr();
sval_t FinalDelta = get_spd(this->FuncInfo, FinalAddr);
#if SMP_DEBUG_FRAMEFIXUP
if (DebugFlag)
msg("strpbrk FinalDelta: %d\n", FinalDelta);
#endif
if (TargetSize == FinalDelta) {
// Back up one instruction
if (FinalAddr == this->Blocks.front().GetFirstInstr()->GetAddr()) {
return BADADDR; // cannot back up from first instruction
}
else {
return (--FinalInstr)->GetAddr();
}
}
else if (!FinalInstr->IsBasicBlockTerminator()) {
// Special case. The basic block does not terminate with a branch or
// return, but falls through to the start of a loop, most likely.
// Thus, the last instruction CAN increase the sp_delta, unlike
// a jump or branch, and the sp_delta would not hit the target until
// the first instruction in the second block. We can examine the
// effect on the stack pointer of this last instruction to see if it
// causes the SP delta to hit the OriginalLocSize.
sval_t LastInstrDelta = get_sp_delta(this->FuncInfo, FinalAddr);
if (TargetSize == (FinalDelta + LastInstrDelta)) {
// Return very last instruction (don't back up 1 here)
return FinalAddr;
}
}
} // end if (this->FuncInfo->analyzed_sp())
#if SMP_DEBUG_FRAMEFIXUP
else {
msg("analyzed_sp() is false for %s\n", this->GetFuncName());
}
#endif
return BADADDR;
} // end of SMPFunction::FindAllocPoint()
// IDA Pro is sometimes confused by a function that uses the frame pointer
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
// register for other purposes. For the x86, a function that uses EBP
// as a frame pointer would begin with: push ebp; mov ebp,esp to save
// the old value of EBP and give it a new value as a frame pointer. The
// allocation of local variable space would have to come AFTER the move
// instruction. A function that begins: push ebp; push esi; sub esp,24
// is obviously not using EBP as a frame pointer. IDA is apparently
// confused by the push ebp instruction being the first instruction
// in the function. We will reset UseFP to false in this case.
// NOTE: This logic should work for both Linux and Windows x86 prologues.
bool SMPFunction::MDFixUseFP(void) {
list<SMPInstr>::iterator CurrInstr = this->Instrs.begin();
ea_t addr = CurrInstr->GetAddr();
if (!UseFP)
return false; // Only looking to reset true to false.
while (addr < this->LocalVarsAllocInstr) {
size_t DefIndex = 0;
while (DefIndex < CurrInstr->NumDefs()) {
if (CurrInstr->GetDef(DefIndex).is_reg(R_bp))
return false; // EBP got set before locals were allocated
++DefIndex;
}
++CurrInstr;
addr = CurrInstr->GetAddr();
}
// If we found no defs of the frame pointer before the local vars
// allocation, then the frame pointer register is not being used
// as a frame pointer, just as a general callee-saved register.
this->UseFP = false;
return true;
} // end of SMPFunction::MDFixUseFP()
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
// Emit the annotations describing the regions of the stack frame.
void SMPFunction::EmitStackFrameAnnotations(FILE *AnnotFile, list<SMPInstr>::iterator Instr) {
ea_t addr = Instr->GetAddr();
if (0 < IncomingArgsSize)
qfprintf(AnnotFile, "%x %d INARGS STACK esp + %d %s \n",
addr, IncomingArgsSize,
(LocalVarsSize + CalleeSavedRegsSize + RetAddrSize),
Instr->GetDisasm());
if (0 < RetAddrSize)
qfprintf(AnnotFile, "%x %d MEMORYHOLE STACK esp + %d ReturnAddress \n",
addr, RetAddrSize, (LocalVarsSize + CalleeSavedRegsSize));
if (0 < CalleeSavedRegsSize)
qfprintf(AnnotFile, "%x %d MEMORYHOLE STACK esp + %d CalleeSavedRegs \n",
addr, CalleeSavedRegsSize, LocalVarsSize);
if (0 < LocalVarsSize)
qfprintf(AnnotFile, "%x %d LOCALFRAME STACK esp + %d LocalVars \n",
addr, LocalVarsSize, 0);
return;
} // end of SMPFunction::EmitStackFrameAnnotations()
// Main data flow analysis driver. Goes through the function and
// fills all objects for instructions, basic blocks, and the function
// itself.
void SMPFunction::Analyze(void) {
list<SMPInstr>::iterator FirstInBlock = this->Instrs.end();
// For starting a basic block
list<SMPInstr>::iterator LastInBlock = this->Instrs.end();
// Terminating a basic block
#if SMP_DEBUG_CONTROLFLOW
msg("Entering SMPFunction::Analyze.\n");
#endif
// Get some basic info from the FuncInfo structure.
this->Size = this->FuncInfo->endEA - this->FuncInfo->startEA;
this->UseFP = (0 != (this->FuncInfo->flags & (FUNC_FRAME | FUNC_BOTTOMBP)));
this->StaticFunc = (0 != (this->FuncInfo->flags & FUNC_STATIC));
get_func_name(this->FuncInfo->startEA, this->FuncName,
sizeof(this->FuncName) - 1);
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: got basic info.\n");
#endif
// Cycle through all chunks that belong to the function.
func_tail_iterator_t FuncTail(this->FuncInfo);
size_t ChunkCounter = 0;
for (bool ChunkOK = FuncTail.main(); ChunkOK; ChunkOK = FuncTail.next()) {
const area_t &CurrChunk = FuncTail.chunk();
++ChunkCounter;
#if SMP_DEBUG_CHUNKS
if (1 < ChunkCounter)
msg("Found tail chunk for %s at %x\n", this->FuncName, CurrChunk.startEA);
#endif
// Build the instruction and block lists for the function.
for (ea_t addr = CurrChunk.startEA; addr < CurrChunk.endEA;
addr = get_item_end(addr)) {
flags_t InstrFlags = getFlags(addr);
if (isHead(InstrFlags) && isCode(InstrFlags)) {
SMPInstr CurrInst = SMPInstr(addr);
// Fill in the instruction data members.
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: calling CurrInst::Analyze.\n");
#endif
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
CurrInst.Analyze();
if (SMPBinaryDebug) {
msg("Disasm: %s \n", CurrInst.GetDisasm());
}
if (CurrInst.GetDataFlowType() == INDIR_CALL)
this->IndirectCalls = true;
// Before we insert the instruction into the instruction
// list, determine if it is a jump target that does not
// follow a basic block terminator. This is the special case
// of a CASE in a SWITCH that falls through into another
// CASE, for example. The first sequence of statements
// was not terminated by a C "break;" statement, so it
// looks like straight line code, but there is an entry
// point at the beginning of the second CASE sequence and
// we have to split basic blocks at the entry point.
if ((FirstInBlock != this->Instrs.end())
&& CurrInst.IsJumpTarget()) {
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: hit special jump target case.\n");
#endif
LastInBlock = --(this->Instrs.end());
SMPBasicBlock CurrBlock = SMPBasicBlock(FirstInBlock,
LastInBlock);
CurrBlock.Analyze();
// If not the first chunk in the function, it is a shared
// tail chunk.
if (ChunkCounter > 1) {
CurrBlock.SetShared();
}
FirstInBlock = this->Instrs.end();
LastInBlock = this->Instrs.end();
this->Blocks.push_back(CurrBlock);
}
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: putting CurrInst on list.\n");
#endif
// Insert instruction at end of list.
this->Instrs.push_back(CurrInst);
// Find basic block leaders and terminators.
if (FirstInBlock == this->Instrs.end()) {
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: setting FirstInBlock.\n");
#endif
FirstInBlock = --(this->Instrs.end());
}
if (CurrInst.IsBasicBlockTerminator()) {
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: found block terminator.\n");
#endif
LastInBlock = --(this->Instrs.end());
SMPBasicBlock CurrBlock = SMPBasicBlock(FirstInBlock, LastInBlock);
CurrBlock.Analyze();
// If not the first chunk in the function, it is a shared
// tail chunk.
if (ChunkCounter > 1) {
CurrBlock.SetShared();
}
FirstInBlock = this->Instrs.end();
LastInBlock = this->Instrs.end();
this->Blocks.push_back(CurrBlock);
}
} // end if (isHead(InstrFlags) && isCode(InstrFlags)
} // end for (ea_t addr = FuncInfo->startEA; ... )
// Handle the special case in which a function does not terminate
// with a return instruction or any other basic block terminator.
// Sometimes IDA Pro sees a call to a NORET function and decides
// to not include the dead code after it in the function. That
// dead code includes the return instruction, so the function no
// longer includes a return instruction and terminates with a CALL.
if (FirstInBlock != this->Instrs.end()) {
LastInBlock = --(this->Instrs.end());
SMPBasicBlock CurrBlock = SMPBasicBlock(FirstInBlock, LastInBlock);
CurrBlock.Analyze();
// If not the first chunk in the function, it is a shared
// tail chunk.
if (ChunkCounter > 1) {
CurrBlock.SetShared();
}
FirstInBlock = this->Instrs.end();
LastInBlock = this->Instrs.end();
this->Blocks.push_back(CurrBlock);
}
} // end for (bool ChunkOK = ...)
#if SMP_DEBUG_CONTROLFLOW
msg("SMPFunction::Analyze: set stack frame info.\n");
#endif
// Figure out the stack frame and related info.
this->SetStackFrameInfo();
return;
} // end of SMPFunction::Analyze()
// Emit all annotations for the function, including all per-instruction
// annotations.
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
void SMPFunction::EmitAnnotations(FILE *AnnotFile) {
// Emit annotation for the function as a whole.
if (this->StaticFunc) {
qfprintf(AnnotFile, "%x %d FUNC LOCAL %s ", FuncInfo->startEA,
this->Size, this->FuncName);
}
else {
qfprintf(AnnotFile, "%x %d FUNC GLOBAL %s ", FuncInfo->startEA,
this->Size, this->FuncName);
}
if (this->UseFP) {
qfprintf(AnnotFile, "USEFP ");
}
else {
qfprintf(AnnotFile, "NOFP ");
}
if (this->FuncInfo->does_return()) {
qfprintf(AnnotFile, "\n");
}
else {
qfprintf(AnnotFile, "NORET \n");
}
// Loop through all instructions in the function.
// Output optimization annotations for those
// instructions that do not require full computation
// of their memory metadata by the Memory Monitor SDT.
list<SMPInstr>::iterator CurrInst;
bool AllocSeen = false; // Reached LocalVarsAllocInstr yet?
bool DeallocTrigger = false;
for (CurrInst = Instrs.begin(); CurrInst != Instrs.end(); ++CurrInst) {
ea_t addr = CurrInst->GetAddr();
if (this->LocalVarsAllocInstr == addr) {
this->EmitStackFrameAnnotations(AnnotFile, CurrInst);
}
// If this is the instruction which deallocated space
// for local variables, we set a flag to remind us to
// emit an annotation on the next instruction.
// mmStrata wants the instruction AFTER the
// deallocating instruction, so that it processes
// the deallocation after it happens. It inserts
// instrumentation before an instruction, not
// after, so it will insert the deallocating
// instrumentation before the first POP of callee-saved regs,
// if there are any, or before the return, otherwise.
if (addr == LocalVarsDeallocInstr) {
DeallocTrigger = true;
}
else if (DeallocTrigger) { // Time for annotation
qfprintf(AnnotFile, "%x %d DEALLOC STACK esp - %d %s\n", addr,
LocalVarsSize, LocalVarsSize, CurrInst->GetDisasm());
CurrInst->EmitAnnotations(this->UseFP, AllocSeen, AnnotFile);
} // end for (ea_t addr = FuncInfo->startEA; ...)
return;
} // end of SMPFunction::EmitAnnotations()
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
// Initialize the DFACategory[] array to define instruction classes
// for the purposes of data flow analysis.
void InitDFACategory(void) {
// Default category is 0, not the start or end of a basic block.
(void) memset(DFACategory, 0, sizeof(DFACategory));
DFACategory[NN_call] = CALL; // Call Procedure
DFACategory[NN_callfi] = INDIR_CALL; // Indirect Call Far Procedure
DFACategory[NN_callni] = INDIR_CALL; // Indirect Call Near Procedure
DFACategory[NN_hlt] = HALT; // Halt
DFACategory[NN_int] = CALL; // Call to Interrupt Procedure
DFACategory[NN_into] = CALL; // Call to Interrupt Procedure if Overflow Flag = 1
DFACategory[NN_int3] = CALL; // Trap to Debugger
DFACategory[NN_iretw] = RETURN; // Interrupt Return
DFACategory[NN_iret] = RETURN; // Interrupt Return
DFACategory[NN_iretd] = RETURN; // Interrupt Return (use32)
DFACategory[NN_iretq] = RETURN; // Interrupt Return (use64)
DFACategory[NN_ja] = COND_BRANCH; // Jump if Above (CF=0 & ZF=0)
DFACategory[NN_jae] = COND_BRANCH; // Jump if Above or Equal (CF=0)
DFACategory[NN_jb] = COND_BRANCH; // Jump if Below (CF=1)
DFACategory[NN_jbe] = COND_BRANCH; // Jump if Below or Equal (CF=1 | ZF=1)
DFACategory[NN_jc] = COND_BRANCH; // Jump if Carry (CF=1)
DFACategory[NN_jcxz] = COND_BRANCH; // Jump if CX is 0
DFACategory[NN_jecxz] = COND_BRANCH; // Jump if ECX is 0
DFACategory[NN_jrcxz] = COND_BRANCH; // Jump if RCX is 0
DFACategory[NN_je] = COND_BRANCH; // Jump if Equal (ZF=1)
DFACategory[NN_jg] = COND_BRANCH; // Jump if Greater (ZF=0 & SF=OF)
DFACategory[NN_jge] = COND_BRANCH; // Jump if Greater or Equal (SF=OF)
DFACategory[NN_jl] = COND_BRANCH; // Jump if Less (SF!=OF)
DFACategory[NN_jle] = COND_BRANCH; // Jump if Less or Equal (ZF=1 | SF!=OF)
DFACategory[NN_jna] = COND_BRANCH; // Jump if Not Above (CF=1 | ZF=1)
DFACategory[NN_jnae] = COND_BRANCH; // Jump if Not Above or Equal (CF=1)
DFACategory[NN_jnb] = COND_BRANCH; // Jump if Not Below (CF=0)
DFACategory[NN_jnbe] = COND_BRANCH; // Jump if Not Below or Equal (CF=0 & ZF=0)
DFACategory[NN_jnc] = COND_BRANCH; // Jump if Not Carry (CF=0)
DFACategory[NN_jne] = COND_BRANCH; // Jump if Not Equal (ZF=0)
DFACategory[NN_jng] = COND_BRANCH; // Jump if Not Greater (ZF=1 | SF!=OF)
DFACategory[NN_jnge] = COND_BRANCH; // Jump if Not Greater or Equal (ZF=1)
DFACategory[NN_jnl] = COND_BRANCH; // Jump if Not Less (SF=OF)
DFACategory[NN_jnle] = COND_BRANCH; // Jump if Not Less or Equal (ZF=0 & SF=OF)
DFACategory[NN_jno] = COND_BRANCH; // Jump if Not Overflow (OF=0)
DFACategory[NN_jnp] = COND_BRANCH; // Jump if Not Parity (PF=0)
DFACategory[NN_jns] = COND_BRANCH; // Jump if Not Sign (SF=0)
DFACategory[NN_jnz] = COND_BRANCH; // Jump if Not Zero (ZF=0)
DFACategory[NN_jo] = COND_BRANCH; // Jump if Overflow (OF=1)
DFACategory[NN_jp] = COND_BRANCH; // Jump if Parity (PF=1)
DFACategory[NN_jpe] = COND_BRANCH; // Jump if Parity Even (PF=1)
DFACategory[NN_jpo] = COND_BRANCH; // Jump if Parity Odd (PF=0)
DFACategory[NN_js] = COND_BRANCH; // Jump if Sign (SF=1)
DFACategory[NN_jz] = COND_BRANCH; // Jump if Zero (ZF=1)
DFACategory[NN_jmp] = JUMP; // Jump
DFACategory[NN_jmpfi] = INDIR_JUMP; // Indirect Far Jump
DFACategory[NN_jmpni] = INDIR_JUMP; // Indirect Near Jump
DFACategory[NN_jmpshort] = JUMP; // Jump Short (only in 64-bit mode)
DFACategory[NN_loopw] = COND_BRANCH; // Loop while ECX != 0
DFACategory[NN_loop] = COND_BRANCH; // Loop while CX != 0
DFACategory[NN_loopd] = COND_BRANCH; // Loop while ECX != 0
DFACategory[NN_loopq] = COND_BRANCH; // Loop while RCX != 0
DFACategory[NN_loopwe] = COND_BRANCH; // Loop while CX != 0 and ZF=1
DFACategory[NN_loope] = COND_BRANCH; // Loop while rCX != 0 and ZF=1
DFACategory[NN_loopde] = COND_BRANCH; // Loop while ECX != 0 and ZF=1
DFACategory[NN_loopqe] = COND_BRANCH; // Loop while RCX != 0 and ZF=1
DFACategory[NN_loopwne] = COND_BRANCH; // Loop while CX != 0 and ZF=0
DFACategory[NN_loopne] = COND_BRANCH; // Loop while rCX != 0 and ZF=0
DFACategory[NN_loopdne] = COND_BRANCH; // Loop while ECX != 0 and ZF=0
DFACategory[NN_loopqne] = COND_BRANCH; // Loop while RCX != 0 and ZF=0
DFACategory[NN_retn] = RETURN; // Return Near from Procedure
DFACategory[NN_retf] = RETURN; // Return Far from Procedure
//
// Pentium instructions
//
DFACategory[NN_rsm] = HALT; // Resume from System Management Mode
// Pentium II instructions
DFACategory[NN_sysenter] = CALL; // Fast Transition to System Call Entry Point
DFACategory[NN_sysexit] = CALL; // Fast Transition from System Call Entry Point
// AMD syscall/sysret instructions NOTE: not AMD, found in Intel manual
DFACategory[NN_syscall] = CALL; // Low latency system call
DFACategory[NN_sysret] = CALL; // Return from system call
// VMX instructions
DFACategory[NN_vmcall] = INDIR_CALL; // Call to VM Monitor
return;
} // end InitDFACategory()