2017년 1월 21일 토요일

Excel(엑셀)로 File Debugger(파일 디버거) 만들기 & 유니코드 파일 분석

옛날 DOS 시절에 사용하던 디버거 프로그램이 없어서 Excel로 만들었다. CPU, O/S, 컴퓨터 언어가 진화하면서 옛날 기계어(어셈블리어)를 사용할 필요가 없어졌다. 고로 CPU 명령어 해석은 불필요하니 파일 데이터만 분석하는 기능만 만들면 된다. Excel의 편리한 인터페이스와 계산 능력을 사용할 수 있어 훨씬 좋은 디버거를 만들 수 있다.





위 화면은 파일의 일부를 바이트 단위로 읽어 여러 방법으로 해석해서 보여주는 화면이다. 옛날 디버거를 사용해 보고 필요하겠다싶은 해석을 넣었다. 일부 바이트를 수정한 후에 써넣을 수도 있다. 그러니 원본 파일을 이름을 바꿔 보전한 후에 해킹을 해야 한다.

8비트 정수를 10진수, 16진수, 2진수로 나타낸다. 16진수는 디버거에서 많이 보는 형태이나 별로 쓸모가 없다. 때론 2진수 패턴을 분석할 필요가 많다. 그리고 16비트로 묶어 정수로 볼 필요도 있다. 문자열이 섞인 경우를 보기 위해 7비트 아스키 문자, 16비트 한글 완성형 문자로 묶어 표현한다. 먼저 기록된 바이트를 상위 바이트로 보는 것은 완성형 코드이고, 하위 바이트로 보는 것은 16비트 정수일 때다.

파일에 따라 때론 32비트 정수, 유니코드 문자 등으로 해석할 필요가 있는데 원리가 같으니 직접 우측 공백에 함수를 이용해 만들어 넣으면 된다. 이런 다양한 해석은 Excel 함수를 이용해서 직접 만들어 넣으면 된다. 





위 화면은 2개 파일의 헤더부분을 비교할 때 사용하는 화면이다. 같은 종류의 파일은 헤더(머리) 부분의 패턴이 거의 같다. 고로 기준이 되는 파일과 내용에 있어 약간의 차이가 나는 비교 파일에서 그 차이 나는 부분만 찾아 표시해 줘서 파일 구조 해석에 도움을 준다.

위와 같이 화면을 만든 후에 다음 코드를 모듈에 복사해 넣어 버튼에 연결시킨다. 화면에서와 같이 정확한 행렬(가로/세로) 위치에 만들어야한다. 그림에서와 같은 Test 파일을 만들어 동작 시험을 해 본다.


Public Sub ReadFile()
    '주소, 값 영역 지우기
    Range("A5:B5").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름, 주소, 읽을 수량 지정
    Filename = Worksheets("디버거").Cells(1, 4).Value
    ByteAddress = Worksheets("디버거").Cells(2, 4).Value
    ByteCount = Worksheets("디버거").Cells(3, 4).Value
    Dim ByteValue As Byte
    '내용 읽어서 배치, EOF보다는 LOF를 쓰는 것이 깔끔하다.
    Open Filename For Binary As 1
        For i = 0 To ByteCount - 1
            If ByteAddress + i > LOF(1) Then Exit For
            Get #1, ByteAddress + i, ByteValue
            Worksheets("디버거").Cells(5 + i, 1).Value = ByteAddress + i
            Worksheets("디버거").Cells(5 + i, 2).Value = ByteValue
        Next i
    Close 1
    '다음 주소 자동 계산
    Worksheets("디버거").Cells(2, 4).Value = ByteAddress + ByteCount
End Sub


Public Sub WriteFile()
    Filename = Worksheets("디버거").Cells(1, 4).Value
    Dim ByteValue As Byte
    Open Filename For Binary As 1
        i = 0
        ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Do While ByteAddress <> ""
            ByteValue = Worksheets("디버거").Cells(5 + i, 2).Value
            Put #1, ByteAddress, ByteValue
            i = i + 1
            ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Loop
    Close 1
End Sub


