본문 바로가기
리버싱/리버싱 걸음마

정보보안 SUA - [정보보안공부] 5주차 과제 - Hooking의 기초

by Royal! 2021. 3. 3.
728x90
반응형

- [필수 1] Hooking의 정의, 개념 등을 이해합니다. (Link 1)
- [필수 2] 모든 프로세스를 열거합니다. (콘솔 프로그램도 무관. Link 2, 3)
- [필수 3] 지정한 프로세스의 메모리에서 특정 주소의 데이터를 읽거나 원하는 데이터를 덮어씁니다. (Link 4, 5, 6)
- [선택 1] 직접 작성한 프로그램의 실행 흐름을 런타임 API Hooking을 이용하여 바꿔봅니다.
- [선택 2] Hooking 여부를 탐지할 수 있는 방법들을 생각하고 직접 구현해봅니다.

 

Link 1 - en.wikipedia.org/wiki/Hooking

Link 2 - 모든 프로세스 ID 열거 (PSAPI 사용): 

docs.microsoft.com/en-us/windows/win32/psapi/enumerating-all-processes

 

Enumerating All Processes - Win32 apps

The following sample code uses the EnumProcesses function to enumerate the current processes in the system.

docs.microsoft.com

Link 3 - 모든 프로세스 정보 열거 (Tlhel32 사용):

docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes

 

Taking a Snapshot and Viewing Processes - Win32 apps

The following simple console application obtains a list of running processes.

docs.microsoft.com

Link 4 - Process ID로 Process Handle 얻기:

docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess

 

OpenProcess function (processthreadsapi.h) - Win32 apps

Opens an existing local process object.

docs.microsoft.com

Link 5 - 지정된 프로세스 및 주소로부터 지정된 크기만큼 데이터 읽기: 

docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-readprocessmemory

 

ReadProcessMemory function (memoryapi.h) - Win32 apps

Reads data from an area of memory in a specified process. The entire area to be read must be accessible or the operation fails.

docs.microsoft.com

Link 6 - 지정된 프로세스 및 주소에 지정된 크기 만큼 데이터 쓰기:

docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory

 

WriteProcessMemory function (memoryapi.h) - Win32 apps

Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or the operation fails.

docs.microsoft.com

[필수1] Hooking의 정의 및 개념 


후킹(Hooking)

리버싱에서 후킹은 정보를 가로채며, 실행 흐름을 변경하고 기존의 프로그램과는 다른 기능을 제공하는 기술.

 

후크(Hook)

후킹 시에 영향을 끼친 함수, 이벤트, 메세지를 처리하는 코드

 


[필수 2-1] 모든 프로세스를 ID 열거 (PSAPI 사용)


EnumProcesses 함수를 사용하여 각 프로세스 개체에 대한 프로세스 식별자(ID)를 검색하는 코드 및 결과

#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>

// To ensure correct resolution of symbols, add Psapi.lib to TARGETLIBS
// and compile with -DPSAPI_VERSION=1

void PrintProcessNameAndID( DWORD processID )
{
    TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");

    // Get a handle to the process.

    HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
                                   PROCESS_VM_READ,
                                   FALSE, processID );

    // Get the process name.

    if (NULL != hProcess )
    {
        HMODULE hMod;
        DWORD cbNeeded;

        if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), 
             &cbNeeded) )
        {
            GetModuleBaseName( hProcess, hMod, szProcessName, 
                               sizeof(szProcessName)/sizeof(TCHAR) );
        }
    }

    // Print the process name and identifier.

    _tprintf( TEXT("%s  (PID: %u)\n"), szProcessName, processID );

    // Release the handle to the process.

    CloseHandle( hProcess );
}

int main( void )
{
    // Get the list of process identifiers.

    DWORD aProcesses[1024], cbNeeded, cProcesses;
    unsigned int i;

    if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
    {
        return 1;
    }


    // Calculate how many process identifiers were returned.

    cProcesses = cbNeeded / sizeof(DWORD);

    // Print the name and process identifier for each process.

    for ( i = 0; i < cProcesses; i++ )
    {
        if( aProcesses[i] != 0 )
        {
            PrintProcessNameAndID( aProcesses[i] );
        }
    }

    return 0;
}

[필수2-1] 결과

A - EnumProcesses 함수를 사용하여 프로세스 목록을 가져온다.

B -  각 프로세스에 대해 main함수는 PrintProcesessNameAndID 함수를 호출하여 프로세스 식별자를 전달한다.

C - PrintProcesessNameAndID는 차례로 OpenProcess 함수를 호출하여 프로세스 핸들을 얻는다.

