ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Fiddler] Fiddler Extension(plugin) 만들기
    # 웹해킹 공부중 2021. 4. 28. 16:19

    기존에 Fiddler를 사용하면서 CustomRules 만을 사용해왔는데 매번 수정해서 쓰기 불편한점도 있고 하나 베이직하게 만들어두면 유용할 것 같아 Fiddler Extension을 만들어보게 되었다. 만드는게 그렇게 어렵지는 않으나 C#을 전혀 모르고 관련 자료들이 산재돼있어서 처음 할 때 삽질을 좀 많이 했다. (ㅠㅠ) 이 포스트에서는 간단하게 Fiddler Extension을 만드는 방법에 대해 알아보고 예시로 특정 도메인에 해당하는 URL에 대해서 전달되는 파라미터들을 가져와서 출력해주는 프로그램을 만들어 볼 것이다. 

    ※ C#을 전혀 모르는 사람이 작성한 글로 부정확한 내용이 있을 수 있습니다...!

     

    0. 환경 구성

    - Visual Studio 2005 이상(혹은 .Net Framework 컴파일러)
    - .Net Framework 최신 버전
    - Fiddler 4 최신 버전

    해당 포스트에서는 Visual Studio 2019, .Net Framework 4.7.2, Fiddelr 4 를 기준으로 설명이 이뤄지지만 Visual Studio의 경우 다른 버전이라도 엇비슷하게 따라할 수 있을듯 하다.

    1. 프로젝트 생성 및 설정

    [그림 1] create new project

    프로젝트는 [클래스 라이브러리9(.NET Framework)' -> '클래스 라이브러리(.NET Framework)] 순으로 선택해서 생성하면 된다.

    [그림 2] create new project - 2

    프로젝트 이름은 임의로 지어주면 되고 프레임 워크는 최신 버전으로 해주면 된다. (아마 그 하위 버전으로도 만들 수 있는 듯 하다)

    [그림 3] create new project - complete

    생성되면 [그림 3]과 같이 Class1.cs 라는 파일이 생성되며 화면에 코드가 나타난다. 이제 몇가지 Fiddler Extension을 개발하기 위한 사전 설정을 할 차례다. 

    [그림 4] Fiddler setting

    prefs set fiddler.debug.extensions.showerrors True
    prefs set fiddler.debug.extensions.verbose True

    Fiddler의 Request list 아래에 보면 검은색 명령 프롬프트 같은곳이 있다. 여기에 위 두 라인을 입력해서 디버깅을 위한 설정을 해준다. 

    [그림 5] Visual Studio 프로젝트 속성

    Visual Studio에서 [프로젝트 속성 - 디버그 - 시작시 외부 프로그램]을 Fiddler.exe 경로를 입력해준다. 우리가 확장 프로그램을 컴파일하면 DLL 파일이 만들어지기 때문에 디버깅 하기 위해선 외부 앱(여기선 Fiddler)을 연결해주어야 한다. 이렇게 설정해주면 이제 실행할 때 기존 Visual Studio에서 디버깅할 때와 똑같이 Breakpoint 걸고 한줄씩 실행하면서 디버깅 할 수 있다.

    [그림 6] Visual Studio Build Event

    빌드 후 컴파일 된 DLL파일을 Fiddler에 자동으로 등록해주고 빌드할 때 Fiddler가 켜져있을 경우 자동으로 꺼주기 위해서 프로젝트 속성에서 빌드 이벤트를 설정한다.

    빌드 이벤트 명령줄 대화 상자:
    taskkill /im fiddler.exe /t /f 2>&1 | exit /B 0
    
    빌드 후 이벤트 명령줄:
    copy "$(TargetPath)" "%userprofile%\My Documents\Fiddler2\Scripts\$(TargetFilename)"

    [그림 7] Reference

    이어서 개발에 필요한 외부 DLL을 참조로 추가해주어야 한다. 프로젝트에 참조를 우클릭 한 다음 참조 추가 메뉴를 눌러준다. 

    [그림 8] Fiddler 추가

    참조 관리자 아래에 찾아보기(Browse)를 누른 다음 Fiddler 가 설치되어있는 경로로 가서 Fiddler.exe 를 추가해준다.

    [그림 9] System.Windows.Forms

    우리가 Fiddler 확장 프로그램에 GUI를 이것저것 수정해주기 위해서는 [그림 9]와 같이 System.Windows.Forms를 추가해준다. 이렇게 앞으로 필요한게 있을 때마다 참조에서 추가해주면 해당 라이브러리들을 사용할 수 있다. 

    [그림 10] basic code

    이제 using 으로 FiddlerSystem.Windows.Forms 를 추가해준다. 그리고 [assembly: RequireVersion()] 을 넣어주는데 이 때 버전은 적당히 현재 설치된 Fiddler 버전과 비슷하게 맞춰주면 된다. 본인은 5.0.0.0 으로 지정해주었다. 이제부터 본격적으로 필요한 코드를 작성하면 된다. 

    2. GUI 만들기

    [그림 11] add wpf

    UI를 만들기 위해서 WPF를 사용할 것이다. [프로젝트 추가 - Windows Form] 을 눌러준다.

    [그림 12] 사용자 정의 컨트롤

    이어서 C# 항목중 WPF를 누르고 '사용자 정의 컨트롤' 을 눌러서 추가해준다.

    [그림 13] UI Edit

    그럼 왼쪽에 UI를 구성할 수 있는 도구상자와 함께 UI 에디터가 나온다. 아래의 코드는 UI에 해당하는 XAML 코드이며 UI가 업데이트 될 때마다 자동으로 업데이트 된다. 코드를 직접 입력해줄 수도 있고 도구상자를 이용해서 UI를 구성해줄 수 있으니 편한 방법대로 사용하면 된다. WPF는 일반 C# 앱 개발할 때와 같기 때문에 관련 자료는 구글링을 해보면 도움이 되는 글들이 많다. 

    [그림 14] UI Edit

    도구 상자를 이용해서 적절히 배치한다. 창 크기에 따라 자동으로 크기 조절되도록 하는 방법은 오른쪽 아래 속성창에서 너비 혹은 높이값을 자동으로 주고 Align을 맞춰주면 된다. 레이아웃과 관련된 부분은 C#과 관련된 부분이므로 해당 포스트에서는 다루지 않는다.

    필자는 TextBox에 IsReadOnly="True" VerticalScrollBarVisibility="Visible" 옵션을 줘서 읽기만 가능하며 스크롤바가 있는 Textbox를 만들었다. Name 속성은 앞으로 해당 Textbox를 이용해서 작업할 때 사용되므로 기억해둔다.

    필요한 요소를 추가하면 아래 XAML코드에도 추가가 된다. <TextBox>와 <Button>이 방금 추가해준 요소들이고 이 안에서 요소의 속성을 지정할 수 있다. 

    3. 본격 코딩

    이제 여기까지 틀을 잡아뒀으면 이제 본격적으로 Fiddler Extension의 기능을 구현해본다. 우리가 만들려는 기능은 request message를 파싱해서 전달되는 인자들을 가져오는 것이다. 우선 request message를 읽어오는 것부터 차근차근 만들어본다. 

    Fiddler에서는 여러 기능들을 interface 로 제공하고 있다. IFiddlerExtension, IAutoTamper, IAutoTamper2 등 여러 인터페이스들이 존재하는데 필요한 기능에 맞는 인터페이스를 상속받고 함수들을 정의해주면 된다. 우리는 이중 IAutoTamper라는 인터페이스를 상속받아서 구현해본다. 이외 다른 인터페이스들은 아래 링크에서 확인할 수 있다. 

    docs.telerik.com/fiddler/extend-fiddler/interfaces

     

    Implement Fiddler Interfaces | Progress Telerik Fiddler

    Extend Fiddler Implement Fiddler interfaces to load your assembly during Fiddler execution. Public classes in your assembly that implement the IFiddlerExtension interface will be loaded by Fiddler during startup. public interface IFiddlerExtension { // Cal

    docs.telerik.com

    public class Class1 : IAutoTamper
        {
            public void AutoTamperRequestAfter(Session oSession)
            {
                // Request message 가 수정된 다음 보내지기 직전에 호출된다.
            }
    
            public void AutoTamperRequestBefore(Session oSession)
            {
                // Request message 가 보내지기 전 호출되며 request message를 수정할 수 있다.
            }
    
            public void AutoTamperResponseAfter(Session oSession)
            {
                // Response msessage 가 수정 된 직후 호출된다.
            }
    
            public void AutoTamperResponseBefore(Session oSession)
            {
                // Response message 가 브라우저로 보내지기 전 호출되며 response message를 수정할 수 있다.
            }
    
            public void OnBeforeReturningError(Session oSession)
            {
                // Fiddler 에서 자체적으로 HTTP Error를 발생시킬 때 호출된다.
            }
    
            public void OnBeforeUnload()
            {
                // Fiddler 가 종료되기 직전 호출된다.
            }
    
            public void OnLoad()
            {
                // Fiddler 가 실행되고 로딩이 끝났을 때 호출된다.
            }
        }

    인터페이스를 상속받고 해당 인터페이스에 있는 함수들을 전부 재정의 해줘야 한다. Fiddler 가 실행됐을 때 UI를 로드해야하기 때문에 OnLoad 함수에서 UI를 로드하는 코드를 넣는다. 그리고 우리는 request message 에서 파라미터들을 읽어와야 하기 때문에 AutoTamperRequestBefore 함수를 사용할 것이다.

    using System.Windows.Forms.Integration; // 추가
    
    public class Class1 : IAutoTamper
        {
         	public UserControl1 uc;
            public ElementHost element;
            ...
            public void OnLoad()
            {
                // UI Loading
                TabPage oPage = new TabPage("Skeleton Ext");
                uc = new UserControl1();
                element = new ElementHost();
                element.Child = uc;
                element.Dock = DockStyle.Fill;
                oPage.Controls.Add(element);
                FiddlerApplication.UI.tabsViews.TabPages.Add(oPage);
            }
            ...
        }

    [그림 15] WindowsFormsIntegration

    우리는 UI를 구성하기 위해 WPF를 사용하고 있기 때문에 Windows Form 위에 WPF를 올리기 위해선 ElementHost라는 클래스 WinForm에 추가하고 그 Child로 우리가 만든 WPF를 추가해야한다. ElementHost는 참조에서 'WindowsFormsIntegration' 을 추가하고 using 으로 코드에 포함해준다. 위 코드대로 하면 Fiddler에 'Skeleton Ext'라는 이름으로 탭이 하나 생기고 우리가 그린 UI가 로드되는 것을 확인할 수 있다. 

    [그림 16] Fiddler UI

    코드를 입력하고 Ctrl + F5 를 누르면 Fiddler가 실행되면서 [그림 16]과 같이 UI가 로드된 것을 확인할 수 있다. 이제 저 Textbox에 우리가 원하는 도메인에 대해 전달되는 인자들을 출력시키는 작업을 해본다.

    oSession.fullUrl //전체 URL 가져오기
    oSession.host // URL에서 host 부분만 가져오기
    oSession.oRequest["Cookie"]; // 쿠키값 가져오기
    System.Net.WebUtility.UrlDecode(query); // URL Decode
    
    oSession.RequestHeaders.ToString() // request header 가져오기
    oSession.RequestMethod // request method 가져오기
    oSession.GetRequestBodyAsString() // request body 가져오기
    oSession.ResponseHeaders.ToString() // response header 가져오기
    oSession.GetResponseBodyAsString() // response body 가져오기

    Fiddler 에서 Session object의 다양한 함수들을 사용할 수 있다. 위에 나열된건 가장 기본적으로 사용할 수 있는 함수들이고 이외에 필요한건 Fiddler를 설치하면서 같이 설치된 'Fiddler ScriptEditor' 를 실행시키면 오른쪽에 사용할 수 있는 객체와 함수들이 간단한 설명과 함께 나열돼있다. 

    [그림 17] Fiddler Scripteditor

    이 객체들을 적절히 활용해서 원하는 기능을 만들어내면 된다. 우리는 request message 로 전달되는 인자값들이 궁금하므로 아래와 같은 흐름으로 코드를 구성한다. 

    1. host 주소를 확인한다
    2. 메소드를 확인한다
    3. 메소드에 따라 인자값을 가져온다
    4. 인자값을 Textbox로 출력한다.

    이런 흐름으로 코드를 작성하면 아래와 비슷하게 코드가 구성될 것이다.

    public void printLog(string val)
    {
    	(element.Child as UserControl1).Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(delegate {
    		(element.Child as UserControl1).textBox.Text += val + "\n";
    	}));
    }
    
    public void AutoTamperRequestBefore(Session oSession)
    {
    	string req_params;
    	if (oSession.host.Contains("domain.com"))
    	{
    		if (oSession.RequestMethod.Equals("GET"))
    		{
    			req_params = (oSession.url + "?").Split('?')[1]; // 전달하는 인자가 없을 경우 null이 반환되도록
    		}
            else
            {
                req_params = oSession.GetRequestBodyAsString();
            }
    		req_params = System.Net.WebUtility.UrlDecode(req_params); // URL Decode
    
            // 인자값들 출력
            string[] q_params;
            if (req_params.Length > 0)
            {
                printLog("#" + oSession.host);
                q_params = req_params.Split('&');
                foreach (string param in q_params)
                {
                    printLog(param);
                }
            }
            printLog("");
    	}
    }    

    코드에서 어려운 부분은 없지만 한가지 봐야할 부분은 위 코드에서 printLog 함수 부분이다. 현재 작업중인 코드는 WPF가 아니기 때문에 우리가 만든 UI 컴포넌트에 접근하기 위해서는 Threading 작업을 해줘야한다. WPF는 UI 처리를 위해 Dispatcher 라는 Queue로 관리가 되기 때문에 WPF에 있는 Textbox 업데이트 작업을 Queue에 넣어줘야하는데 이 작업을 하는게 printLog 함수이다. 

    우리가 Winform 위에 올린 element의 Child에 있는 UserControl1 클래스에 textBox 라는 컴포넌트가 있으므로 위와같이 코드가 만들어진다. (필자도 이 이상 자세한 내용은 모른다 ㅠㅠ C# Newbie..) Dispatcher를 사용하기 위해선 'using System.Windows.Threading;' 을 코드 상단에 추가해줘야한다.

    [그림 18] 중간 확인

    실행시켜보면 원하는대로 출력이 된다. 그럼 이제 버튼을 눌렀을 때 textBox 를 clear 시키는 기능을 넣어보자.

    [그림 19] Button Action

    WPF의 XAML에서 액션을 넣고 싶은 요소에 Click 속성으로 함수이름을 지정해주면 클릭했을 때 해당 함수가 호출된다. 

    [그림 19] xaml.cs

    솔루션 탐색기에서 UserControl1.xaml 옆의 화살표를 눌러 펼치면 클래스 파일이 있다. 해당 클래스에 위에 Click 속성으로 지정한 이름으로 함수를 정의하면 된다. 

    public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }
        private void btn_clear_clicked(object sender, RoutedEventArgs e)
        {
            this.textBox.Text = "";
        }
    }

    이렇게까지하면 우리가 처음에 원하던 기능은 모두 구현이 되었다. 

    4. Tip

    Extension 을 구현하다보면 UI와 메인 클래스간의 요소 접근이 필요한 경우가 종종 발생한다.

    메인클래스(Winform) 에서 WPF의 요소에 접근할 경우:

    (element.Child as UserControl1).value1 = 10;

    WPF에서 메인클래스(Winform) 의 데이터에 접근할 경우:

    // MainClass.cs
    
    public void OnLoad()
    {
    	// ....
    	(element.Child as UserControl1).parentForm = this;
    	// ....
    }
        
    // UserControl1.xaml.cs
    
    public Class1 parentForm { get; set; }
    public UserControl1()
    {
    	InitializeComponent();
    }
    
    private void btn_clicked(object sender, RoutedEventArgs e)
    {
    	parentForm.value1 = 10;
    }

    이 경우엔 메인 클래스에서 WPF에 객체를 넘겨줘야한다. 그렇게 전달받은 객체를 이용해서 WPF에서 메인클래스에 있는 요소에 접근이 가능해진다. 

    이렇게 간단하게 Fiddler Extension을 만들어보았다. 여기에 원하는 기능들을 더 추가해서 웹 분석에 용이하게 사용할 수 있길 바란다. 

    댓글

Designed by Tistory.