security/리버싱핵심원리

Registry를 이용한 DLL Injection

민사민서 2023. 3. 18. 17:30
// myhack2.dll

#include "windows.h"
#include "tchar.h"

#define DEF_CMD			(TEXT("C:\\Program Files\\Internet Explorer\\iexplore.exe"))
#define DEF_ADDR		(TEXT("https://www.naver.com"))
#define DEF_DST_PROC	(TEXT("notepad.exe"))

BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) {
	TCHAR szCmd[MAX_PATH] = {0,}; // command line argument
	TCHAR szPath[MAX_PATH] = {0,}; // path of process where dll is injected
	TCHAR *p = NULL;
	STARTUPINFO si = {0,}; // structure which describes how to start new process
	PROCESS_INFORMATION pi = {0,}; // structure which receives information about newly created process

	si.cb = sizeof(STARTUPINFO); // size of structure in bytes
	si.dwFlags = STARTF_USESHOWWINDOW; // indicates that wShowWindow member is valid
	si.wShowWindow = SW_HIDE; // new process should be displayed as hidden

	switch(fdwReason) {
	case DLL_PROCESS_ATTACH:
		if( !GetModuleFileName(NULL, szPath, MAX_PATH) )
			break;
		if( !(p=_tcsrchr(szPath, '\\')) )
			break;
		if( _tcsicmp(p+1, DEF_DST_PROC) )
			break;

		// szCmd = path to iexplore.exe + URL to be opened
		wsprintf(szCmd, TEXT("%s %s"), DEF_CMD, DEF_ADDR);
		if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi) )
			break;

		if(pi.hProcess != NULL)
			CloseHandle(pi.hProcess);

		break;
	}

	return TRUE;
}

 

Registry에 추가하기 전에 InjectDll.exe를 이용해 notepad.exe 프로세스에 inject 해보았더니 숨김모드로 잘 실행된다

(작업 표시줄에도 안 뜸. process explorer로만 확인 가능)

해당 프로세스(iexplorer.exe in hidden mode)가 어떤 IP/Port에 연결되어있는지 확인하려 했는데, 제대로 네이버에 접속이 되지 않은 것 같았다. 그래서 cmd 창에서 직접 szCmd 값 입력해서 확인해봄

- chrome은 naver 창 잘 뜸

- internet explorer는 웹페이지를 표시할 수 없다고 뜸

=> 그냥 internet explorer 문제 (ㅋㅋㅋ)

 

레지스트리에 추가하는 방법은

1. regedit.exe 실행

2. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows로 이동

3. AppInit_DLLs 항목의 값에 myhack2.dll의 전체 경로 추가

4. LoadAppInit_DLLs 항목의 값을 1로 변경

=> 레지스트리 변경 후 실행되는 모든 프로세스에 해당 DLL inject 해줌 (강력쓰)

=> 동작원리: User32.dll이 프로세스에 로딩될 때 AppInit_DLLs 항목을 읽어서 LoadLibrary API 이용해 사용자 DLL을 로딩하는 방식. 즉, user32.dll을 로딩하는 프로세스에만 inject 된다.

 

1. Internet Explorer가 아니라 Chrome으로?

DEF_CMD를 chrome.exe 경로로 바꿔보았다. (C:\Program Files\Google\Chrome\Application\chrome.exe)

그랬더니 naver.com에 잘 접속되었지만 hide mode가 적용되지는 않았다.

 => 크롬이 이미 실행중이라면 notepad.exe의 child가 아닌 기존 chrome.exe 하위에 새로운 프로세스가 생기고, 새로 열린 naver 탭 또한 볼 수 있었다.

PID=5748 새로 생성된 프로세스

=> 크롬이 실행 중이 아니라면 notepad.exe 하위에 새로운 프로세스가 생기고, 마찬가지로 탭이 보였다.

=> 반면 IE는 이미 실행중이든 아니든 notepad.exe 하위에 새로운 프로세스가 생기고, 사용자에게 창이 안보였다

 

Why?

크롬은 탭 하나하나를 독립된 프로세스로 운영하는 반면 익스플로어는 모든 탭을 하나의 프로세스로 처리

크롬/엣지 동일한 현상. 내부 엔진 구현 차이 때문인가?

 

(ChatGPT 4.0)

1. 새 탭이 Chrome에서 보이는 이유:
Chrome은 Internet Explorer와는 다르게 멀티 프로세스 구조를 사용하고 있습니다. 이로 인해 Chrome에서 SW_HIDE 플래그가 동일한 방식으로 작동하지 않을 수 있습니다. 일부 최신 브라우저는 다중 프로세스를 사용하므로, SW_HIDE 플래그가 적용되지 않거나 일관되지 않게 적용될 수 있습니다. 따라서 Chrome에서 탭이 보이는 것은 기대했던 것과 달리 SW_HIDE 플래그가 정상적으로 작동하지 않기 때문입니다.