D - PrintProcesessNameAndID는 EnumProcessModules 함수를 호출하여 모듈핸들을 얻는다.

F - PrintProcesessNameAndID는 GetModuleBaseName 함수를 호출하여 실행파일의 이름을 얻고 프로세스 식별자와 함께 이름이 표시된다.


 

 

[필수2-2] 모든 프로세스 정보 열거 (Tlhel32 사용)


#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>

//  Forward declarations:
BOOL GetProcessList( );
BOOL ListProcessModules( DWORD dwPID );
BOOL ListProcessThreads( DWORD dwOwnerPID );
void printError( TCHAR* msg );

int main( void )
{
  GetProcessList( );
  return 0;
}

BOOL GetProcessList( )
{
  HANDLE hProcessSnap;
  HANDLE hProcess;
  PROCESSENTRY32 pe32;
  DWORD dwPriorityClass;

  // Take a snapshot of all processes in the system.
  hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
  if( hProcessSnap == INVALID_HANDLE_VALUE )
  {
    printError( TEXT("CreateToolhelp32Snapshot (of processes)") );
    return( FALSE );
  }

  // Set the size of the structure before using it.
  pe32.dwSize = sizeof( PROCESSENTRY32 );

  // Retrieve information about the first process,
  // and exit if unsuccessful
  if( !Process32First( hProcessSnap, &pe32 ) )
  {
    printError( TEXT("Process32First") ); // show cause of failure
    CloseHandle( hProcessSnap );          // clean the snapshot object
    return( FALSE );
  }

  // Now walk the snapshot of processes, and
  // display information about each process in turn
  do
  {
    _tprintf( TEXT("\n\n=====================================================" ));
    _tprintf( TEXT("\nPROCESS NAME:  %s"), pe32.szExeFile );
    _tprintf( TEXT("\n-------------------------------------------------------" ));

    // Retrieve the priority class.
    dwPriorityClass = 0;
    hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
    if( hProcess == NULL )
      printError( TEXT("OpenProcess") );
    else
    {
      dwPriorityClass = GetPriorityClass( hProcess );
      if( !dwPriorityClass )
        printError( TEXT("GetPriorityClass") );
      CloseHandle( hProcess );
    }

    _tprintf( TEXT("\n  Process ID        = 0x%08X"), pe32.th32ProcessID );
    _tprintf( TEXT("\n  Thread count      = %d"),   pe32.cntThreads );
    _tprintf( TEXT("\n  Parent process ID = 0x%08X"), pe32.th32ParentProcessID );
    _tprintf( TEXT("\n  Priority base     = %d"), pe32.pcPriClassBase );
    if( dwPriorityClass )
      _tprintf( TEXT("\n  Priority class    = %d"), dwPriorityClass );

    // List the modules and threads associated with this process
    ListProcessModules( pe32.th32ProcessID );
    ListProcessThreads( pe32.th32ProcessID );

  } while( Process32Next( hProcessSnap, &pe32 ) );

  CloseHandle( hProcessSnap );
  return( TRUE );
}


BOOL ListProcessModules( DWORD dwPID )
{
  HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
  MODULEENTRY32 me32;

  // Take a snapshot of all modules in the specified process.
  hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID );
  if( hModuleSnap == INVALID_HANDLE_VALUE )
  {
    printError( TEXT("CreateToolhelp32Snapshot (of modules)") );
    return( FALSE );
  }

  // Set the size of the structure before using it.
  me32.dwSize = sizeof( MODULEENTRY32 );

  // Retrieve information about the first module,
  // and exit if unsuccessful
  if( !Module32First( hModuleSnap, &me32 ) )
  {
    printError( TEXT("Module32First") );  // show cause of failure
    CloseHandle( hModuleSnap );           // clean the snapshot object
    return( FALSE );
  }

  // Now walk the module list of the process,
  // and display information about each module
  do
  {
    _tprintf( TEXT("\n\n     MODULE NAME:     %s"),   me32.szModule );
    _tprintf( TEXT("\n     Executable     = %s"),     me32.szExePath );
    _tprintf( TEXT("\n     Process ID     = 0x%08X"),         me32.th32ProcessID );
    _tprintf( TEXT("\n     Ref count (g)  = 0x%04X"),     me32.GlblcntUsage );
    _tprintf( TEXT("\n     Ref count (p)  = 0x%04X"),     me32.ProccntUsage );
    _tprintf( TEXT("\n     Base address   = 0x%08X"), (DWORD) me32.modBaseAddr );
    _tprintf( TEXT("\n     Base size      = %d"),             me32.modBaseSize );

  } while( Module32Next( hModuleSnap, &me32 ) );

  CloseHandle( hModuleSnap );
  return( TRUE );
}

