PE-sieve
Scans all running processes. Recognizes and dumps a variety of potentially malicious implants (replaced/implanted PEs, shellcodes, hooks, in-memory patches).
Loading...
Searching...
No Matches
patch_analyzer.cpp
Go to the documentation of this file.
1#include "patch_analyzer.h"
2//---
3using namespace pesieve;
4
5template <typename DELTA_T>
6ULONGLONG pesieve::PatchAnalyzer::getJmpDestAddr(ULONGLONG currVA, int instrLen, DELTA_T lVal)
7{
8 int delta = instrLen + int(lVal);
9 ULONGLONG addr = currVA + delta;
10 return addr;
11}
12
13size_t pesieve::PatchAnalyzer::parseShortJmp(PatchList::Patch &patch, BYTE* patch_ptr, ULONGLONG patch_va)
14{
15 const size_t instr_size = 2;
16 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, instr_size)) {
17 return 0;
18 }
19 BYTE *lval = reinterpret_cast<BYTE*>((ULONG_PTR)patch_ptr + 1);
20 ULONGLONG addr = getJmpDestAddr<BYTE>(patch_va, instr_size, (*lval));
21
22 patch.setHookTarget(addr);
23 return instr_size;
24}
25
26size_t pesieve::PatchAnalyzer::parseJmp(PatchList::Patch &patch, BYTE* patch_ptr, ULONGLONG patch_va)
27{
28 const size_t instr_size = 5;
29 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, instr_size)) {
30 return 0;
31 }
32 DWORD *lval = reinterpret_cast<DWORD*>((ULONG_PTR) patch_ptr + 1);
33 ULONGLONG addr = getJmpDestAddr<DWORD>(patch_va, instr_size, (*lval));
34
35 patch.setHookTarget(addr);
36 return instr_size;
37}
38
39size_t pesieve::PatchAnalyzer::parseJmpViaAddr(PatchList::Patch &patch, BYTE* patch_ptr, ULONGLONG patch_va)
40{
41 const size_t instr_size = 6;
42
43 DWORD *lval = reinterpret_cast<DWORD*>((ULONG_PTR)patch_ptr + 2);
44 if (!isModule64bit) { //32bit
45 patch.setHookTarget(*lval, false);
46 }
47 else { //64bit
48 ULONGLONG addr = getJmpDestAddr<DWORD>(patch_va, instr_size, (*lval));
49 patch.setHookTarget(addr, false);
50 }
51 return instr_size;
52}
53
54size_t pesieve::PatchAnalyzer::parseMovJmp(PatchList::Patch &patch, BYTE* patch_ptr, bool is_long)
55{
56 size_t mov_instr_len = is_long ? 9 : 5;
57 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, mov_instr_len + 2)) {
58 return 0;
59 }
60
61 BYTE* jmp_ptr = patch_ptr + mov_instr_len; // next instruction
62 if (is64Modifier(*patch_ptr)) {
63 patch_ptr++;
64 jmp_ptr++;
65 mov_instr_len++; // add length of modifier
66 }
67
68 DWORD reg_id0 = patch_ptr[0] - 0xB8;
69
70 // before call/jmp there can be also the modifier...
71 if (is64Modifier(*jmp_ptr)) {
72 jmp_ptr++;
73 mov_instr_len++; // add length of modifier
74 }
75 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, jmp_ptr, 2)) {
76 return 0;
77 }
78 DWORD reg_id1 = 0;
79 if (jmp_ptr[0] == 0xFF && jmp_ptr[1] >= 0xE0 && jmp_ptr[1] <= 0xEF ) { // jmp reg
80 //jmp reg
81 reg_id1 = jmp_ptr[1] - 0xE0;
82 } else if (jmp_ptr[0] == 0xFF && jmp_ptr[1] >= 0xD0 && jmp_ptr[1] <= 0xDF ) { // call reg
83 //jmp reg
84 reg_id1 = jmp_ptr[1] - 0xD0;
85 } else {
86#ifdef _DEBUG
87 std::cerr << "It is not MOV->JMP" << std::hex << (DWORD)jmp_ptr[0] << std::endl;
88#endif
89 return 0;
90 }
91 //TODO: take into account also modifiers
92 if (reg_id1 != reg_id0) {
93#ifdef _DEBUG
94 std::cerr << "MOV->JMP : reg mismatch" << std::endl;
95#endif
96 return 0;
97 }
98 size_t patch_size = mov_instr_len;
99 ULONGLONG addr = 0;
100 if (!is_long) { //32bit
101 DWORD *lval = reinterpret_cast<DWORD*>((ULONG_PTR) patch_ptr + 1);
102 addr = *lval;
103 } else { //64bit
104 ULONGLONG *lval = reinterpret_cast<ULONGLONG*>((ULONG_PTR) patch_ptr + 1);
105 addr = *lval;
106 }
107 patch_size += 2; //add jump reg size
108 patch.setHookTarget(addr);
109#ifdef _DEBUG
110 std::cout << "----> Target: " << std::hex << addr << std::endl;
111#endif
112 return patch_size;
113}
114
116{
117 size_t instr_size = 5;
118 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, instr_size + 1)) {
119 return 0;
120 }
121 BYTE* ret_ptr = reinterpret_cast<BYTE*>((ULONG_PTR) patch_ptr + instr_size); // next instruction
122 if (ret_ptr[0] != 0xC3) {
123 return 0; // this is not push->ret
124 }
125 instr_size++;
126 DWORD *lval = reinterpret_cast<DWORD*>((ULONG_PTR) patch_ptr + 1);
127 patch.setHookTarget(*lval);
128 return instr_size;
129}
130
132{
133 if (!isModule64bit) return false;
134 if (op >= 0x40 && op <= 0x4F) { // modifier
135 return true;
136 }
137 return false;
138}
139
141{
142 if (!isModule64bit) return false;
143 if (op >= 0x48 && op <= 0x4F) { // modifier
144 return true;
145 }
146 return false;
147}
148
149size_t pesieve::PatchAnalyzer::_analyzeHook(PatchList::Patch &patch, BYTE* patch_ptr, ULONGLONG patch_va)
150{
151 BYTE op = patch_ptr[0];
152 if (op == OP_JMP || op == OP_CALL_DWORD) {
153 return parseJmp(patch, patch_ptr, patch_va);
154 }
155 if (op == OP_SHORTJMP) {
156 return parseShortJmp(patch, patch_ptr, patch_va);
157 }
158 if (op == OP_PUSH_DWORD) {
159 return parsePushRet(patch, patch_ptr);
160 }
161 if (op == OP_JMP_VIA_ADDR_B1 && patch_ptr[1] == OP_JMP_VIA_ADDR_B2) {
162 return parseJmpViaAddr(patch, patch_ptr, patch_va);
163 }
164
165 bool is_long = false;
166 if (is64Modifier(op)) { // mov modifier
167 if (isLongModifier(op)) {
168 is_long = true;
169 }
170 op = patch_ptr[1];
171 }
172
173 if (op >= 0xB8 && op <= 0xBF) { // is mov
174 return parseMovJmp(patch, patch_ptr, is_long);
175 }
176 return 0;
177}
178
180{
181 if (this->relocs.find(patch.startRva) == this->relocs.end()) {
182 return 0;
183 }
184 // This patch is a relocated field
185 const size_t fieldSize = (this->moduleData.is64bit()) ? sizeof(ULONGLONG) : sizeof(DWORD);
186 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, fieldSize)) {
187 return 0;
188 }
189 ULONGLONG field = (this->moduleData.is64bit()) ? *((ULONGLONG*)patch_ptr) : *((DWORD*)patch_ptr);
191 return fieldSize;
192}
193
195{
196 const ULONGLONG patch_va = moduleData.rvaToVa(patch.startRva);
197 const size_t patch_offset = patch.startRva - sectionRVA;
198 BYTE* patch_ptr = this->patchedCode + patch_offset;
199 size_t size = patch.endRva - patch.startRva;
200
201 if (size > 1) {
202 bool isPadding = true;
203 for (size_t i = 1; i < size; ++i) {
204 if (patch_ptr[0] != patch_ptr[i]) {
205 isPadding = false;
206 }
207 }
208 if (isPadding) {
209 patch.type = PATCH_PADDING;
210 patch.paddingVal = patch_ptr[0];
211 }
212 }
213 if (patch_ptr[0] == 0xCC) {
214 if (size == 1 || patch.type == PATCH_PADDING) {
215 patch.type = PATCH_BREAKPOINT;
216 }
217 }
218 return size;
219}
220
222{
223 const ULONGLONG patch_va = moduleData.rvaToVa(patch.startRva);
224 const size_t patch_offset = patch.startRva - sectionRVA;
225 BYTE* patch_ptr = this->patchedCode + patch_offset;
226
227 size_t size = _analyzeRelocated(patch, patch_ptr);
228 if (size) {
229 return size;
230 }
231 const size_t kMinSize = 3;
232 if (!peconv::validate_ptr(this->patchedCode, this->codeSize, patch_ptr, kMinSize)) {
233 return 0;
234 }
235 size = _analyzeHook(patch, patch_ptr, patch_va);
236 if (size == 0 && patch_offset > 0) {
237 //it may happen that the address of an existing JMP/CALL was replaced
238 //try to parse a byte before the patch...
239 size = _analyzeHook(patch, patch_ptr -1, patch_va - 1);
240 if (size > 0) {
241 // subtract the added position:
242 size--;
243 }
244 }
245 return size;
246}
247
size_t parsePushRet(PatchList::Patch &patch, BYTE *patch_ptr)
size_t _analyzeRelocated(PatchList::Patch &patch, BYTE *patch_ptr)
size_t parseJmp(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
std::set< DWORD > relocs
size_t analyzeOther(PatchList::Patch &patch)
size_t parseMovJmp(PatchList::Patch &patch, BYTE *patch_ptr, bool is_long)
ULONGLONG getJmpDestAddr(ULONGLONG currVA, int instrLen, DELTA_T lVal)
size_t _analyzeHook(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
size_t parseJmpViaAddr(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
size_t parseShortJmp(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
size_t analyzeHook(PatchList::Patch &patch)
void setHookTarget(ULONGLONG target_va, bool is_direct=true, t_patch_type hook_type=pesieve::HOOK_INLINE)
Definition patch_list.h:57
@ PATCH_BREAKPOINT
Definition patch_list.h:16
@ HOOK_ADDR_REPLACEMENT
Definition patch_list.h:14
@ PATCH_PADDING
Definition patch_list.h:15