PE-sieve
Scans all running processes. Recognizes and dumps a variety of potentially malicious implants (replaced/implanted PEs, shellcodes, hooks, in-memory patches).
Toggle main menu visibility
Loading...
Searching...
No Matches
scanners
patch_analyzer.cpp
Go to the documentation of this file.
1
#include "
patch_analyzer.h
"
2
//---
3
using namespace
pesieve
;
4
5
template
<
typename
DELTA_T>
6
ULONGLONG
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
13
size_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
26
size_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
39
size_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
54
size_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
115
size_t
pesieve::PatchAnalyzer::parsePushRet
(
PatchList::Patch
&patch, BYTE* patch_ptr)
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
131
bool
pesieve::PatchAnalyzer::is64Modifier
(BYTE op)
132
{
133
if
(!
isModule64bit
)
return
false
;
134
if
(op >= 0x40 && op <= 0x4F) {
// modifier
135
return
true
;
136
}
137
return
false
;
138
}
139
140
bool
pesieve::PatchAnalyzer::isLongModifier
(BYTE op)
141
{
142
if
(!
isModule64bit
)
return
false
;
143
if
(op >= 0x48 && op <= 0x4F) {
// modifier
144
return
true
;
145
}
146
return
false
;
147
}
148
149
size_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
179
size_t
pesieve::PatchAnalyzer::_analyzeRelocated
(
PatchList::Patch
&patch, BYTE* patch_ptr)
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);
190
patch.
setHookTarget
(field,
true
,
pesieve::HOOK_ADDR_REPLACEMENT
);
191
return
fieldSize;
192
}
193
194
size_t
pesieve::PatchAnalyzer::analyzeOther
(
PatchList::Patch
& patch)
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
221
size_t
pesieve::PatchAnalyzer::analyzeHook
(
PatchList::Patch
&patch)
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
pesieve::PatchAnalyzer::parsePushRet
size_t parsePushRet(PatchList::Patch &patch, BYTE *patch_ptr)
Definition
patch_analyzer.cpp:115
pesieve::PatchAnalyzer::_analyzeRelocated
size_t _analyzeRelocated(PatchList::Patch &patch, BYTE *patch_ptr)
Definition
patch_analyzer.cpp:179
pesieve::PatchAnalyzer::sectionRVA
DWORD sectionRVA
Definition
patch_analyzer.h:52
pesieve::PatchAnalyzer::parseJmp
size_t parseJmp(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
Definition
patch_analyzer.cpp:26
pesieve::PatchAnalyzer::relocs
std::set< DWORD > relocs
Definition
patch_analyzer.h:56
pesieve::PatchAnalyzer::is64Modifier
bool is64Modifier(BYTE op)
Definition
patch_analyzer.cpp:131
pesieve::PatchAnalyzer::analyzeOther
size_t analyzeOther(PatchList::Patch &patch)
Definition
patch_analyzer.cpp:194
pesieve::PatchAnalyzer::patchedCode
PBYTE patchedCode
Definition
patch_analyzer.h:53
pesieve::PatchAnalyzer::parseMovJmp
size_t parseMovJmp(PatchList::Patch &patch, BYTE *patch_ptr, bool is_long)
Definition
patch_analyzer.cpp:54
pesieve::PatchAnalyzer::getJmpDestAddr
ULONGLONG getJmpDestAddr(ULONGLONG currVA, int instrLen, DELTA_T lVal)
Definition
patch_analyzer.cpp:6
pesieve::PatchAnalyzer::isModule64bit
bool isModule64bit
Definition
patch_analyzer.h:49
pesieve::PatchAnalyzer::_analyzeHook
size_t _analyzeHook(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
Definition
patch_analyzer.cpp:149
pesieve::PatchAnalyzer::OP_PUSH_DWORD
@ OP_PUSH_DWORD
Definition
patch_analyzer.h:17
pesieve::PatchAnalyzer::OP_JMP_VIA_ADDR_B2
@ OP_JMP_VIA_ADDR_B2
Definition
patch_analyzer.h:19
pesieve::PatchAnalyzer::OP_JMP_VIA_ADDR_B1
@ OP_JMP_VIA_ADDR_B1
Definition
patch_analyzer.h:18
pesieve::PatchAnalyzer::OP_JMP
@ OP_JMP
Definition
patch_analyzer.h:15
pesieve::PatchAnalyzer::OP_SHORTJMP
@ OP_SHORTJMP
Definition
patch_analyzer.h:14
pesieve::PatchAnalyzer::OP_CALL_DWORD
@ OP_CALL_DWORD
Definition
patch_analyzer.h:16
pesieve::PatchAnalyzer::parseJmpViaAddr
size_t parseJmpViaAddr(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
Definition
patch_analyzer.cpp:39
pesieve::PatchAnalyzer::isLongModifier
bool isLongModifier(BYTE op)
Definition
patch_analyzer.cpp:140
pesieve::PatchAnalyzer::codeSize
size_t codeSize
Definition
patch_analyzer.h:54
pesieve::PatchAnalyzer::moduleData
ModuleData & moduleData
Definition
patch_analyzer.h:51
pesieve::PatchAnalyzer::parseShortJmp
size_t parseShortJmp(PatchList::Patch &patch, BYTE *patch_ptr, ULONGLONG patch_va)
Definition
patch_analyzer.cpp:13
pesieve::PatchAnalyzer::analyzeHook
size_t analyzeHook(PatchList::Patch &patch)
Definition
patch_analyzer.cpp:221
pesieve::PatchList::Patch
Definition
patch_list.h:23
pesieve::PatchList::Patch::setHookTarget
void setHookTarget(ULONGLONG target_va, bool is_direct=true, t_patch_type hook_type=pesieve::HOOK_INLINE)
Definition
patch_list.h:57
pesieve::PatchList::Patch::type
t_patch_type type
Definition
patch_list.h:93
pesieve::PatchList::Patch::endRva
DWORD endRva
Definition
patch_list.h:90
pesieve::PatchList::Patch::paddingVal
BYTE paddingVal
Definition
patch_list.h:96
pesieve::PatchList::Patch::startRva
DWORD startRva
Definition
patch_list.h:89
pesieve
Definition
pesieve.py:1
pesieve::PATCH_BREAKPOINT
@ PATCH_BREAKPOINT
Definition
patch_list.h:16
pesieve::HOOK_ADDR_REPLACEMENT
@ HOOK_ADDR_REPLACEMENT
Definition
patch_list.h:14
pesieve::PATCH_PADDING
@ PATCH_PADDING
Definition
patch_list.h:15
patch_analyzer.h
Generated by
1.17.0