지난번 강좌에 이어서 디스어셈블 기능과 분석 기능을 살펴보기로 합니다. Reflector가 인기있는 이유는 사실 강력한 내장 디스어셈블러 때문입니다. 디스어셈블링 작업은 소스 코드를 역으로 되가져올 수 있다는 것을 의미하는데 이것은 양날의 칼과 같습니다. 어떻게 쓰느냐에 따라서 유용할 수도 있지만 아주 치명적일 수 있습니다.
디스어셈블러로 Base Class Library의 버그를 찾은 사례가 있습니다. 바로 System.Windows.Forms.WebBrowser 클래스에서 "덜 구현된" 프로퍼티의 문제점을 찾을 수 있었는데 HTML 문서가 저장된 스트림을 이 클래스의 어떤 프로퍼티에 넘기면 무조건 서유럽언어의 인코딩으로만 처리하려는 문제점이었습니다. 이 문제를 파악한 이후부터는 이 프로퍼티를 직접 사용하지 않고 Document Object Model 객체를 직접 가져와서 접근하는 방식을 이용하기 시작하였습니다. (이 클래스의 프로퍼티들 중에서 Dom~이라는 접두어가 붙는 프로퍼티들이 Internet Explorer의 COM 객체를 직접 노출하는 프로퍼티들입니다.)
하지만 디스어셈블러를 사용하기에 따라서는 소스 코드의 취약점을 파악하거나 소스 코드의 저작권을 보호할 수 없게된다는 문제도 같이 안고 있습니다. 그래서 나온 것이 Obfuscation, 즉 코드 난독화 기술입니다. 다행히 Reflector는 코드 난독화 기술을 정복하면서까지 디스어셈블을 시도하는 해킹 도구는 아니기 때문에 코드 난독화 기술이 적용된 어셈블리에 대해서는 "잘못된 결과"를 나타내거나 작업 자체를 실패로 처리합니다.
디스어셈블러를 이용하여 다른 어셈블리를 살펴볼 때에는 주의할 점이 있습니다. 디스어셈블러로 얻어진 결과는 어디까지나 기술적인 진단을 위한 목적으로서 "개인적"으로 사용하여야 합니다. 이러한 결과를 상업적으로 재구성하여 판매하거나 무단으로 공개하는 경우 저작권 분쟁이나 EULA 계약 위반에 대한 법적 조치에 놓일 수 있습니다. 일반적으로 EULA 계약에는 디스어셈블에 대한 금지 조약이 포함되어있기 때문입니다. 그리고 디스어셈블한 내용을 특정 언어의 문법에 맞게 재구성하는 기능을 너무 신뢰하지는 마십시오.
서두가 좀 길었습니다만 충분히 이야기하고 넘어가야 할 부분들이었으니 숙지해주시길 바랍니다. 이제 Reflector를 이용하여 Base Class Library를 한 번 분석해 보도록 하겠습니다.
"그림 펼치기" 버튼을 눌러서 핵심 라이브러리의 System 네임스페이스 안에는 무엇이 들어있는지 쭉 살펴봅시다.
Object 클래스 안에도 전에는 보이지 않았던 숨겨진 멤버들을 보실 수 있습니다. 특히 재미있는 부분은 Equals, GetHashCode, GetType과 같은 기본 멤버 메서드들입니다. 이 메서드들을 디스어셈블링하기 위하여 위의 트리뷰에서 Object 클래스의 노드를 펼치면 됩니다. 그리고 해당 멤버 위에서 Space 키를 누르면 디스어셈블러가 작동하여 특정 언어로 변환된 결과를 트리뷰 왼쪽편에 크게 펼쳐서 나타냅니다.
막상 살펴보면 이들 메서드들은 MSIL로 구현된 것이 아니라는 걸 알 수 있는데 다음과 같습니다.


Equals 메서드를 디스어셈블링한 결과를 보면 InternalEquals라는 메서드를 호출한 것을 볼 수 있습니다. 그런데 이 메서드는 우리가 알고 있는 플랫폼 호출 메서드와는 다른 양상을 띄고있는데 MethodImplAttribute만이 지정되어있습니다. 이것의 실제 구현은 .NET Framework 안에 숨겨져 있다는 것을 뜻합니다. 재미있는 사실은 성능과 관련이 깊거나 혹은 사용 빈도가 높은 유형의 기능들은 모두 이와 같은 방식으로 처리된다는 점입니다.
GetType 메서드, GetHashCode 메서드 등은 또 어떻게 구성되어있는지 살펴보도록 하겠습니다.

