1. lab 6-3과 비교했을때 main에서 변경된 부분

sub_401000함수는 인터넷 연결상태를 확인하고 sub_401040함수는 User-Agent를 "Internet Explorer 7.50/pma%d"로 설정하고 경유지에 연결하여 내용을 읽어온다.

읽어온 html의 데이터를 파싱하여 처음 문자열이 <!--인지 확인한다.

sub_4012B5함수는 print함수이고 sub_401150함수는 switch구문이다.

 

이전과 동일한것들은 0x401000(인터넷연결 확인함수), 0x401040(HTML파싱), 0x4012B5(printf)는 동일하다.

추가로 왼쪽에 반복문이 추가된것을 확인 할 수 있다.

 

2. lab 6-3과 비교했을떄 sub_401040에서 변경된 부분

전에 HTML파싱을 수행했던 0X401040서브루틴의 경우 인자값이 없었는데 6-4에서는 for문의 var_c변수를 인자값으로 받는다.

인자값이 어떻게 사용되는지 살펴보면 sub_401040의 매개변수 a1으로 선언되어 sprintf함수의 세번째 인자로 사용된다.

 

3. 해당 악성 프로그램에서 네트워크 기반에 대한 지표 무엇

새로운 User-Agent인 Internet Explorer 7.50/pma%d"를 사용하였다. 여기서 %d는 프로그램이 실행되는 분을 나타내는 숫자이다.

공격자가 악성코드가 얼마나 오랫동안 작동하지는지 추적하기 위해 웹서버를 관리하고 모니터링하는데 이용된다.

얼마나 동작하는지 보기위해 sleep함수를 보면 0EA60h는 60,000으로 60초이다.

for문이 1440번 반복되어 60이 곱해지면서 1440분이 된다. 1440분은 24시간으로 하루동안 동작하는 프로그램이다.

 

4. 해당 악성 프로그램의 목적

인터넷 연결을 확인하고 특정 User-Agent를 이용해 프로그램이 동작하는 분을 나타내는 숫자를 추적하는 카운터를 담고있는 웹페이지의 다운로드를 시도한다.

다운로드된 웹 페이지는 <!--로 시작하는 임베디드 HTML주석을 담고있다.

주석 다ㅏ음 문자는 로컬 시스템에서 동작을 결정짓는 swith구문에 사용된다.

하드코딩된 동작으로 파일삭제, 디렉토리 생성, 레지스트리 실행키 설정, 100초간 sleep기능, 프로그램은 종료되기 전까지 24시간 동안 동작할 것이다.

 

참고

https://redscreen.tistory.com/100?category=197361

https://m.blog.naver.com/wwwkasa/220345445200

'보안 > 리버싱' 카테고리의 다른 글

LAB 6-3 실습  (0) 2021.11.20
[리버싱] DLL Injection, DLL Ejection 코드  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-8 풀이  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-7 풀이  (0) 2021.11.13
IAT, EAT 로딩 과정  (0) 2021.10.02

1. 서브루틴 sub_401040이 수행하는 것은 무엇인가?

알고자 하는 0x401040에 들어가보면 "http://www.practicalmalwareanalysis.com"에 위치한 웹 페이지를 다운로드한다.

user-Agent로 internet Explorer 7.5/pma를 사용하는 것을 알 수 있다.

위에 분홍색으로 적힌 코드 구조를 보면

- InternetOpenA : WinlNet라이브러리 사용을 초기화하는데 이용하고 HTTP통신에 사용하는 User-Agent를 설정한다.

- InternetOpenUrlA : 완전한 ftp나 html url이 명시하는 위치로 핸들을 오픈하는데 사용

- InternetReadFile : InternetOpenUrlA가 오픈한 핸들에서 데이터 읽을 떄 사용

- InternetCloseHandle : 파일이 오픈한 핸들을 닫을 떄 사용

