본문 바로가기

My Study/Programming&Theory

DLL 만들어보기 ( Implicit Linking, Explicit Linking )

다들 프로그래밍을 해보셨지만 저 같이 dll도 제대로 못만드는 분들도 계시기 때문에 이렇게 글을 써 봅니다 ^^
다들 사이트에서 프로그램을 다운 받아서 써보신적 계실탠데요. 가장 많이들 쓰시는 네이트온을 가지고 예를 들어보겠습니다.
네이트온이 설치 된 폴더로 가보겠습니다.

위와 같이 네이트온을 키는 exe파일도 있구요. 여러 dll들도 존재합니다. 이런 dll들을 어디에 쓰일까요??
당연히 예상 하신대로 exe파일을 실행시키면 해당 파일에 필요한 라이브러리들이 따라 붙게됩니다. 그 때 붙는 라이브러리들이 DLL입니다. DLL의 약자는 Dynamic Linking Library인데 이름만 봐도 알 수 있겠죠..그리고 dll안에는 exe파일에서 필요한 함수들이 정의되어 있겠죠. 한번 네이트온을 켰을 때 어떠어떠한 dll들이 필요한지 간단히 봐보겠습니다.
보는 방법은 올리디버거로 볼 수도 있구요. PE구조에서 Import table을 보고 알 수도 있지만 전 Depends라는 툴을 사용해 바로 봐보겠습니다. 뭐 프로그램이 실행 될 때 필요한 dll만 봐보겠습니다. 특정 기능을 할 때 또 다른 dll이 붙을 수도 있는데요. 그런거 까지 볼 필요는 없으니 넘어가겠습니다.

많은 dll들이 붙는 것을 볼 수 있습니다. 그러면 이제 우리도 dll을 직접 만들어서 사용해보도록 하겠습니다.
dll을 쓰는 이유에 대해서는 많은 문서들이 있으니 참조해 주시면 됩니다. 뭐 그래도 간략하게 설명하자면
보통 2가지 이유 때문에 dll을 쓰게되겠죠.
첫째, 성능 향상.. 성능 향상은 해당 dll을 쓰는 프로세스가 여러개일 수록 빛을 냅니다. 잘 생각해보시면 알 수 있을 것입니다.
둘째, 코드 수정 용이?? 만약에 사용자의 메시지 내용을 암호화 시키는 dll을 만들었다고 생각해보겠습니다. 몇 년후 해당 암호화 방법이 많이 취약해 졌을 때 암호화 시키는 dll만 바꿔서 배포를 하면 되기 때문입니다.

이제 진짜로 dll을 만들어 보도록 하겠습니다.

첫번째로 만들어볼 방법은 Implicit Linking방법입니다. 이 방법은 소스코드 내에 DLL 연결에 대한 코드가 존재하지 않는 방법입니다. 먼저 dll을 만들도록 하겠습니다.
프로젝트 만드는 방법입니다.

콘솔 응용 프로그램이 아닌 Win32 프로젝트로 만들어 주시고

다음을 눌러서 나온 화면인데 여기서 DLL에 체크하시고 추가 옵션에서 빈 프로젝트를 클릭해 만들어 줍니다.
그 다음에 소스코드와 헤더파일을 만들어야 합니다. 소스코드 부분에는 cpp로 파일을 하나 생성해 주시고
간단하게 함수를 만들어 줍니다. 제가 만든 아주 간단한 암호화, 복호화 함수 입니다;;

숫자 하나와 키 값 하나를 받아서 암호화 시키는 함수와 복호화 시키는 함수를 만들었습니다.
위에 __declspec(dllexport)부분은 아직 신경 쓰지 않아도 됩니다.
그 다음에는 위 함수들의 선언 부분이 필요합니다. 선언 부분은 따로 헤더를 만들어서 거기에 넣겠습니다.
헤더파일 폴더에 헤더(.h)파일 하나 만들어서 두 함수에 대한 선언을 넣어줍니다.

위와 비슷하게 __declspec(dllimport)라는 것을 사용했는데요. 이제 설명을 하도록 하겠습니다.

함수가 선언 된 부분에서 사용 된 __declspec(dllimport)은 선언 된 함수를 DLL로부터 제공받겠다는 뜻입니다.
함수가 정의 된 부분에서 사용 된 __declspec(dllexport)은 정의 된 함수를 외부에서 사용할 수 있는 형태로 만들겠다는 뜻입니다.

이제 dll을 만들 준비는 끝났습니다. 컴파일을 시켜줍니다.

5개의 파일이 생성 되었는데 여기서 우리는 2가지의 파일만 사용할 것입니다. dll과 lib파일입니다.
여기서 궁금한 것이 생길 탠데요. dll만 사용하면 되지 lib파일은 무엇이길래 사용합니까??