GetType 메서드는 실제로 위와 같습니다.


그리고 GetHashCode 역시도 Equals 메서드와 같은 방식으로 구현된 것을 볼 수 있습니다.
대강 이와 같은 방법으로 디스어셈블링할 수 있습니다. 만약 평소에 내부 구현이 어떻게 되어있는지 궁금했던 클래스나 형식이 있다면 Reflector에서 열어서 보십시오. 해답이 보이실 겁니다. 그리고 만약 어떤 어셈블리를 여는 도중이거나 특정 멤버를 탐색하려고 할 때 다음과 같은 대화 상자가 나타날 수 있습니다.

현재 Reflector가 로드한 어셈블리들 중에서 관련된 어셈블리가 없으면 GAC를 조사하여 일치하는 어셈블리를 자동으로 추천하는 대화 상자가 위와 같이 나타나게 됩니다. 대개의 경우 저 대화 상자에서 추천하는 내용이 정확하지만 그렇지 않은 경우도 있습니다.
Reflector의 디스어셈블러가 지원하는 재미있는 기능을 또 하나 더 살펴보기로 합시다. 지금까지 본 언어는 C#이 전부인데, C# 말고 다른 언어로도 디스어셈블링 결과를 볼 수 있습니다. 좀 더 복잡한 형식을 이용하여 결과를 살펴보기로 합니다. 살펴보려는 멤버는 System.Environment 클래스의 GetEnvironmentVariable 정적 메서드입니다.
이제 디스어셈블링 외에 또 다른 편리한 기능인 Analyze 기능을 살펴보도록 하겠습니다. Analyze는 다양한 분석 옵션을 지원합니다. 주로 종속 관계를 조사하는 경우가 많으며 다른 유용한 분석 옵션도 제공합니다.
모든 항목에 대하여
* Depends On: 선택한 항목이 필요로 하는 다른 항목들입니다.
* Used By: 선택한 항목이 반드시 필요한 다른 항목들입니다.
클래스/구조체/대리자/나열 상수/인터페이스 전용
* Exposed By: 선택한 형식을 인수로 받아들이거나 반환하는 멤버 함수 및 프로퍼티들을 조사합니다.
* Instaniated By: 선택한 형식의 객체를 생성하는 멤버 함수 및 프로퍼티들을 조사합니다. 여기에는 현재 선택한 형식을 상속받는 자식 클래스의 생성자들도 모두 포함됩니다.
네임스페이스 전용
* Depends On: 현재 선택한 네임스페이스가 아닌 다른 네임스페이스 및 어셈블리들을 대상으로 이 네임스페이스 안의 개체들이 반드시 필요한 항목들입니다.
모듈 및 어셈블리 전용
* P/Invoke Imports: 해당 모듈이 System.Runtime.InteropServices.DllImportAttribute로 가져온 DLL 파일 및 참조한 DLL 함수들의 목록을 나타냅니다. 여기서 조사할 수 있는 내용은 Visual Studio에서 제공하는 Depency Walker나 Binary Dump Utility로는 조사할 수 없는 내용입니다.
Analyze 기능을 사용하기 위하여 다음의 그림과 같이 사용하면 됩니다.

그리고 위와 같은 방식으로 여러 개의 항목을 분석하도록 지정하면 Analyzer 창에 여러 항목을 쌓아둘 수 있습니다.