페이지 앞부분에서 HTML주석인 <!--문자열을 파싱한다. 3Ch,21h,2Dh,2Dh를 우클릭으로 변환하면 <!--임을 알쑤있다.

 

2. 서브루틴 sub_401130이 필요로 하는 매개변수는 무엇인가

argv[0]으로 표준 main파라미터

 

3. 해당함수 sub_401130에서 switch문을 사용하여 할수 있는 기능은 무엇인가

디렉토리 생성, 파일 삭제, 레지스트리 값 설정, sleep 100초 를 할수 있는 기능이 있다.

switch문으로는 주석의 첫번째 문자가 파싱되어 로컬 시스템에서 어떤 행위를 할지 결정한다.

 

4. 해당 악성 프로그램에 호스트 기반에 대한 지표는 무엇인가

C:\Temp\cc.exe

Software\Microsoft\Windows\CurrentVersion\Run\Malware

 

5. 해당 악성 프롤그램의 목적

인터넷 연결 활성 여부를 확인하고 연결되지 않으면 에러 메세지를 출력하고 프로그램을 종료한다.

연결된 경우에는 특정 User-Agent를 사용해 웹 페이지 다운을 시도한다.

 

참고

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=wwwkasa&logNo=220323000736

https://m.blog.naver.com/wwwkasa/220323000736

https://min-12.tistory.com/43

'보안 > 리버싱' 카테고리의 다른 글

LAB 6-4 실습  (0) 2021.11.20
[리버싱] DLL Injection, DLL Ejection 코드  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-8 풀이  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-7 풀이  (0) 2021.11.13
IAT, EAT 로딩 과정  (0) 2021.10.02

DLL(Dynamic Link Library)파일

Library는 함수, 데이터, 타입들을 여러가지 프로그래밍 요소들의 집합이다. 자주사용하는 함수를 미리 만들어 모아놓은게 Library라고 한다.

Library는 정적링크와 동적 링크 방식이 있는데 동적링크 Dynamic Link방식을 사용한게 DLL이다.

 

정적링크 방식을 사용하면 컴파일할때 라이브러리 코드를 실행파일에 복사하기 때문에 한번 컴파일 하게 되면 라이브러리파일 없이도 실행 가능한다.

 

동적링크 방식을 사용하면 실행파일 실행시 라이브러리에 실행파일이 연결된다. 실행파일에는 라이브러리에있는 함수의 정보만 포함되고 실제함수의 코드는 복사되지 않기 떄문에 실행파일이 작아진다. 또 필요할때 가져다 사용하기 떄문에 메모리 사용에 효율적이다.

 

▶ DLL injection

다른 프로세스에 특정 DLL파일을 강제 삽입하는것

DLL injection의 원리는 외부에서 다른 프로세스가 LoadLibrary() API를 호출하도록 하는 것이기 떄문에 일반적이 DLL loading과 마찬가지로 강제 삽입된 DLL의 DllMain()함수가 실행된다.

 

예를들면 notepad는 myhack.dll인 라이브러리를 호출하지 않지만 강제로 삽입할 수 있다. notepad.exe에 로딩된 myhack.dll은 notepad.exe프로세스에 이미 로딩된 DLL들과 마찬가지로 notepad.exe 프로세스 메로리에 대한 접근 권한을 갖는다. notepad에 사용자가 원한다면 통신기능을 추가하여 메신저로서의 기능을 하도록 할 수 있는 것이다.

 

▶ DLL injection사용하는 목적

프로세스에 삽입된 DLL파일은 프로세스 메모리에 대한 정당한 접근권한을 가지기 떄문에 사용자가 원하는 모든일을 수행할 수 있다. 이런점을 이용해 버그패치, 기능추가 등 다양한 곳에서 이용할 수 있지만 대부분 악의적인 용도인 악성코드로 사용된다.

 

// 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;
}

