본문 바로가기

.NET Framework/BUNDLE

[.NET] 코드 보안(창과 방패) - 04. Dotfuscator 프로그램 비교 #2(Control Flow, String Encryption, Pruning)

 회차
[.NET] Code Securit(Spear and Shield) : 01. DisAssembler
[.NET] Code Securit(Spear and Shield) : 02. Obfuscation

[.NET] Code Securit(Spear and Shield) : 03. Dotfuscator Program Comparison #1
[.NET] Code Securit(Spear and Shield) : 04. Dotfuscator Program Comparison #2
[.NET] Code Securit(Spear and Shield) : 05. Obfuscation Design Tip And Notabilia
[.NET] Code Securit(Spear and Shield) : 06. Security Tools : Strong Name Tool
04| 코드 보안(창과 방패) - Dotfuscator 프로그램 비교 #2
구독자 여러분 안녕하세요
이번 회차에서는 지난 포스팅에 이어 Control Flow, String Encryption, Pruning 기능에 대서 알아보도록 하겠습니다.  예제를 위해서 지난 회차에서 사용했던 예제코드를 인용하도록 하겠습니다.

[코드1] 변환 대상 예제 코드
namespace ObfuscationEx
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("안녕하세요 방갑습니다.");
            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(string.Format("100 + {0}의 합은? {1}", i, 100 + i));
            }
            Console.WriteLine();
            Console.WriteLine(new People().ToString());
        }
    }
    class People
    {
        private string NameKor = "정은성";
        private string NameEng = "Jeong Eun Seong";

        public override string ToString()
        {
            return string.Format("한글 이름은? {0} 영문 이름은? {1}", NameKor, NameEng);
        }
    }
}

2.Control Flow
Control Flow는 프로그램의 조건, 분기, 반복문과 같은 논리적 구조를 스파케티 코드화하여 구조를 어렵게 변경을 하게 됩니다. 이과정에서는 성능 저하가 발생 할수 있지만 코드의 가독성을 떨어트리면서 분석이 어려울 수 있습니다.

보통 실무 프로젝트를 가지고 Control Flow를 적용을 했었는데요. 구독자 여러분들께 소개해드리고자 간단한 코드를 가지고 테스트 중에 흥미로운 사실을 알게 되었습니다. Control Flow 기능이 생각보다 강력 했던 이유인지 역에섬블러 프로그램 마저 해독하지 못하는 상황이 나타났습니다. 의도 했던 코드는 Main 메소드 인데요.

간단한 코드임에 불구하고 Control Flow의 영향으로 인해 최고?! 라고 불리는 Red Gate's .Net Reflector에서도 확인 해 볼 수 없었습니다.  훗..코드가 복잡했나?

[그림 1] Control Flow 적용


그래도 여러분들께 소개를 해드려야할것 같아 더 간단한 코드를 가지고 비교를 해보도록 하겠습니다.
비교하기 위해 의미 없는 임시코드를 다음과 같이 작성 하였습니다.

[코드 1] Control Flow 비교용 샘플 코드
private static int testMethod()
{
      int n = 0;
      if (n == 0)
      {
           n = string.Compare("정은성", "Jeong");
      }
      return n;          
}

아주 심플한 코드입니다. 위 논리적 구조가 어떤 형태로 변경이 되는지 살펴보도록 하죠.

[그림 2] Control Flow 적용 비교

Clontrol Flow를 적용을 해보았는데요.
적용 전후를 보시고  " 에이~ 코드 제어 흐름만 변경 됬자나? ",  " 별거 없네요. "  이런 생각이 드실것 같아요.

이해를 돕고자 단순화 한 코드이며 큰 의미는 없습니다. 그래도 정말 어떻게 변경이 되는지 보여드려야 할것 같아요.
그래서 dotfuscator 유저가이드 내용을 발취하였습니다. 

[사례 1-1] Control Flow 적용 전 [ 출처 : dotfuscator's user guide ]
public int CompareTo(Object o) {
    int n = occurrences – ((WordOccurrence)o).occurrences;
    if (n == 0) {
        n = String.Compare(word, ((WordOccurrence)o).word);
    }
    return(n);
}

[사례 1-1] Control Flow 적용 후 [ 출처 : dotfuscator's user guide ]
public virtual int _a(Object A_0) {
    int local0;
    int local1;
    local0 = this.a – (c) A_0.a;
    if (local0 != 0) goto i0;
    goto i1;
    while (true) {
        return local1;
        i0: local1 = local0;
    }
    i1: local0 = System.String.Compare(this.b, (c) A_0.b);
    goto i0;
}

내용을 보시면 " 아~ 이렇게 읽기 어려운 구조로 변경이 되는구나!! " 라고 느낌을 받으실것 같아요?
저만 그런가요? 

이렇게 변경이 되었지만 주의 해야할 부분이 존재를 합니다. 앞서 말씀 드린적이 있는데요.
이기능을 사용하게 되면 가독성을 떨어지지만 속도 측면에서 성능이 저하가 될 우려가 있습니다.
따라서 필요에 따라 부분적으로 Control Flow 기능을 적용하실 필요가 있음을 알려드립니다. : )

이어서 기능에 대해서 알아보도록 하겠습니다.
이 기능 또한 Rename 기능과 같은 패턴의 오퍼레이션을 가지고 있고 다음과 같습니다.

[그림 3-1] Dotfuscator 프로그램 Control Flow 기능 화면 - Exclude 영역


① 텝 메뉴 : Control Flow기능은 2가지 기능으로 구성 되어 있습니다.
     - Exclude : Control Flow 적용시 배제 될  적용 대상 설정
     - Options : Control Flow 적용시 옵션으로 가독성 강도에 대해 High, Medium, Low로 구성

② 트리영역 : 해당 어셈블러의 구조를 제공하며 Control Flow 적용 대상을 제외하여 빌드가 가능합니다.
③ 적용 규칙 영역 : 해당 영역은 배제 가능한 규칙을 정규식을 정의 하여 적용 대상을 제외 하여 빌드 가능합니다.
                                적용 규칙에 대해서 궁금하시면 별도로 문의 바랍니다 : )


