PE-sieve
Scans all running processes. Recognizes and dumps a variety of potentially malicious implants (replaced/implanted PEs, shellcodes, hooks, in-memory patches).
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
iat_block.cpp
Go to the documentation of this file.
1#include "iat_block.h"
2#include <peconv.h>
3
4namespace pesieve {
5 size_t get_longest_func_name(std::map<ULONGLONG, std::set<peconv::ExportedFunc>> &addrToFunc)
6 {
7 size_t max_len = 0;
8 std::map<ULONGLONG, std::set<peconv::ExportedFunc>>::iterator itr;
9 for (itr = addrToFunc.begin(); itr != addrToFunc.end(); ++itr) {
10 std::set<peconv::ExportedFunc> &expSet = itr->second;
11 const peconv::ExportedFunc& exp = *(expSet.begin());
12 if (exp.isByOrdinal) {
13 continue;
14 }
15 if (exp.funcName.length() > max_len) {
16 max_len = exp.funcName.length();
17 }
18 }
19 return max_len;
20 }
21};
22
23//---
24
25bool pesieve::IATThunksSeries::makeCoverage(IN const peconv::ExportsMapper* exportsMap)
26{
27 delete cov; //delete previous
28 cov = new peconv::ImportedDllCoverage(funcAddresses, *exportsMap);
29 if (!cov->findCoveringDll()) {
30 // DLL not found
31 return false;
32 }
33 size_t covered_count = cov->mapAddressesToFunctions(cov->dllName);
34 this->dllFullName = exportsMap->get_dll_fullname(cov->dllName);
35 this->covered = (covered_count == this->funcAddresses.size());
36 return this->covered;
37}
38
39bool pesieve::IATThunksSeries::fillNamesSpace(const BYTE* buf_start, size_t buf_size, DWORD bufRVA, bool is64b)
40{
41 if (!buf_start || !this->cov) return false;
42
43 if (!this->cov->isMappingComplete()) {
44 return false; //TODO: make a workaround for this case
45 }
46
47 const size_t longest_name = get_longest_func_name(this->cov->addrToFunc);
48 const size_t field_size = is64b ? sizeof(ULONGLONG) : sizeof(DWORD);
49
50 const size_t thunks_count = this->funcCount();
51 const size_t thunks_area_size = (thunks_count * field_size) + field_size;
52
53 size_t names_rva = bufRVA + thunks_area_size;
54
55 //fill thunks:
56 BYTE *buf = const_cast<BYTE*>(buf_start);
57 const BYTE *buf_end = buf_start + buf_size;
58 for (size_t i = 0; i < thunks_count; i++) {
59 if (buf >= buf_end) {
60#ifdef _DEBUG
61 std::cerr << "ERR: run out of buffer for names! Failed to make space for the name\n";
62#endif
63 break;
64 }
65 if (is64b) {
66 ULONGLONG *val = (ULONGLONG*)buf;
67 *val = names_rva;
68 }
69 else {
70 DWORD *val = (DWORD*)buf;
71 *val = MASK_TO_DWORD(names_rva);
72 }
73 //no need to fill names, they will be autofilled during dumping
74 buf += field_size;
75 names_rva += sizeof(IMAGE_IMPORT_BY_NAME) + longest_name;
76 }
77 return true;
78}
79
81{
82 if (!cov) return 0;
83
84 size_t space_size = 0;
85 if (!this->cov->isMappingComplete()) {
86 return 0; //TODO: make a workaround for this case
87 }
88 const size_t longest_name = get_longest_func_name(this->cov->addrToFunc);
89 const size_t field_size = is64b ? sizeof(ULONGLONG) : sizeof(DWORD);
90 const size_t entriesCount = this->funcCount();
91 for (size_t i = 0; i < entriesCount; i++) {
92 space_size += field_size;
93 space_size += sizeof(IMAGE_IMPORT_BY_NAME) + longest_name;
94 }
95 if (space_size > 0) {
96 space_size += sizeof(field_size);
97 }
98 return space_size;
99}
100
102{
103 return this->dllFullName;
104}
105
106//---
107
108bool pesieve::IATBlock::makeCoverage(IN const peconv::ExportsMapper* exportsMap)
109{
110 if (!exportsMap) return false;
111
112 IATThunksSeriesSet::iterator itr;
113 std::set<IATThunksSeries*>to_split;
114
115 for (itr = this->thunkSeries.begin(); itr != thunkSeries.end(); ++itr) {
116
117 IATThunksSeries* series = *itr;
118 if (!series->makeCoverage(exportsMap)) {
119 to_split.insert(series);
120 }
121 }
122
123 std::set<IATThunksSeries*>::iterator sItr;
124 for (sItr = to_split.begin(); sItr != to_split.end(); ++sItr) {
125 IATThunksSeries *series = *sItr;
126 IATThunksSeriesSet splitted = splitSeries(series, *exportsMap);
127 if (!splitted.size()) {
128 continue;
129 }
130#ifdef _DEBUG
131 std::cout << "Uncovered series: " << std::hex << series->startOffset << " splitted into: " << std::dec << splitted.size() << " series\n";
132#endif
133
134 this->thunkSeries.erase(series);
135 this->thunkSeries.insert(splitted.begin(), splitted.end());
136
137 delete series; // delete the series that have been splitted
138 }
139
140 size_t covered_count = 0;
141 for (itr = this->thunkSeries.begin(); itr != thunkSeries.end(); ++itr) {
142 IATThunksSeries* series = *itr;
143
144 if (series->isCovered() || series ->makeCoverage(exportsMap)) {
145 covered_count++;
146 }
147 }
148#ifdef _DEBUG
149 size_t total = 0;
150 for (auto it1 = thunkSeries.begin(); it1 != thunkSeries.end(); ++it1) {
151 total += (*it1)->funcCount();
152 }
153 std::cout << "[#] IAT block: " << std::hex << this->iatOffset << " Total: " << std::dec << total << " Missed: " << (this->countThunks() - total) << std::endl;
154#endif
155 isCoverageComplete = (covered_count == this->thunkSeries.size());
156 return isCoverageComplete;
157}
158
159pesieve::IATThunksSeriesSet pesieve::IATBlock::splitSeries(IN IATThunksSeries* series, IN const peconv::ExportsMapper &exportsMap)
160{
161 IATThunksSeriesSet splitted;
162 if (!series) return splitted;
163
164 std::map<DWORD, ULONGLONG> addresses = series->getRvaToFuncMap();
165
166 IATThunksSeries *new_series = nullptr;
167 std::map<DWORD, ULONGLONG>::iterator itr;
168 std::string last_dll = "";
169
170 for (itr = addresses.begin(); itr != addresses.end(); ++itr) {
171 ULONGLONG func_addr = itr->second;
172 DWORD offset = itr->first;
173 const peconv::ExportedFunc *func = exportsMap.find_export_by_va(func_addr);
174 if (new_series && (!func || func->libName != last_dll)) {
175 //close series
176 splitted.insert(new_series);
177 new_series = nullptr;
178 last_dll = "";
179 }
180 if (!func) continue;
181
182 if (!new_series) {
183 new_series = new IATThunksSeries(offset);
184 last_dll = func->libName;
185#ifdef _DEBUG
186 std::cout << std::hex << "addr: " << offset << " set DLL: " << last_dll << "\n";
187#endif
188 }
189 new_series->insert(offset, func_addr);
190 }
191 if (new_series) {
192 splitted.insert(new_series);
193 }
194 return splitted;
195}
196
198{
199 size_t max_size = 0;
200 IATThunksSeriesSet::iterator itr;
201 for (itr = this->thunkSeries.begin(); itr != thunkSeries.end(); ++itr) {
202 IATThunksSeries* series = *itr;
203 size_t curr_size = series->getDllName().length() + 1;
204 if (curr_size > max_size) max_size = curr_size;
205 }
206 return max_size;
207}
208
210{
211 const size_t max_len = maxDllLen();
212 return max_len * (thunkSeries.size() + 1);
213}
214
216{
217 std::stringstream stream;
218 stream << "---\nIAT at: " << std::hex << iatOffset << ", size: " << iatSize << ", thunks: "
219 << countThunks() << ", is_terminated: " << isTerminated << ", in_main: " << isInMain << "\n";
220
221 if (this->importTableOffset) {
222 stream << "ImportTable: " << std::hex << importTableOffset << "\n";
223 }
224 stream << "---\n";
225 std::map<ULONGLONG, const peconv::ExportedFunc*>::const_iterator itr;
226 for (itr = functions.begin(); itr != functions.end(); ++itr) {
227 ULONGLONG offset = itr->first;
228 const peconv::ExportedFunc* exp = itr->second;
229
230 stream << std::hex << offset << "," << addrToFunctionVA[offset] << "," << exp->toString() << "\n";
231 }
232 return stream.str();
233}
std::map< ULONGLONG, const peconv::ExportedFunc * > functions
Definition iat_block.h:181
size_t countThunks() const
Definition iat_block.h:144
DWORD importTableOffset
Definition iat_block.h:171
std::string toString()
size_t sizeOfDllsSpace()
bool makeCoverage(IN const peconv::ExportsMapper *exportsMap)
IATThunksSeriesSet thunkSeries
Definition iat_block.h:176
std::map< ULONGLONG, ULONGLONG > addrToFunctionVA
Definition iat_block.h:182
IATThunksSeriesSet splitSeries(IN IATThunksSeries *notCoveredSeries, IN const peconv::ExportsMapper &exportsMap)
bool makeCoverage(IN const peconv::ExportsMapper *exportsMap)
Definition iat_block.cpp:25
bool insert(DWORD rva, ULONGLONG funcAddr)
Definition iat_block.h:29
size_t sizeOfNamesSpace(bool is64b)
Definition iat_block.cpp:80
bool fillNamesSpace(const BYTE *buf_start, size_t buf_size, DWORD bufRVA, bool is64b)
Definition iat_block.cpp:39
#define MASK_TO_DWORD(val)
Definition iat_finder.h:9
size_t get_longest_func_name(std::map< ULONGLONG, std::set< peconv::ExportedFunc > > &addrToFunc)
Definition iat_block.cpp:5
std::set< IATThunksSeries *, IATThunksSeriesPtrCompare > IATThunksSeriesSet
Definition iat_block.h:85