PDA

Просмотр полной версии : Сканирование функции на брейкпоинты и хуки


colby57
08.02.2023, 20:07
Шалом, это мой первый тред на борде.

И решил немного доработать старый исходник, который проходится по функциям IAT (Import Address Table), проверяя их на опкоды джампа, и брейкпоинтов x32dbg (int3, int 0x3, ud2), Ordinal, кстати, он не проверяет, но можете допилить за меня

Такой приём будет очень полезен против новичков в вашем лоадере, которые любят ставить бряки куда попало без предварительного динамического анализа защиты :)

Для незнающих скажу, что если ваша функция накрыта виртуализацией кода, которая за собой как бонус будет вызывать функцию по своему функцию и и на этот адрес нет никаких других внешних вызовов в вашей программе, то этот чек пользователь спокойно пройдет, поскольку данной функции не будет в IAT.

Вот и сам исходник:

IATScan.hpp:






#pragma once
#include
#include
#include
#include
namespace
Engine
{
struct
S_CorruptedFunction
{
std
::
string m_cModuleName
;
std
::
string m_cFunctionName
;
std
::
uintptr_t m_pAddress
;
S_CorruptedFunction
(
std
::
string cModule
,
std
::
string cFunc
,
std
::
uintptr_t pAddress
)
:
m_cModuleName
(
std
::
move
(
cModule
)
)
,
m_cFunctionName
(
std
::
move
(
cFunc
)
)
,
m_pAddress
(
std
::
move
(
pAddress
)
)
{
}
}
;
inline
BYTE bInt3Breakpoint
=
0xCC
;
inline
BYTE bJumpOpcode
=
0xE9
;
inline
WORD wUd2Breakpoint
=
0x0B0F
;
inline
WORD wInt3Breakpoint
=
0x03CD
;
inline
std
::
vector

m_cCorruptedFunctions
{
}
;
void
OutputCorruptedFunctions
(
)
;
void
AddFunction
(
const
S_CorruptedFunction
&
cCorruptedFunctions
)
;
bool
IATScan
(
)
;
}




IATScan.cpp:






