[ 출처 : 닌자보이 란타로 ]


이 Ada Cracked 시리즈 글을 올릴 때는 이 사진을 이용할 예정이다.



이번 주 고른 프로그램이다.


이 프로그램은 베타 버전때의 파일인데.



사용 기한이 만료되었다며 메세지 창을 띄우고 종료를 해버린다.

메인을 따라가다보면 날짜를 비교하는 부분을 발견할 수 있다.



로컬 시간이랑 임의로 정해둔 0x07DE비교하는 것을 알 수 있다.




여기서 임의로 ECX의 값을 높은 값으로 바꿔주면 된다.


10초 제한도 비교하는 변수(?)명을 알아내서 시간 체크하는 부분을 찾아내면 되겠다.



문자열 발견



0x2710(10000) (1000ms = 1초)

10초와 비교하는 부분을 발견할 수 있었다.


이번 주 과제 끝,

'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글

[Reverse Lab 스터디] DLL Injection  (0) 2016.08.10
[ReverseLab 스터디] UPX Packing  (155) 2016.07.26

DLL Injection



이번 글 내용 한 줄 요약할 수 있는 사진을 만들어버렸다...

DLL 인젝션은 말 그대로 다른 프로세서에 DLL을 강제로 박아버리는 것이다.

LoadLibrary() API를 호출하여 DLL을 로딩(Loading) 시키는 것인데.


DLL Injection은 다른 프로세서에 DLL을 삽입시키느냐, 자신의 프로세스에 삽입시키느냐에 따라 로딩시키는 대상에 따라 달라집니다.


What is DLL?

DLL은 Dynamic Linked Library의 약자로서 동적 연결 라이브러리입니다.
흔히 디엘엘 파일 이라고 읽고 드르르라고 읽기도 합니다.
이 DLL 파일은 프로그램 실행 후 동적으로 로드되고, 정적라이브러리에 비해 크기를 줄일 수 있습니다.
게다가 패치, 기능 개선에 효율성이 높습니다.

Purpose of DLL file use

보통 대게 DLL 파일의 사용 목적은 여러가지가 있습니다.

1. 크기

정적 라이브러리에 비해 동적 라이브러리가 크기가 작기 때문에,
실행 파일의 크기를 줄일 수 있습니다.

2. 악성 코드

정상적인 프로세스에 DLL 파일을 삽입하여 악의적인 행위를 합니다.

3. 패치

어떠한 프로그램의 패치가 필요할 때에 DLL Injection을 이용하여 문제가 있는 Data 수정할 수 있습니다.
그 패치가 필요한 프로세스에 침투한 DLL은 메모리들에 접근이 가능하기에 패치가 가능합니다.

그외에도 많겠지만...

Make DLL File in VS2015

New Project - Visual C++ - Win32 Project - DLL(D) - End


이제 코드를 작성하시고 빌드를 하고 DLL을 사용하면 됩니다.


Injector

Code는 아래와 같다.
출처 : http://www.reversecore.com/40


#include "stdio.h"
#include "windows.h"
#include "tlhelp32.h"

#define DEF_PROC_NAME ("notepad.exe")
#define DEF_DLL_PATH ("c:\\Malware.dll")

DWORD FindProcessID(LPCTSTR szProcessName);
BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName);

int main(int argc, char* argv[])
{
    DWORD dwPID = 0xFFFFFFFF;
 
    // find process
    dwPID = FindProcessID(DEF_PROC_NAME);
    if( dwPID == 0xFFFFFFFF )
    {
        printf("There is no <%s> process!\n", DEF_PROC_NAME);
        return 1;
    }

    // inject dll
    InjectDll(dwPID, DEF_DLL_PATH); 

    return 0;
}

DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof( PROCESSENTRY32 );
    hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
        if(!_stricmp(szProcessName, pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    HANDLE hProcess, hThread;
    HMODULE hMod;
    LPVOID pRemoteBuf;
    DWORD dwBufSize = lstrlen(szDllName) + 1;
    LPTHREAD_START_ROUTINE pThreadProc;

    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
        return FALSE;

    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);

    hMod = GetModuleHandle("kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");

    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE); 

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;


DWORD FindProcessID(LPCTSTR szProcessName)

LPCTSTR szProcessName : 프로세스 이름 [ Ex) notepad.exe, kakaotalk.exe ]

Return : 프로세스 PID 값을 반환합니다.


BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName) 

DWORD dwPID : 프로세스의 PID 값 

LPCTSTR szDllName : DLL 경로 [ Ex) C:\dsadasdas.dll, d:\asdasdas.dll ]

Return : 무조건 True를 반환하나, Handle을 구하지 못할 경우에는 False를 반환합니다.


InjectDll() 함수 코드 설명


if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
        return FALSE;


먼저 OpenProcess에서 PID의 핸들을 구합니다.

핸들을 구하여 이 프로세스를 제어할 수 있습니다.


pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

VirtualAllocEx() 함수로 그 프로세스의 메모리 공간에 버퍼를 할당합니다.

dll을 인젝션할 경로의 길이 만큼 할당합니다.


WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);


할당 받은 주소가 담긴 pRemoteBuf에 WriteProcessMemory 함수로 DLL 경로 문자열 #define DEF_DLL_PATH ("c:\\Malware.dll") 를 써줍니다.

이로서 원하는 프로세서 메모리 공간에 DLL 파일의 경로가 적재되었습니다.


hMod = GetModuleHandle("kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryA");

LoadLibraryA를 호출하기 위해서는 그주소가 필요합니다.
그러기 위해 kernel32.dll의 핸들을 구해와서 LoadLibraryA() 의 주소를 얻어냈습니다.


hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE); 