[그림 3-2] Dotfuscator 프로그램 Control Flow 기능 화면 - Options 영역


Control Flow 기능은 해당 코드를 스파게티화 하면서 가독성 측면과 성능 측면에서 반비례 성향을 알수 있었는데요.
많은 스레드를 요구를 한다거나 엑세스 타임이 짧은 프로세스를 처리해야 한다면 필요에 따라서 배제 대상을 지정하여 빌드를 하셔야 합니다.

이렇게 가볍게 리뷰를 해보았는데요 이기능을 적용해야 한다면 개발 중신 프로그램의 특정부분을 어떻게 배제 해야할지 조금이라도 도움이 되셨기를 바랍니다.

이어서 String Encryption 기능에 대해서 살펴보도록 하겠습니다.

3. String Encryption
흔히 크래커는 리버싱 하려고 할때 가장 쉽게 접근이 가능한 메세지 부분을 검색을 하는 편인데요. 이기능은 이러한 케이스를 조금이라도 접근을 하지 못하도록 해당 문자열을 암호화하여 문자열을 찾을수 없도록 하는 기능입니다.

간단한 예제를 통해 변경 전후를 살펴보도록 하며 이어서 사용방법에 대해서 알아보도록 하겠습니다.

[코드2-1] String Encryption 적용 전 
Console.WriteLine("안녕하세요 방갑습니다."); 

[코드2-2] String Encryption 적용 후 
Console.WriteLine(a("쪼좾뛀ꇄꋆ뿈믊꓌껎￐냒뫔뫖飚軜럞胠釢闤짦ꓨꫪ꣬뫮뷰", num2));

적용 후의 형태를 보시면 깨진 문자열을 보이는데.
" 파일 포맷이 인코딩이 잘못됬나?" , " 저렇게 깨진 문자를 심어두면 프로그램이 돌아갈까? " 라고 
의심 스러울것 같은데. 걱정 안하셔도 됩니다.

아무쪼록 알수 없는 문자열 형태로 변경되어 있는데요. 내부적 로직은 리버싱을 통해 분석을 해봐야 알수 있겠지만
a라는 메소드와 인수에 의해 복원이 되는것으로 추측 됩니다.

이기능 또한 어떻게 사용하는지 알아봐야할것 같은데요. 해당 기능 또한 Control Flow기능과 유사한 인터페이스지만 다른 오퍼레션을 가지고 있습니다.

[그림 4] Dotfuscator 프로그램 String Encryption 기능 화면



보시는 화면과 같이 구성은 Rename, Control Flow과 달리 Include 텝만 구성이 되어있고 또한 배제해야할 대상이 아니라 적용 해야할 대상을 지정을 해줘야만 난독화가 가능합니다.

① 트리 영역 : 해당 어셈블러의 구조를 제공하며 String Encryption 적용 대상을 지정해야만 빌드가 가능합니다.
적용 규칙 영역 : 해당 영역은 적용해야할 규칙을 정규식을 통해 정의 하여 적용하면 빌드 가능합니다.
                               이 기능에 대해서 궁금하신점 있으시면 문의 바랍니다 : )

String Encryption 기능은 해당 문자열을 알수 없는 형태로 변경함으로써 크래커에게 리버싱을 하기 어렵도록 제공을 하고 있는데요. 막강한 기능에도 불구하고 타입체킹, 리플렉션, 통신 등 케이스의 경우 프로그램이 정상적으로 작동하지 않으므로 가능한 유저가이드를 숙지하시고 적용하시길 바랍니다. 만약 번그럽거나 내용이 어려우시다면 다음 회차에서 소개 되는 "난독화 디자인 팁 & 주의사항해야 할 사항" 포스팅을 보시면 조금이라도 도움이 되실것 같습니다.

드디어 소개해드릴 마지막 기능에 도달했습니다. 그럼 Pruning(Removal) 기능에 대해 살펴보도록 할까요?