BOOL ListProcessThreads( DWORD dwOwnerPID ) 
{ 
  HANDLE hThreadSnap = INVALID_HANDLE_VALUE; 
  THREADENTRY32 te32; 
 
  // Take a snapshot of all running threads  
  hThreadSnap = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 ); 
  if( hThreadSnap == INVALID_HANDLE_VALUE ) 
    return( FALSE ); 
 
  // Fill in the size of the structure before using it. 
  te32.dwSize = sizeof(THREADENTRY32); 
 
  // Retrieve information about the first thread,
  // and exit if unsuccessful
  if( !Thread32First( hThreadSnap, &te32 ) ) 
  {
    printError( TEXT("Thread32First") ); // show cause of failure
    CloseHandle( hThreadSnap );          // clean the snapshot object
    return( FALSE );
  }

  // Now walk the thread list of the system,
  // and display information about each thread
  // associated with the specified process
  do 
  { 
    if( te32.th32OwnerProcessID == dwOwnerPID )
    {
      _tprintf( TEXT("\n\n     THREAD ID      = 0x%08X"), te32.th32ThreadID ); 
      _tprintf( TEXT("\n     Base priority  = %d"), te32.tpBasePri ); 
      _tprintf( TEXT("\n     Delta priority = %d"), te32.tpDeltaPri ); 
      _tprintf( TEXT("\n"));
    }
  } while( Thread32Next(hThreadSnap, &te32 ) ); 

  CloseHandle( hThreadSnap );
  return( TRUE );
}

void printError( TCHAR* msg )
{
  DWORD eNum;
  TCHAR sysMsg[256];
  TCHAR* p;

  eNum = GetLastError( );
  FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
         NULL, eNum,
         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
         sysMsg, 256, NULL );

  // Trim the end of the line and terminate it with a null
  p = sysMsg;
  while( ( *p > 31 ) || ( *p == 9 ) )
    ++p;
  do { *p-- = 0; } while( ( p >= sysMsg ) &&
                          ( ( *p == '.' ) || ( *p < 33 ) ) );

  // Display the message
  _tprintf( TEXT("\n  WARNING: %s failed with error %d (%s)"), msg, eNum, sysMsg );
}

[필수2-2] 결과

A - GetProcessList 함수는 CreateToolhelp32Snapshot을 사용하여 시스템에서 현재 실행중인 프로세스의 스냅 샷을 만든 다음 Process32First 및 Process32Next를 사용하여 스냅 샷에 기록된 목록을 살펴본다.

B -  차례대로 각 프로세스에 대해 모듈목록탐색에 설명 도니 함수와 스레드 목록탐색에 설명된 함수를 GetProcessList로 함수로 호출한다.

C -printError은 일반적으로 보안 제한으로 인해 발생하는 오류의 원인을 표시한다.


 [필수 3] 지정한 프로세스의 메모리에서 특정 주소의 데이터를 읽거나 원하는 데이터를 덮어쓴다.


OpenProcess function - 기존 로컬 프로세스 개체를 열어주는 함수

ReadProcessMemory function - 프로세스 메로리를 읽는 함수

WriteProcessMemory function - 지정된 프로세스의 메모리 영역에 데이터를 쓰는 함수

현재 사용중인 카카오톡 PID 11224. 위의 함수를 이용하여 [필수3]과제를 해보겠습니다.

(참고 - debugjung.tistory.com/entry/%ED%8E%8C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%95%88%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B3%A0-%EC%9D%BD%EA%B3%A0-%EC%93%B0%EA%B8%B0)

 

#include <stdio.h>
#include <windows.h>

int main() {
	HANDLE num;
	
	if ((num = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 11224)) == NULL)
		printf("Error\n\n");
	else
		printf("Handle: %d\n\n");

	return 0;
}

 

 

#include <stdio.h>
#include <windows.h>
#include <memoryapi.h>
int main() {
	HANDLE num;
	
	if ((num = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 11224)) == NULL)
		printf("Error\n\n");
	else
		printf("Handle: %d\n\n",num);

	DWORD buf = 0;
	
	if (ReadProcessMemory(num, (LPVOID)0xA70000, &buf, sizeof(DWORD), NULL) != 0)
	{ 
		buf++;
		WriteProcessMemory(num, (LPVOID)0xA70000, (LPCVOID)&buf, sizeof(DWORD), NULL);
	}
	return 0;
}

[필수3] 결과 값

 

728x90
반응형

최근댓글

최근글

Royal! © 2024