0x0c0de
17.03.2009, 21:04
Ничего принципиально нового, но кому-то вероятно будет интересно. Речь здесь пойдет о волокнах. Поток в винде начинается с BaseThreadStartThunk, процесс с BaseProcessStartThunk, а волокно с BaseFiberStart. Значит, чтобы выполнить свой код до запуска кода волокна надо изменить эту неэкспортируемую функу BaseFiberStart. Сделать это просто, особенно в случае с волокнами. Кратко поясню как
Посмотрим на прототип функции CreateFiber
LPVOID WINAPI CreateFiber(
__in SIZE_T dwStackSize,
__in LPFIBER_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter
);
Возвращаемое значение - адрес волокна. Что такое адрес волокна? А вот тут уже msdn ничего не скажет. Ну ок, берем IDA и начинаем дизасмить kernel32.dll.
Для начала разберемся с WindowsXP SP3
CreateFiber вызывает CreateFiberEx, а в CreateFiberEx вызывается _BaseInitializeContext@20. BaseInitializeContext как раз и определяет какой адрес записать BaseThreadStartThunk, BaseProcessStartThunk или BaseFiberStart. В качестве первого параметра эта функция принимает указатель на структуру CONTEXT.
Как видим в структуре CONTEXT BaseFiberStart кладется по смещению 0B8h.
ifFiber: ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+4C4F j
.text:7C82FF6E mov dword ptr [eax+0B8h], offset _BaseFiberStart@0 ; BaseFiberStart()
.text:7C82FF78 jmp loc_7C8104A4
Однако, взглянув на код вызова BaseInitializeContext можно увидеть, что на самом деле структура CONTEXT сама находиться по смещению 0x14h в другой структуре, которую и возвращает функция CreateFiber. А значит адрес BaseFiberStart находиться в этой структуре по смещению 0x14 + 0xb8 = 0xCC.
.text:7C830031 push 2
.text:7C830033 mov [esi+10h], ecx
.text:7C830036 push [ebp+var_C]
.text:7C830039 push [ebp+arg_C]
.text:7C83003C push eax
.text:7C83003D lea eax, [esi+14h]
.text:7C830040 push eax
.text:7C830041 call _BaseInitializeContext@20 ; BaseInitializeContext(x,x,x,x,x)
.text:7C830046 mov eax, esi
Таким образом мы вызываем CreateFiber, берем адрес волокна и меняем адрес по смещению 0xCC. Но на самом деле это круто только для WinXP. В висте и семерке все несколько иначе:
CreateFiber->CreateFiberEx-> _BaseInitializeFiberContext@16
А там уже по смещению 0xC4 кладется адрес, а по этому адресу уже лежит BaseFiberStart.
.text:77E02070 mov [esi+0C4h], eax
.text:77E02076 mov dword ptr [eax], offset _BaseFiberStart@0 ; BaseFiberStart()
Так что теперь непосредственно 0xC4 + 0x14 = 0xd8. И меняем адрес по этому адресу. Вот такие дела.
И непосредственно кодес
#include "stdafx.h"
#include <windows.h>
extern "C"
{
LPVOID WINAPI CreateFiber(
__in SIZE_T dwStackSize,
__in PVOID lpStartAddress,
__in_opt LPVOID lpParameter
);
VOID WINAPI DeleteFiber(
__in LPVOID lpFiber
);
VOID WINAPI SwitchToFiber(
__in LPVOID lpFiber
);
LPVOID WINAPI ConvertThreadToFiber(
__in_opt LPVOID lpParameter
);
BOOL WINAPI ConvertFiberToThread(void);
}
PVOID pMainFiber,TrueBaseFiberStart;
VOID _declspec(naked) NewBaseFiberStart(VOID)
{
MessageBox(0,L"Fiber hooked",L"Hooked",0);
_asm
{
jmp [TrueBaseFiberStart]
ret
}
}
VOID StartFiber(VOID)
{
PVOID pCurFiber;
printf("StartFiber: I am in fiber\n");
pCurFiber = GetCurrentFiber();
printf("StartFiber: current fiber 0x%08X\n",pCurFiber);
SwitchToFiber(pMainFiber);
}
BOOL HookBaseFiberStart(PVOID pFiber,PVOID pAddressNewBaseFiberStart,PDWORD pdwSaveAddress)
{
BOOL bRes=FALSE;
DWORD dwVersion;
dwVersion = GetVersion();
//учитываем на какой системе мы
switch(LOBYTE(LOWORD(dwVersion)))
{
// модифицируем _BaseFiberStart
case 5:
// до Vista
_asm
{
mov edi,pFiber
mov esi,dword ptr [edi + 0xcc]
mov eax,pdwSaveAddress
mov dword ptr [eax], esi
mov esi,pAddressNewBaseFiberStart
mov dword ptr [edi + 0xcc],esi
}
bRes = TRUE;
break;
// начиная с Vista
case 6:
_asm
{
mov edi,pFiber
mov esi,dword ptr [edi+ 0xD8]
mov edi,dword ptr [esi]
mov eax, pdwSaveAddress
mov dword ptr [eax],edi
mov edi,pAddressNewBaseFiberStart
mov dword ptr [esi],edi
}
bRes = TRUE;
break;
default: break;
}
return bRes;
}
int _tmain(int argc, _TCHAR* argv[])
{
LPVOID pFiber;
// создаем волокно
pFiber = CreateFiber(0,&StartFiber,0);
if(!pFiber)
{
printf ("Cannot create fiber\n");
}
else
{
if(!HookBaseFiberStart(pFiber,NewBaseFiberStart,(P DWORD)&TrueBaseFiberStart))
{
return -1;
}
// делаем тред волокном, так как только волокно может запустить другое волокно
pMainFiber = ConvertThreadToFiber(0);
// запускаем наше свежесозданное волокно
SwitchToFiber(pFiber);
ConvertFiberToThread();
// удаляем волокно
printf("Deleting fiber 0x%08X...\n",pFiber);
DeleteFiber(pFiber);
}
Sleep(-1);
return 0;
}
В принципе можно применить в какой-нибудь защите.
Посмотрим на прототип функции CreateFiber
LPVOID WINAPI CreateFiber(
__in SIZE_T dwStackSize,
__in LPFIBER_START_ROUTINE lpStartAddress,
__in_opt LPVOID lpParameter
);
Возвращаемое значение - адрес волокна. Что такое адрес волокна? А вот тут уже msdn ничего не скажет. Ну ок, берем IDA и начинаем дизасмить kernel32.dll.
Для начала разберемся с WindowsXP SP3
CreateFiber вызывает CreateFiberEx, а в CreateFiberEx вызывается _BaseInitializeContext@20. BaseInitializeContext как раз и определяет какой адрес записать BaseThreadStartThunk, BaseProcessStartThunk или BaseFiberStart. В качестве первого параметра эта функция принимает указатель на структуру CONTEXT.
Как видим в структуре CONTEXT BaseFiberStart кладется по смещению 0B8h.
ifFiber: ; CODE XREF: BaseInitializeContext(x,x,x,x,x)+4C4F j
.text:7C82FF6E mov dword ptr [eax+0B8h], offset _BaseFiberStart@0 ; BaseFiberStart()
.text:7C82FF78 jmp loc_7C8104A4
Однако, взглянув на код вызова BaseInitializeContext можно увидеть, что на самом деле структура CONTEXT сама находиться по смещению 0x14h в другой структуре, которую и возвращает функция CreateFiber. А значит адрес BaseFiberStart находиться в этой структуре по смещению 0x14 + 0xb8 = 0xCC.
.text:7C830031 push 2
.text:7C830033 mov [esi+10h], ecx
.text:7C830036 push [ebp+var_C]
.text:7C830039 push [ebp+arg_C]
.text:7C83003C push eax
.text:7C83003D lea eax, [esi+14h]
.text:7C830040 push eax
.text:7C830041 call _BaseInitializeContext@20 ; BaseInitializeContext(x,x,x,x,x)
.text:7C830046 mov eax, esi
Таким образом мы вызываем CreateFiber, берем адрес волокна и меняем адрес по смещению 0xCC. Но на самом деле это круто только для WinXP. В висте и семерке все несколько иначе:
CreateFiber->CreateFiberEx-> _BaseInitializeFiberContext@16
А там уже по смещению 0xC4 кладется адрес, а по этому адресу уже лежит BaseFiberStart.
.text:77E02070 mov [esi+0C4h], eax
.text:77E02076 mov dword ptr [eax], offset _BaseFiberStart@0 ; BaseFiberStart()
Так что теперь непосредственно 0xC4 + 0x14 = 0xd8. И меняем адрес по этому адресу. Вот такие дела.
И непосредственно кодес
#include "stdafx.h"
#include <windows.h>
extern "C"
{
LPVOID WINAPI CreateFiber(
__in SIZE_T dwStackSize,
__in PVOID lpStartAddress,
__in_opt LPVOID lpParameter
);
VOID WINAPI DeleteFiber(
__in LPVOID lpFiber
);
VOID WINAPI SwitchToFiber(
__in LPVOID lpFiber
);
LPVOID WINAPI ConvertThreadToFiber(
__in_opt LPVOID lpParameter
);
BOOL WINAPI ConvertFiberToThread(void);
}
PVOID pMainFiber,TrueBaseFiberStart;
VOID _declspec(naked) NewBaseFiberStart(VOID)
{
MessageBox(0,L"Fiber hooked",L"Hooked",0);
_asm
{
jmp [TrueBaseFiberStart]
ret
}
}
VOID StartFiber(VOID)
{
PVOID pCurFiber;
printf("StartFiber: I am in fiber\n");
pCurFiber = GetCurrentFiber();
printf("StartFiber: current fiber 0x%08X\n",pCurFiber);
SwitchToFiber(pMainFiber);
}
BOOL HookBaseFiberStart(PVOID pFiber,PVOID pAddressNewBaseFiberStart,PDWORD pdwSaveAddress)
{
BOOL bRes=FALSE;
DWORD dwVersion;
dwVersion = GetVersion();
//учитываем на какой системе мы
switch(LOBYTE(LOWORD(dwVersion)))
{
// модифицируем _BaseFiberStart
case 5:
// до Vista
_asm
{
mov edi,pFiber
mov esi,dword ptr [edi + 0xcc]
mov eax,pdwSaveAddress
mov dword ptr [eax], esi
mov esi,pAddressNewBaseFiberStart
mov dword ptr [edi + 0xcc],esi
}
bRes = TRUE;
break;
// начиная с Vista
case 6:
_asm
{
mov edi,pFiber
mov esi,dword ptr [edi+ 0xD8]
mov edi,dword ptr [esi]
mov eax, pdwSaveAddress
mov dword ptr [eax],edi
mov edi,pAddressNewBaseFiberStart
mov dword ptr [esi],edi
}
bRes = TRUE;
break;
default: break;
}
return bRes;
}
int _tmain(int argc, _TCHAR* argv[])
{
LPVOID pFiber;
// создаем волокно
pFiber = CreateFiber(0,&StartFiber,0);
if(!pFiber)
{
printf ("Cannot create fiber\n");
}
else
{
if(!HookBaseFiberStart(pFiber,NewBaseFiberStart,(P DWORD)&TrueBaseFiberStart))
{
return -1;
}
// делаем тред волокном, так как только волокно может запустить другое волокно
pMainFiber = ConvertThreadToFiber(0);
// запускаем наше свежесозданное волокно
SwitchToFiber(pFiber);
ConvertFiberToThread();
// удаляем волокно
printf("Deleting fiber 0x%08X...\n",pFiber);
DeleteFiber(pFiber);
}
Sleep(-1);
return 0;
}
В принципе можно применить в какой-нибудь защите.