#include "IATScan.hpp"
void
Engine
::
OutputCorruptedFunctions
(
)
{
if
(
m_cCorruptedFunctions
.
empty
(
)
)
{
printf
(
"[~] No corrupted functions found!\n"
)
;
return
;
}
for
(
const
auto
&
Iterator
:
m_cCorruptedFunctions
)
printf
(
"[!] Module: %s\tFunction: %s\tAddress: 0x%p\n"
,
Iterator
.
m_cModuleName
.
c_str
(
)
,
Iterator
.
m_cFunctionName
.
c_str
(
)
,
Iterator
.
m_pAddress
)
;
}
void
Engine
::
AddFunction
(
const
S_CorruptedFunction
&
cCorruptedFunctions
)
{
m_cCorruptedFunctions
.
push_back
(
cCorruptedFunctions
)
;
}
bool
Engine
::
IATScan
(
)
{
LPVOID lpBaseAddress
=
(
LPVOID
)
GetModuleHandle
(
NULL
)
;
PIMAGE_DOS_HEADER pDosHeader
;
PIMAGE_NT_HEADERS pNtHeader
;
IMAGE_OPTIONAL_HEADER pOptionalHeader
;
IMAGE_DATA_DIRECTORY pImportDirectory
;
DWORD dwStartRVA
;
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor
;
pDosHeader
=
(
PIMAGE_DOS_HEADER
)
lpBaseAddress
;
if
(
pDosHeader
->
e_magic
!=
IMAGE_DOS_SIGNATURE
)
return
false
;
pNtHeader
=
(
PIMAGE_NT_HEADERS
)
(
(
DWORD_PTR
)
lpBaseAddress
+
pDosHeader
->
e_lfanew
)
;
if
(
pNtHeader
->
Signature
!=
IMAGE_NT_SIGNATURE
)
return
false
;
pOptionalHeader
=
pNtHeader
->
OptionalHeader
;
pImportDirectory
=
pOptionalHeader
.
DataDirectory
[
IMAGE_DIRECTORY_ENTRY_IMPORT
]
;
dwStartRVA
=
pImportDirectory
.
VirtualAddress
;
pImportDescriptor
=
(
PIMAGE_IMPORT_DESCRIPTOR
)
(
(
DWORD_PTR
)
lpBaseAddress
+
dwStartRVA
)
;
if
(
pImportDescriptor
==
NULL
)
return
false
;
DWORD dwIndex
=
-
1
;
while
(
pImportDescriptor
[
++
dwIndex
]
.
Characteristics
!=
0
)
{
PIMAGE_THUNK_DATA pOriginalFirstThunk
;
PIMAGE_THUNK_DATA pFirstThunk
;
char
*
pDllName
=
(
char
*
)
(
(
DWORD_PTR
)
lpBaseAddress
+
pImportDescriptor
[
dwIndex
]
.
Name
)
;
HMODULE hModule
=
GetModuleHandleA
(
pDllName
)
;
pOriginalFirstThunk
=
(
PIMAGE_THUNK_DATA
)
(
(
DWORD_PTR
)
lpBaseAddress
+
pImportDescriptor
[
dwIndex
]
.
OriginalFirstThunk
)
;
pFirstThunk
=
(
PIMAGE_THUNK_DATA
)
(
(
DWORD_PTR
)
lpBaseAddress
+
pImportDescriptor
[
dwIndex
]
.
FirstThunk
)
;
if
(
pOriginalFirstThunk
==
nullptr
||
pFirstThunk
==
nullptr
)
return
false
;
while
(
pOriginalFirstThunk
->
u1
.
AddressOfData
)
{
if
(
!
(
pOriginalFirstThunk
->
u1
.
Ordinal
&
IMAGE_ORDINAL_FLAG
)
)
{
PIMAGE_IMPORT_BY_NAME pImageImport
=
(
PIMAGE_IMPORT_BY_NAME
)
(
(
LPBYTE
)
lpBaseAddress
+
pOriginalFirstThunk
->
u1
.
AddressOfData
)
;
auto
pFn
=
GetProcAddress
(
hModule
,
(
LPCSTR
)
pImageImport
->
Name
)
;
if
(
(
*
(
BYTE
*
)
pFn
==
bInt3Breakpoint
||
*
(
BYTE
*
)
pFn
==
bJumpOpcode
)
||
(
*
(
WORD
*
)
pFn
==
wUd2Breakpoint
||
*
(
WORD
*
)
pFn
==
wInt3Breakpoint
)
)
{
Engine
::
AddFunction
(
S_CorruptedFunction
(
std
::
string
(
pDllName
)
,
std
::
string
(
pImageImport
->
Name
)
,
(
std
::
uintptr_t
)
pFn
)
)
;
}
}
pFirstThunk
++
;
pOriginalFirstThunk
++
;
}
}
return
true
;
}




Пример его использования:

Entry.cpp:






#include "IATScan.hpp"
int
main
(
)
{
printf
(
"t.me/colby5engineering\n\n"
)
;
if
(
Engine
::
IATScan
(
)
==
true
)
Engine
::
OutputCorruptedFunctions
(
)
;
else
printf
(
"[-] Failed to scan iat :(\n"
)
;
std
::
cin
.
get
(
)
;
return
0
;
}




Результат использования функции:

1675872223136.pngcolby57 · 8 Фев 2023 в 19:07' data-fancybox="lb-post-1252723" data-lb-caption-extra-html="" data-lb-sidebar-href="" data-single-image="1" data-src="https://www.blast.hk/attachments/189178/" style="cursor: pointer;" title="1675872223136.png">
https://forum.antichat.xyz/attachments/28252723/

Также забыл сказать, что функция _initialize_narrow_environment будет находится в списке, поскольку первый её байт - это 0xE9. Для неё можно спокойно сделать проверку.

Полный исходный код находится на гитхабе: https://github.com/colby57/IAT-Scanner

Всем пока!

digitalpurple
08.02.2023, 20:12
нус, тема бомба, добро пожаловать

arkwhtp
08.02.2023, 20:16
Годно

kin4stat
09.02.2023, 00:13
Много чего не учтено. По хорошему тут чуть ли не эмулятор прикручивать надо. Тот же long jmp игнорируется(ff 25).

Можно например сделать вообще что-нибудь типа

Код:






xor eax, eax
jno code

code:
int 3




Но против нубов спасет, да. Только по хорошему сюда еще проверку региона на PAGE GUARD прикрутить

