SFC

eXeL@B DVD

Материал из Справочник исследователя программ

Перейти к: навигация, поиск

SFC(Stack Frame Chain) - цепочка стековых фреймов. Обычно процедуры используют адресацию аргументов и локальных переменных посредством регистра Ebp. Это следствие stdcall-конвенции вызова. При этом каждая процедура владеет своим пространством стека. Эта область где находятся аргументы функции, локальные переменные и данные сохраненные в текущий момент называется стековым фреймом. В начале работы функции сохраняется в стеке текущее значение регистра Ebp и в него загружается ссылка на дно стека(текущее значение регистра Esp). Таким образом в любой момент времени регистр Ebp адресует аргументы функции и локальные переменные расположенные ниже(стек растёт вниз). Изза сохранённого значения регистра Ebp при входе в функцию формируется цепочка, фреймы таким образом связываются:

 - Дно стека -
Esp:	[...]
	[Локальные переменные]
Ebp:	[Предыдущее значение регистра Ebp, тоесть ссылка на следующий фрейм]
	[Адрес возврата из функции, который сформирован процедурным ветвлением]
	[Аргументы функции]
	[...]

Для формирования фрейма и его удаления существуют инструкции Enter и Leave. Обычно вместо инструкции Enter используется пара инструкций push ebp и mov ebp,esp. Часть стека из двух слов, которую адресует регистр Ebp всегда может быть описана структурой:

STACK_FRAME struct
Frame	PVOID ?	; Ссылка на следующий фрейм.
Ip	PVOID ?	; Адрес возврата из процедуры.
STACK_FRAME ends

Часто фрейм содержит защитную информацию, это например куки(програмный DEP) и SEH-фрейм:

LdrLoadDll:
	push 26C	; Размер стека выделяемого под локальные переменные.
	push ntdll.7C9164F8	; Информация для SEH.
	call ntdll.__SEH_prolog
	mov eax,dword ptr ds:[___security_cookie]	; Куки для защиты стека от переполнений.
	mov dword ptr ss:[ebp - 0x1C],eax

Системная функция _SEH_prolog формирует стековый фрейм и устанавливает SEH. Фрейм будет выглядеть следующим образом:

Esp:
$	[Локальные переменные]
$+26C	[Куки, рандомное значение]
$+270	[XXXX]
$+274	[XXXX]
$+278	[Ссылка на следующий SEH-фрейм]
$+27C	[Ссылка на SEH]
$+280	[Информация для SEH]
$+284	[-//-]
Ebp:
$+288	[Предыдущее значение Ebp]
$+28C	[Адрес возврата]
$+290	[Аргументы]

Так как стековые фреймы связаны, то возможно их перечисление, тоесть проход по цепочке. Эта манипуляция называется бактрейсом. Получив значение регистра Ebp из контекста потока которое постоянно в пределах текущей процедуры, можно определить аргументы функции и её локальные переменные, также и следующей процедуры получив ссылку на её фрейм из текущего.

NL(Nesting Level) - это уровень вложенности процедур, тоесть число процедурных ветвлений между вызывающей и вызываемой процедурой.

SFN(Stack Frame Number) - это номер стекового фрейма, равный NL относительно вызывающей процедуры.

Так как NL является константой, то зная его и значение регистра Ebp вызываемой процедуры можно определить фрейм вызывающей процедуры, выполнив бактрейс из NL итераций. Так например получив управление гдето внутри целевой функции можно выполнить бактрейс, найти необходимый фрейм вызывающей процедуры и заменить в нём адрес возврата. Тогда при возврате из вызывающей процедуры будет выполнен переход на новую процедуру. Этот механизм лежит в основе альтернативных способов перехвата кода(таких как IDP), применяемых в руткитах(тут можно увидеть семпл для CreateProcess()). В SFC последний фрейм не содержит ссылку, она заменена маркером конца цепи(EXCEPTION_CHAIN_END = -1), либо ноль. Это позволяет прекратить бактрейс и избежать обращения за пределы стека. В ядре модель SFC немного иная - происходит изоляция трап-фремов. Пример бактрейса в ядре для поиска трап-фрейма:

STACK_FRAME struct
Frame	PVOID ?
Ip	PVOID ?
STACK_FRAME ends

TsDbgArgMark equ 00008H	; KTRAP_FRAME.DbgArgMark

PcStackBase		equ 4
PcStackLimit		equ 8

TsEip		equ 00068H
TsSegCs		equ 0006CH
TsEflags		equ 00070H

MODE_MASK equ 00001H

	mov eax,ebp
	assume eax:PTR STACK_FRAME
Scan:
	cmp eax,-1
	je ChainEnd
	test eax,eax
	jz ChainEnd
	
	cmp fs:[PcStackBase],eax
	jna Error
	cmp fs:[PcStackLimit],eax
	ja Error

	cmp dword ptr [eax + TsDbgArgMark],0BADB0D00H
	je TrapFrame
	
	mov eax,[eax].Next
	jmp Scan
	
TrapFrame:
	assume eax:PKTRAP_FRAME
	test dword ptr [eax + TsSegCs],MODE_MASK
	jz KMode
	test dword ptr [eax + TsEflags],EFLAGS_IF
	jz Error
	...
Источник — «https://exelab.ru/faq/SFC»