Простой call stack walk x64
От: Caracrist https://1pwd.org/
Дата: 03.07.13 10:25
Оценка:
Есть вот такой примитивный но быстрый и надежный способ получить адреса возвратов функций:
DECLSPEC_NOINLINE void SimpleStackWalk3286(unsigned int maxFrames)
{
    unsigned int * ebp = &maxFrames - 2;
    for(unsigned int frame = 0; frame < maxFrames; ++frame)
    {
        unsigned int eip = ebp[1];
        if(eip == 0)
            // No caller on stack
            break;
        ebp = reinterpret_cast<unsigned int *>(ebp[0]);
    }
}


К сожалению это работает только для x32-86.
есть ли подобное решение для x64(Без вызова StackWalk64 с сопутствующими проблемами синхронизации)?

04.07.13 01:32: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
~~~~~
~lol~~
~~~ Single Password Solution
Re: Простой call stack walk x64
От: Сергей Мухин Россия  
Дата: 03.07.13 10:36
Оценка:
Здравствуйте, Caracrist, Вы писали:

C>Есть вот такой примитивный но быстрый и надежный способ получить адреса возвратов функций:


Надежный? а если fpo?
---
С уважением,
Сергей Мухин
Re[2]: Простой call stack walk x64
От: Caracrist https://1pwd.org/
Дата: 03.07.13 11:18
Оценка:
Здравствуйте, Сергей Мухин, Вы писали:

СМ>Надежный? а если fpo?


что есть fpo?
~~~~~
~lol~~
~~~ Single Password Solution
Re[3]: Простой call stack walk x64
От: Сергей Мухин Россия  
Дата: 03.07.13 11:30
Оценка:
Здравствуйте, Caracrist, Вы писали:

C>Здравствуйте, Сергей Мухин, Вы писали:


СМ>>Надежный? а если fpo?


C>что есть fpo?
---
С уважением,
Сергей Мухин
Re[3]: Простой call stack walk x64
От: Сергей Мухин Россия  
Дата: 03.07.13 11:31
Оценка: 2 (1)
Здравствуйте, Caracrist, Вы писали:

СМ>>Надежный? а если fpo?


C>что есть fpo?


если Вы занимаетесь стеком, то должны бы знать

http://msdn.microsoft.com/ru-ru/library/38kk9e60(v=vs.110).aspx
---
С уважением,
Сергей Мухин
Re: Простой call stack walk x64
От: anonymous185  
Дата: 03.07.13 22:39
Оценка: 12 (2)
есть. надёжный в отличии от приведённого метода (он базируется на предположении что функции устанавливают ebp-фрейм, что не всегда верно). но и гораздо более сложный.
сначало
    DWORD64 ImageBase;
    PRUNTIME_FUNCTION pRT = RtlLookupFunctionEntry(Rip, &ImageBase, 0);
    PUNWIND_INFO pui = (PUNWIND_INFO)RtlOffsetToPointer(ImageBase, pRT->UnwindData);

ну а затем раскручивать цепочку структур UNWIND_INFO. базовая информация об этом здесь
Re: Простой call stack walk x64
От: Caracrist https://1pwd.org/
Дата: 04.07.13 07:52
Оценка:
нашел вот такое решение:

ULONG StackBackTrace(ULONG FramesToSkip, ULONG FramesToCapture, PVOID * BackTrace, PULONG BackTraceHash)
{    
    static USHORT (WINAPI*s_pfnCaptureStackBackTrace)(ULONG, ULONG, PVOID*, PULONG) = 0;  
    if (s_pfnCaptureStackBackTrace == 0)  
    {  
        const HMODULE hNtDll = ::GetModuleHandleA("ntdll.dll");  
        reinterpret_cast<void*&>(s_pfnCaptureStackBackTrace) = ::GetProcAddress(hNtDll, "RtlCaptureStackBackTrace");
    }
    if (!BackTrace) {
        BackTrace = (PVOID*)alloca(sizeof(void*) * FramesToCapture);
    }
    if (s_pfnCaptureStackBackTrace != 0)
    {
        return s_pfnCaptureStackBackTrace(FramesToSkip, FramesToCapture, BackTrace, BackTraceHash);
    }
    return (ULONG)-1;
}

работает как на 86 так и 64
однако, это работает медленнее аллокации (operator new) в 40 раз...
~~~~~
~lol~~
~~~ Single Password Solution
Re[2]: Простой call stack walk x64
От: Caracrist https://1pwd.org/
Дата: 04.07.13 08:03
Оценка:
Здравствуйте, anonymous185, Вы писали:

A>есть. надёжный в отличии от приведённого метода (он базируется на предположении что функции устанавливают ebp-фрейм, что не всегда верно). но и гораздо более сложный.