그리고 한 가지 더 유용한 정보를 알려드리면 위의 팝업 메뉴 가장 끝 항목인 Search MSDN 메뉴도 같이 활용하시면 좋습니다. 물론 MSDN이 모든 내용을 가지고 있는 것은 아니지만 적어도 BCL을 탐색하는 동안 MSDN과 비교하여 내용을 살펴보면 도움이 되는 점이 많습니다.
긴 글을 읽어주셔서 감사합니다. 다음 강좌에서는 Reflector의 리소스 분석 기능과 다양한 플러그인을 소개하는 시간을 가지도록 하겠습니다. :-)
디스어셈블러로 Base Class Library의 버그를 찾은 사례가 있습니다. 바로 System.Windows.Forms.WebBrowser 클래스에서 "덜 구현된" 프로퍼티의 문제점을 찾을 수 있었는데 HTML 문서가 저장된 스트림을 이 클래스의 어떤 프로퍼티에 넘기면 무조건 서유럽언어의 인코딩으로만 처리하려는 문제점이었습니다. 이 문제를 파악한 이후부터는 이 프로퍼티를 직접 사용하지 않고 Document Object Model 객체를 직접 가져와서 접근하는 방식을 이용하기 시작하였습니다. (이 클래스의 프로퍼티들 중에서 Dom~이라는 접두어가 붙는 프로퍼티들이 Internet Explorer의 COM 객체를 직접 노출하는 프로퍼티들입니다.)
하지만 디스어셈블러를 사용하기에 따라서는 소스 코드의 취약점을 파악하거나 소스 코드의 저작권을 보호할 수 없게된다는 문제도 같이 안고 있습니다. 그래서 나온 것이 Obfuscation, 즉 코드 난독화 기술입니다. 다행히 Reflector는 코드 난독화 기술을 정복하면서까지 디스어셈블을 시도하는 해킹 도구는 아니기 때문에 코드 난독화 기술이 적용된 어셈블리에 대해서는 "잘못된 결과"를 나타내거나 작업 자체를 실패로 처리합니다.
디스어셈블러를 이용하여 다른 어셈블리를 살펴볼 때에는 주의할 점이 있습니다. 디스어셈블러로 얻어진 결과는 어디까지나 기술적인 진단을 위한 목적으로서 "개인적"으로 사용하여야 합니다. 이러한 결과를 상업적으로 재구성하여 판매하거나 무단으로 공개하는 경우 저작권 분쟁이나 EULA 계약 위반에 대한 법적 조치에 놓일 수 있습니다. 일반적으로 EULA 계약에는 디스어셈블에 대한 금지 조약이 포함되어있기 때문입니다. 그리고 디스어셈블한 내용을 특정 언어의 문법에 맞게 재구성하는 기능을 너무 신뢰하지는 마십시오.
서두가 좀 길었습니다만 충분히 이야기하고 넘어가야 할 부분들이었으니 숙지해주시길 바랍니다. 이제 Reflector를 이용하여 Base Class Library를 한 번 분석해 보도록 하겠습니다.
"그림 펼치기" 버튼을 눌러서 핵심 라이브러리의 System 네임스페이스 안에는 무엇이 들어있는지 쭉 살펴봅시다.
그림 펼치기
간간히 전에는 보이지 않았던 낯설은 이름들이 보입니다. __Canon, __ComObject, MDA와 같은 것들인데, 이것들은 공식적으로 문서화되지 않은 내부 클래스입니다. 접근 제한자는 internal이 주로 많을 것이고 중첩 형식의 경우 private, protected, internal 중 하나일 수 있습니다.
Reflector를 이용하면 이와 같이 밖으로 드러나지 않는 형식들도 쉽게 파악할 수 있습니다. 우리가 지금 살펴볼 디스어셈블 기능 역시 밖으로 드러나는 형식이든 아니든 상관없이 작동합니다.
위의 그림에서처럼 트리를 확장한 상태에서 Object 클래스를 찾아서 이름 앞의 "+" 기호를 클릭하면 다음 그림과 같이 됩니다.
막상 살펴보면 이들 메서드들은 MSIL로 구현된 것이 아니라는 걸 알 수 있는데 다음과 같습니다.