lib파일은 DLL이 제공하고자 하는 함수 정보를 지닙니다. 이름 정보가 되겠죠. 그렇기 때문에 dll을 사용하고자 할 때는 lib파일까지 같이 해서 링크를 해주어야 합니다. 

설명은 여기까지 하면 됬고 이제 저 dll을 사용하는 exe파일 하나를 만들도록 하겠습니다.

새로운 프로젝트를 콘솔모드로 만들어서 소스를 짰습니다. 위에 주요 헤더 3개는 생략했습니다.
25번째 줄과 38번째 줄에서 사용하고 있는 두 함수는 해당 소스코드 어디에서도 정의되지 않았지만 사용되고 있습니다.
예상은 하셨겠지만 dll에서 가져오겠죠.
그리고 4,5번째 줄을 보시면 아까 설명했듯이 dll을 사용하기 위해서 dll이 제공하고있는 함수 정보를 지니고 있는 lib파일을 포함하고 있구요. 두 함수가 선언 된 "Secret.h"라는 헤더도 포함시켰습니다. 
(.lib파일과 .h파일을 해당 프로젝트 cpp파일이 있는 곳에 옮겨두세요 ^^)
이로서 모든 준비는 끝났습니다. 이제 컴파일을 시켜보겠습니다. 컴파일까진 아마 성공할 탠데 
실행을 시켜보면 아마

위와 같이 오류창을 뜰 것입니다. 말 그대로 아까 만든 dll을 찾을 수 없다는 것입니다.
그러면 아까 만들었던 dll을 어디에 두어야 할까요?? OS가 해당 파일을 실행 시켰을 때 dll을 찾는 경로입니다.

1. 실행파일이 존재하는 디렉터리
2. 프로세스의 현재 디렉터리
3. Windows 시스템 디렉터리
4. Windows 디렉터리
5. PATH 환경변수에 등록되어 있는 모든 디렉터리

위 5가지에서 찾게 됩니다. 그러면 당연히 우리가 아까 만들었던 dll은 위 5가지 위치 중에 어느 곳에도 속하지 않았습니다. 그래서 dll을 실행파일이 있는 경로로 옮겨 두고 실행시켜보도록 하겠습니다.


실행 성공입니다. ( 단순.. 암호화 복호화 .. ;; )


이로서 사람들에게 배포할 때는 위와같이 exe파일과 dll파일 하나를 배포하면 됩니다.

여기까지가 Implicit Linking방법 입니다.

이제 두번째 방법이 남았는데요. 이번에 소개할 방법은 Explicit Linking방법입니다.
이 방법은 Implicit Linking방법과 달리 소스코드 내에 필요한 DLL이름을 지정해 줍니다.
그렇기 때문에 위에서 사용했던 lib파일이 필요가 없습니다.
대신에 위에서 사용하지 않았던 API함수를 사용해서 연결을 하게 됩니다.
바로 LoadLibrary, GetProcAddress, FreeLibrary 함수입니다.
LoadLibrary로 필요한 DLL을 프로세스 가상 메모리에 올리는 기능을 합니다. 그 다음에
GetProcAddress로 필요한 함수의 포인터를 얻습니다. 마지막으로
FreeLibrary로 DLL을 반환합니다.
말이 간단한데요. 소스코드 또한 간단합니다. 소스코드 보기전에 각 함수를 자세히 봐보겠습니다.
각 함수의 자세한 내용은 MSDN에서 가져왔습니다. ( 나름대로 정리.. )

이제 3개의 함수의 사용법과 의미를 알았으니 소스코드에 옮겨 보도록 하겠습니다.
dll은 아까 위에서 만들었던 dll을 그대로 사용하도록 하겠습니다. 소스코드는 아까 그 소스를 수정 시키도록 하겠습니다.

소스코드 전체가 한 화면에 들어오지 않아서 일부만 찍었습니다.
17번째 줄에서 LoadLibrary로 dll을 찾아서 핸들을 가져오고있습니다.
36번째 줄에서 얻어온 핸들로 Encryption함수의 주소를 얻고 있습니다.
그리고 마지막 부분에는 FreeLibrary함수를 사용해서 핸들 반환을 해줘야합니다.

Implicit Linking방법과 달리 Explicit Linking방법은 원하는 때 dll을 가져오고 해제 시켜버릴 수 있는 장점이 있습니다.
하지만.. 소스코드를 보시면 아시겠지만 Implicit Linking방법이 더욱 쓰기 쉽고 소스코드 또한 간결하기 때문에 Implicit Linking방법을 사용하는 사람들도 많습니다.

지금까지 dll을 만들어서 사용하는 방법이었습니다.