A>сначало
A>
A>    DWORD64 ImageBase;
A>    PRUNTIME_FUNCTION pRT = RtlLookupFunctionEntry(Rip, &ImageBase, 0);
A>    PUNWIND_INFO pui = (PUNWIND_INFO)RtlOffsetToPointer(ImageBase, pRT->UnwindData);
A>

A>ну а затем раскручивать цепочку структур UNWIND_INFO. базовая информация об этом здесь

не смотря на всю печальку тут: http://www.rsdn.ru/forum/asm/5220239
Автор: Caracrist
Дата: 04.07.13

один только вот этот код уже занимает в 20 раз больше одной аллокации:
        HANDLE hProcess = ::GetCurrentProcess();
        HANDLE hThread = ::GetCurrentThread();
        CONTEXT c;
        c.ContextFlags = CONTEXT_FULL;
        if (!::GetThreadContext(hThread, &c)) {
            DWORD err = ::GetLastError();
            printf("GetThreadContext error: 0x%x\n", err);
            return p;
        }
        DWORD64 ImageBase;
        static PVOID (WINAPI*s_pfnRtlLookupFunctionEntry)(ULONGLONG ControlPc, PULONGLONG ImageBase, PULONGLONG TargetGp);  
        if (s_pfnRtlLookupFunctionEntry == 0)  
        {  
            const HMODULE hKernelDll = ::GetModuleHandleA("Kernel32.dll");  
            reinterpret_cast<void*&>(s_pfnRtlLookupFunctionEntry) = ::GetProcAddress(hKernelDll, "RtlLookupFunctionEntry");
        }
        PVOID pRT = s_pfnRtlLookupFunctionEntry(c.Rip, &ImageBase, 0);


пока остановлюсь на RtlCaptureStackBackTrace
~~~~~
~lol~~
~~~ Single Password Solution
Re[3]: Простой call stack walk x64
От: anonymous185  
Дата: 04.07.13 08:30
Оценка:
        HANDLE hProcess = ::GetCurrentProcess();
        HANDLE hThread = ::GetCurrentThread();
        CONTEXT c;
        c.ContextFlags = CONTEXT_FULL;
        if (!::GetThreadContext(hThread, &c)) {
            DWORD err = ::GetLastError();
            printf("GetThreadContext error: 0x%x\n", err);
            return p;
        }

зачем всё это ? ради c.Rip и c.Rsp только ?? а как насчет _ReturnAddress() и _AddressOfReturnAddress() использовать ?
а линковаться с ntdll.lib, чтобы напрямую вызывать RtlLookupFunctionEntry без всякой фигни — сложно ?
Re[4]: Простой call stack walk x64
От: Caracrist https://1pwd.org/
Дата: 04.07.13 12:57
Оценка:
Здравствуйте, anonymous185, Вы писали:


A>зачем всё это ? ради c.Rip и c.Rsp только ?? а как насчет _ReturnAddress() и _AddressOfReturnAddress() использовать ?

A>а линковаться с ntdll.lib, чтобы напрямую вызывать RtlLookupFunctionEntry без всякой фигни — сложно ?

про _ReturnAddress() и _AddressOfReturnAddress() не знал, спасибо.

у меня не драйвер, просто хук RtlOffsetToPointer

Callers of RtlOffsetToPointer must be running at IRQL < DISPATCH_LEVEL if the memory range from Base to Offset is pageable.


правильно ли я понимаю, что эта функция мне не доступна?
~~~~~
~lol~~
~~~ Single Password Solution
Re[5]: Простой call stack walk x64
От: anonymous185  
Дата: 04.07.13 14:07
Оценка:
RtlOffsetToPointer — это не функция, это макрос.
#define RtlOffsetToPointer(B,O) ((PCHAR)( ((PCHAR)(B)) + ((ULONG_PTR)(O)) ))
он доступен всем. просто хук — непонял. опять таки это макрос.

Callers of RtlOffsetToPointer must be running at IRQL < DISPATCH_LEVEL if the memory range from Base to Offset is pageable.

идиотское примечание. да в kernel mode нельзя обращаться к pageable memory on IRQL >= DISPATCH_LEVEL. какое это отношение имеет к макросу я плохо понимаю.
вообщем всё это спокойно можно использовать в обычном приложении без всяких проблем
Re[6]: Простой call stack walk x64
От: mike_rs Россия  
Дата: 09.07.13 11:19
Оценка:
Здравствуйте, anonymous185, Вы писали:

A>

A>Callers of RtlOffsetToPointer must be running at IRQL < DISPATCH_LEVEL if the memory range from Base to Offset is pageable.

A>идиотское примечание. да в kernel mode нельзя обращаться к pageable memory on IRQL >= DISPATCH_LEVEL. какое это отношение имеет к макросу я плохо понимаю.

самое прямое — если адрес pagable и страница сброшена, то при попытке использования этого макроса на высоких irql получишь bsod. Так что насчет "без проблем" — это сильное заявление, надо все-таки смотреть что там за адрес памяти.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.