요즘 악성코드 분석이나 리버싱 작업에 한창이신 분들이라면, 분명 ‘안티 디버깅'이라는 벽에 부딪혀 답답함을 느끼셨을 거예요. 개발자들이나 악성코드 제작자들은 자신들의 로직을 숨기기 위해 끊임없이 새로운 방어 기술을 도입하고 있거든요. 특히 동적 분석을 시도할 때, 디버거를 감지하고 실행을 멈추거나 아예 다른 동작을 해버리는 녀석들 때문에 정말 애를 먹곤 하죠.
제가 직접 수많은 애플리케이션과 샘플들을 분석하면서 느낀 건, 이 안티 디버깅 기법들이 정말 날이 갈수록 교묘해지고 있다는 점이에요. 단순히 몇 가지 우회 기술만으로는 더 이상 통하지 않는 시대가 왔다는 거죠. 하지만 걱정 마세요!
여러분의 답답함을 시원하게 해결해 줄, 최신 트렌드를 반영한 동적 분석 안티 디버깅 우회 기법들을 제가 쉽고 명확하게 알려드릴게요. 오늘은 그 숨겨진 비밀들을 하나씩 파헤쳐보고, 어떻게 하면 효율적으로 분석을 이어갈 수 있는지 정확하게 알아보도록 하겠습니다!
디버거를 속이는 교묘한 위장술: 감지 기법 우회 노하우
IsDebuggerPresent 와 같은 API 함수 속이기
악성코드 분석을 시작하면서 가장 먼저 마주하는 벽이 바로 디버거 감지 기법일 거예요. 저도 처음에는 “왜 실행이 안 되지?” 하고 고개를 갸우뚱했던 적이 한두 번이 아니었습니다. 대부분의 악성코드나 보호 솔루션은 와 같은 Windows API 함수를 호출해서 현재 디버거가 붙어있는지 확인하거든요.
이 함수는 간단하게 현재 프로세스에 디버거가 연결되어 있으면 TRUE를 반환하고, 아니면 FALSE를 반환합니다. 개발자 입장에서는 너무나 편리한 기능이지만, 분석가에게는 눈앞이 캄캄해지는 순간이죠. 하지만 걱정 마세요!
이 녀석들을 속이는 방법은 의외로 간단할 때도 있습니다. 예를 들어, 함수의 반환값을 조작하거나, 아예 해당 API 호출 자체를 우회하도록 바이너리를 패치하는 방법이 있습니다. 제가 직접 해보니, OllyDbg 나 IDA Pro 같은 디버거에서 해당 함수 호출 직전에 브레이크포인트를 걸고 EAX 레지스터 값을 FALSE(0)로 강제로 변경해주면 마치 디버거가 없는 것처럼 악성코드를 속일 수 있더군요.
물론 이 방법은 정적 분석으로 해당 호출 지점을 미리 파악해야 한다는 전제가 있지만, 동적 분석 초기에 효과적으로 사용할 수 있는 꿀팁 중 하나입니다. 이런 기본적인 API 감지 기법들을 무력화하는 것만으로도 분석의 첫 단추를 성공적으로 끼울 수 있답니다. 정말 디버거를 붙이자마자 프로그램이 종료되는 통에 여러 번 좌절했는데, 이 방법으로 해결했을 때의 쾌감은 이루 말할 수 없었죠.
NtGlobalFlag 값 변경으로 감지 회피하기
말고도 디버거를 감지하는 또 다른 흔한 방법 중 하나는 프로세스 환경 블록(PEB)에 있는 값을 확인하는 겁니다. 운영체제가 프로세스를 디버거 하에서 실행시키면 특정 플래그를 이 에 설정하게 되는데, 악성코드는 이 값을 읽어서 디버거 존재 여부를 판단하곤 합니다. 이 부분에서 정말 난감했던 기억이 많아요.
특히 WinDbg 같은 커널 디버거를 사용할 때 이 값을 건드리는 경우가 많아서 애를 먹었거든요. 저도 수많은 샘플들을 분석하면서 를 확인하는 루틴을 발견할 때마다 한숨부터 나왔습니다. 하지만 이 역시 우회할 수 있습니다.
는 PEB 구조체의 특정 오프셋에 위치해 있는데, 디버거를 통해 이 메모리 위치에 직접 접근해서 값을 변경해주는 방식으로 우회가 가능해요. 보통 , , 같은 플래그들이 디버거가 붙었을 때 활성화되는 경우가 많습니다. 디버거가 실행 중일 때 이 플래그들을 수동으로 0 으로 설정해주면 악성코드는 자신이 디버거 환경에 있지 않다고 착각하게 됩니다.
처음에는 단순히 API만 우회하면 되는 줄 알았는데, 이렇게 시스템 내부 구조까지 파고들어 감지하는 것을 보고 정말 놀랐던 기억이 나네요. 하지만 이런 세부적인 부분까지 파고들어 조작할 수 있다는 걸 알게 된 이후로는 어떤 악성코드를 만나도 ‘해볼 만하다'는 자신감을 얻었습니다.
시간 차이로 숨어드는 악성코드, 어떻게 잡을까?
실행 시간 측정 기법의 허점 파고들기
제가 분석했던 많은 악성코드들은 ‘시간'을 이용한 안티 디버깅 기법을 자주 사용했습니다. 이게 참 교묘한데요, 디버거로 한 단계씩 실행해보면 정상적으로 작동하는데, 그냥 실행하면 자기 혼자 종료되거나 이상한 동작을 하는 경우가 바로 이 시간 기반 안티 디버깅 때문입니다.
특정 코드 블록의 실행 시간을 측정해서, 너무 느리게 실행되면(즉, 디버거가 붙어서 한 단계씩 실행하고 있으면) 디버거 환경이라고 판단하는 거죠. 보통 (Read Time-Stamp Counter) 명령어 같은 CPU 클럭 카운터를 읽는 명령어나, , 같은 고해상도 타이머 함수를 사용해서 시간을 측정합니다.
저도 처음에는 이걸 어떻게 우회해야 할지 몰라 답답했었어요. 마치 숨바꼭질하는 기분이었죠. 하지만 몇 번의 시도 끝에 힌트를 얻었습니다.
이런 시간 측정 루틴을 발견하면, 해당 루틴의 조건 분기문을 패치해서 항상 정상적인 시간으로 인식하도록 만들거나, 아예 시간 측정 함수 자체를 후킹해서 원하는 값을 반환하도록 조작하는 방법을 사용합니다. 예를 들어, 시간 차이가 특정 임계값을 넘어가면 디버거로 판단하게 되어 있다면, 그 임계값을 아주 크게 늘려버리거나 아니면 아예 측정된 시간 값을 조작해서 임계값 이하로 보이게 만드는 거죠.
이게 생각보다 섬세한 작업이라 여러 번 실패도 했지만, 성공했을 때는 정말 뿌듯했답니다.
Sleep 함수 조작을 통한 분석 지연 우회
시간 기반 안티 디버깅 기법 중 또 하나 자주 쓰이는 것이 함수를 이용하는 것입니다. 악성코드는 이 함수를 아주 짧은 시간 동안 여러 번 호출해서 전체 실행 시간을 지연시키곤 합니다. 만약 디버거가 붙어있지 않다면 이 호출들은 빠르게 지나가겠지만, 디버거가 붙어서 매번 브레이크포인트에 멈추게 되면 엄청난 시간이 소요되겠죠.
결국 분석가가 지쳐서 분석을 포기하도록 유도하는 겁니다. 제가 직접 경험해본 바로는 정말 악랄한 기법이라고 생각했어요. 불필요하게 많은 호출 때문에 분석 속도가 현저히 느려지니까요.
하지만 이 또한 우회할 수 있는 방법이 있습니다. 함수를 호출하는 부분을 (No Operation) 명령어로 패치해서 이 아무런 동작도 하지 않도록 만들거나, 아니면 함수 자체를 후킹해서 호출되더라도 즉시 반환되도록 조작하는 방식입니다. 이렇게 하면 악성코드는 을 호출했다고 생각하지만, 실제로는 전혀 지연되지 않고 빠르게 다음 코드를 실행하게 됩니다.
처음에는 일일이 으로 바꾸느라 손이 많이 갔는데, 스크립트를 짜서 자동으로 처리하니 훨씬 효율적으로 분석을 이어갈 수 있었습니다. 악성코드가 얼마나 분석가를 괴롭히려고 애썼는지 느껴지는 대목이랄까요?
프로세스 환경을 조작하여 방어막 뚫기
PEB 구조 변경으로 디버거 시야 가리기
앞서 이야기를 잠깐 했었는데, 프로세스 환경 블록(PEB)은 디버거 감지에 활용되는 다양한 정보의 보고입니다. 디버거가 연결되었을 때 PEB의 특정 필드들이 변경되는 경우가 많아서, 악성코드들은 이 필드들의 값을 읽어 디버거 존재 여부를 판단하곤 합니다. 예를 들어, 필드가 대표적이죠.
이 필드 값이 1 로 설정되어 있으면 디버거가 붙어있다는 뜻이 됩니다. 저도 수많은 악성코드를 분석하며 이 플래그를 확인하는 코드를 지겹도록 봤습니다. 보통 레지스터를 통해 PEB에 접근한 후 특정 오프셋에서 이 값을 읽어오는데, 이 값을 0 으로 조작하는 것이 일반적인 우회 방법입니다.
직접 해보니 디버거에서 해당 메모리 위치를 찾아서 값을 수정해주면 간단하게 속일 수 있었죠. 물론 이 외에도 PEB에는 이나 같은 다른 구조체에 대한 포인터가 포함되어 있는데, 이들을 통해 로드된 모듈 목록을 확인하거나 힙 영역의 특성을 분석하여 디버거 환경임을 유추하기도 합니다.
이 모든 것을 완벽히 조작하는 것은 쉽지 않지만, 주요 감지 포인트를 정확히 알고 우회하는 것이 중요합니다. 처음에는 PEB 구조를 이해하는 것 자체가 어려웠지만, 한 번 파고드니 악성코드들이 어떤 정보를 노리는지 명확하게 보이더라고요.
부모 프로세스 확인 우회 전략
악성코드 중에는 실행될 때 자신의 부모 프로세스를 확인하여 디버거 또는 분석 도구인지를 판단하는 경우가 있습니다. 예를 들어, 분석가가 실행시킨 디버거(OllyDbg.exe, IDA.exe 등)가 악성코드의 부모 프로세스라면, 악성코드는 “아, 내가 지금 분석되고 있구나!” 하고 감지하는 거죠.
이런 유형의 안티 디버깅은 꽤 영리하다고 생각했습니다. 저도 처음에는 아무 생각 없이 디버거에서 바로 실행했다가 프로그램이 바로 종료되는 바람에 당황했던 적이 있어요. 이 경우 우회하는 방법은 몇 가지가 있습니다.
가장 간단한 방법은 디버거의 이름을 변경하는 것입니다. 예를 들어 를 같은 정상적인 프로그램 이름으로 바꾸고 실행하는 것이죠. 물론 이렇게 해도 완벽한 우회는 아닐 수 있습니다.
더 정교한 방법으로는 악성코드를 일반적인 프로세스(예: )의 자식 프로세스로 실행시키는 이나 기법을 사용하여 악성코드를 주입하는 것입니다. 이렇게 하면 악성코드가 부모 프로세스를 확인했을 때 정상적인 프로세스가 보이기 때문에 디버거를 감지하지 못하게 됩니다. 때로는 특정 툴(예: CFF Explorer)에서 제공하는 기능으로 부모 프로세스 정보를 조작하여 실행하기도 하는데, 제가 사용해보니 이 방법이 초보자에게는 가장 접근하기 쉬운 방법 중 하나였습니다.
API 후킹 탐지를 회피하는 고난도 전략
인라인 후킹 탐지 기법 무력화
동적 분석에서 API 후킹은 필수적인 기술 중 하나입니다. 하지만 악성코드나 보호 솔루션들은 이런 후킹 시도를 감지하고 방어하기 위한 다양한 기법을 사용합니다. 특히 인라인 후킹(함수 프롤로그를 패치하여 다른 코드로 점프시키는 방식)은 가장 흔하게 사용되는 후킹 방법인데, 이를 감지하는 기법 또한 많이 발전했습니다.
예를 들어, 함수 프롤로그의 원본 바이트를 저장해두고 주기적으로 현재 프롤로그와 비교하여 변경 여부를 확인하는 방식이 있습니다. 제가 직접 분석했던 한 샘플은 의 함수 프롤로그를 계속해서 확인하더라고요. 만약 원본과 다르면 디버거가 붙어있다고 판단하고 실행을 중단시키는 식이었죠.
이런 경우 우회하기 위해서는 악성코드가 프롤로그를 검사하는 시점 이전에 후킹을 적용하고, 검사 시점에는 후킹이 없는 것처럼 보이게끔 후킹을 일시적으로 비활성화하거나, 아니면 후킹 자체를 우회하는 다른 분석 방법을 사용해야 합니다. 예를 들어, 코드 인젝션을 통해 직접 함수를 호출하는 대신, 인젝션된 코드가 직접 라이브러리를 로드하도록 하는 방식도 생각해볼 수 있습니다.
이 과정이 굉장히 복잡해서 처음에는 여러 번 실패했지만, 결국에는 함수 호출 과정을 추적하여 어디에서 검사가 일어나는지 파악하고 나서야 해결할 수 있었습니다.
Import Address Table (IAT) 변조 방어 우회
API 후킹 방식 중에는 를 변조하여 API 호출을 가로채는 방법도 있습니다. 이 테이블에는 프로그램이 사용하는 외부 DLL의 함수 주소들이 저장되어 있는데, 이 주소들을 조작하면 원하는 함수로 제어를 넘길 수 있습니다. 물론 악성코드나 보호 솔루션들은 IAT 변조 여부도 확인하여 분석을 방해하곤 합니다.
예를 들어, 프로그램 시작 시점의 IAT 스냅샷을 찍어두고, 나중에 다시 IAT를 검사하여 변경된 부분이 있는지 확인하는 식이죠. 제가 분석하면서 느낀 건, 단순히 함수 포인터 하나 바꾸는 게 아니라, 이런 방어 로직까지 고려해야 한다는 점이었어요. IAT 변조 방어를 우회하기 위해서는 악성코드가 IAT를 검사하기 전에 변조를 완료하거나, 검사 로직 자체를 무력화해야 합니다.
때로는 악성코드가 를 통해 동적으로 함수 주소를 얻어와 사용하는 경우도 있는데, 이 경우에는 IAT 후킹으로는 잡히지 않으므로 해당 호출 자체를 후킹하거나, 동적으로 얻어온 함수 주소 대신 우리가 원하는 함수 주소를 반환하도록 조작해야 합니다. 이처럼 후킹 기술도 다양하고, 그에 대한 방어 기술도 다양하기 때문에, 분석가는 악성코드가 어떤 방식으로 API를 호출하고 검사하는지 정확히 이해하는 것이 중요합니다.
코드 흐름을 비틀어 분석가를 혼란시키는 방법
가상화된 코드와 안티-디버깅의 결합
요즘 악성코드들은 단순히 디버거 감지를 넘어, 코드 자체를 이해하기 어렵게 만드는 ‘코드 가상화' 기법과 안티 디버깅을 결합하는 경우가 많습니다. 코드를 가상 머신(VM)에서 실행되는 바이트코드로 변환하고, 이 바이트코드를 해석하는 인터프리터를 내부에 포함시키는 방식이죠.
제가 이런 샘플을 만났을 때 정말 당황했습니다. IDA로 열어보면 온통 알 수 없는 바이트 코드들만 보이고, 실제 로직은 가상 머신 내부에서만 동작하니 분석이 거의 불가능하더라고요. 마치 블랙박스를 열었는데 안에 또 다른 블랙박스가 있는 느낌이었습니다.
이런 가상화된 코드를 우회하기 위해서는 결국 가상 머신 자체의 동작을 이해하고, 가상 머신이 바이트코드를 실제 CPU 명령어로 변환하여 실행하는 과정을 분석해야 합니다. 이를 위해 가상 머신의 핸들러 함수들을 식별하고, 각 핸들러가 어떤 바이트코드를 어떤 CPU 명령어로 변환하는지 파악해야 합니다.
때로는 가상 머신을 에뮬레이션하는 툴을 사용하거나, 가상 머신 내부에서 실행되는 코드를 덤프하여 분석하는 방법을 사용하기도 합니다. 이 과정이 정말 시간과 노력이 많이 드는 작업이지만, 한번 가상화된 코드를 뚫고 나면 숨겨진 악성 로직을 볼 수 있다는 점에서 큰 보람을 느낄 수 있습니다.
Self-modifying code 동적 분석 시 주의할 점
‘자기 수정 코드(Self-modifying code)'는 또 다른 골치 아픈 안티 디버깅 기법입니다. 악성코드 자신이 실행 중에 자신의 코드를 변경하는 방식인데, 이렇게 되면 정적 분석으로는 원래의 코드를 파악하기 어렵고, 동적 분석 중에도 예상치 못한 동작을 보이게 됩니다.
저도 처음에는 분명히 저 부분에 이런 코드가 있었는데, 다시 보니 완전히 다른 코드로 바뀌어 있는 것을 보고 깜짝 놀랐습니다. 디버거가 코드 캐시를 유지하거나, 메모리 보호 기능 때문에 제대로 된 코드를 보여주지 않는 경우도 발생하죠. 이런 유형을 분석할 때는 특히 주의해야 할 점이 많습니다.
가장 중요한 것은 코드 변경이 일어나는 시점을 정확히 파악하는 것입니다. 메모리 쓰기 브레이크포인트를 사용하거나, 특정 메모리 영역의 접근 권한을 변경하여 쓰기 시도를 감지하는 방식으로 접근합니다. 코드가 변경된 후에는 변경된 코드를 메모리에서 덤프하여 다시 분석해야 합니다.
제가 직접 해보니, 같은 메모리 보호 함수 호출을 감시하여 코드 섹션의 쓰기 권한이 변경되는 시점을 포착하는 것이 효과적이었습니다. 그리고 변경된 코드가 실행되기 전에 재빨리 코드를 복사하거나 스냅샷을 찍어두는 것이 중요합니다. 이처럼 자기 수정 코드는 분석가의 끈기와 세심한 관찰을 요구하는 까다로운 기법입니다.
가상 환경에서 몸을 숨기는 녀석들 따라잡기
VMWare, VirtualBox 감지 로직 우회
최근 악성코드들은 분석가들이 주로 사용하는 가상 머신(VMWare, VirtualBox 등) 환경을 감지하고, 이 환경에서는 아예 실행되지 않거나 다른 동작을 하도록 만드는 안티-VM 기법을 많이 사용합니다. 저도 처음에는 분석용 가상 머신에서 아무리 실행해도 반응이 없어서 답답했던 경험이 많습니다.
마치 악성코드가 “나는 VM에서는 안 놀아!”라고 말하는 것 같았죠. VM 감지 방식은 다양합니다. 예를 들어, 가상 머신에만 존재하는 특정 파일이나 레지스트리 키를 확인하거나, MAC 주소, 설치된 드라이버, 프로세스 이름 등 VM 환경의 고유한 특징을 찾아내는 식입니다.
이를 우회하기 위해서는 VM 환경의 이러한 고유한 특징들을 실제 물리 머신과 유사하게 조작해야 합니다. MAC 주소를 일반 PC처럼 변경하고, VM 툴의 드라이버나 서비스 이름을 숨기거나 삭제하는 등의 방법이 있습니다. 제가 직접 해보니, VMWare Tools 나 VirtualBox Guest Additions 같은 VM 전용 소프트웨어를 설치하지 않거나, 설치하더라도 해당 프로세스의 이름을 변경하는 것만으로도 상당 부분 우회가 가능했습니다.
하지만 어떤 악성코드는 정말 집요하게 VM 환경을 탐지하기 때문에, 완벽한 우회를 위해서는 더 깊이 있는 VM 내부 구조 이해가 필요합니다.
CPU 명령어 기반 가상 환경 탐지 무력화
더 고도화된 안티-VM 기법은 를 활용하여 가상 환경을 탐지합니다. 명령어는 CPU의 특성 정보를 알려주는데, 가상 환경에서는 이 명령어를 실행했을 때 실제 물리 CPU와 다른 정보를 반환하는 경우가 있습니다. 또한, (Virtual Machine Extensions)나 (Secure Virtual Machine) 같은 가상화 관련 CPU 기능이 활성화되어 있는지를 확인하여 VM 환경 여부를 판단하기도 합니다.
제가 이런 악성코드를 만났을 때, 단순한 환경 조작만으로는 우회가 안 된다는 것을 깨달았습니다. 이런 명령어 기반 감지를 무력화하기 위해서는 가상 머신의 하이퍼바이저 수준에서 명령어의 반환값을 조작하거나, 가상화 관련 플래그를 위조하여 물리 머신처럼 보이게 만들어야 합니다.
이는 일반적인 분석가 개인이 직접 하기에는 난이도가 높은 작업이지만, 일부 VM 소프트웨어는 이러한 명령어 기반 탐지를 우회할 수 있는 설정이나 패치를 제공하기도 합니다. 제가 사용했던 팁 중 하나는 의 설정 파일에서 특정 항목들을 수정하여 명령어의 가상화 상태를 숨기는 것이었습니다.
이런 세부적인 설정 하나하나가 악성코드의 안티-VM 기법을 뚫는 데 결정적인 역할을 하더군요.
예외 처리 기법을 역이용하는 영리한 우회
SEH (Structured Exception Handling) 기반 감지 우회
Windows 시스템의 은 프로그램 실행 중 발생하는 예외 상황을 처리하는 메커니즘입니다. 그런데 악성코드 제작자들은 이 SEH 메커니즘을 역이용하여 디버거를 감지하기도 합니다. 예를 들어, 존재하지 않는 메모리 주소에 접근하는 등의 비정상적인 작업을 의도적으로 수행하여 예외를 발생시키고, 이 예외가 디버거에 의해 처리되는 방식과 일반 실행 환경에서 처리되는 방식의 차이를 비교하여 디버거 유무를 판단하는 식이죠.
제가 이런 기법을 처음 만났을 때는 “와, 이렇게까지 생각한다고?” 하며 감탄했던 기억이 있습니다. 이런 SEH 기반 감지를 우회하기 위해서는 예외가 발생했을 때 디버거가 해당 예외를 처리하기 전에 악성코드 자신이 등록해둔 예외 핸들러가 먼저 실행되도록 조작하거나, 아니면 예외를 발생시키는 코드를 우회하여 아예 예외가 발생하지 않도록 만들어야 합니다.
디버거의 예외 처리 옵션을 조정하여 특정 예외를 디버거가 무시하고 프로그램에 전달하도록 설정하는 것도 하나의 방법입니다. 때로는 사용자 정의 예외 핸들러를 직접 등록하여 악성코드의 예외 처리 루틴을 가로채는 방식도 시도해볼 수 있습니다. 이처럼 예외 처리 메커니즘은 단순히 오류를 잡는 것을 넘어, 악성코드 분석에 있어서도 중요한 우회 포인트가 될 수 있습니다.
디버거가 예외를 다루는 방식의 차이점 활용
디버거는 일반적으로 프로그램에서 발생하는 예외를 가로채서 분석가에게 보여주고 처리할 기회를 제공합니다. 하지만 일반 실행 환경에서는 운영체제가 이 예외를 처리하게 되죠. 악성코드는 이 미묘한 차이를 이용하여 디버거를 감지합니다.
예를 들어, 특정 예외가 발생했을 때 디버거가 붙어있으면 예외 처리 루틴으로 진입하기 전에 디버거의 메시지 박스가 뜨거나 실행이 일시 정지되는 등의 현상이 발생합니다. 반면 디버거가 없으면 운영체제가 등록된 핸들러로 바로 제어를 넘기거나 프로그램을 종료시킵니다. 제가 분석했던 일부 샘플들은 디버거가 예외를 ‘먼저' 처리하는 특성을 이용하여 디버거 존재 여부를 확인하더군요.
이런 경우 우회하기 위해서는 디버거의 예외 처리 설정을 세밀하게 조정해야 합니다. 예를 들어, 같은 특정 예외가 발생했을 때, 디버거가 이를 즉시 처리하지 않고 프로그램의 예외 핸들러로 제어를 넘기도록 설정하는 것입니다. IDA Pro 나 OllyDbg 같은 디버거에는 이런 예외 처리 옵션을 설정할 수 있는 기능이 있습니다.
이를 잘 활용하면 악성코드가 기대하는 디버거의 예외 처리 방식과 다른 흐름을 만들어내어 감지를 회피할 수 있습니다. 이런 세심한 부분까지 파고드는 악성코드들을 보면서 정말 ‘지능적이다'라는 생각을 많이 했습니다.
자주 사용되는 안티 디버깅 기법 및 우회 전략
아래 표는 동적 분석 시 자주 마주치는 안티 디버깅 기법들과 제가 직접 경험하며 효과를 보았던 우회 전략들을 정리해본 것입니다. 참고하셔서 여러분의 분석에 도움이 되셨으면 좋겠습니다.
안티 디버깅 기법 | 주요 원리 | 주요 우회 전략 |
---|---|---|
API 함수 기반 감지 (예: IsDebuggerPresent) | 특정 API 호출을 통해 디버거 연결 여부 확인 | 해당 API 함수의 반환값 조작, 호출 지점 처리 또는 패치 |
PEB (, ) 기반 감지 | PEB 구조체 내 특정 플래그 값 확인 | PEB의 해당 플래그 값을 0 으로 직접 조작 |
시간 측정 기반 감지 (, ) | 코드 실행 시간 측정하여 디버거의 느린 실행 감지 | 시간 측정 루틴의 조건 분기 패치, 타이머 함수 후킹 후 값 조작 |
부모 프로세스 확인 | 자신을 실행시킨 부모 프로세스 이름/ID 확인 | 디버거/분석 도구 이름 변경, 정상 프로세스로 실행 |
API 후킹 탐지 (프롤로그 검사, IAT 검사) | API 함수 프롤로그나 IAT 변조 여부 확인 | 후킹 타이밍 조절, 검사 루틴 우회, 후킹 |
SEH (예외 처리) 기반 감지 | 예외 발생 시 디버거와 일반 환경의 처리 방식 차이 이용 | 디버거 예외 처리 옵션 조정, 예외 발생 코드 우회, 커스텀 SEH 등록 |
분석 효율을 극대화하는 멀티툴 활용법
IDA Pro 와 OllyDbg/x64dbg 의 유기적 연동
악성코드 분석을 하다 보면, 한 가지 툴만으로는 한계를 느낄 때가 많습니다. 정적 분석의 강자인 IDA Pro 와 동적 분석의 대명사인 OllyDbg 나 x64dbg 는 서로의 약점을 보완해주는 최고의 조합이라고 생각합니다. 저도 처음에는 한 툴에만 의존하려다 효율이 떨어지는 것을 느끼고 두 툴을 병행하기 시작했습니다.
IDA Pro 로 바이너리의 전체적인 구조와 함수 흐름을 파악하고 의심스러운 부분을 미리 표시해둡니다. 그리고 그 정보를 바탕으로 OllyDbg 나 x64dbg 를 사용하여 해당 부분에서 동적 분석을 진행하는 식이죠. IDA에서 찾은 주소를 OllyDbg 에 바로 적용하거나, OllyDbg 에서 발견한 런타임 정보를 IDA에 주석으로 추가하며 분석해나가면 시너지가 엄청납니다.
특히 인라인 패치나 메모리 조작이 필요한 안티 디버깅 우회 시에는 OllyDbg/x64dbg 의 강력한 디버깅 기능이 빛을 발합니다. 하지만 전체적인 흐름을 이해하려면 IDA의 그래프 뷰가 필수적이니, 이 두 툴을 마치 한 몸처럼 사용하는 연습을 계속해야 합니다. 제가 직접 사용해보니, 이렇게 연동하는 것만으로도 분석 시간이 절반 이상으로 줄어드는 경험을 했습니다.
스크립트 자동화를 통한 반복 작업 최소화
안티 디버깅 우회 작업 중에는 반복적인 수작업이 필요한 경우가 많습니다. 예를 들어, 수많은 함수 호출을 으로 바꾸거나, 특정 플래그 값을 계속해서 0 으로 설정해야 할 때가 그렇죠. 이런 반복적인 작업을 매번 수동으로 하다 보면 시간도 오래 걸리고 실수할 확률도 높아집니다.
제가 수많은 악성코드를 분석하면서 가장 크게 느꼈던 부분 중 하나가 바로 이 ‘자동화'의 중요성입니다. OllyDbg 나 x64dbg 는 파이썬(Python) 같은 스크립트 언어를 지원하여 디버깅 과정을 자동화할 수 있는 플러그인이나 기능을 제공합니다. 이를 활용하여 특정 주소 범위 내의 호출을 자동으로 패치하거나, 특정 레지스터 값을 감시하다가 조건에 맞으면 자동으로 변경하는 스크립트를 작성하면 분석 효율을 획기적으로 높일 수 있습니다.
처음 스크립트를 짜는 것이 어렵게 느껴질 수 있지만, 한번 만들어두면 두고두고 유용하게 사용할 수 있습니다. 저는 개인적으로 특정 DLL의 로딩 시점을 감지하고 자동으로 브레이크포인트를 설정하는 스크립트나, PEB의 플래그를 자동으로 초기화하는 스크립트를 만들어 아주 잘 활용하고 있습니다.
이런 자동화 툴들이 없었다면 정말 분석가로서의 삶이 너무 고되었을 거예요.
디버거를 속이는 교묘한 위장술: 감지 기법 우회 노하우
IsDebuggerPresent 와 같은 API 함수 속이기
악성코드 분석을 시작하면서 가장 먼저 마주하는 벽이 바로 디버거 감지 기법일 거예요. 저도 처음에는 “왜 실행이 안 되지?” 하고 고개를 갸우뚱했던 적이 한두 번이 아니었습니다. 대부분의 악성코드나 보호 솔루션은 와 같은 Windows API 함수를 호출해서 현재 디버거가 붙어있는지 확인하거든요.
이 함수는 간단하게 현재 프로세스에 디버거가 연결되어 있으면 TRUE를 반환하고, 아니면 FALSE를 반환합니다. 개발자 입장에서는 너무나 편리한 기능이지만, 분석가에게는 눈앞이 캄캄해지는 순간이죠. 하지만 걱정 마세요!
이 녀석들을 속이는 방법은 의외로 간단할 때도 있습니다. 예를 들어, 함수의 반환값을 조작하거나, 아예 해당 API 호출 자체를 우회하도록 바이너리를 패치하는 방법이 있습니다. 제가 직접 해보니, OllyDbg 나 IDA Pro 같은 디버거에서 해당 함수 호출 직전에 브레이크포인트를 걸고 EAX 레지스터 값을 FALSE(0)로 강제로 변경해주면 마치 디버거가 없는 것처럼 악성코드를 속일 수 있더군요.
물론 이 방법은 정적 분석으로 해당 호출 지점을 미리 파악해야 한다는 전제가 있지만, 동적 분석 초기에 효과적으로 사용할 수 있는 꿀팁 중 하나입니다. 이런 기본적인 API 감지 기법들을 무력화하는 것만으로도 분석의 첫 단추를 성공적으로 끼울 수 있답니다. 정말 디버거를 붙이자마자 프로그램이 종료되는 통에 여러 번 좌절했는데, 이 방법으로 해결했을 때의 쾌감은 이루 말할 수 없었죠.
NtGlobalFlag 값 변경으로 감지 회피하기
말고도 디버거를 감지하는 또 다른 흔한 방법 중 하나는 프로세스 환경 블록(PEB)에 있는 값을 확인하는 겁니다. 운영체제가 프로세스를 디버거 하에서 실행시키면 특정 플래그를 이 에 설정하게 되는데, 악성코드는 이 값을 읽어서 디버거 존재 여부를 판단하곤 합니다. 이 부분에서 정말 난감했던 기억이 많아요.
특히 WinDbg 같은 커널 디버거를 사용할 때 이 값을 건드리는 경우가 많아서 애를 먹었거든요. 저도 수많은 샘플들을 분석하면서 를 확인하는 루틴을 발견할 때마다 한숨부터 나왔습니다. 하지만 이 역시 우회할 수 있습니다.
는 PEB 구조체의 특정 오프셋에 위치해 있는데, 디버거를 통해 이 메모리 위치에 직접 접근해서 값을 변경해주는 방식으로 우회가 가능해요. 보통 , , 같은 플래그들이 디버거가 붙었을 때 활성화되는 경우가 많습니다. 디버거가 실행 중일 때 이 플래그들을 수동으로 0 으로 설정해주면 악성코드는 자신이 디버거 환경에 있지 않다고 착각하게 됩니다.
처음에는 단순히 API만 우회하면 되는 줄 알았는데, 이렇게 시스템 내부 구조까지 파고들어 감지하는 것을 보고 정말 놀랐던 기억이 나네요. 하지만 이런 세부적인 부분까지 파고들어 조작할 수 있다는 걸 알게 된 이후로는 어떤 악성코드를 만나도 ‘해볼 만하다'는 자신감을 얻었습니다.
시간 차이로 숨어드는 악성코드, 어떻게 잡을까?
실행 시간 측정 기법의 허점 파고들기
제가 분석했던 많은 악성코드들은 ‘시간'을 이용한 안티 디버깅 기법을 자주 사용했습니다. 이게 참 교묘한데요, 디버거로 한 단계씩 실행해보면 정상적으로 작동하는데, 그냥 실행하면 자기 혼자 종료되거나 이상한 동작을 하는 경우가 바로 이 시간 기반 안티 디버깅 때문입니다.
특정 코드 블록의 실행 시간을 측정해서, 너무 느리게 실행되면(즉, 디버거가 붙어서 한 단계씩 실행하고 있으면) 디버거 환경이라고 판단하는 거죠. 보통 (Read Time-Stamp Counter) 명령어 같은 CPU 클럭 카운터를 읽는 명령어나, , 같은 고해상도 타이머 함수를 사용해서 시간을 측정합니다.
저도 처음에는 이걸 어떻게 우회해야 할지 몰라 답답했었어요. 마치 숨바꼭질하는 기분이었죠. 하지만 몇 번의 시도 끝에 힌트를 얻었습니다.
이런 시간 측정 루틴을 발견하면, 해당 루틴의 조건 분기문을 패치해서 항상 정상적인 시간으로 인식하도록 만들거나, 아예 시간 측정 함수 자체를 후킹해서 원하는 값을 반환하도록 조작하는 방법을 사용합니다. 예를 들어, 시간 차이가 특정 임계값을 넘어가면 디버거로 판단하게 되어 있다면, 그 임계값을 아주 크게 늘려버리거나 아니면 아예 측정된 시간 값을 조작해서 임계값 이하로 보이게 만드는 거죠.
이게 생각보다 섬세한 작업이라 여러 번 실패도 했지만, 성공했을 때는 정말 뿌듯했답니다.
Sleep 함수 조작을 통한 분석 지연 우회
시간 기반 안티 디버깅 기법 중 또 하나 자주 쓰이는 것이 함수를 이용하는 것입니다. 악성코드는 이 함수를 아주 짧은 시간 동안 여러 번 호출해서 전체 실행 시간을 지연시키곤 합니다. 만약 디버거가 붙어있지 않다면 이 호출들은 빠르게 지나가겠지만, 디버거가 붙어서 매번 브레이크포인트에 멈추게 되면 엄청난 시간이 소요되겠죠.
결국 분석가가 지쳐서 분석을 포기하도록 유도하는 겁니다. 제가 직접 경험해본 바로는 정말 악랄한 기법이라고 생각했어요. 불필요하게 많은 호출 때문에 분석 속도가 현저히 느려지니까요.
하지만 이 또한 우회할 수 있는 방법이 있습니다. 함수를 호출하는 부분을 (No Operation) 명령어로 패치해서 이 아무런 동작도 하지 않도록 만들거나, 아니면 함수 자체를 후킹해서 호출되더라도 즉시 반환되도록 조작하는 방식입니다. 이렇게 하면 악성코드는 을 호출했다고 생각하지만, 실제로는 전혀 지연되지 않고 빠르게 다음 코드를 실행하게 됩니다.
처음에는 일일이 으로 바꾸느라 손이 많이 갔는데, 스크립트를 짜서 자동으로 처리하니 훨씬 효율적으로 분석을 이어갈 수 있었습니다. 악성코드가 얼마나 분석가를 괴롭히려고 애썼는지 느껴지는 대목이랄까요?
프로세스 환경을 조작하여 방어막 뚫기
PEB 구조 변경으로 디버거 시야 가리기
앞서 이야기를 잠깐 했었는데, 프로세스 환경 블록(PEB)은 디버거 감지에 활용되는 다양한 정보의 보고입니다. 디버거가 연결되었을 때 PEB의 특정 필드들이 변경되는 경우가 많아서, 악성코드들은 이 필드들의 값을 읽어 디버거 존재 여부를 판단하곤 합니다. 예를 들어, 필드가 대표적이죠.
이 필드 값이 1 로 설정되어 있으면 디버거가 붙어있다는 뜻이 됩니다. 저도 수많은 악성코드를 분석하며 이 플래그를 확인하는 코드를 지겹도록 봤습니다. 보통 레지스터를 통해 PEB에 접근한 후 특정 오프셋에서 이 값을 읽어오는데, 이 값을 0 으로 조작하는 것이 일반적인 우회 방법입니다.
직접 해보니 디버거에서 해당 메모리 위치를 찾아서 값을 수정해주면 간단하게 속일 수 있었죠. 물론 이 외에도 PEB에는 이나 같은 다른 구조체에 대한 포인터가 포함되어 있는데, 이들을 통해 로드된 모듈 목록을 확인하거나 힙 영역의 특성을 분석하여 디버거 환경임을 유추하기도 합니다.
이 모든 것을 완벽히 조작하는 것은 쉽지 않지만, 주요 감지 포인트를 정확히 알고 우회하는 것이 중요합니다. 처음에는 PEB 구조를 이해하는 것 자체가 어려웠지만, 한 번 파고드니 악성코드들이 어떤 정보를 노리는지 명확하게 보이더라고요.
부모 프로세스 확인 우회 전략
악성코드 중에는 실행될 때 자신의 부모 프로세스를 확인하여 디버거 또는 분석 도구인지를 판단하는 경우가 있습니다. 예를 들어, 분석가가 실행시킨 디버거(OllyDbg.exe, IDA.exe 등)가 악성코드의 부모 프로세스라면, 악성코드는 “아, 내가 지금 분석되고 있구나!” 하고 감지하는 거죠.
이런 유형의 안티 디버깅은 꽤 영리하다고 생각했습니다. 저도 처음에는 아무 생각 없이 디버거에서 바로 실행했다가 프로그램이 바로 종료되는 바람에 당황했던 적이 있어요. 이 경우 우회하는 방법은 몇 가지가 있습니다.
가장 간단한 방법은 디버거의 이름을 변경하는 것입니다. 예를 들어 를 같은 정상적인 프로그램 이름으로 바꾸고 실행하는 것이죠. 물론 이렇게 해도 완벽한 우회는 아닐 수 있습니다.
더 정교한 방법으로는 악성코드를 일반적인 프로세스(예: )의 자식 프로세스로 실행시키는 이나 기법을 사용하여 악성코드를 주입하는 것입니다. 이렇게 하면 악성코드가 부모 프로세스를 확인했을 때 정상적인 프로세스가 보이기 때문에 디버거를 감지하지 못하게 됩니다. 때로는 특정 툴(예: CFF Explorer)에서 제공하는 기능으로 부모 프로세스 정보를 조작하여 실행하기도 하는데, 제가 사용해보니 이 방법이 초보자에게는 가장 접근하기 쉬운 방법 중 하나였습니다.
API 후킹 탐지를 회피하는 고난도 전략
인라인 후킹 탐지 기법 무력화
동적 분석에서 API 후킹은 필수적인 기술 중 하나입니다. 하지만 악성코드나 보호 솔루션들은 이런 후킹 시도를 감지하고 방어하기 위한 다양한 기법을 사용합니다. 특히 인라인 후킹(함수 프롤로그를 패치하여 다른 코드로 점프시키는 방식)은 가장 흔하게 사용되는 후킹 방법인데, 이를 감지하는 기법 또한 많이 발전했습니다.
예를 들어, 함수 프롤로그의 원본 바이트를 저장해두고 주기적으로 현재 프롤로그와 비교하여 변경 여부를 확인하는 방식이 있습니다. 제가 직접 분석했던 한 샘플은 의 함수 프롤로그를 계속해서 확인하더라고요. 만약 원본과 다르면 디버거가 붙어있다고 판단하고 실행을 중단시키는 식이었죠.
이런 경우 우회하기 위해서는 악성코드가 프롤로그를 검사하는 시점 이전에 후킹을 적용하고, 검사 시점에는 후킹이 없는 것처럼 보이게끔 후킹을 일시적으로 비활성화하거나, 아니면 후킹 자체를 우회하는 다른 분석 방법을 사용해야 합니다. 예를 들어, 코드 인젝션을 통해 직접 함수를 호출하는 대신, 인젝션된 코드가 직접 라이브러리를 로드하도록 하는 방식도 생각해볼 수 있습니다.
이 과정이 굉장히 복잡해서 처음에는 여러 번 실패했지만, 결국에는 함수 호출 과정을 추적하여 어디에서 검사가 일어나는지 파악하고 나서야 해결할 수 있었습니다.
Import Address Table (IAT) 변조 방어 우회
API 후킹 방식 중에는 를 변조하여 API 호출을 가로채는 방법도 있습니다. 이 테이블에는 프로그램이 사용하는 외부 DLL의 함수 주소들이 저장되어 있는데, 이 주소들을 조작하면 원하는 함수로 제어를 넘길 수 있습니다. 물론 악성코드나 보호 솔루션들은 IAT 변조 여부도 확인하여 분석을 방해하곤 합니다.
예를 들어, 프로그램 시작 시점의 IAT 스냅샷을 찍어두고, 나중에 다시 IAT를 검사하여 변경된 부분이 있는지 확인하는 식이죠. 제가 분석하면서 느낀 건, 단순히 함수 포인터 하나 바꾸는 게 아니라, 이런 방어 로직까지 고려해야 한다는 점이었어요. IAT 변조 방어를 우회하기 위해서는 악성코드가 IAT를 검사하기 전에 변조를 완료하거나, 검사 로직 자체를 무력화해야 합니다.
때로는 악성코드가 를 통해 동적으로 함수 주소를 얻어와 사용하는 경우도 있는데, 이 경우에는 IAT 후킹으로는 잡히지 않으므로 해당 호출 자체를 후킹하거나, 동적으로 얻어온 함수 주소 대신 우리가 원하는 함수 주소를 반환하도록 조작해야 합니다. 이처럼 후킹 기술도 다양하고, 그에 대한 방어 기술도 다양하기 때문에, 분석가는 악성코드가 어떤 방식으로 API를 호출하고 검사하는지 정확히 이해하는 것이 중요합니다.
코드 흐름을 비틀어 분석가를 혼란시키는 방법
가상화된 코드와 안티-디버깅의 결합
요즘 악성코드들은 단순히 디버거 감지를 넘어, 코드 자체를 이해하기 어렵게 만드는 ‘코드 가상화' 기법과 안티 디버깅을 결합하는 경우가 많습니다. 코드를 가상 머신(VM)에서 실행되는 바이트코드로 변환하고, 이 바이트코드를 해석하는 인터프리터를 내부에 포함시키는 방식이죠.
제가 이런 샘플을 만났을 때 정말 당황했습니다. IDA로 열어보면 온통 알 수 없는 바이트 코드들만 보이고, 실제 로직은 가상 머신 내부에서만 동작하니 분석이 거의 불가능하더라고요. 마치 블랙박스를 열었는데 안에 또 다른 블랙박스가 있는 느낌이었습니다.
이런 가상화된 코드를 우회하기 위해서는 결국 가상 머신 자체의 동작을 이해하고, 가상 머신이 바이트코드를 실제 CPU 명령어로 변환하여 실행하는 과정을 분석해야 합니다. 이를 위해 가상 머신의 핸들러 함수들을 식별하고, 각 핸들러가 어떤 바이트코드를 어떤 CPU 명령어로 변환하는지 파악해야 합니다.
때로는 가상 머신을 에뮬레이션하는 툴을 사용하거나, 가상 머신 내부에서 실행되는 코드를 덤프하여 분석하는 방법을 사용하기도 합니다. 이 과정이 정말 시간과 노력이 많이 드는 작업이지만, 한번 가상화된 코드를 뚫고 나면 숨겨진 악성 로직을 볼 수 있다는 점에서 큰 보람을 느낄 수 있습니다.
Self-modifying code 동적 분석 시 주의할 점
‘자기 수정 코드(Self-modifying code)'는 또 다른 골치 아픈 안티 디버깅 기법입니다. 악성코드 자신이 실행 중에 자신의 코드를 변경하는 방식인데, 이렇게 되면 정적 분석으로는 원래의 코드를 파악하기 어렵고, 동적 분석 중에도 예상치 못한 동작을 보이게 됩니다.
저도 처음에는 분명히 저 부분에 이런 코드가 있었는데, 다시 보니 완전히 다른 코드로 바뀌어 있는 것을 보고 깜짝 놀랐습니다. 디버거가 코드 캐시를 유지하거나, 메모리 보호 기능 때문에 제대로 된 코드를 보여주지 않는 경우도 발생하죠. 이런 유형을 분석할 때는 특히 주의해야 할 점이 많습니다.
가장 중요한 것은 코드 변경이 일어나는 시점을 정확히 파악하는 것입니다. 메모리 쓰기 브레이크포인트를 사용하거나, 특정 메모리 영역의 접근 권한을 변경하여 쓰기 시도를 감지하는 방식으로 접근합니다. 코드가 변경된 후에는 변경된 코드를 메모리에서 덤프하여 다시 분석해야 합니다.
제가 직접 해보니, 같은 메모리 보호 함수 호출을 감시하여 코드 섹션의 쓰기 권한이 변경되는 시점을 포착하는 것이 효과적이었습니다. 그리고 변경된 코드가 실행되기 전에 재빨리 코드를 복사하거나 스냅샷을 찍어두는 것이 중요합니다. 이처럼 자기 수정 코드는 분석가의 끈기와 세심한 관찰을 요구하는 까다로운 기법입니다.
가상 환경에서 몸을 숨기는 녀석들 따라잡기
VMWare, VirtualBox 감지 로직 우회
최근 악성코드들은 분석가들이 주로 사용하는 가상 머신(VMWare, VirtualBox 등) 환경을 감지하고, 이 환경에서는 아예 실행되지 않거나 다른 동작을 하도록 만드는 안티-VM 기법을 많이 사용합니다. 저도 처음에는 분석용 가상 머신에서 아무리 실행해도 반응이 없어서 답답했던 경험이 많습니다.
마치 악성코드가 “나는 VM에서는 안 놀아!”라고 말하는 것 같았죠. VM 감지 방식은 다양합니다. 예를 들어, 가상 머신에만 존재하는 특정 파일이나 레지스트리 키를 확인하거나, MAC 주소, 설치된 드라이버, 프로세스 이름 등 VM 환경의 고유한 특징을 찾아내는 식입니다.
이를 우회하기 위해서는 VM 환경의 이러한 고유한 특징들을 실제 물리 머신과 유사하게 조작해야 합니다. MAC 주소를 일반 PC처럼 변경하고, VM 툴의 드라이버나 서비스 이름을 숨기거나 삭제하는 등의 방법이 있습니다. 제가 직접 해보니, VMWare Tools 나 VirtualBox Guest Additions 같은 VM 전용 소프트웨어를 설치하지 않거나, 설치하더라도 해당 프로세스의 이름을 변경하는 것만으로도 상당 부분 우회가 가능했습니다.
하지만 어떤 악성코드는 정말 집요하게 VM 환경을 탐지하기 때문에, 완벽한 우회를 위해서는 더 깊이 있는 VM 내부 구조 이해가 필요합니다.
CPU 명령어 기반 가상 환경 탐지 무력화
더 고도화된 안티-VM 기법은 를 활용하여 가상 환경을 탐지합니다. 명령어는 CPU의 특성 정보를 알려주는데, 가상 환경에서는 이 명령어를 실행했을 때 실제 물리 CPU와 다른 정보를 반환하는 경우가 있습니다. 또한, (Virtual Machine Extensions)나 (Secure Virtual Machine) 같은 가상화 관련 CPU 기능이 활성화되어 있는지를 확인하여 VM 환경 여부를 판단하기도 합니다.
제가 이런 악성코드를 만났을 때, 단순한 환경 조작만으로는 우회가 안 된다는 것을 깨달았습니다. 이런 명령어 기반 감지를 무력화하기 위해서는 가상 머신의 하이퍼바이저 수준에서 명령어의 반환값을 조작하거나, 가상화 관련 플래그를 위조하여 물리 머신처럼 보이게 만들어야 합니다.
이는 일반적인 분석가 개인이 직접 하기에는 난이도가 높은 작업이지만, 일부 VM 소프트웨어는 이러한 명령어 기반 탐지를 우회할 수 있는 설정이나 패치를 제공하기도 합니다. 제가 사용했던 팁 중 하나는 의 설정 파일에서 특정 항목들을 수정하여 명령어의 가상화 상태를 숨기는 것이었습니다.
이런 세부적인 설정 하나하나가 악성코드의 안티-VM 기법을 뚫는 데 결정적인 역할을 하더군요.
예외 처리 기법을 역이용하는 영리한 우회
SEH (Structured Exception Handling) 기반 감지 우회
Windows 시스템의 은 프로그램 실행 중 발생하는 예외 상황을 처리하는 메커니즘입니다. 그런데 악성코드 제작자들은 이 SEH 메커니즘을 역이용하여 디버거를 감지하기도 합니다. 예를 들어, 존재하지 않는 메모리 주소에 접근하는 등의 비정상적인 작업을 의도적으로 수행하여 예외를 발생시키고, 이 예외가 디버거에 의해 처리되는 방식과 일반 실행 환경에서 처리되는 방식의 차이를 비교하여 디버거 유무를 판단하는 식이죠.
제가 이런 기법을 처음 만났을 때는 “와, 이렇게까지 생각한다고?” 하며 감탄했던 기억이 있습니다. 이런 SEH 기반 감지를 우회하기 위해서는 예외가 발생했을 때 디버거가 해당 예외를 처리하기 전에 악성코드 자신이 등록해둔 예외 핸들러가 먼저 실행되도록 조작하거나, 아니면 예외를 발생시키는 코드를 우회하여 아예 예외가 발생하지 않도록 만들어야 합니다.
디버거의 예외 처리 옵션을 조정하여 특정 예외를 디버거가 무시하고 프로그램에 전달하도록 설정하는 것도 하나의 방법입니다. 때로는 사용자 정의 예외 핸들러를 직접 등록하여 악성코드의 예외 처리 루틴을 가로채는 방식도 시도해볼 수 있습니다. 이처럼 예외 처리 메커니즘은 단순히 오류를 잡는 것을 넘어, 악성코드 분석에 있어서도 중요한 우회 포인트가 될 수 있습니다.
디버거가 예외를 다루는 방식의 차이점 활용
디버거는 일반적으로 프로그램에서 발생하는 예외를 가로채서 분석가에게 보여주고 처리할 기회를 제공합니다. 하지만 일반 실행 환경에서는 운영체제가 이 예외를 처리하게 되죠. 악성코드는 이 미묘한 차이를 이용하여 디버거를 감지합니다.
예를 들어, 특정 예외가 발생했을 때 디버거가 붙어있으면 예외 처리 루틴으로 진입하기 전에 디버거의 메시지 박스가 뜨거나 실행이 일시 정지되는 등의 현상이 발생합니다. 반면 디버거가 없으면 운영체제가 등록된 핸들러로 바로 제어를 넘기거나 프로그램을 종료시킵니다. 제가 분석했던 일부 샘플들은 디버거가 예외를 ‘먼저' 처리하는 특성을 이용하여 디버거 존재 여부를 확인하더군요.
이런 경우 우회하기 위해서는 디버거의 예외 처리 설정을 세밀하게 조정해야 합니다. 예를 들어, 같은 특정 예외가 발생했을 때, 디버거가 이를 즉시 처리하지 않고 프로그램의 예외 핸들러로 제어를 넘기도록 설정하는 것입니다. IDA Pro 나 OllyDbg 같은 디버거에는 이런 예외 처리 옵션을 설정할 수 있는 기능이 있습니다.
이를 잘 활용하면 악성코드가 기대하는 디버거의 예외 처리 방식과 다른 흐름을 만들어내어 감지를 회피할 수 있습니다. 이런 세심한 부분까지 파고드는 악성코드들을 보면서 정말 ‘지능적이다'라는 생각을 많이 했습니다.
자주 사용되는 안티 디버깅 기법 및 우회 전략
아래 표는 동적 분석 시 자주 마주치는 안티 디버깅 기법들과 제가 직접 경험하며 효과를 보았던 우회 전략들을 정리해본 것입니다. 참고하셔서 여러분의 분석에 도움이 되셨으면 좋겠습니다.
안티 디버깅 기법 | 주요 원리 | 주요 우회 전략 |
---|---|---|
API 함수 기반 감지 (예: IsDebuggerPresent) | 특정 API 호출을 통해 디버거 연결 여부 확인 | 해당 API 함수의 반환값 조작, 호출 지점 처리 또는 패치 |
PEB (, ) 기반 감지 | PEB 구조체 내 특정 플래그 값 확인 | PEB의 해당 플래그 값을 0 으로 직접 조작 |
시간 측정 기반 감지 (, ) | 코드 실행 시간 측정하여 디버거의 느린 실행 감지 | 시간 측정 루틴의 조건 분기 패치, 타이머 함수 후킹 후 값 조작 |
부모 프로세스 확인 | 자신을 실행시킨 부모 프로세스 이름/ID 확인 | 디버거/분석 도구 이름 변경, 정상 프로세스로 실행 |
API 후킹 탐지 (프롤로그 검사, IAT 검사) | API 함수 프롤로그나 IAT 변조 여부 확인 | 후킹 타이밍 조절, 검사 루틴 우회, 후킹 |
SEH (예외 처리) 기반 감지 | 예외 발생 시 디버거와 일반 환경의 처리 방식 차이 이용 | 디버거 예외 처리 옵션 조정, 예외 발생 코드 우회, 커스텀 SEH 등록 |
분석 효율을 극대화하는 멀티툴 활용법
IDA Pro 와 OllyDbg/x64dbg 의 유기적 연동
악성코드 분석을 하다 보면, 한 가지 툴만으로는 한계를 느낄 때가 많습니다. 정적 분석의 강자인 IDA Pro 와 동적 분석의 대명사인 OllyDbg 나 x64dbg 는 서로의 약점을 보완해주는 최고의 조합이라고 생각합니다. 저도 처음에는 한 툴에만 의존하려다 효율이 떨어지는 것을 느끼고 두 툴을 병행하기 시작했습니다.
IDA Pro 로 바이너리의 전체적인 구조와 함수 흐름을 파악하고 의심스러운 부분을 미리 표시해둡니다. 그리고 그 정보를 바탕으로 OllyDbg 나 x64dbg 를 사용하여 해당 부분에서 동적 분석을 진행하는 식이죠. IDA에서 찾은 주소를 OllyDbg 에 바로 적용하거나, OllyDbg 에서 발견한 런타임 정보를 IDA에 주석으로 추가하며 분석해나가면 시너지가 엄청납니다.
특히 인라인 패치나 메모리 조작이 필요한 안티 디버깅 우회 시에는 OllyDbg/x64dbg 의 강력한 디버깅 기능이 빛을 발합니다. 하지만 전체적인 흐름을 이해하려면 IDA의 그래프 뷰가 필수적이니, 이 두 툴을 마치 한 몸처럼 사용하는 연습을 계속해야 합니다. 제가 직접 사용해보니, 이렇게 연동하는 것만으로도 분석 시간이 절반 이상으로 줄어드는 경험을 했습니다.
스크립트 자동화를 통한 반복 작업 최소화
안티 디버깅 우회 작업 중에는 반복적인 수작업이 필요한 경우가 많습니다. 예를 들어, 수많은 함수 호출을 으로 바꾸거나, 특정 플래그 값을 계속해서 0 으로 설정해야 할 때가 그렇죠. 이런 반복적인 작업을 매번 수동으로 하다 보면 시간도 오래 걸리고 실수할 확률도 높아집니다.
제가 수많은 악성코드를 분석하면서 가장 크게 느꼈던 부분 중 하나가 바로 이 ‘자동화'의 중요성입니다. OllyDbg 나 x64dbg 는 파이썬(Python) 같은 스크립트 언어를 지원하여 디버깅 과정을 자동화할 수 있는 플러그인이나 기능을 제공합니다. 이를 활용하여 특정 주소 범위 내의 호출을 자동으로 패치하거나, 특정 레지스터 값을 감시하다가 조건에 맞으면 자동으로 변경하는 스크립트를 작성하면 분석 효율을 획기적으로 높일 수 있습니다.
처음 스크립트를 짜는 것이 어렵게 느껴질 수 있지만, 한번 만들어두면 두고두고 유용하게 사용할 수 있습니다. 저는 개인적으로 특정 DLL의 로딩 시점을 감지하고 자동으로 브레이크포인트를 설정하는 스크립트나, PEB의 플래그를 자동으로 초기화하는 스크립트를 만들어 아주 잘 활용하고 있습니다.
이런 자동화 툴들이 없었다면 정말 분석가로서의 삶이 너무 고되었을 거예요.
글을 마치며
지금까지 악성코드 분석 과정에서 마주하게 되는 다양한 안티 디버깅 기법들과 저만의 우회 노하우를 풀어보았는데요. 정말 악성코드 개발자들의 창의력은 끝이 없다는 것을 다시 한번 느끼게 됩니다. 하지만 이런 난관들을 하나씩 해결해나갈 때마다 얻는 지식과 성취감은 이루 말할 수 없어요.
이 글이 여러분의 분석 여정에 조금이나마 도움이 되어, 복잡한 악성코드의 비밀을 성공적으로 밝혀내는 데 기여할 수 있다면 더할 나위 없이 기쁠 것 같습니다. 우리 모두 지치지 말고 꾸준히 배우고, 끊임없이 도전해서 더욱 안전한 디지털 세상을 만들어 나가자구요!
알아두면 쓸모 있는 정보
1. 악성코드 분석은 정적 분석과 동적 분석을 병행할 때 가장 효율적입니다. 각 기법의 장점을 활용하여 시너지를 극대화하세요.
2. 최신 악성코드 트렌드를 꾸준히 파악하는 것이 중요합니다. 새로운 안티 디버깅 기법이나 공격 방식이 계속해서 등장하니까요.
3. 자동화 도구와 스크립트 활용은 분석 시간을 단축시키고 반복적인 작업을 줄여주는 핵심적인 요소입니다. 꾸준히 스크립트 작성 연습을 해보세요.
4. 가상 환경을 사용할 때는 안티-VM 기법에 대비하여 가상 머신 설정을 최대한 실제 환경과 유사하게 조작하는 노력이 필요합니다.
5. 분석 중 막히는 부분이 있다면 커뮤니티나 포럼을 적극적으로 활용하여 다른 전문가들의 지식과 경험을 공유받는 것도 좋은 방법입니다.
중요 사항 정리
악성코드 분석은 단순히 코드를 보는 것을 넘어, 악성코드 개발자의 의도를 읽고 그들의 방해 공작을 꿰뚫어 보는 과정이라고 할 수 있습니다. 와 같은 간단한 API 기반 감지부터 시작해, PEB 구조 변경, 실행 시간 측정, 부모 프로세스 확인, 그리고 심지어 API 후킹 탐지나 코드 가상화, 자기 수정 코드, 안티-VM, 예외 처리 메커니즘 역이용 등 매우 다양하고 고도화된 안티 디버깅 기법들이 존재합니다.
이 모든 기법들은 분석가의 시간과 노력을 소모시키고, 심지어 분석 자체를 방해하여 악성코드의 진정한 목적을 숨기려는 의도에서 출발합니다. 하지만 우리에게는 이들을 우회할 수 있는 다양한 전략과 기술, 그리고 강력한 분석 도구들이 있습니다. 여러 툴의 유기적인 연동, 그리고 스크립트를 통한 자동화는 분석 효율을 극대화하는 핵심 열쇠가 됩니다.
끊임없이 진화하는 악성코드에 맞서기 위해서는 우리 또한 끊임없이 배우고, 새로운 기술을 익히며, 지치지 않는 탐구심으로 무장해야 합니다. 이 글에서 다룬 내용들이 여러분이 더 깊이 있는 분석의 세계로 나아가는 데 든든한 디딤돌이 되기를 진심으로 바랍니다.
자주 묻는 질문 (FAQ) 📖
질문: 동적 분석을 시도할 때, 안티 디버깅 기법 때문에 분석이 유독 더 어려운 이유는 무엇인가요?
답변: 악성코드나 보호된 애플리케이션을 동적으로 분석하다 보면 ‘이거 왜 이렇게 어렵지?' 하는 순간이 자주 오는데, 그 중심에 바로 안티 디버깅 기법이 있어요. 제가 직접 수많은 샘플을 다뤄보면서 느낀 건, 개발자들이나 공격자들이 디버거를 감지하는 방법이 정말 교묘해졌다는 점이죠.
예를 들어, 어떤 앱들은 디버거가 붙어있으면 아예 실행을 멈춰버리거나, 중요한 로직 대신 엉뚱한 코드를 실행해서 분석가를 혼란스럽게 만들어요. 심지어는 프로그램의 실행 흐름을 완전히 바꿔버리기도 하고요. 이렇게 되면 디버거로 한 줄 한 줄 따라가면서 내부 동작을 파악하는 게 거의 불가능해집니다.
마치 미로에 들어갔는데, 길을 따라갈 때마다 미로의 구조가 계속 바뀌는 느낌이랄까요? 그래서 단순히 ‘여기서 브레이크포인트를 걸어야지' 하는 생각만으로는 절대 쉽게 뚫리지 않는 거죠. 분석 도구가 있다는 걸 알아채는 순간, 자기 본모습을 감추고 다른 행동을 하기 때문에 더더욱 우리를 힘들게 하는 것 같아요.
질문: 흔히 사용되는 안티 디버깅 우회 기법 중, 초보자도 쉽게 시도해볼 만한 방법들이 있을까요?
답변: 물론이죠! 저도 처음엔 막막했지만, 몇 가지 기본적인 우회 기법들을 익히고 나면 훨씬 수월하게 분석할 수 있다는 걸 경험으로 알게 되었어요. 가장 기본적으로는 디버거가 자신을 숨기는 방법들을 활용하는 겁니다.
예를 들어, 분석 도구의 이름을 조금 변경하는 것만으로도 일부 안티 디버깅은 속아 넘어가기도 해요. 이건 마치 이름표를 바꿔 달아서 다른 사람인 척하는 것과 비슷하죠. 또 다른 방법으로는 같은 API 함수를 직접 건드려서 디버거가 연결되어 있지 않은 것처럼 속이는 기술이 있어요.
실제로 제가 몇몇 오래된 악성코드들을 분석할 때 이 방법을 써보니, ‘어? 왜 잘 안 되지?' 했던 부분들이 의외로 쉽게 풀리는 경우가 많았어요. 물론 모든 경우에 통하는 만능 키는 아니지만, 시작점으로는 정말 유용하다고 생각합니다.
중요한 건, 대상 프로그램이 어떤 방식으로 디버거를 감지하는지 먼저 파악하고, 그에 맞는 우회 전략을 세우는 것이죠.
질문: 요즘같이 안티 디버깅 기법이 고도화되는 상황에서, 최신 트렌드를 반영한 효과적인 우회 전략은 어떤 것들이 있을까요?
답변: 요즘 안티 디버깅 기법들을 보면 정말 ‘와, 여기까지 생각했네!' 싶을 정도로 정교해졌어요. 단순히 같은 API 후킹만으로는 이제 어림도 없죠. 제가 최근에 분석했던 몇몇 사례들을 보면, 실행 시간 차이를 이용한 안티 디버깅 기법이 많이 보이더라고요.
디버거가 붙어있으면 코드가 실행되는 시간이 미묘하게 달라진다는 점을 이용해 디버거 존재 여부를 판단하는 방식인데, 이건 정말 까다로워요. 이런 경우에는 단순히 함수 하나를 속이는 걸 넘어서, 코드 실행 흐름 자체를 최적화하거나, 가상 머신 환경에서 디버깅하는 방법을 고려해야 합니다.
또 다른 최신 트렌드로는 부모 프로세스 확인이나 스레드 열거 같은 복합적인 방법들이 사용되기도 해요. 제가 직접 겪어보니, 이런 고도화된 안티 디버깅을 뚫으려면 단순히 한두 가지 기술에 의존하기보다는, 여러 우회 기법을 유기적으로 조합하고 필요하다면 스크립트나 커스텀 도구를 활용해서 능동적으로 대응하는 자세가 중요하더라고요.
끊임없이 새로운 기법들을 학습하고 적용해보는 경험이 무엇보다 중요하다고 말씀드리고 싶습니다.