pThreadProc : 프로세으의 kernel32.dll LoadLibraryA() 주소
pRemoteBuf : 프로세스 메모리에 적재된 dll 경로가 담긴 주소

그 뒤에 CreateRemoteThread로 프로세스에 kernel32.dll LoadLibraryA()를 호출합니다.


'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글

[Reverse Lab 스터디] Ada Cracked(0)  (1) 2016.08.10
[ReverseLab 스터디] UPX Packing  (155) 2016.07.26

실행압축 (Packing)

여기서 UPX는 흔하디 흔한 패킹 툴의 하나의 종류이다.
이런 패커들은 실행압축을 하여 실행 파일을 파일의 형태를 그대로 유지를 하고 Size를 최대한 줄여 주는 압축 방식인데.

대게 악성 프로그램들이 이런 압축 방식을 이용하여 분석하는 데에 귀차니즘을 남겨줌.
거기에 안티 디버깅까지 들어가버리면 개 빡친다. ㅇㄱㄹㅇ ㅂㅂㅂㄱ ㅇㅈ? 어 ㅇㅈ


실행되는 순간 메모리에서 압축을 해제하고 실행을 시키는 기술이기에,

어딘가에 압축 해제 루틴이 담겨있어서, 이런 루틴을 지난 후에 실행시킨다는 게 특징이다.


이런 패킹을 사용하는 목적은 아래라고 생각합니다.


1. 파일 자체의 크기를 줄이기 위함

2. 내부 코드와 리소스등을 사용자가 보지 못하게 방해하는 것.

3. 분석을 하지 않게 귀차니즘을 넘겨주기 위해


이라고 볼 수 있습니다.


UPX Packing

먼저 보기 전에 UPX 패킹이 이루어지고 난 뒤에 PE가 어떻게 변경되는지 보도록 하겠습니다.



앗, 딱 봐도 IMAGE_SECTION_HEADER, SECTION 들이 UPX0, UPX1 들로 압축된 것을 볼 수 있습니다.




또 UPX0 SECTION의 RawDataSize를 보면 0으로 되있는 것을 볼 수 있으므로,

파일 내에 존재하지 않는 것을 볼 수 있습니다.

하지만 UPX0 SECTION의 Virtual Size를 보면 알 수 있습니다.

실제 파일에서의 사이즈는 0으로 되어있는데 메모리에서의 사이즈는 큰 것을 볼 수 있습니다.




그렇다면 이유는 압축 해제 코드와 압축된 원본 코드들은 UPX1 SECTION에 존재하다는 것을 예상할 수 있습니다.

그렇다면 파일이 실행이 되면 원본코드는 UPX0 섹션에 해제시킨다는 것을 예상할 수 있겠네요 (웃음)


Analyze

그렇다면 이제 올리디버거로 열어서 분석을 하도록 해보자!


PUSHAD 

CPU 레지스터 값들을 모두 스택에 넣는 명령어입니다.

POPAD

PUSHAD 명령어의 반대로 스택에 넣은 CPU 레지스터 값들을 CPU 레지스터로 복귀시키는 명령어입니다.


처음 실행될 때에 PUSHAD로 CPU 레지스터 들의 값들을 스택에 집어넣는 것을 볼 수 있습니다.




스택에 들어간 것을 볼 수 있습니다.

EAX~EDI의 값들이 순서대로 스택에 들어간 것을 볼 수 있습니다.



그 후에 ESI에 UPX1 시작 주소를 넘겨주어 세팅을 합니다.

EDI에는 0x001CE000(UPX1) + 0xFFFFFFFFFFFE3000 = 0x1B1000(UPX0)


(이 뒤에서 주소가 갑자기 바뀌는데... 디버거가 한번 팅겨서 주소가 달라진 것이니 착각하지마시길 바랍니다!)


아무튼 UPX0의 주소를 스택에 넣고 이제 점프합니다.



EBX에 [UPX1] 주소에 담긴 값을 저장합니다.



그리고 ESI, EDI 레지스터에는 위의 사진과 같이 세팅이 되어있는데.

0x41e000과 0x401000은 UPX1(.data) 주소와 0x401000(.text)주소입니다.



맞는 것을 볼 수 있습니다.



[EDX]에서 값을 가져와서 AL에 쳐박고 

EDI(UPX0)에 복사함을 알 수 있습니다.

FFF2만큼 UPX0에 0으로 초기화하는 부분임을 알 수 있습니다.


이 뒤의 0x40370F주소 이후에 IMAGE_SECTION_HEADER를 채우게 됩니다.



ESI가 가르키는 곳에 있는 값을 EDI에 값들을 복사하는 것을 볼 수 있습니다.



그리고 EDX의 값을 가져와서 EDI에 쑤컹쑤컹하는 것을 볼 수 있습니다.



그리고 이 부분이 압축 해제 루프입니다.



그 뒤로는 IAT 세팅입니다.




퍄퍄 이 곳에서 하나하나 이동시키는 것을 볼 수 있습니다!



그 뒤에 스택에 EAX~EBP까지 PUSH한 후 POPAD를 하는 모습입니다.

POPAD를 한 후의 CPU 레지스터의 상태입니다.



모든 압축 해제 과정이 끝나게 되면 OEP로 점프를 하게 됩니다.



넵!



실제로 패킹 안한 프로그램을 열어보면 같음을 확인할 수 있습니다.



그렇다면 하나의 꼼수를 알아내자면 UPX 패킹일 경우에는 그저 POPAD를 찾아서 그 뒤에 있는 JMP 주소가 oep라는 것을 알 수 있습니다. (웃음)

'0x30 Study > 0x31 Reverse Lab' 카테고리의 다른 글

[Reverse Lab 스터디] Ada Cracked(0)  (1) 2016.08.10
[Reverse Lab 스터디] DLL Injection  (0) 2016.08.10

+ Recent posts