Equals 메서드를 디스어셈블링한 결과를 보면 InternalEquals라는 메서드를 호출한 것을 볼 수 있습니다. 그런데 이 메서드는 우리가 알고 있는 플랫폼 호출 메서드와는 다른 양상을 띄고있는데 MethodImplAttribute만이 지정되어있습니다. 이것의 실제 구현은 .NET Framework 안에 숨겨져 있다는 것을 뜻합니다. 재미있는 사실은 성능과 관련이 깊거나 혹은 사용 빈도가 높은 유형의 기능들은 모두 이와 같은 방식으로 처리된다는 점입니다.
GetType 메서드, GetHashCode 메서드 등은 또 어떻게 구성되어있는지 살펴보도록 하겠습니다.
GetType 메서드는 실제로 위와 같습니다.
그리고 GetHashCode 역시도 Equals 메서드와 같은 방식으로 구현된 것을 볼 수 있습니다.
대강 이와 같은 방법으로 디스어셈블링할 수 있습니다. 만약 평소에 내부 구현이 어떻게 되어있는지 궁금했던 클래스나 형식이 있다면 Reflector에서 열어서 보십시오. 해답이 보이실 겁니다. 그리고 만약 어떤 어셈블리를 여는 도중이거나 특정 멤버를 탐색하려고 할 때 다음과 같은 대화 상자가 나타날 수 있습니다.
현재 Reflector가 로드한 어셈블리들 중에서 관련된 어셈블리가 없으면 GAC를 조사하여 일치하는 어셈블리를 자동으로 추천하는 대화 상자가 위와 같이 나타나게 됩니다. 대개의 경우 저 대화 상자에서 추천하는 내용이 정확하지만 그렇지 않은 경우도 있습니다.
Reflector의 디스어셈블러가 지원하는 재미있는 기능을 또 하나 더 살펴보기로 합시다. 지금까지 본 언어는 C#이 전부인데, C# 말고 다른 언어로도 디스어셈블링 결과를 볼 수 있습니다. 좀 더 복잡한 형식을 이용하여 결과를 살펴보기로 합니다. 살펴보려는 멤버는 System.Environment 클래스의 GetEnvironmentVariable 정적 메서드입니다.
MSIL Version Open
C# Version Open
Delphi Version Open
Visual Basic .NET Version Open
Visual C++ 7.x with Managed Extensions Version Open
RemObject Chrome Version Open
이제 디스어셈블링 외에 또 다른 편리한 기능인 Analyze 기능을 살펴보도록 하겠습니다. Analyze는 다양한 분석 옵션을 지원합니다. 주로 종속 관계를 조사하는 경우가 많으며 다른 유용한 분석 옵션도 제공합니다.
모든 항목에 대하여
* Depends On: 선택한 항목이 필요로 하는 다른 항목들입니다.
* Used By: 선택한 항목이 반드시 필요한 다른 항목들입니다.
클래스/구조체/대리자/나열 상수/인터페이스 전용
* Exposed By: 선택한 형식을 인수로 받아들이거나 반환하는 멤버 함수 및 프로퍼티들을 조사합니다.
* Instaniated By: 선택한 형식의 객체를 생성하는 멤버 함수 및 프로퍼티들을 조사합니다. 여기에는 현재 선택한 형식을 상속받는 자식 클래스의 생성자들도 모두 포함됩니다.
네임스페이스 전용
* Depends On: 현재 선택한 네임스페이스가 아닌 다른 네임스페이스 및 어셈블리들을 대상으로 이 네임스페이스 안의 개체들이 반드시 필요한 항목들입니다.
모듈 및 어셈블리 전용
* P/Invoke Imports: 해당 모듈이 System.Runtime.InteropServices.DllImportAttribute로 가져온 DLL 파일 및 참조한 DLL 함수들의 목록을 나타냅니다. 여기서 조사할 수 있는 내용은 Visual Studio에서 제공하는 Depency Walker나 Binary Dump Utility로는 조사할 수 없는 내용입니다.
Analyze 기능을 사용하기 위하여 다음의 그림과 같이 사용하면 됩니다.
그리고 위와 같은 방식으로 여러 개의 항목을 분석하도록 지정하면 Analyzer 창에 여러 항목을 쌓아둘 수 있습니다.
그리고 한 가지 더 유용한 정보를 알려드리면 위의 팝업 메뉴 가장 끝 항목인 Search MSDN 메뉴도 같이 활용하시면 좋습니다. 물론 MSDN이 모든 내용을 가지고 있는 것은 아니지만 적어도 BCL을 탐색하는 동안 MSDN과 비교하여 내용을 살펴보면 도움이 되는 점이 많습니다.
긴 글을 읽어주셔서 감사합니다. 다음 강좌에서는 Reflector의 리소스 분석 기능과 다양한 플러그인을 소개하는 시간을 가지도록 하겠습니다. :-)
'Software Development > Reflector' 카테고리의 다른 글
| Reflector 강좌 #3: 리소스 분석 및 5.x 버전을 위한 다양한 플러그인 (0) | 2007/04/08 |
|---|---|
| Reflector 강좌 #2: Disassembling & Analyzing (0) | 2007/04/06 |
| Reflector 강좌 #1: 둘러보기 (0) | 2007/04/03 |
TAG .Net Framework,
c#,
Disassemble,
Lutz Roeder,
MSIL,
reflector,
rkttu,
rkttu.com,
남정현,
대한민국>서울>송파구>잠실동
