동적 링크 라이브러리는 마이크로소프트 윈도우 운영체제에 구현된 라이브러리로 프로그램의 반복적인 함수들을 한 곳에 모아놓고 다양한 프로그램에서 공유하여 사용할 수 있는 파일이다.

프로그램을 개발할 때 자주 사용하는 함수들을 미리 만들어서 모아놓으면 새롭게 만들 필요도 없이 필요에 따라 불러서 사용할 수 있다. 또한, 수정 및 변경 시 해당 파일만 수정하면 되므로 효율적이므로 개발 속도도 빨라지고 신뢰성도 확보할 수 있다.

이런 라이브러리는 메인 프로그램에 어떻게 연결하는지에 따라 정적 링크와 동적 링크로 분류되며 DLL이 동적 링크에 해당한다.


동적 링크 라이브러리의 기본 개념, 라이브러리를 만들 때 명시적 및 암시적 방법, 기본 문법과 활용에 대해 알아보자.


동적 링크 라이브러리 (DLL, Dynamic Link Library)

Library
ㅇ 함수, 데이터, 타입 등 여러 가지 프로그래밍 요소들의 집합
ㅇ LIB 확장자

ㅇ 정적 링크 (Static Link) : 전통적인 라이브러리 연결 방식



정적 링크 (Static Link)

ㅇ 실행파일에 함수의 코드가 복사
ㅇ 파일의 크기가 증가되는 단점
ㅇ 완전한 단독 실행파일이 되어 LIB 파일이 없어도 프로그램 실행 가능


동적 링크 (Dynamic Link)
ㅇ 실행 시 함수가 실행파일에 연결
ㅇ 실행파일에 호출할 함수의 정보만 포함 (Import Table)
ㅇ 코드가 복사되지 않으므로 파일의 크기가 작아지는 장점
ㅇ 프로그램 실행 시 DLL 파일이 꼭 존재해야 함



DLL 장점

ㅇ DLL을 한 번만 읽어와서 여러 프로그램이 공유 -> 메모리 절약
ㅇ 프로그램의 크기가 작음
ㅇ DLL을 교체하여 성능 향상 -> 업데이트가 쉬움
ㅇ 리소스 교체 가능 -> 한국어 리소스 DLL, 일본어 리소스DLL 교체 용이
ㅇ 디버깅 용이 -> 모든 코드분석 필요 없음, DLL 제외한 핵심코드만 분석
ㅇ 혼합 프로그래밍 가능 -> 비주얼 베이직 기반, 비주얼 스튜디오 DLL 혼합 등
ㅇ 분담 작업과 재사용 용이 -> 핵심 DLL 개발 , 여러 프로그램에 공유


DLL 관리
ㅇ DLL의 코드는 가상 메모리에 한번 로드
ㅇ DLL의 고유변수는 프로그램 실행마다 로드
- 데이터 공유 불가
- 변경되지 않는 데이터는 공유 가능

->코드는 공유, 데이터는 공유 불가


ㅇ DLL별 사용 카운트 (usage count, 참조 카운트) 존재
- ProA.exe 실행 HAN.DLL 메모리에 로드. 카운트 =1
- ProB.exe 실행 카운트 =2
- ProC.exe 실행 카운트 =3
- ProB.exe 종료 카운트=2
- ProC.exe 종료 카운트=1
- ProA.exe 종료 카운트 =0. HAN.DLL 메모리에서 삭제


가상 메모리
ProA.exe
ProB.exe
ProC.exe
HAN.dll

DLL 접속 (제작 기초)
ㅇ Import, Export 선언


ㅇ __declspec
- MS에서 C++문법을 확장한 예 중의 하나


- 사용 가능한 인수 4가지
> Thread : TLS 데이터로 지정, 이 지정자가 붙은 변수는 해당 thread에서 사용 가능
> Naked : 함수만 적용. 어셈블리어를 사용하여 직접 접두, 접미를 달고자 할 때 가상 디바이스 드라이버를 작성할 때 기억부류를 사용
> Dllimport : DLL에 있는 데이터, 오브젝트, 함수를 import 할 때 사용
> DllExport : DLL에 있는 데이터, 오브젝트, 함수를 export 할 때 사용. DLL 정보를 명시적으로 제공