Public Sub CompareFile()
    '주소, 값 영역 지우기
    Range("A3:B3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    Range("I3:J3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름 지정
    FileName1 = Worksheets("비교").Cells(1, 4).Value
    FileName2 = Worksheets("비교").Cells(1, 12).Value
    '파일 길이 비교
    Dim ByteValue1 As Byte
    Dim ByteValue2 As Byte
    Open FileName1 For Binary As 1
    Open FileName2 For Binary As 2
    filelength1 = LOF(1)
    filelength2 = LOF(2)
    MaxLength = filelength1
    minlength = filelength2
    If filelength1 < filelength2 Then
        MaxLength = filelength2
        minlength = filelength1
    End If
    '헤더 부분 비교 차이 나는 부분만 표시
    r = 3
    For i = 1 To minlength
        Get #1, i, ByteValue1
        Get #2, i, ByteValue2
        If ByteValue1 <> ByteValue2 Then
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
            r = r + 1
        End If
    Next i
    For i = minlength + 1 To MaxLength
        If filelength1 = MaxLength Then
            Get #1, i, ByteValue1
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
        End If
        If filelength2 = MaxLength Then
            Get #2, i, ByteValue2
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
        End If
        r = r + 1
    Next i
    Close
End Sub


이걸 왜 만들었냐고? 옛날 게임 데이터 파일 해킹하려고. 요즘 것은 암호화 되어 있다.

자 이제 어떻게 사용하는지 보도록 하자. 사용법을 알면 감이 잡힐 것이다. 노트패드에서 숫자, 알파벳, 한글의 시작과 끝 문자를 입력해 넣고 4가지 표준으로 저장한다.


  1. ANSI(기존표준) = 영문 아스키 + 한글 완성형(한글 부족) + MS CP949(한글 보충)
  2. 유니코드(UTF-8) = 통신 목적으로 사용하며 기존 영문 아스키와 호환을 유지한다.
  3. 유니코드(UTF-16 little Endian) = 16비트로 세계 문자 표현. 하위 바이트 먼저 기록.
  4. 유니코드(UTF-16 Big Endian) = 16비트로 세계 문자 표현. 상위 바이트 먼저 기록.


※ Endian(엔디안)은 여러 바이트가 하나의 의미를 나타낼 때 바이트 저장 순서를 말한다. 예를 들어 123(백이십삼)을 저장(기록)할 때 기계에 따라 1, 2, 3 순서와 3, 2, 1 순서로 저장해서 구분이 필요한 것이다. IBM PC의 Intel CPU는 3, 2, 1 순서로 기록한다. 옛날 Mac PC 등 다른 컴퓨터 CPU는 1, 2, 3 순서로 기록할 수도 있다.





기존 파일 형식에선 문자 파일의 경우 아무런 예고도 없이 내용이 나온다. 왜냐하면 파일 확장자에 *.txt로 문자 파일임을 나타내기 때문이다. 계산해 보면 0~9, a~z, A~Z까지 숫자가 순서대로 할당되어 있다. 한글 완성형의 경우는 2바이트를 묶어 해석해야 한다. 이 때 상위/하위 바이트를 앞에 것으로 하느냐 뒤의 것으로 하느냐 정해야 하는데 문자의 경우 먼저 나온 것을 보고 뒤의 것을 해석하기 때문에 앞의 것을 상위 바이트로 본다. 16비트 정수 해석은 여기선 필요 없으니 무시한다.

※ 완성형의 경우 시작과 끝 글자의 순서가 뒤죽박죽인데 띨띨하게 완성형 만들 때 모든 한글을 반영하지 않았다. 약 2천 개만 등록했던 것이다. 그래서 MS사(社)에서 불쌍하다며 CP949란 보충 코드를 만들어 주다보니 이렇게 되었다.

유니코드 중에 UTF-8은 기존의 ANSI 문서 파일이 아님을 나타내기 위해서 첫 3바이트가 헤더 형태로 나온다. 그 다음은 아스키와 호환성을 유지하니 같다. 한글 코드가 3바이트로 할당 되어 있는데 이건 가변길이 코드이다. 1번 바이트의 특징을 보고 2번 바이트를 이어 해석하고, 2번째 바이트를 보고 3번 바이트를 이어 해석하는 방식이다. 그러다 보니 한글이 3바이트나 차지하게 되었다. 이건 유니코드를 공부해야 해석이 가능하다. 비트 패턴을 보고 해석해야 한다.

유니코드 중에 UTF-16 Little Endian 순서가 PC에서 표준인 것 같다. PC에선 16비트 정수의 경우 하위 바이트를 먼저 저장한다. 고로 문자 코드는 16비트 정수 해석과 정확히 일치한다. 기존 ANSI 파일이 아니며 UTF-16 Little Endian이란 헤더가 나온다. UTF-16 Big Endian과 정확히 반대 순서이다. 아스키(ASCII) 코드는 8비트에서 상위 바이트에 0을 더해 16비트로 그대로 확장했고, 한글의 경우 16비트 정수 해석 값이 그대로 문자 코드가 된다.

여기서 UTF-16 Big Endian과 UTF-8 가변 길이 코드를 비교해 보자. Big Endian은 우리가 숫자를 나열하는 방식과 같다. 좌측에 먼저 적는 숫자가 높은 자릿수이다. 낮은 자릿수는 가장 늦게 우측에 적는다. 사람이 기록하는 순서와 컴퓨터가 기록하는 순서가 같다. 이 방식이 인간에겐 더 자연스러운 순서이다.

가 = 1010 1100 - 0000 0000 = 1110 1010 - 1011 0000 - 1000 0000
힣 = 1101 0111 - 1010 0011 = 1110 1101 - 1001 1110 - 1010 0011

이제 알겠는가? UTF-8은 비트의 앞을 보고 뒤를 이어 해석하는 방식이다. 비트의 나머지 부분을 묶어 연결해서 UTF-16으로 만들어서 문자처리를 한다. 왜 이 짓을 하느냐? 아스키 코드와 호환성을 위해서다. 아스키 코드엔 옛날 구식 기계 동작을 제어하는 코드가 포함 되어 있어 그 코드를 피하기 위함이다. 일단 UTF-16으로 바꾸면 아주 쉽게 간단한 계산으로 한글의 초성, 중성, 종성을 분리해 낼 수 있다. 완성형은 이게 불가능하다.

첫 바이트 1110은 총 3바이트란 의미다. 1이 3번 나온다. 0은 그냥 구분용이다. 같은 방식으로 110일 경우는 총 2바이트, 11110일 경우는 총 4바이트란 의미다. 뒤에 이어지는 바이트 모두 10으로 시작한다. 만약 첫 비트가 0이면 그건 아스키 코드와 같다는 뜻이다.

대충 사용법은 알겠지? 16비트 정수 해석 순서를 바꾸려면 빈 셀에 계산식만 추가로 더 입력해 넣으면 된다.








인간처럼 똥 많이 싸고 쓰레기 많이 버리는 동물도 없을 거다.
불필요하게 많이 먹고, 불필요하게 많이 생산한다.
살기 위해 돈 벌어야 하고, 돈 벌려고 쓰레기를 만들어야 한다.
미래엔 진짜 1명만 일하고 9명이 백수인 세상이 올 것이다.
로봇(기계+컴퓨터)에 의한 복지국가 외엔 답이 없을 것이다.

댓글 없음:

댓글 쓰기