1 #include "disassemblerbase.h"
2 #include "../database/signaturedb.h"
3 #include "../graph/functiongraph.h"
4 #include <cctype>
5
6 namespace REDasm {
7
DisassemblerBase(AssemblerPlugin * assembler,LoaderPlugin * loader)8 DisassemblerBase::DisassemblerBase(AssemblerPlugin* assembler, LoaderPlugin *loader): DisassemblerAPI()
9 {
10 m_loader = std::unique_ptr<LoaderPlugin>(loader);
11 m_assembler = std::unique_ptr<AssemblerPlugin>(assembler);
12 }
13
loader() const14 LoaderPlugin *DisassemblerBase::loader() const { return m_loader.get(); }
assembler() const15 AssemblerPlugin *DisassemblerBase::assembler() const { return m_assembler.get(); }
document() const16 const ListingDocument& DisassemblerBase::document() const { return m_loader->document(); }
document()17 ListingDocument& DisassemblerBase::document() { return m_loader->document(); }
getReferences(address_t address) const18 ReferenceVector DisassemblerBase::getReferences(address_t address) const { return m_referencetable.referencesToVector(address); }
getTargets(address_t address) const19 ReferenceSet DisassemblerBase::getTargets(address_t address) const { return m_referencetable.targets(address); }
20
getCalls(address_t address)21 ListingItems DisassemblerBase::getCalls(address_t address)
22 {
23 auto& document = this->document();
24 auto it = document->functionStartItem(address);
25 ListingItems calls;
26
27 for( ; it != document->end(); it++)
28 {
29 ListingItem* item = it->get();
30
31 if(item->is(ListingItem::InstructionItem))
32 {
33 InstructionPtr instruction = document->instruction(item->address);
34
35 if(!instruction->is(InstructionType::Call))
36 continue;
37
38 calls.push_back(item);
39 }
40 else if(item->is(ListingItem::SymbolItem))
41 {
42 const Symbol* symbol = document->symbol(item->address);
43
44 if(!symbol->is(SymbolType::Code))
45 break;
46 }
47 else if(item->is(ListingItem::FunctionItem) && (item->address == address)) // Skip declaration
48 continue;
49 else
50 break;
51 }
52
53 return calls;
54 }
55
getTarget(address_t address) const56 address_location DisassemblerBase::getTarget(address_t address) const { return m_referencetable.target(address); }
getTargetsCount(address_t address) const57 u64 DisassemblerBase::getTargetsCount(address_t address) const { return m_referencetable.targetsCount(address); }
getReferencesCount(address_t address) const58 u64 DisassemblerBase::getReferencesCount(address_t address) const { return m_referencetable.referencesCount(address); }
59
computeBasicBlocks()60 void DisassemblerBase::computeBasicBlocks()
61 {
62 auto lock = x_lock_safe_ptr(m_loader->document());
63 lock->functions().invalidateGraphs();
64
65 for(const ListingItem* item : lock->functions())
66 this->computeBasicBlocks(lock, item);
67 }
68
popTarget(address_t address,address_t pointedby)69 void DisassemblerBase::popTarget(address_t address, address_t pointedby) { m_referencetable.popTarget(address, pointedby); }
pushTarget(address_t address,address_t pointedby)70 void DisassemblerBase::pushTarget(address_t address, address_t pointedby) { m_referencetable.pushTarget(address, pointedby); }
pushReference(address_t address,address_t refby)71 void DisassemblerBase::pushReference(address_t address, address_t refby) { m_referencetable.push(address, refby); }
72
checkLocation(address_t fromaddress,address_t address)73 void DisassemblerBase::checkLocation(address_t fromaddress, address_t address)
74 {
75 Segment* segment = this->document()->segment(address);
76
77 if(!segment || this->checkString(fromaddress, address))
78 return;
79
80 if(!this->document()->symbol(address))
81 this->document()->symbol(address, SymbolType::Data);
82
83 this->pushReference(address, fromaddress);
84 }
85
checkString(address_t fromaddress,address_t address)86 bool DisassemblerBase::checkString(address_t fromaddress, address_t address)
87 {
88 bool wide = false;
89
90 if(this->locationIsString(address, &wide) < MIN_STRING)
91 return false;
92
93 if(wide)
94 {
95 this->document()->symbol(address, SymbolType::WideString);
96 this->document()->autoComment(fromaddress, "WIDE STRING: " + REDasm::quoted(this->readWString(address)));
97 }
98 else
99 {
100 this->document()->symbol(address, SymbolType::String);
101 this->document()->autoComment(fromaddress, "STRING: " + REDasm::quoted(this->readString(address)));
102 }
103
104 this->pushReference(address, fromaddress);
105 return true;
106 }
107
checkAddressTable(const InstructionPtr & instruction,address_t startaddress)108 s64 DisassemblerBase::checkAddressTable(const InstructionPtr &instruction, address_t startaddress)
109 {
110 Symbol* symbol = this->document()->symbol(startaddress);
111
112 if(symbol && symbol->is(SymbolType::TableItemMask))
113 return -1;
114
115 address_t target = 0, address = startaddress;
116
117 if(!this->readAddress(address, m_assembler->addressWidth(), &target))
118 return 0;
119
120 REDasm::statusAddress("Checking address table", startaddress);
121 std::unordered_set<address_t> targets;
122
123 while(this->readAddress(address, m_assembler->addressWidth(), &target))
124 {
125 const Segment* segment = this->document()->segment(target);
126
127 if(!segment || !segment->is(SegmentType::Code))
128 break;
129
130 targets.insert(target);
131
132 if(instruction->is(InstructionType::Branch))
133 this->pushTarget(target, instruction->address);
134 else
135 this->checkLocation(startaddress, target);
136
137 address += m_assembler->addressWidth();
138 }
139
140 if(!targets.empty())
141 {
142 if(targets.size() > 1)
143 {
144 u64 i = 0;
145 address = startaddress;
146
147 for(auto it = targets.begin(); it != targets.end(); it++, address += m_assembler->addressWidth(), i++)
148 {
149 if(address == startaddress)
150 this->document()->table(address, targets.size());
151 else
152 this->document()->tableItem(address, startaddress, i);
153
154 this->pushReference(address, instruction->address);
155 }
156 }
157 else
158 {
159 this->pushReference(startaddress, instruction->address);
160 this->document()->pointer(startaddress, SymbolType::Data);
161 }
162 }
163
164 return targets.size();
165 }
166
references()167 ReferenceTable *DisassemblerBase::references() { return &m_referencetable; }
168
locationIsString(address_t address,bool * wide) const169 u64 DisassemblerBase::locationIsString(address_t address, bool *wide) const
170 {
171 const Segment* segment = this->document()->segment(address);
172
173 if(!segment || segment->is(SegmentType::Bss))
174 return 0;
175
176 if(wide) *wide = false;
177
178 u64 count = this->locationIsStringT<u8>(address,
179 [](u16 b) -> bool { return ::isprint(b) || ::isspace(b); },
180 [](u16 b) -> bool { return (b == '_') || ::isalnum(b) || ::isspace(b); });
181
182 if(count == 1) // Try with wide strings
183 {
184 count = this->locationIsStringT<u16>(address,
185 [](u16 wb) -> bool { u8 b1 = wb & 0xFF, b2 = (wb & 0xFF00) >> 8; return !b2 && (::isprint(b1) || ::isspace(b1)); },
186 [](u16 wb) -> bool { u8 b1 = wb & 0xFF, b2 = (wb & 0xFF00) >> 8; return ( (b1 == '_') || ::isalnum(b1) || ::isspace(b1)) && !b2; });
187
188 if(wide)
189 *wide = true;
190 }
191
192 return count;
193 }
194
readString(const Symbol * symbol,u64 len) const195 std::string DisassemblerBase::readString(const Symbol* symbol, u64 len) const
196 {
197 address_t memaddress = 0;
198
199 if(symbol->is(SymbolType::Pointer) && this->dereference(symbol->address, &memaddress))
200 return this->readString(memaddress, len);
201
202 return this->readString(symbol->address, len);
203 }
204
readWString(const Symbol * symbol,u64 len) const205 std::string DisassemblerBase::readWString(const Symbol* symbol, u64 len) const
206 {
207 address_t memaddress = 0;
208
209 if(symbol->is(SymbolType::Pointer) && this->dereference(symbol->address, &memaddress))
210 return this->readWString(memaddress, len);
211
212 return this->readWString(symbol->address, len);
213 }
214
getHexDump(address_t address,const Symbol ** ressymbol)215 std::string DisassemblerBase::getHexDump(address_t address, const Symbol **ressymbol)
216 {
217 const REDasm::ListingItem* item = this->document()->functionStart(address);
218
219 if(!item)
220 return std::string();
221
222 const REDasm::Symbol* symbol = this->document()->symbol(item->address);
223
224 if(!symbol)
225 return std::string();
226
227 REDasm::BufferView br = this->getFunctionBytes(symbol->address);
228
229 if(br.eob())
230 return std::string();
231
232 if(ressymbol)
233 *ressymbol = symbol;
234
235 return REDasm::hexstring(br, br.size());
236 }
237
dereferenceSymbol(const Symbol * symbol,u64 * value)238 Symbol* DisassemblerBase::dereferenceSymbol(const Symbol *symbol, u64* value)
239 {
240 address_t address = 0;
241 Symbol* ptrsymbol = nullptr;
242
243 if(symbol->is(SymbolType::Pointer) && this->dereference(symbol->address, &address))
244 ptrsymbol = this->document()->symbol(address);
245
246 if(value)
247 *value = address;
248
249 return ptrsymbol;
250 }
251
dereference(address_t address,u64 * value) const252 bool DisassemblerBase::dereference(address_t address, u64 *value) const
253 {
254 if(!value)
255 return false;
256
257 return this->readAddress(address, m_assembler->addressWidth(), value);
258 }
259
getFunctionBytes(address_t address)260 BufferView DisassemblerBase::getFunctionBytes(address_t address)
261 {
262 const ListingItem* item = this->document()->functionStart(address);
263
264 if(!item)
265 return BufferView();
266
267 auto it = this->document()->functionItem(item->address);
268
269 if(it == this->document()->end())
270 return BufferView();
271
272 it++;
273 address_t endaddress = 0;
274
275 for( ; it != this->document()->end(); it++)
276 {
277 if((*it)->type == ListingItem::SymbolItem)
278 {
279 const Symbol* symbol = this->document()->symbol((*it)->address);
280
281 if(!symbol->is(SymbolType::Code))
282 break;
283
284 continue;
285 }
286
287 if((*it)->type == ListingItem::InstructionItem)
288 {
289 InstructionPtr instruction = this->document()->instruction((*it)->address);
290 endaddress = instruction->endAddress();
291 continue;
292 }
293
294 break;
295 }
296
297 BufferView view = m_loader->view(address);
298
299 if(it != this->document()->end())
300 view.resize(endaddress - address);
301
302 return view;
303 }
304
readAddress(address_t address,size_t size,u64 * value) const305 bool DisassemblerBase::readAddress(address_t address, size_t size, u64 *value) const
306 {
307 if(!value)
308 return false;
309
310 const Segment* segment = this->document()->segment(address);
311
312 if(!segment || segment->is(SegmentType::Bss))
313 return false;
314
315 offset_location offset = m_loader->offset(address);
316
317 if(!offset.valid)
318 return false;
319
320 return this->readOffset(offset, size, value);
321 }
322
readOffset(offset_t offset,size_t size,u64 * value) const323 bool DisassemblerBase::readOffset(offset_t offset, size_t size, u64 *value) const
324 {
325 if(!value)
326 return false;
327
328 BufferView viewdest = m_loader->viewOffset(offset);
329
330 if(size == 1)
331 *value = static_cast<u8>(viewdest);
332 else if(size == 2)
333 *value = static_cast<u16>(viewdest);
334 else if(size == 4)
335 *value = static_cast<u32>(viewdest);
336 else if(size == 8)
337 *value = static_cast<u64>(viewdest);
338 else
339 {
340 REDasm::problem("Invalid size: " + std::to_string(size));
341 return false;
342 }
343
344 return true;
345 }
346
readString(address_t address,u64 len) const347 std::string DisassemblerBase::readString(address_t address, u64 len) const
348 {
349 return this->readStringT<char>(address, len, [](char b, std::string& s) {
350 bool r = ::isprint(b) || ::isspace(b);
351 if(r) s += b;
352 return r;
353 });
354 }
355
readWString(address_t address,u64 len) const356 std::string DisassemblerBase::readWString(address_t address, u64 len) const
357 {
358 return this->readStringT<u16>(address, len, [](u16 wb, std::string& s) {
359 u8 b1 = wb & 0xFF, b2 = (wb & 0xFF00) >> 8;
360 bool r = !b2 && (::isprint(b1) || ::isspace(b1));
361 if(r) s += static_cast<char>(b1);
362 return r;
363 });
364 }
365
loadSignature(const std::string & signame)366 bool DisassemblerBase::loadSignature(const std::string &signame)
367 {
368 std::string signaturefile = REDasm::isPath(signame) ? signame : REDasm::makeSignaturePath(signame);
369
370 if(!REDasm::endsWith(signaturefile, ".json"))
371 signaturefile += ".json";
372
373 SignatureDB sigdb;
374
375 if(!sigdb.load(signaturefile))
376 {
377 REDasm::log("Failed to load " + REDasm::quoted(signaturefile));
378 return false;
379 }
380
381 if(!sigdb.isCompatible(this))
382 {
383 REDasm::log("Signature " + REDasm::quoted(sigdb.name()) + " is not compatible");
384 return false;
385 }
386
387 REDasm::log("Loading Signature: " + REDasm::quoted(sigdb.name()));
388 u64 c = 0;
389
390 this->document()->symbols()->iterate(SymbolType::FunctionMask, [&](const Symbol* symbol) -> bool {
391 if(symbol->isLocked())
392 return true;
393
394 BufferView view = this->getFunctionBytes(symbol->address);
395 offset_location offset = m_loader->offset(symbol->address);
396
397 if(view.eob() || !offset.valid)
398 return true;
399
400 sigdb.search(view, [&](const json& signature) {
401 std::string signame = signature["name"];
402 this->document()->lock(symbol->address, signame, signature["symboltype"]);
403 c++;
404 });
405
406 return true;
407 });
408
409 if(c)
410 REDasm::log("Found " + std::to_string(c) + " signature(s)");
411 else
412 REDasm::log("No signatures found");
413
414 return true;
415 }
416
computeBasicBlocks(document_x_lock & lock,const ListingItem * functionitem)417 void DisassemblerBase::computeBasicBlocks(document_x_lock &lock, const ListingItem *functionitem)
418 {
419 REDasm::status("Computing basic blocks @ " + REDasm::hex(functionitem->address));
420 auto g = std::make_unique<Graphing::FunctionGraph>(this);
421
422 if(!g->build(functionitem))
423 return;
424
425 lock->functions().graph(functionitem, g.release());
426 }
427
428 } // namespace REDasm
429