- Extern "C" __declspec(dllexport) int AddInteger(int, int); 형식으로 사용

ㅇ Extern "C"
- C 문법 사용 선언
- C++과 C의 문법이 조금 다르기 때문 (오버로딩)


Import 라이브러리
ㅇ DLL 파일 찾는 순서
- 클라이언트 프로그램이 포함된 디렉토리
- 프로그램의 현재 디렉토리
- 윈도우의 시스템 디렉토리
- 윈도우 디렉토리
- PATH 환경 변수가 지정하는 모든 디렉토리


DLL 제작

암시적 연결
ㅇ 함수가 어떤 DLL에 있는지 밝히지 않고 사용 -> 선언부에 포함
ㅇ 프로젝트에 import 라이브러리를 포함
ㅇ 윈도우는 import 라이브러리를 참조하여 DLL을 로드하고 함수를 찾음
ㅇ 로드 될 경우 사용 카운트를 1 증가
ㅇ 프로그램이 실행될 때 DLL 로드 -> 실행 시 연결 (Load time Linking)


ㅇ 어떤 DLL의 함수인지 명시하지 않음

ㅇ 비주얼 스튜디오 -> 프로젝트 우클릭 -> 속성 -> 링커 -> 입력 -> k_DLL_1.lib 추가 (명시)

ㅇ k_1.cpp가 있는 경로에 컴파일로 생성된 DLL파일과 LIB파일 이동

ㅇ 실행파일 생성. 결과물 출력


k_1.cpp ( k_1.exe)
k_DLL_1.cpp ( k_DLL_1.dll)


명시적 연결
ㅇ 어떤 DLL에 있는 함수인지 밝히고 사용
ㅇ 로드 하라는 명령이 있을때 로드
ㅇ 필요할 때 선택적으로 DLL을 로드 -> 상황에 따른 리소스 교체 가능
ㅇ DLL을 명시적으로 지정하여 호출 -> import 라이브러리 불필요
ㅇ 프로그램 실행 중 DLL 로드 -> 실행 중 연결 (Run time Linking)

ㅇ DLL을 읽고 사용하는데 사용하는 3가지 함수
- HINSTANCE LoadLibrary(LPCTSTR lpLibFileName);
> 지정한 DLL을 메모리로 읽어와서 현재 프로세스의 주소 공간에 맵핑시켜 사용
> 이미 메모리에 로드된 상태라면 사용 카운트만 1 증가
> 인수는 DLL의 파일 이름 (경로 생략)
> DLL을 읽어오는데 성공하면 DLL의 모듈 핸들 리턴. GetProcAddress함수에서 사용
> 에러 발생시 NULL 리턴


- FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
> DLL에서 Export한 함수의 번지를 찾아 그 함수의 함수 포인터를 리턴
> 첫 번째 인자는 LoadLibrary함수의 리턴값
> 두 번째 인자는 함수명 또는 함수의 서수(ordinal) 값
> 성공 시 함수 포인터 리턴
> 에러발생 시 NULL 리턴


- BOOL FreeLibrary(HMODULE hLibModule);
> DLL 사용카운트 1 감소 시키며 사용카운트 0이 되었을 경우 메모리에서 삭제


ㅇ Extern"C"__declspec(dllimport)int Addinteger(int,int) 부분 삭제
ㅇ LoadLibrary,GetProcAddress 부분 추가

ㅇ LoadLibrary, GetProcAddress, FreeLibrary 부분 추가
ㅇ DLL파일은 k_DLL_1.dll 로 동일

ㅇ Dll.lib 파일은 마찬가지로 같은 폴더 안에 넣어 주었다


k_2.cpp (k_2.exe)
k_2.cpp (k_2.exe) 밑부분


명시적 연결의 장점
ㅇ 필요할 때만 DLL을 읽어오기 때문에 메모리와 리소스가 절약

- 암시적 연결일 경우 프로그램 시작 시 DLL이 메모리에 상주