DLLMain에서 DLL_PROCESS_ATTACH에서 디버그 문자열이 출력되고 CreateThread를 통해 ThreadProc이 실행된다.

ThreadProc내부에서는 URLDownloadToFile() API를 통해 index.html을 다운로드 한다. 해당 dll이 notepad.exe에 인젝션 되면 URLDownloadToFile() API가 호출되는 구조이다.

 

// InjectDll.cpp

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

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
			              &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

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;

    // #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구한다.
    // 제어권 얻기 : 대상 프로세스 핸들 구하기
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    // #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당한다.
    // DLL경로를 프로세스에 기록: 대상 프로세스 메로리에 인젝션할 DLL경로 적어주기
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 쓴다.
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);

    // #4. LoadLibraryA() API 주소를 구한다.
    // DLL로드 작업: LoadLibraryW() API주소 구하기
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
	
    // #5. notepad.exe 프로세스에 스레드를 실행
    // 원격 스레드 생성을 통한 프로세스에 로드: 대상 프로세스에 원겨 스레드를 실행
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

//tmain함수의 마지막 조건문에서 EjectDLL함수의 결과에 따라 success와 failed가 출력된다.
int _tmain(int argc, TCHAR *argv[])
{
    if( argc != 3)
    {
        _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
        return 1;
    }

    // change privilege
    if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // inject dll
    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;
}

main()부분의 주요기능은 프로그램의 파라미터들에 대해 체크하고 injectDll()함수를 호출하는 것이다. InjectDll()함수가 DLL인젝션을 하는 주요 함수이다.

 


DLL Ejection

DLL인젝션과 반대 개념으로 프로세스에 강제로 삽입한 DLL을 뺴내는 기술이다. 동작 원리는 CreateRemoteThread API를 이용한 DLL인젝션 동작 원리와 같다.

 

대상 프로세스가 LoadLibrary() API를 호출하도록 하는 것이 DLL인젝션의 포인트라면 이젝션에서는 FreeLibrary()API를 호출한다.

 

즉, CreateRemoteThread()의 lpStartAddress 파라미터에 FreeLibrary() API 주소를 넘겨주고, lpParameter 파라미터에 이젝션 할 DLL의 핸들을 넘겨주면 된다.

또한, Windows에는 Reference Count 라는 것이 있는데, LoadLibrary("a.dll")을 10번 호출하면 a.dll에 대한 참조 카운트도 10이 되어서 나중에 a.dll을 해제할 때 FreeLibrary()를 10번 호출 해야 한다.

 

// EjectDll.exe

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

#define DEF_PROC_NAME	(L"notepad.exe")
#define DEF_DLL_NAME	(L"myhack.dll")

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(!_tcsicmp(szProcessName, (LPCTSTR)pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
			              &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

//EjecDll()함수는 FreeLibrary()API를 호출하도록 만드는 역할을 한다.
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    BOOL bMore = FALSE, bFound = FALSE;
    HANDLE hSnapshot, hProcess, hThread;
    HMODULE hModule = NULL;
    MODULEENTRY32 me = { sizeof(me) };
    LPTHREAD_START_ROUTINE pThreadProc;

    // dwPID = notepad 프로세스 ID
    // TH32CS_SNAPMODULE 파라미터를 이용해서 notepad 프로세스에 로딩된 DLL 이름을 얻음
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
    //CreateToolhelp32snapshot()API를 이용하면 프로세스에 로딩된 DLL정보를 얻을수 있다. 
    //이렇게 구한 hSnapshot핸들을 Module32First()/Module32Next함수에 넘기면 MODULEENTRY32구조체에 해당 모듈의 정보가 세팅된다.

    bMore = Module32First(hSnapshot, &me);
    for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
    {
        if( !_tcsicmp((LPCTSTR)me.szModule, szDllName) || 
            !_tcsicmp((LPCTSTR)me.szExePath, szDllName) )
        {
            bFound = TRUE;
            break;
        }
    }

    if( !bFound )
    {
        CloseHandle(hSnapshot);
        return FALSE;
    }

    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    hModule = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
    hThread = CreateRemoteThread(hProcess, NULL, 0, 
                                 pThreadProc, me.modBaseAddr, 
                                 0, NULL);
    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);
    CloseHandle(hSnapshot);

    return TRUE;
}

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

    _tprintf(L"PID of \"%s\" is %d\n", DEF_PROC_NAME, dwPID);

    // change privilege
    if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // eject dll
    if( EjectDll(dwPID, DEF_DLL_NAME) )
        _tprintf(L"EjectDll(%d, \"%s\") success!!!\n", dwPID, DEF_DLL_NAME);
    else
        _tprintf(L"EjectDll(%d, \"%s\") failed!!!\n", dwPID, DEF_DLL_NAME);

    return 0;
}

 

 

 

