프로그래머는 프로그래밍 언어로 프로그램을 제작한다. 어떻게 소스코드가 프로그램이 될 수 있었을까? 소스코드를 프로그램으로 build 해주는 Visual Studio 같은 도구가 필요하다. 그럼 Windows 같은 운영체제는 어떻게 이 프로그램을 해석해서 실행시켜주는 것일까? Visual Studio 프로그램은 소스코드를 프로그램으로 만들 때 정해진 포맷으로 프로그램을 제작해준다. 이 프로그램 구조(포맷)가 PE구조이다. PE 구조는 인터넷에 자료가 너무 많으므로 생략하겠다. 하지만 리버싱에 관심이 있는 학생이라면 반드시 공부해야하는 구조이다. 간단하게 설명하자면 프로그램 코드 외에 다음과 같은 정보가 담겨있다.
- 32비트 or 64비트
- 프로그램 최초 시작 위치
- 사용하는 라이브러리 정보 및 함수 이름
- 서명정보
- 컴파일 시간
- 아이콘, 그림 파일과 같은 리소스
- Hello World와 같은 문자열, 프로그램이 사용하는 기본 데이터
이러한 데이터가 일정한 포맷에 맞게 헤더, 섹션에 나눠져서 생성된다.
그리고 사용자가 이 프로그램을 실행시키면 운영체제는 적절한 메모리 공간을 할당하고 위에 있는 정보들을 메모리에 배치시킨다. xp는 매번 정해진 위치에 배치됐으나 win7부터는 그 메모리 주소가 매번 바뀐다. 분석가 입장에서는 이 PE구조를 눈으로 볼 수 있게 해석만 해줘도 어떤 기능을 하는지 추정이 가능하다. 예를들어 서명정보, 사용하는 윈도우 기능(라이브러리)정보만 봐도 어떤 기능을 하는지 추정할 수 있다.
패킹, 난독화
악성코드를 제작한 사람 입장에서는 자신의 악성코드가 최대한 늦게 분석되기를 원할 것이다. 백신이 프로그램이 악성코드임을 분별해버리면 제작하느냐고 만든 노력이 물거품이 되기 때문이다. 그리고 민감한 정보를 다루는 프로그램은 자신의 프로그램이 분석되기를 꺼려할 것이다. 이렇게 분석을 방해하기 위해 사용하는 방법이 packing이다. 위의 PE구조를 요리조리 바꿔서 해석하기 어렵게 만드는 것이다. 또는 가상환경 및 디버거같은 분석도구에서는 실행이 안 되도록 제한해버린다. 가장 대표적인 패킹 도구로 UPX와 themida가 있다. 요즘은 C, C++언어 말고도 C#, JAVA와 같은 언어로도 프로그램을 개발한다. 이러한 프로그램은 운영체제에서 동작하는게 아니라 특정한 엔진에서 실행이 되기 때문에 기계어로 빌드가 되지 않는다. 때문에 얼마든지 소스코드로 되돌릴 수가 있다. 개발자 입장에서는 자신이 소중하게 만든 코드이기 때문에 원상복귀 되는 것을 원하지 않는다. 그래서 이때는 패킹도구 뿐만 아니라 난독화 프로그램을 이용해 함수명, 파일명, 개행, 띄어쓰기를 마구 섞어버리는 작업을 한다.
악성코드 동적분석
악성코드를 굳이 분석하지 않더라도 가상환경 같은 곳에서 실행하고 그 행위를 모니터링해서 정상/악성 여부를 판단할 수 있다. 행위에서 악성여부를 판단하려면 악성 URL에 연결을 시도하고 시스템 정보 및 인터넷 정보, 암호 정보를 탈취하고 자동실행을 위해 레지스트리, 특정 경로에 프로그램 경로를 등록하고 은닉한다. 그리고 광고, 랜섬웨어, 시스템 정보 탈취, 다운로드 & 실행 등의 행위를 한다. 동적 분석을 위해 직접 만든 VM 환경에 실행시킬 수도 있고 샌드박스와 같은 환경에 던져서 자동으로 분석하게 만들 수도 있다. 직접 만든 VM에서 파악하기 위해서는 네트워크 패킷을 캡쳐하고 사용한 라이브러리 정보를 파악해주는 Process monitor와 같은 도구가 필요하다.
마지막으로 직접 하나하나 코드를 따라가는 디버깅 작업이 있다. 진짜로 감염이 된 PC에서 발견된 악성코드의 경우 악성코드의 기능을 확인해 피해규모 파악하고 추가로 C&C주소(공격 제어 명령을 내리는 서버 주소)가 존재 하는지 확인해 다른 PC에서도 연결됐는지 확인해서 추가 감염 여부를 파악해야한다. 이때는 기계어와 소스코드의 중간단계인 어셈블러 언어로 하나하나 동작을 진행시켜서 그 기능을 파악하고 메모리 주소에 있는 데이터를 확인해서 추가 C&C 주소를 알아낸다.
악성코드 정적분석
악성코드를 실행하지 않고 정밀하게 분석하는 작업을 말한다. 우선 PE구조를 뜯어보면 서명정보, 사용한 라이브러리, 패킹, 문자열, hash 값, yara rule등을 통해 악성코드임을 판단할 수 있다.
virus total 사이트에는 전세계의 수많은 의심스러운 파일들이 업로드 되고 백신프로그램에 의해 분석된다. 여기에 hash 값만 던지더라도 기존에 분석이 된 경우 분석 레포트 주소가 작성되어 있고 언제 최초 발견됐고 이름이 무엇이었고 어느 나라에서 발견됐는지, 유사한 악성코드가 무엇이 있고 어떤 네트워크에 통신하는지 정보를 미리 파악할 수 있다.
yara rule은 기존에 발견된 악성코드에서 패턴을 추출해서 작성한 규칙을 말한다. 해커는 자신이 제작함을 알리기 위해 특정한 코드를 숨기기도 하고 mutex와 같은 고유값 뿐만 아니라 편리성을 위해 반복적으로 특정 암호화 모듈 라이브러리를 사용하기 때문에 패턴이 발생할 수 있다. 바이러스 토탈 뿐만 아니라 여러 프로그램, 사이트에 프로그램이 올라오면 이 패턴으로 확인을 해서 악성코드임을 빠르게 파악할 수 있다.
또한 IDA와 같은 디어셈블러와 같은 프로그램을 이용해서 실행파일을 소스코드 수준까지 만들어낼 수 있다. 분석가는 IDA와 같은 도구를 이용해 쉽게 기능을 추정할 수 있다. 다만 앞서 말한 더미다같은 패킹 도구를 사용하면 IDA에서 분석이 불가능하도록 막아놓을 수 있다. 창과 방패의 싸움이랄까?
여기에서 PPEE (puppy) 를 다운받는다. 분석환경이 xp라서 실행이 잘 안 될 경우 1.09 버전을 사용하면 된다. 보통 PE파일을 분석하기위해 peview를 사용한다. PPEE는 peview보다 악성코드 분석에 특화된 툴이다. 사용법은 사이트의 Feature에 나와있다.
- 늦게 load하는 dll 파일
- 의심 문자열, url, 레지스트리
- 서명정보
- 리소스
와 같은 정보를 확인할 수 있다.
우선 악성코드 빌더를 열어보겠다. plugins -> fileInfo를 클릭해서 악성코드 기본 속성을 확인한다.
32비트, 해시값, 빌드 정보를 확인할 수 있다.
패킹 정보 파악
가끔 리버싱을 방지하기 위해 패킹해놓은 경우가 있다. PEiD를 통해 확인해보자. PEiD는 userdb가 있어야 제대로 packing된 정보를 보여준다.
ASPack 2.12로 packing된 것을 확인할 수 있다. PEiD의 Unpacker Plugin으로 unpacking 해준다.
언패킹된 파일을 다시 확인하면 정상적인 정보를 확인할 수 있다.
기존 분석 정보 확인
해시값을 확인 후 바이러스 토탈에 기존에 업로드된 적이 있는지 확인한다.
바이러스 토탈을 이용할 경우 여러 백신제품의 진단명 뿐만 아니라 최초 업로드 시점과 업로드 시 어떤 파일명으로 업로드 되었는지 확인할 수 있다. 조심해야할 사항은 여기에 업로드된 파일들은 유료 회원은 다 다운받을 수 있으니 회사 파일을 업로드 하지 않도록 조심해야 한다. 이 파일은 빌더이기 때문에 바이러스 토탈을 통해 특별한 행위는 확인할 수 없었다.
의심스러운 정보 확인
이제 빌더로 만들어낸 악성코드에 대해 분석해보자. 바로 바이러스토탈을 통해 행위를 파악하면 처음부터 다시 확인해보겠다. bintext 또는 방금 사용한 PPEE를 통해 의심되는 URL이 있는지 확인한다.
레지스트리 뿐만 아니라 IAT(import address table) 정보만 보더라도 매우 악성코드처럼 보인다.
동적 분석 - 모니터링
동적분석을 위해 Process Monitor을 실행한다. 악성코드를 실행하고 상단의 Tools -> Process Tree를 클릭하여 악성코드를 지정한다.
여기서 직접 필터를 작성하는건 경험이 필요하므로 우선 상단의 아이콘으로 보고싶은 항목만 필터를 걸거나 Tools 의 summary기능을 이용해서 보고싶은 항목만 보도록 한다.
공격자가 명령을 내릴 경우 작업을 한 내역들이 모니터링 된다.
메모리에 존재한 문자열 확인
이제 Process Explorer로 정보를 상세히 확인해보자. 메모리에 있는 문자열 정보만 확인해도 내 시스템의 정보가 탈취되고 인터넷 방문기록을 가져가는지, 어떤 레지스트리 값을 사용하는지를 알 수 있다. 레지스트리를 확인하는 이유는 자동실행을 위해 꼭 사용하는 경로가 있기 때문이다. 위 도구에서 실제 메모리에서 사용중인 문자열 정보를 확인할 수 있다. 메모리 바이너리를 추출하려면 ollydbg같은 디버깅 툴이나 Lord PE 툴을 이용하면 된다. 자신의 프로세스가 아닌 다른 프로세스에 메모리를 할당하고 악성코드를 삽입하는 방법도 있다. 이때는 정상프로세스의 메모리를 확인해야하는게 VMMap을 주로 사용했었다.
의심스러운 네트워크 연결 확인
위 도구 뿐만 아니라 TCPView, 와이어샤크, 네트워크마이너, 네트워크모니터 툴을 통해 의심스러운 주소로 통신을 하는지 확인할 수 있다.
악성코드 패턴
사실 요즘은 이런식으로 악성코드를 실행하지 않는다. IAT 같은 라이브러리 참조 정보를 보면 악성코드인지 쉽게 파악이 가능하다. 따라서 처음에는 최소한으로 라이브러리를 load하고 실행중에 동적으로 필요한 라이브러리 파일을 load 후 악성행위를 진행한다. 다른 방법으로 서버로부터 암호화 또는 난독화된 바이너리를 받아서 복호화 후 메모리를 추가 할당하여 거기에서 악성행위를 하는 코드를 실행한다. 이때는 일단 악성코드를 실행시키고 추가 할당된 메모리로부터 프로세스를 덤프뜨거나 직접 바이너리를 복호화해서 새로운 악성코드를 추출해야한다. 루트킷 악성코드는 공부할 때만 분석해보고 실제로는 본적이 없어서 패스했다.
디버깅
디버거 툴로 동적으로 로딩한다음 악성행위를 분석해보자. 여기서 키포인트는 가상머신의 snapshot을 활용하는 것이다. 디버깅하다가 분석 실패할 확률이 높기 때문에 현재의 상태를 지속적으로 원상복귀해야하기 때문이다.
디버깅도구는 진입장벽이 존재한다. 어셈블러 언어도 존재하는데 그 사용방법을 알려주는 책, 강의가 부족하기 때문이다. 분석환경이 윈도우7이상인 경우 x64dbg를 쓰는것을 추천한다. 한글을 지원할 뿐만 아니라 64비트 환경에서 리버싱을 하려면 꼭 써야하는 도구이기에 32비트 환경에서부터 미리 사용해두면 좋다. 기본적인 기능은 ollydbg랑 동일하다. 나같은 경우 DeRox를 많이 사용했었다. x64dbg랑 기능이 거의 동일하므로 오늘은 이 도구로 악성코드를 분석해보겠다.
여기서는 디버거로 악성코드가 연결하고자 하는 서버의 주소를 찾아내는 과정에 대해 작성하겠다. 어셈블러 언어는 가독성이 떨어지기 때문에 ida와 같이 분석하면 좀 더 빠르게 분석할 수 있다.
File -> Open -> 악성코드 열기를 통해 악성코드를 디버거로 실행한다.
네트워크 통신 라이브러리를 호출 할 때 실행을 멈춘다면 어떤 주소로 연결하는지 빠르게 파악할 수 있을 것이다. PE구조에는 사용하는 모듈에 대한 정보가 저장되어 있다.
Name (label) in current module을 선택한다. 그리고 connect 함수를 찾는다. 보통 악성코드들은 connect 기본 모듈을 통해 서버에 연결한다. 그리고 마우스 우클릭을 한다음 Find references to import를 선택한다.
이 작업을 통해 어느 소스코에서 connect를 사용하는지 파악할 수 있다.
창이 뜨면 어디서 connect를 호출할지 모르므로 모두 브레이크포인트(F2)를 걸어준다.
이제 Alt+C를 눌러서 CPU화면으로 넘어온다음 Debug->Run(F9)를 선택해서 코드를 진행시킨다. 이러면 코드가 멈추지 않고 정상적으로 동작한다.
connect를 호출하는 시점에서 코드가 멈췄다. 메모리를 분석하기 위해서는 사용하는 모듈에 어떤 인자를 전달하는지 사전조사를 해야한다. connect 함수의 경우 어떤 ip주소에 연결할지에 대한 정보를 인자로 건네줘야 한다. 전달하는 데이터 구조는 sockaddr_in를 참고하면 된다.
메모리값을 확인하기 위해 우측 하단의 pSockAddr = 00D0FF24를 참고한다. 이 값은 ip, port 정보가 저장된 메모리 주소이다.
이 뿐만 아니라 디버거의 검색 기능을 이용해 문자열 참조, dll 참조 등을 이용하면 쉽게 그 기능을 호출하는지점에서 멈출 수 있다.
디버거에는 더 다양한 기능이 존재한다. 동작하면 사용된 모듈 정보를 trace할 수 있고 특정 메모리 영역에 쓰기, 읽기 작업을 할 때 break를 걸어서 외부에서 악성코드를 다운로드해서 실행하는 경우 그 시점에 break를 걸수도 있다. 친해져야하고 이것저것 다 눌러봐야만 한다.
정적분석
악성코드가 사용하는 하나의 모듈을 지정해서 분석을 시도해보겠다. PE구조를 분석해보니 의심스로운 모듈을 사용하고 있음을 확인했다. 클립보드에 있는 데이터를 스틸해가는 기능인 user32.dll에 있는 GetClipboadData 함수를 참조하고 있었다.
ida를 통해 악성코드가 무엇을 할 수 있는지 기능을 분석해보자. 보통 이러한 RAT 악성코드는 서버로부터 명령을 받아 switch 문으로 분기하는 특징이 있다. 의심스러운 함수를 호출하는 위치를 역으로 추적하면 전체적인 명령을 내리는 코드를 찾을 수 있을 것이다.
ida의 import를 이용해서 함수를 찾아보자.
의심스러운 모듈을 찾았으면 더블클릭한다.
DATA XREF에 마우스 우클릭을 한 뒤 List cross reference to(Ctrl+x)를 클릭한다. 이 기능을 통해 이 함수를 참조하는 모든 위치를 확인할 수 있다.
참조하는 주소를 더블클릭하거나 동일하게 Ctrl+x를 이용해서 참조하는 주소를 찾는다.
call하는 함수이름을 보니 사용자의 클립보드 데이터를 읽는 기능임을 대충봐도 알 수 있다. 이 위치에 rename으로 함수이름을 만들어준다. 이래야 기억하기 좋다.
그리고 한번 더 추적한다.
라벨이 덕지덕지붙어 있는게 딱 악성코드의 메인함수임을 찾을 수 있었다. 오른쪽 잘 보면 case 87이라고도 적혀있다. F5를 눌러 헥사레이로 코드를 한번더 확인해보자. 이러면 C언어로 어셈블러언어를 변경해줘서 가독성이 좋아진다.
악성코드의 기능이 매우 많다. 일일이 분석하려면 너무 오래걸린다. 분석은 여기까지..
마지막으로 분석 팁을 몇개 공유하자면 아래와 같다. 이 부분은 어느 정도 실력이 생겼을 때 이해가 되므로 처음 리버싱을 하는 분은 건너뛰고 나중에 확인하길 바란다.
실행과 동시에 분석하려면 open으로, 이미 실행중인 악성코드를 분석하려면 attach로 디버깅을 시작한다. 만약 악성코드를 실행해서 처음부터 쫒아갈 경우 불필요한 stub코드(컴파일러가 넣은 코드)와 동적으로 dll을 load하는 코드를 지나쳐서 원하는 코드까지 진행시킬 수 있는 경험과 지식이 필요하다. stub코드를 넘어가는 가장 쉬운 방법은 ida와 같은 도구의 힘을 빌리거나 main 함수가 주로 사용하는 3개의 함수(GetCommandLine, GetEnvironmentStrings, GetModuleFileName)가 있는 코드를 찾으면 된다.
악성코드가 주로 사용하는 api를 확인하는 것이 중요하다. 초보자들이 자주하는 실수가 운영체제 api에서 디버깅을 하는 것이다. 여기 dll에서는 디버깅하지말고 빠져나오자....
KERNEL32.DLL, NTDLL.DLL: 가장 기본적인 라이브러리로 컴퓨터 부팅시에 자동으로 로드된다. 유저 애플리케이션과 커널 API를 연결해서 프로세스, 메모리, 스레드를 관리하는 역할을 한다.
GDI32.DLL: 윈도우에서 gui 기본적인 기능을 제공함
USER32.DLL: 프로그램이 실행 될 때 gdi32.dll 파일과 드라이버를 불러주는 WIN32K.SYS 를 호출함. 그 외 유저의 행위를 가로챌 수 있음
MSVCRT.DLL: 마우스, 키보드, 모니터와 같은 하드웨어와 그래픽과 같은 기본적인 장치들을 선언함
WS2_32.DLL: 윈도우에서 네트워크 통신을 위한 기본 라이브러리
특히 WS2_32.dll은 악성코드가 서버에 연결할 때 기본적으로 사용한다. 여기서 connect 함수를 필수적으로 기억해두자.
동적으로 dll파일을 load하는 악성코드를 분석하는 방법은 다음과 같다.
1. ollydbg실행
2. Debugging Options 열기 (단축키:Alt+O)
3. Events Tab 클릭
4. [v] Break on new module (DLL) 항목에 체크 표시
5. [ OK ] 버튼 클릭해서 창닫고 작업할 프로그램(.EXE)을 로드
6. 대상 프로그램에서 새로운 모듈(DLL)을 사용할때 마다 브레이크가 걸린다.
7. Executable modules창에 디버깅하길 원하는 DLL파일이 보일때 까지 F9키를 누른다.
8. 찾았다면 원하는 모듈에 마우스를 Duble Click하면 해당 모듈로 들어간다.
9. Analyse code (Ctrl+A) 한 후 작업.
dll 에서 내가 원하는 함수를 호출하고 싶은 경우엔 %system%\rundll32.exe을 실행하고 인자로 함수명을 넣어준다. GetProcAddress로 Export 함수 주소를 가져오는 코드 뒤에 해당 함수를 호출하는 CALL문에 진입하면 된다.