DLL 분석

안상준·2025년 4월 30일

Reversing

목록 보기
1/5

DLL(Dynamic Link Library)

  • 동적 링크 라이브러리 : 컴파일시에 함수의 코드가 실행 파일에 복사되는 것이 아니라 실행 중에 라이브러리에 있는 함수를 호출하는 방법으로 동적 링킹이라고 한다.
  • 장점 : 한 코드를 여러 프로그램이 동시에 사용하기 대문에 메모리가 절약된다. 결과적으로 실행파일의 크기가 작다.

dll, exe


C++코드로, 파라미터로 a, b를 받아 둘을 더한 결과를 리턴하는 함수를 dll로 만든 코드이다.
DllMain은 dll파일이 로드되거나, 언로드 될때 조작할 수 있도록 예시로 작성하였다.
해당 파일은 컴파일시 g++ -shared -o example_dll.dll example_dll.cpp 이런식으로 작성하 주면 해당 코드가 dll형식으로 빌드된다.

dll에 있는 함수를 호출하는 코드이다. 마찬가지로 C++로 작성하였으며, 윗 부분에 AddNumbersFunc가 선언돼 있는 것을 볼 수 있다.
그 다음 HMODULE hDLL = LoadLibraryW(L'example_dll.dll");코드가 있는데 함수 명에서도 알 수 있듯이 example_dll.dll 을 메모리에 로드하는 코드이다.
AddNumbersFunc AddNumbers = (AddNumbersFunc)GetProcAddress(hDLL, "AddNumbers"); 코드를 통해 dll에서 AddNumberse 함수의 주소를 가져온다.

PE(Portable Executable)

  • PE : Windows 운영체제에서 실행 가능한 파일 포맷을 의미한다.(.exe, .dll)
  • PE Header : Windows 로더가 파일을 실항하기 위해 필요한 정보를 담고 있는 데이터 이다.


PEView 프로그램을 이용하여 PE를 분석한 사진이다.

ImageBase


ImageBase : PE 파일이 메모리에 로드될 때 기본적으로 로드되는 시작주소

코드 시작주소


사진은 IDA로 실행파일을 분석한 결과이다. 코드의 시작주소는 Imagebase + Base of Code가 된다. 코드의 시작주소는6DC41000이고, ImageBase의 주소는 6DC40000이다. Base Of Code는 1000인 것을 알 수 있다.

데이터 시작주소


데이터 시작주소는 Imagebase + Base of Data가 된다. 시작주소는 6Dc42000인 것을 볼 수 있고, Base of Data는 2000이 된다.

Re-allocation


위에서 작성한 dll파일을 로드하여 함수를 호출하는 코드에서 .dll파일을 2개 로드해 보았다.
IDA를 통해 확인해본 결과

서로 다른 위치에 dll파일이 로드되는 것을 볼 수 있다.
PE헤더에서 기존 주소에 로드되지 못했을 경우 재배치 섹션을 제공한다.

Export Function


위에서 AddNumbers라는 함수를 dll파일에서 선언을 했었다.

IDA를 통해 export 된 함수를 보면 AddNumbers가 있는 것을 볼 수 있다.
이 외에 3개의 함수가 더 있는 것을 볼 수 있는데 각각 살펴보면

  • DllMainCRTStartup : 컴파일러에 의해 자동으로 생성
  • Tls(Thread Loacl Storage) : 각 스레드에 고유한 데이터를 저장하기 위한 메커니즘
  • TlsCallback : dll이 메모리에 로드디거나, 새로운 스레드가 생성/종료될 때 호출

Process vs Thread

  • Process : 실행중인 프로그램을 의미한다. 독립적이고 비용이 크다.
  • Thread : 프로세스 내에서 실행되는 작업의 가장 작은 단위다. 메모리를 공유하고 빠른 장점이 있다.

dll Main

업로드중..
dll의 main을 IDA를 이용해서 찾아 보았다. cmp, jz 가 반복적으로 나오는 것을 볼 수 있는데 이는 main에 있는 switch case문이다.
그러면 eax = ul_reason_for_call 이라는 것을 알 수 있고, 어셈블리 코드에서 eax = [ebp+fdwReason] 인 것을 볼 수 있다.
먼저 ul_reason_for_call은 dll이 특정 이벤트에 의해 호출될 때, 호출된 이유를 나타내는 값을 전달한다.
fdwReason을 이용하여 스택프레임에 있는 값을 가져와 분기를 하는 방식이다.

main 찾기의 중요성

먼저 dll의 메인문에서는 dll을 로드하거나 언로드할 대 실행되는 초기화/정리하는 함수이다.
해당 위치에 전역 변수 초기화, 후킹, 백도어 설치, 안티티버깅 코드 삽입 등 의 작업이 들어갈 수 있다.
악성 dll의 경우, dll의 main에서 실제 악성 페이로드를 실행할 수 있다.
이러한 이유로 리버싱 관점에서 분석자는 dll main을 중요하게 살펴 보아야 한다.

switch-case?

왜 switch-case로 찾을까? 그 이유는 switch-case 기반의 패턴으로 dll의 main을 찾는 것이 실용적이고 흔히 쓰이는 벙법이다.
하지만 최적화 옵션에 따라 switch 대신 jump table을 사용하는 경우도 있고, 난독화나 VMProtect 같은 보호가 들어간 경우에는 사용하기 어려운 방법이다.

출처 : 리버스 엔지니어링 바이블(지은이 강병탁, 2012)

0개의 댓글