참고

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ilikebigmac&logNo=221470285183

https://hackersstudy.tistory.com/75

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=ilikebigmac&logNo=221470036755

https://kyumoonhan.tistory.com/125

https://maple19out.tistory.com/37

'보안 > 리버싱' 카테고리의 다른 글

LAB 6-4 실습  (0) 2021.11.20
LAB 6-3 실습  (0) 2021.11.20
[리버싱] 드림핵 rev-basic-8 풀이  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-7 풀이  (0) 2021.11.13
IAT, EAT 로딩 과정  (0) 2021.10.02

이문제 역시 어떤 값을 넣었을때 correct,wrong이 나오는 문제다.

main함수 부분을 찾으면 역시 eax값이 0이면 wrong을 출력해서 eax값이 0이 나오면 안된다.

correct인지 wrong인지 확인하는 부분을 자세히 보면

rsp는 인덱스 역할을 하고

rsp+20은 사용자가 임력한 값이다.

cmp,jae로 올바른 문자열을 입력했을때 0x14번 문자열을 출력하고 아니면 0x15에 eax=1을 리턴해서 correct를 반환한다.

블로그를 참고해 분석결과를 적어보면

AND 0XFF는 user[idx]와 0xFB를 한 값이 0x100~0xFF일 경우에 하위 2비트만 추출해 그 값이 prog[]값과 일치하는 지 빅하는 것이다.

user[idx]=user[idx]*0xFB

user[idx] AND 0xFF

이런 순서로 프로그램이 진행되어 사용자가 입력하는 값을 찾기위해서는 반대로 연산하면 된다.

user[idx] AND 0xFF

user[idx]=user[idx]*0xFB

(위에 두줄이 아직 이해는 안됨.....일단 마저 설명을 정리하면)

AND와 0xFF는 나머지 연산인 %256치환될수 있다. 그 이유는 256을 1 0000 0000로 나눈 값의 나머지를 의미하는데 이 값은 하위 2비트만 남도록 한다고 한다.

그래서 251*user[i] = prog[i]%256이 된다.--> 현재 251은 256으로 나눴을때 0이 되어 값을 사용할 수 없다. 256으로 나눠서 나머지가 1이 나오도록 해야 된다.

251*51*user[i]=prog[i]*51%256 --> 12801*user[i] = prog[i]*51%256-->user[i]=prog[i]*51%256이 나온다.....

 

이번 문제는 위에껄 이해하면 코드는 짤수 있을꺼같은데 위에껄 더 공부해야 할듯 싶다.......

'보안 > 리버싱' 카테고리의 다른 글

LAB 6-3 실습  (0) 2021.11.20
[리버싱] DLL Injection, DLL Ejection 코드  (0) 2021.11.13
[리버싱] 드림핵 rev-basic-7 풀이  (0) 2021.11.13
IAT, EAT 로딩 과정  (0) 2021.10.02
[리버싱] 드림핵 rev-basic-6 풀이  (0) 2021.10.02

+ Recent posts