ㅇ 경우에 따라 사용할 DLL 교체
- DLL명, 함수명만 바꾸어 사용 가능


ㅇ 필요한 DLL이 없는 경우에도 프로그램 실행 가능
- 못 읽어 왔을 경우 에러처리 후 진행 혹은 에러메시지 출력
- 암시적 연결일 경우 프로그램 실행 불가

ㅇ 클라이언트 프로그램의 시작이 빠름  실행 중 필요할 때 DLL 로드
- 암시적 연결일 경우 DLL 개수가 많으면 로딩 시간이 길어질 수 있음

ㅇ 함수 호출 속도가 느리다는 단점 존재 (암시적은 이미 메모리에)


DLL 문법

DEF 파일

ㅇ DLL 안에 같은 이름의 함수들이 존재 할 경우 정의해주기 위해 사용
ㅇ k_DLL_1.DEF라는 파일을 만들어 프로젝트에 포함

-> k_DLL_1.DEF
LIBRARY k_DLL_1

EXPORTS
MyPlus = AddInteger

이런 방식으로 이름 치환

ㅇ Extern "C" __declspec(dllimport) int MyPlus(int,int); -> 사용 가능


DLL Main 함수
ㅇ DLL의 Entry Point (진입점)

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpRes);
ㅇ DLL이 처음 메모리에 올라올 때, 제거될 때 호출되는 함수
ㅇ hInst -> DLL의 인스턴스 핸들
ㅇ fdwReason -> 함수가 호출된 이유 지정. 4가지 값 중 하나
ㅇ lpRes -> 사용되지 않는 예약 인수

최근에는 TRUE : 암시적 연결, FALSE : 명시적 연결


핸들 (HANDLE)
ㅇ 구분을 위한 것이며, 핸들끼리 중복되지 않아야 하며 32bit 정수형
ㅇ 운영체제가 발급. 사용자는 쓰기만 함
ㅇ 같은 종류의 핸들끼리는 절대 중복된 값을 가지지 않음
ㅇ 다른 종류의 핸들끼리는 중복된 값을 가질 수도 있음
ㅇ 윈도우즈에서 핸들은 무조건 접두어 H로 시작
ㅇ 핸들 값을 저장하기 위해 별도의 데이터 형 정의
ㅇ HWND HPEN HBRUSH HDC HINSTANCE 등이 핸들을 담기 위한 데이터 형
ㅇ 사용자가 핸들을 만드는 경우는 없음. 불가능


fdwReason
ㅇ DLL_PROCESS_ATTACH
- DLL이 프로세스 주소공간에 맵핑 될 때 이 값과 MAIN이 호출


ㅇ DLL_PROCESS_DETACH
- DLL이 프로세스 주소공간에서 분리될 때 이 값과 MAIN이 호출


ㅇ DLL_THREAD_ATTACH
- DLL을 사용하는 프로세스에서 스레드를 생성할 때마다 이 값과 MAIN이 호출
- 스레드 별 초기화 수행. 스레드가 새로 만들어질 경우에만 호출
- 최초 스레드 에는 DLL_PROCESS_ATTACH가 호출


ㅇ DLL_THREAD_DETACH
- DLL을 사용하는 프로세스에서 스레드가 종료될 때마다 이 값과 MAIN이 호출


DLL 활용

DLL 인젝션

ㅇ PE파일구조를 이용해 DLL 로딩
- NT헤더의 크기정보들, DataDirectory의 Import Table 정보
- 세션정보, INT, IAT 등의 값들을 수정하여 DLL과 함수 명 삽입

ㅇ 원격 스레드 생성 (CreateRemoteThread()API)

ㅇ 레지스트리 이용(AppInit_DLLs 값)

ㅇ 메시지 후킹(SetWindowsHookEX() API)

- 키로깅


▶ 윈도우 시작 레지스트리 (자동실행 조작)


  • 카카오톡-공유
  • 네이버-블로그-공유
  • 네이버-밴드-공유
  • 페이스북-공유
  • 트위터-공유
  • 카카오스토리-공유

댓글을 달아 주세요