F0RQU1N and
09.02.2023, 03:57
О колби на бластхак пришел, ку

colby57
09.02.2023, 08:31
Можно например сделать вообще что-нибудь типа


Пример неуместный

Если ты ещё не понял, то есть разница между бряком, который вписываешь ты, и бряком который ставит непосредственно сам дебаггер.

В твоём примере я вижу, что бряк вписываешь именно ты

У дебаггера есть 4 вида бряков: хардвейр в виде 4 штук, int3, ud2 и int 0x3, который как раз ты и поставил.

При выполнении данного кода ты поймаешь краш с исключением EXCEPTION_BREAKPOINT, поскольку отладчик будет обрабатывать только опкоды бряков, которые он сам выставил. Можешь конечно сам модифицировать отладчик, и вместо условного int 0x3, который ты показал можешь поменять на тот асм-код, который ты решил предоставить в своём примере, но произойдет нечто страшное))

Да и где тут вообще восстановление регистра eax? Ты просто контекст ломаешь, а если у меня в eax важный аргумент лежит с поинтером, который ты затёр?

Раз уже показываешь пример, то показывай правильный, ака:

Код:






push eax
xor eax, eax
jno code

code:
nop

pop eax






Тот же long jmp игнорируется(ff 25).


Никто не спорит, ведь в теме я упомянул как раз о том, что мой исходник это лишь пример того, как можно обнаруживать бряки или релативный джамп, и ещё упомянул, что ничего не мешает добавлять свои детекты



Но против нубов спасет, да. Только по хорошему сюда еще проверку региона на PAGE GUARD прикрутить


Опять же, ответ выше :)

Можно долго спорить о том, чего у меня не хватает в коде, и какие меры предпринимать по обходу этого. Сам я мог бы это обойти как раз 1 хардвейр бряком на любую функцию из IAT, но тем не менее я уже объяснил, что против новичков такое сработает, да и люди в основном абузят MinHook и в x86 и в x64 в наглую, где как раз в начале хукнутой функции будет установлен угадай что? Релативный джамп, т.е. первый байт у тебя будет 0xE9 :) Поэтому в любом случае даже если не улучшать детекты в моём исходнике, то детектов будет предостаточно, чтобы отсеивать новичков.

kin4stat
09.02.2023, 20:15
При выполнении данного кода ты поймаешь краш с исключением EXCEPTION_BREAKPOINT, поскольку отладчик будет обрабатывать только опкоды бряков, которые он сам выставил.


От отладчика все же зависит. Ну и кстати этот эксепшен скипается легко



Да и где тут вообще восстановление регистра eax? Ты просто контекст ломаешь, а если у меня в eax важный аргумент лежит с поинтером, который ты затёр?
Раз уже показываешь пример, то показывай правильный, ака:


Это псевдокод все же был, а не реальный пример. Очевидно что регистр не сохранен.

colby57
11.02.2023, 12:10
От отладчика все же зависит. Ну и кстати этот эксепшен скипается легко


чего?)))

Нет, ну если ты написал свой дебаггер, который по всем параметрам превосходит x64dbg, который обновляют часто, то тут прям моё увожение, однако я повторяю тебе еще раз, x64dbg на сегодняшний момент является самым стабильным и актуальным отладчиком в юзермоде, но тем не менее, хендлить бряки, которые ставят удаленно с дллки или в самом дебаггере прописывая инт3 в ручную он не умеет, что приводит к... о боже мой!

https://forum.antichat.xyz/attachments/28254021/

Первая строка это то, как x64dbg сам ставит дебаггер по кнопочке F2, а другие строчки это попытки дебаггера обработать инт3, который пренадлежит не ему

Да и что значит твоё "легко скипается"? Понятное дело, что если ты свой вех поставишь, то сможешь сам проскипать, только я не понимаю к чему это мне было писать)



Это псевдокод все же был, а не реальный пример. Очевидно что регистр не сохранен.


Ну, раз ты решил докапываться до моей идеи, то и я буду преследовать ту же цель в твоих ответах мне

Бля, мужик..

Если пытаешься меня чему-то учить (будучи зная меньше чем я), то хотя бы выписывай правильно примеры, отговариваться по типу "это просто псевдокод" не нужно, если бы в дебаггере вместо инт3 юзался твой код, бедные реверсеры бы каждый день тебе отписывали с баг-репортом, что контекст их программы каким-то чудом умудрился сломаться...