2. 생성된 프로세스의 위치가 다른 이유:
이 문제도 Chrome의 멀티 프로세스 구조 때문입니다. 기존에 실행 중인 chrome.exe 프로세스가 있을 때, 새로운 프로세스를 만들면 Chrome이 자동으로 기존 프로세스와 통합합니다. 이 경우 새로운 프로세스는 기존 chrome.exe 프로세스의 하위 프로세스가 되고, notepad.exe의 하위 프로세스가 아닙니다. 이와는 대조적으로 Internet Explorer는 단일 프로세스 구조를 가지고 있기 때문에 새 프로세스가 항상 notepad.exe의 하위 프로세스로 생성됩니다.

 

2. CreateProcess() 함수

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

void _tmain( int argc, TCHAR *argv[] )
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;

    ZeroMemory( &si, sizeof(si) );
    si.cb = sizeof(si);
    ZeroMemory( &pi, sizeof(pi) );

    if( argc != 2 )
    {
        printf("Usage: %s [cmdline]\n", argv[0]);
        return;
    }

    // Start the child process. 
    if( !CreateProcess( NULL,   // No module name (use command line)
        argv[1],        // Command line
        NULL,           // Process handle not inheritable
        NULL,           // Thread handle not inheritable
        FALSE,          // Set handle inheritance to FALSE
        0,              // No creation flags
        NULL,           // Use parent's environment block
        NULL,           // Use parent's starting directory 
        &si,            // Pointer to STARTUPINFO structure
        &pi )           // Pointer to PROCESS_INFORMATION structure
    ) 
    {
        printf( "CreateProcess failed (%d).\n", GetLastError() );
        return;
    }

    // Wait until child process exits.
    WaitForSingleObject( pi.hProcess, INFINITE );

    // Close process and thread handles. 
    CloseHandle( pi.hProcess );
    CloseHandle( pi.hThread );
}

- CreateProcess()가 호출되는 프로세스와 독립적으로 실행되는 새 프로세스를 만든다, 부모-자식 관계가 형성된다

- 인자 설명

1.lpApplicationName

  생성할 프로세스의 실행파일 이름을 인자로 전달.. 경로명을 추가로 지정 할 수 있으며, 경로명을 지정

  하지 않을 경우 프로그램의 현재 디렉터리에서 실행파일을 찾게 된다.

2.lpCommandLine

  생성하는 프로세스에 인자를 전달할때 사용된다. 첫 번째 매개변수에 NULL을 전달한 경우, 실행파일의

  이름을 더불어 전달 할 수도 있다. 이런 경우 실행파일의 이름은 표준 검색경로를 기준으로 찾게된다.

  내부적으로 문자열에 변경을 가한다. 따라서 전달인자의 문자열은 변수 형태여야한다.

3.lpProcessAttributes

  프로세스의 보안 속성을 지정할때 사용한다. NULL을 전달할경우 기본보안속성이 적용된다.

4.lpThreadAttributes

  쓰레도의 보안 속성이다. NULL인경우 기본보안 속성이다.

5.bInheritHandles

  전달 인자가 TRUE 인경우 생성되는 자식프로세스는 부모 프로세스가 소유한 핸들 중 일부(상속가능한

  핸들)를 상속한다.

6.dwCreationFlags

  생성하는 프로세스의 특성(우선 순위)를 결정지을때 사용되는 옵션입니다. 특별히 설정할 필요가 없을

  경우 0을 전달합니다.

7.lpEnvironment

  프로세스마다 ENvironment Block(환경블록)이라는 메모리 블록을 관리한다. 이 블록을 통해서 프로세

  스가 실행에 필요호 하는 문자열을 저장할 수있다. 이 전달 인자를 통해서 생성하는 프로세스의

  Environment Block을 지정한다. NULL이 전달되면 자식 프로세스는 부모 프로세스의 환경 블록에 저장

  되어 있는 문자열을 복사하게 된다.

8.lpCurrentDirectory

  생성하는 프로세스의 현재 디렉터리를 설정하는 인자이다. 전달인자는 디렉터리 정보를 포함하는 완전

  경로 형태로 구성되어야 하며 NULL이 전달될 경우 부모 프로세스의 현재 디렉터리가 새로 생성하는

  자식 프로세스의 현재 디렉터리가 된다. 역시 NULL이 일반적으로 전달된다.

9.lpStartupInfo

  STARTUPINFO 구조체 변수를 초기화한 다음 이 변수의 포인터를 인자로 전달한다 STARTUPINFO 구조

  체 변수는 생성하는 프로세스의 속성을 지정할 때 사용된다.

10.lpProcessInformation

  생성하는 프로세스 정보를 얻기 위해 사용되는 인자이다. PROCESS_INFORMATION 구조체 변수의 주

  소값을 진자로 전달한다. 그러면 전달된 주소값이 가리키는 변수에 프로세스 정보가 채워진다.

STARTUPINFO 구조체