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