4. Pruning(Removal)
지난 회차에서 소개를 해드린 내용을 인용한다면 " 프로그램을 좀더 빠르게 실행하도록 돕는 기능! " 라고 말씀 드릴 수 있습니다. 그렇다면 " 도대체 어떤 기능이길래? 속도가 향상이 된다는것인지 "  궁금증을 가지시는분이 계실것 같은데요. 이 기능을 적용하게 된다면 실제 작성된 코드 중 사용하지 않는 코드를 제거하게 됩니다. 

즉, 필드, caller에 의해 반환 받는 시그니처, 생성자(.ctor) 등을 스택에서 제거하므로 속도를 향상이 시킬 수 있습니다.

그럼 간단한 예제를 통해 어떻게 달라지는지 살펴보고 사용 방법에 대해서 알아보도록 하겠습니다.

[코드 3] Pruning(Removal) 예제 코드 
namespace ObfuscationEx
{
    class Program2
    {
        Program2()
        {
            string test = "";
        }
        static void Main(string[] args)
        {
            string test = "";
            Console.Write("안녕하세요 구독자 여러분 : )");
            string test2 = MyTestMethod();
        }
        public static string MyTestMethod()
        {
            return "test";
        }
    }


간단한 예제를 통해 변경 전후를 살펴보도록 하며 이어서 사용방법에 대해서 알아보도록 하겠습니다.

[그림 5-1] Pruning(Removal) 적용 전 


여기서 유심히 살펴보실 내용은 빨간색 테투리 영역 입니다.  다음 적용 후와 비교를 해보시길 바랍니다.

[그림 5-2] Pruning(Removal) 적용 후 


[코드 4] Pruning(Removal) 적용 전(Main 메소드 일부)
string test = "";
Console.Write("안녕하세요 : )");
string test2 = MyTestMethod();


[그림 5-3] Pruning(Removal) 적용 후  


적용 전후를 정리해보면...
1. 실제 사용하는 클래스와 사용하지 않는 클래스 영역 정리
2. 메소드와 필드, 생성자(.ctor()) 영역 정리
3. 반환 메소드의 타입이 사용이 되지 않는 영역 정리

이기능 또한 어떻게 사용하는지 알아봐야할것 같습니다.

[그림 6-1] Dotfuscator 프로그램 Pruning(Removal) 기능 화면
                Include Triggers, Conditional Includes 영역



① 텝 메뉴 : Pruning(Removal)기능은 3가지 기능으로 구성 되어 있습니다.
     - Include Triggers : Pruning 적용하기 위한 트리거 설정
     - Conditional Includes : Input 어셈블러에 관련된(DLL, 리소스) 모듈등 어셈블러간의 모든 종류의 공유하는 경우
                                           Pruning 조건을 설정 가능 합니다. 
     - Options : Pruning 적용시 어떤 어셈블러를 제거 해야할지 XML/HTML로 보고서를 생성 기능

② 트리영역 : 해당 어셈블러의 구조를 제공하며 Pruning 적용 대상을 트리거 형태로 지정하여 빌드가 가능합니다.
③ 적용 규칙 영역 : 해당 영역은 적용해야할 규칙을 정규식을 통해 정의 하여 적용하면 빌드 가능합니다.
                                적용 규칙에 대해서 궁금하시면 별도로 문의 바랍니다 : )

[그림 6-2] Dotfuscator 프로그램 Pruning(Removal) 기능 화면 - Options 영역


옵션 영역은 어떤 어셈블러를 제거를 할것인지에 대해 XML 형태로 제거 보고서를 생성하는 기능을 제공합니다.
입력된 어셈블러의 구성원들을 어떻게 정리를 할것인지를 설명해주며 필요에 따라 XML이 아닌 HTML로 변환이 가능하며 유용하게 사용이 가능합니다.

Pruning(Removal) 기능은 흔히 개발을 하시다보면 의도하지 않게 불필요한 코드를 남겨 두거나 의미 없는 코드를 반환 하는 경우가 있는데요. 리펙토링을 통해 정정이 가능하지만 미쳐 발견을 하지 못햇을때 유용하지 않을까 하는 개인적인 생각이 듭니다.

그외 많은 기능들이 존재를 하는데요. 자세한 사항은 유저 가이드를 참고 하시길 바랍니다.

참고 문헌

- Dotfuscator 4.0 : : http://msdn.microsoft.com/ko-kr/library/ms227240.aspx
- Dotfuscator 4.0 유저 가이드( ※ 저작권 문제로 파일은 첨부 할수 없습니다. )
 
포스팅을 마치며...

지금까지 Dotfuscator프로그램의 기능들에 대해서 살펴보았는데요. 여러분들께 유익한 내용을 전달해드리고자 나름?! 알차게 구성을 했는데요. 어떠셨는지 모르겠네요. 아직 난독화를 하지 못하셨다면 지금 바로 적용해보세요. 

앞으로 회차가 더 있는데, 관심있게 봐주실거죠?

더 궁금하신점이 있으시면 댓글 혹은 http://blog.tobegin.net/notice/1 프로필정보를 통해 문의 바랍니다.
감사합니다.

정은성 드림