Найдешь к чему ещё прикопаться, или закончим наш чудесный диалог?

kin4stat
12.02.2023, 00:07
чего?)))
Нет, ну если ты написал свой дебаггер, который по всем параметрам превосходит x64dbg, который обновляют часто, то тут прям моё увожение, однако я повторяю тебе еще раз, x64dbg на сегодняшний момент является самым стабильным и актуальным отладчиком в юзермоде, но тем не менее, хендлить бряки, которые ставят удаленно с дллки или в самом дебаггере прописывая инт3 в ручную он не умеет, что приводит к... о боже мой!

https://forum.antichat.xyz/attachments/28254576/

Первая строка это то, как x64dbg сам ставит дебаггер по кнопочке F2, а другие строчки это попытки дебаггера обработать инт3, который пренадлежит не ему

Да и что значит твоё "легко скипается"? Понятное дело, что если ты свой вех поставишь, то сможешь сам проскипать, только я не понимаю к чему это мне было писать)



Или можно просто Ctrl+F8 нажать. Тоже работает.

Думаю вопрос исчерпан.



Ну, раз ты решил докапываться до моей идеи, то и я буду преследовать ту же цель в твоих ответах мне

Бля, мужик..
Если пытаешься меня чему-то учить (будучи зная меньше чем я)


Давай не будем прибегать к оценке чужих знаний. Это как минимум некультурно.

Я тоже мог доебаться до каждой строки кода у тебя, но как ты видишь я этого не сделал.



если бы в дебаггере вместо инт3 юзался твой код, бедные реверсеры бы каждый день тебе отписывали с баг-репортом, что контекст их программы каким-то чудом умудрился сломаться...


Я тебе просто указал на неполноценность твоего решения. Это не general-purpose решение. В этом моя основная претензия.

Как минимум сканер сломается если брейкпоинт ставить не на первую инструкцию функции. Ну и хотя бы на ff25 проверку добавил бы.

Ahora57
12.02.2023, 03:54
Я тебе просто указал на неполноценность твоего решения. Это не general-purpose решение. В этом моя основная претензия.
Как минимум сканер сломается если брейкпоинт ставить не на первую инструкцию функции. Ну и хотя бы на ff25 проверку добавил бы.


Давай не будем прибегать к оценке чужих знаний -> Много чего не учтено. По хорошему тут чуть ли не эмулятор прикручивать надо.

Тот же long jmp игнорируется(ff 25) -> Ну и хотя бы на ff25 проверку добавил бы.

Простите, но это больше напоминает: "Ты должен добавить, но у меня точно нет претензий и из-за этого я прошу 2 раза добавить проверку на ff 25".



Или можно просто Ctrl+F8 нажать. Тоже работает.

Думаю вопрос исчерпан.


Я не совсем понимаю, зачем вы показали в одношаговой трассировке обработку INT3 для программы (параметры-> ядро -> пропускать INT3 при пошаговом выполнении). Да, некоторые ошибки можно пропустить (проглотить исключения/пропустить исключения).

Опять же смотрим, как тот же x64dbg обрабатывает это (https://github.com/x64dbg/TitanEngine/blob/d0b7e5addd9fce3ddd4d2cf3af502c3067e704e4/TitanEngine/TitanEngine.Debugger.DebugLoop.cpp#L506).

Я не хочу смотреть и продолжать этот бессмысленный спор. Давайте его закончим со следующими мыслями:

1)Это просто пример.

Давайте не будем утверждать, что те же некоторые отсталые AV хукают в DLL Api/NtApi или что нужно больше детектов.

Это сделано для новичкови мы не будем говорить 1000 раз НО.

2)Не надо доказывать, что я умнее тебя и продолжать из-за этого спор + при этом докапываться при этом к любому слову.

Как-то Frostiest (https://www.blast.hk/redirect/aHR0cHM6Ly93d3cudW5rbm93bmNoZWF0cy5tZS9mb3J1bS9tZW 1iZXJzLzI5MDM4NDUuaHRtbA) сказал умную, но простую мысль ~его слова: "Вам не нужно быть гением, чтобы общаться из-за этого с гениями".