* 서론
PETYA 랜섬웨어는 MBR Locker 랜섬웨어 종류라 PETYA 랜섬웨어가 변조시키는 MBR을 분석 해봤다.
* 분석
0. 들어가기 전
(1) MBR의 Boot Code(부팅 하기 위한 코드)는 메모리의 0x7C00 주소에 풀려 코드를 읽기 시작한다.
(2) MBR 분석하기 위해 VMWARE + IDA32의 Remote Debugger을 활용했다.
1. 악성 MBR Code 설정 후 이동
![]() |
<그림 1. 악성 MBR 설정 후 이동> |
MEMORY:7C00 cli ; 인터럽트 플래그(IF) 비활성화
MEMORY:7C01 xor eax, eax
MEMORY:7C04 mov ss, ax
MEMORY:7C06 mov es, ax
MEMORY:7C08 mov ds, ax
MEMORY:7C0A mov sp, 7C00h ; SP = 0x7C00
MEMORY:7C0D sti ; 인터럽트 플래그 (IF) 활성화
MEMORY:7C0E mov 0x7C93, dl ; [0x7C93] = 0x80
MEMORY:7C12 mov ax, 20h ; AX = 0x20
MEMORY:7C18 mov bx, 22h ; BX = 0x22
MEMORY:7C1E mov cx, 8000h ; CX = 0x8000
; 0x20 만큼 반복하여 악성 MBR 및 그 이후 복사한 모든 데이터를 복사
MEMORY:7C21 call 0x7C38 ; 함수(악성 MBR 복사 함수) 이동
MEMORY:7C24 dec eax ; EAX -= 1
MEMORY:7C26 cmp eax, 0 ; if EAX == 0
MEMORY:7C2A jnz 0x7C21 ; if EAX == 0 is false 일 경우 0x7C21 점프
MEMORY:7C2C mov eax, dword ptr 0x8000 ; EAX = [0x8000]
MEMORY:7C30 jmp 0x8000 ; 0x8000 점프, 악성 MBR 코드로 이동
2. 악성 MBR 코드 복사 (Extended Read Sectors From Drive)
![]() |
<그림 2. 악성 MBR 코드 복사 과정> |
MEMORY:7C38 push eax
MEMORY:7C3A xor eax, eax
MEMORY:7C3D push dx
MEMORY:7C3E push si
MEMORY:7C3F push di
MEMORY:7C40 push eax
MEMORY:7C42 push ebx
MEMORY:7C44 mov di, sp
; Extended Read Sectors From Drive
MEMORY:7C46 push eax ; EAX setup
MEMORY:7C48 push ebx ; EBX setup
MEMORY:7C4A push es ; ES setup
MEMORY:7C4B push cx ; EC setup
MEMORY:7C4C push 1 ; 0x01 setup
MEMORY:7C4E push 10h ; 0x10 setup
MEMORY:7C50 mov si, sp
MEMORY:7C52 mov dl, 0x7C93 ; DL = 0x80
MEMORY:7C56 mov ah, 42h ; 'B' ; INT 13h Function 설정
MEMORY:7C58 int 13h ; Interrupt 발생
MEMORY:7C5A mov sp, di
MEMORY:7C5C pop ebx
MEMORY:7C5E pop eax
MEMORY:7C60 jnb 0x7C6A ; 호출에 성공 했을 경우 점프
; Reset Disk Drive Setup
MEMORY:7C62 push ax ; AX = 0x00 setup
MEMORY:7C63 xor ah, ah ; INT 13h Function 번호 설정
MEMORY:7C65 int 13h ; Interrupt 발생
MEMORY:7C67 pop ax
MEMORY:7C68 jmp 0x7C40 ; 0x7C40 점프
![]() |
<그림 3. INT 16h 중 Extended Read Sectors From Drive 호출 조건> |
![]() |
<그림 4. INT 16h 중 Extended Read Sectors From Drive 결과 값> |
설정 된 DAP 값은 다음과 같다.
(1) 빨간색 - DAP의 크기, 보통 16(0x10)
(2) 주황색 - 사용되지 않는 영역, 0x00
(3) 노란색 - 읽어올 Sector의 수 (Sector 당 0x200)
(4) 연두색 - 복사할 메모리 Buffer의 segment:offset
(5) 하늘색 - 읽어올 Sector의 번호 (0x200 * 0x22 = 0x4400 = 악성 MBR이 존재하는 영역)
Offset 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Value 10 00 01 00 00 80 00 00 22 00 00 00 00 00 00 00
해석하자면 Sector 22에 존재하는 악성 MBR 데이터를 0x8000으로 1 Sector 만큼(0x200) 복사한다.
![]() |
<그림 5. 다음 복사를 위한 연산 과정> |
MEMORY:7C6A add ebx, 1 ; EBX(0x0022) + 1 = 0x0023 (Sector 번호 증가)
MEMORY:7C6E adc eax, 0 ; EAX(0x0000) + CF(0x0) + 0 = 0
MEMORY:7C72 add cx, 200h ; CX(0x8000) + 0x0200 = 0x8200 ; (복사 할 메모리 Buffer 주소 증가)
MEMORY:7C76 jnb 0x7C7F
MEMORY:7C78 mov dx, es
MEMORY:7C7A add dh, 10h
MEMORY:7C7D mov es, dx
MEMORY:7C7F pop di
MEMORY:7C80 pop si
MEMORY:7C81 pop dx
MEMORY:7C82 pop eax
MEMORY:7C84 retn
3-1. 악성 MBR 내 화면 처리 (Set Video Mode, Select Active Display Page, Set Cursor Type)
![]() |
<그림 6. Set Video Mode, Select Active Display Page, Set Cursor Type 과정> |
![]() |
<그림 7. INT 10h 중 Set Video Mode> |
![]() |
<그림 8. INT 10h 중 Select Active Display Page> |
![]() |
<그림 9. INT 10h 중 Set Cursor Type> |
MEMORY:8A8A push bp
MEMORY:8A8B mov bp, sp
; Set Video Mode (AH = 0x00)
MEMORY:8A8D mov ax, 3 ; AX = 0x0300(AH = 0x03, AL = 0x00)
; INT 10h Function 번호 설정
MEMORY:8A90 int 10h ; Interrupt 발생
; Select Active Display Page (AH = 0x05)
MEMORY:8A92 mov ax, 500h ; AX = 0x0500(AH = 0x05, AL = 0x00)
; INT 10h Function 번호 설정
MEMORY:8A95 int 10h ; Interrupt 발생
; Set Cursor Type (AH = 0x01)
MEMORY:8A97 mov cx, 2607h ; CX = 0x2607(CH = 0x26, CL = 0x07)
MEMORY:8A9A mov ah, 1 ; AX = 0x0100 (AH = 0x01, AL = 0x00)
; INT 10h Function 번호 설정
MEMORY:8A9C int 10h ; Interrupt 발생
MEMORY:8A9E leave
MEMORY:8A9F retn
3-2. 악성 MBR 내 화면 처리 (Scroll Window Up, Set Cursor Position)
![]() |
<그림 10. Scroll Window Up, Set Cursor Position 과정> |
![]() |
<그림 11. INT 10h 중 Scroll Window Up> |
![]() |
<그림 12. INT 10h 중 Set Cursor Position> |
MEMORY:8AA8 push bp
MEMORY:8AA9 mov bp, sp
; Scroll Window Up (AH = 0x06)
MEMORY:8AAB mov bh, [bp+arg_0] ; BX = 0x0742, BH = 0x07, BL = 0x42
MEMORY:8AAE xor cx, cx ; CX = 0x0000, CH = 0x00, CL = 0x00
MEMORY:8AB0 mov dx, 184Fh ; DX = 0x184F, DH = 0x18, DL = 0x4F
MEMORY:8AB3 mov ax, 600h ; AX = 0x0600(AH = 0x06, AL = 0x00)
; INT 10h Function 번호 설정
MEMORY:8AB6 int 10h ; Interrupt 발생
; Set Cursor Position (AH = 02)
MEMORY:8AB8 xor bh, bh ; BX = 0x0020(BH = 0x00, BL=0x20)
MEMORY:8ABA xor dx, dx ; DX = 0x0000
MEMORY:8ABC mov ah, 2 ; AX = 0x0200(AH = 0x02, AL = 0x20)
; INT 10h Function 번호 설정
MEMORY:8ABE int 10h ; Interrupt 발생
MEMORY:8AC0 leave
MEMORY:8AC1 retn
4. 디스크 정보 추출 (Get Current Drive Parameters)
![]() |
<그림 13. Get Current Drive Parameters 과정> |
![]() |
<그림 14. INT 13h 중 Get Current Drive Parameters> |
MEMORY:8CF2 enter 8, 0
MEMORY:8CF6 push es
MEMORY:8CF7 mov dl, [bp+4] ; DX = 0x0080(DH = 0x00, DL = 0x80)
MEMORY:8CFA mov ah, 8 ; AX = 0x0880(AH = 0x08, AL = 0x80)
; INT 13h Function 번호 설정
MEMORY:8CFC int 13h ; Interrupt 발생
MEMORY:8CFE mov [bp-8], ah ; [bp-8] = Status (0 = No Error)
MEMORY:8D01 mov [bp-2], ch ; [bp-2] = CMOS Drive Type
; 01 = 360K, 02 = 1.2Mb, 03 = 720K, 04 = 1.44Mb
MEMORY:8D04 mov [bp-6], cl ; [bp-6] = sectors per track
MEMORY:8D07 mov [bp-4], dh ; [bp-4] = number of sides
MEMORY:8D0A pop es
MEMORY:8D0B cmp byte ptr [bp-8], 0 ; if [bp-8] == 0
MEMORY:8D0F jnz 0x8D36 ; [bp-8]가 No Error가 아닌 경우 점프
MEMORY:8D11 mov al, [bp-6]
MEMORY:8D14 and ax, 0C0h
MEMORY:8D17 shl ax, 2
MEMORY:8D1A mov cl, [bp-2]
MEMORY:8D1D sub ch, ch
MEMORY:8D1F or ax, cx
MEMORY:8D21 mov bx, [bp+6]
MEMORY:8D24 inc ax
MEMORY:8D25 mov [bx], ax
MEMORY:8D27 mov al, [bp-4]
MEMORY:8D2A inc al
MEMORY:8D2C mov [bx+2], al
MEMORY:8D2F mov al, [bp-6]
MEMORY:8D32 and al, 3Fh
MEMORY:8D34 jmp 0x8D42
MEMORY:8D36 mov bx, [bp+6]
MEMORY:8D39 xor al, al
MEMORY:8D3B mov word ptr [bx], 0
MEMORY:8D3F mov [bx+2], al
MEMORY:8D42 mov [bx+3], al
MEMORY:8D45 mov al, [bp-8]
MEMORY:8D48 leave
MEMORY:8D49 retn
5. 악성 MBR Boot Code 백업 (Extended Read Sectors From Drive)
![]() |
<그림 15. 악성 MBR Boot Code 복사 과정> |
MEMORY:8D6C cmp [bp+arg_E], 1
MEMORY:8D70 sbb al, al
MEMORY:8D72 and al, 0FFh
MEMORY:8D74 add al, 43h ; 'C'
MEMORY:8D76 mov [bp+var_2], al
MEMORY:8D79 mov [bp+var_6], 3
MEMORY:8D7D mov [bp+var_4], 0
MEMORY:8D81 mov bx, 55AAh
MEMORY:8D84 mov dl, [bp+arg_0] ; DX = 0x0080 (DH = 0x00, DL = 0x80)
MEMORY:8D87 mov si, [bp+arg_2]
MEMORY:8D8A mov ah, [bp+var_2] ; INT 13h Function 번호 설정
MEMORY:8D8D xor al, al ; AX = 0x4300(AH = 0x43, AL = 0x00)
MEMORY:8D8F int 13h ; Interrupt 발생
MEMORY:8D91 jnb 0x8D96
MEMORY:8D93 mov [bp+var_4], ah
설정 된 DAP 값은 다음과 같다.
(1) 빨간색 - DAP의 크기, 보통 16(0x10)
(2) 주황색 - 사용되지 않는 영역, 0x00
(3) 노란색 - 읽어올 Sector의 수 (Sector 당 0x200)
(4) 연두색 - 복사할 메모리 Buffer의 segment:offset
(5) 하늘색 - 읽어올 Sector의 번호 (0x200 * 0x0 = 0x0 = 악성 MBR Boot Code 영역)
Offset 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Value 10 00 01 00 60 77 00 00 00 00 00 00 00 00 00 00
6-1. 시간을 기반으로 입력값 처리하기 (Read System Clock Counter)
Low Level 시스템을 만들때 시간 값을 자주 활용 한다. Boot Level에서 입력값은 이벤트가 발생하지 않고 입력 Buffer에 쌓여있다가 Interrupt로 읽어온다. 능률적인 알고리즘을 위해 시간 값 중 Tickcount을 활용한다. 이전 값과 현재 값의 Tickcount를 체크했을 때 현재의 Tickcount가 더 작으면 입력이 되었다고 처리한다.
![]() |
<그림 16. Read System Clock Counter 과정> |
![]() |
<그림 17. INT 1Ah 중 Read System Clock Count> |
MEMORY:8BAC enter 2, 0
; Read System Clock Counter (AH = 0x00)
MEMORY:8BB0 mov ah, 0 ; AX = 0x0000 (AH = 0x00, AL = 0x00)
; INT 1Ah Function 번호 설정
MEMORY:8BB2 int 1Ah ; Interrupt 발생
MEMORY:8BB4 mov [bp-2], dx
MEMORY:8BB7 mov ax, [bp-2] ; AX (Return Value) = Low Tickcount
MEMORY:8BBA leave
MEMORY:8BBB retn
![]() |
<그림 18. Tickcount 비교 과정> |
MEMORY:8848 lea cx, [si+1] ; CX = 과거 Tickcount
MEMORY:884B cmp ax, cx ; AX = 현재 Tickcount
MEMORY:884D jbe 0x886D ; CX가 더 크면 입력값 Buffer 확인 함수로 이동
MEMORY:884F mov si, ax ; 현재의 TickCount 백업
6-2. 해골 ASCII ART 문자열 출력하기 (Scroll Page Up, Set Cursor Position, Write Text in Teletype Mode)
(1) 화면 초기화 및 화면 깜빡이게 만들기
![]() |
<그림 19. 반복으로 빨간색, 회색 출력 과정> |
MEMORY:8851 cmp byte ptr [bp-1], 1 ; if [bp-1] == 1
MEMORY:8855 jnz 0x885B ; false일 경우 점프
MEMORY:8857 mov al, 0F4h ; 화면 회색
MEMORY:8859 jmp 0x885D
MEMORY:885B mov al, 4Fh ; 화면 빨간색
MEMORY:885D push ax
MEMORY:885E call 0x887E ; 해골 ASCII ART 출력
MEMORY:8861 pop bx
MEMORY:8862 cmp byte ptr [bp-1], 1 ; Carry Flag 생성
MEMORY:8866 sbb al, al ; al -= al - Carry Flag
MEMORY:8868 neg al ; 0xFFFF가 나올 수 있기 때문에 2의 보수
MEMORY:886A mov [bp-1], al ; 저장
![]() |
<그림 20. Scroll Page Up, Set Cursor Position 과정> |
MEMORY:8AA8 push bp
MEMORY:8AA9 mov bp, sp
; Scroll Window Up (AH = 0x06)
MEMORY:8AAB mov bh, [bp+4] ; BX = 0x4FAA(BH = 0x4F, BL = 0xAA)
MEMORY:8AAE xor cx, cx ; CX = 0x0000
MEMORY:8AB0 mov dx, 184Fh ; DX = 0x184F(DH = 0x18, DL = 0x4F)
MEMORY:8AB3 mov ax, 600h ; AX = 0x0600(AH = 0x06, AL = 0x00)
; INT 10h Function 번호 설정
MEMORY:8AB6 int 10h ; Interrupt 발생
; Set Cursor Position (AH = 02)
MEMORY:8AB8 xor bh, bh ; BX = 0x0000
MEMORY:8ABA xor dx, dx ; DX = 0x0000
MEMORY:8ABC mov ah, 2 ; AX = 0x0220(AH = 0x02, AL = 0x20)
; INT 10h Function 번호 설정
MEMORY:8ABE int 10h ; Interrupt 발생
MEMORY:8AC0 leave
MEMORY:8AC1 retn
* 라인마다 다음과 같은 과정을 반복한다.
(2) Space 문자열로 밀어내기
![]() |
<그림 21. Space 문자열 출력 Calling Convention 과정> |
MEMORY:8889 push 20h ; 반복 횟수
MEMORY:888B push 20h ; 출력할 문자열 (Space)
MEMORY:888D call 0x87B8 ; 횟수만큼 Loop 돌며 출력
(3) 해골 ASCII ART 출력
![]() |
<그림 18. 해골 ASCII ART 문자열 출력 Calling Convention 과정> |
![]() |
<그림 22. Stack에 존재 하는 해골 ASCII ART> |
MEMORY:8892 push 9C42h ; 해골 ASCII ART가 있는 문자열 주소
MEMORY:8895 call 0x8736 ; 줄내림(0x0D, 0x0A) 까지면 출력 NULL(0x00)을 만나면 출력 중지
(4) 문자열 출력 (Write Text in Teletype Mode)
![]() |
<그림 23. Write Text in Teletype Mode 과정> |
![]() |
<그림 24. INT 10h 중 Write Text in Teletype Mode> |
MEMORY:8726 push bp
MEMORY:8727 mov bp, sp
; Write Text in Teletype Mode (AH = 0x0E)
MEMORY:8729 mov bx, 7 ; BX = 0x0007(BH = 0x00, BL = 0x07)
MEMORY:872C mov al, [bp+arg_0]
MEMORY:872F mov ah, 0Eh ; AX = 0x0E75(AH = 0x0E, AL = 0x75)
; INT 10h Function 번호 설정
MEMORY:8731 int 10h ; Interrupt 발생
MEMORY:8733 leave
MEMORY:8734 retn
6-3 "PRESS ANY KEY!" 처리를 위한 키보드 입력값 처리 (Get Keyboard Status)
![]() |
<그림 25. Get Keyboard Status 과정> |
![]() |
<그림 26. INT 16h 중 Get Keyboard Status> |
MEMORY:8AF2 enter 2, 0
MEMORY:8AF6 mov byte ptr [bp-2], 0
; Get Keyboard Status (AH = 0x01)
MEMORY:8AFA mov ah, 1 ; AX = 0x0101(AH = 0x01, AL = 0x01)
; INT 16h Function 번호 설정
MEMORY:8AFC int 16h ; Interrupt 발생
MEMORY:8AFE jz 0x8B04
MEMORY:8B00 mov byte ptr [bp-2], 1
MEMORY:8B04 mov al, [bp-2]
MEMORY:8B07 leave
MEMORY:8B08 retn
7. 경고 메시지 및 Tor 주소 와 같은 메시지 출력
(1) Sector에서 읽어 Tor 주소 및 Key 값 복사 (Extended Read Sectors From Drive)
![]() |
<그림 27. Extended Read Sectors From Drive 과정> |
; Extended Read Sectors From Drive
MEMORY:8D6C cmp [bp+arg_E], 1
MEMORY:8D70 sbb al, al
MEMORY:8D72 and al, 0FFh
MEMORY:8D74 add al, 43h
MEMORY:8D76 mov [bp+var_2], al
MEMORY:8D79 mov [bp+var_6], 3
MEMORY:8D7D mov [bp+var_4], 0
MEMORY:8D81 mov bx, 55AAh
MEMORY:8D84 mov dl, [bp+arg_0] ; DX = 0x0080(DH = 0x00, DL = 0x80)
MEMORY:8D87 mov si, [bp+arg_2]
MEMORY:8D8A mov ah, [bp+var_2]
MEMORY:8D8D xor al, al ; AX = 0x4200(AH = 0x42, AL = 0x00)
; INT 13h Function 번호 설정
MEMORY:8D8F int 13h ; Interrupt 발생
설정 된 DAP 값은 다음과 같다.
(1) 빨간색 - DAP의 크기, 보통 16(0x10)
(2) 주황색 - 사용되지 않는 영역, 0x00
(3) 노란색 - 읽어올 Sector의 수 (Sector 당 0x200)
(4) 연두색 - 복사할 메모리 Buffer의 segment:offset
(5) 하늘색 - 읽어올 Sector의 번호 (0x200 * 0x36 = 0x6C00 = 악성 MBR 중 Tor 주소 및 Key가 존재 하는 곳)
Offset 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Value 10 00 01 00 22 77 00 00 36 00 00 00 00 00 00 00
(2) 문자열 출력
![]() |
<그림 28. 문자열 출력 과정> |
![]() |
<그림 29. Stack 내 경고 메시지> |
MEMORY:85AB add sp, 0Ch
MEMORY:85AE push 994Ah ; 문자열이 들어있는 Stack 주소
MEMORY:85B1 call 0x8736 ; 문자열 출력
![]() |
<그림 30. Tor 주소 및 Key 출력> |
![]() |
<그림 31. Stack내 Tor 주소 및 Key> |
MEMORY:85C5 pop bx
MEMORY:85C6 lea ax, [bp-223h] ; Tor 주소 및 Key가 들어있는 Stack
MEMORY:85CA push ax
MEMORY:85CB call 0x8736 ; 문자열 출력
8. Key 검증
(1) NULL을 0x37 XOR 데이터를 복사 (Extended Read Sectors From Drive)
![]() |
<그림 32. Extended Read Sectors From Drive> |
; Extended Read Sectors From Drive (AH = 0x42)
MEMORY:8D7D mov [bp+var_4], 0
MEMORY:8D81 mov bx, 55AAh
MEMORY:8D84 mov dl, [bp+arg_0] ; DX = 0x0080(DH = 0x00, DL = 0x80)
MEMORY:8D87 mov si, [bp+arg_2]
MEMORY:8D8A mov ah, [bp+var_2]
MEMORY:8D8D xor al, al ; AX = 0x4200(AH = 0x42, AL = 0x00)
; INT 13h Function 번호 설정
MEMORY:8D8F int 13h ; Interrupt 발생
설정 된 DAP 값은 다음과 같다.
(1) 빨간색 - DAP의 크기, 보통 16(0x10)
(2) 주황색 - 사용되지 않는 영역, 0x00
(3) 노란색 - 읽어올 Sector의 수 (Sector 당 0x200)
(4) 연두색 - 복사할 메모리 Buffer의 segment:offset
(5) 하늘색 - 읽어올 Sector의 번호 (0x200 * 0x36 = 0x6C00 = 악성 MBR 중 NULL XOR 데이터가 있는 곳)
Offset 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
Value 10 00 01 00 DE 72 00 00 37 00 00 00 00 00 00 00
![]() |
<그림 33. 깨져있는 NULL XOR 데이터> |
XOR 데이터가 깨져 있으나 크게 의미가 없다.
XOR 데이터를 이용해서 + 다른 암호화를 연계해서 사용하기 때문이다.
(2) Key 검증 알고리즘
![]() |
<그림 34. 입력된 문자열을 기반으로 암호화하는 암호화 알고리즘 과정> |
입력된 문자열을 Salsa20 이란 스트림 암호화 방식 중 "expand 32-byte k" 으로 암호화가 진행 된다.
암호화 과정 중 NULL XOR 한 데이터와 연계 되는데, 최종 결과 값이 0x37의 값이 20개 이상 나와야 한다.
![]() |
<그림 35. 검증 알고리즘 과정> |
암호화 된 결과값이 0x37이 20번 이상 반복되어야 이후 복호화 과정을 진행하게 된다.
![]() |
<그림 36. 에러메시지 출력 과정> |
![]() |
<그림 37. Stack 내 존재 하는 에러메시지> |
MEMORY:8632 push 9BFCh ; 에러메시지 문자열이 위치한 주소
MEMORY:8635 call 0x8736 ; 에러메시지 출력
실패 시, 에러메시지를 출력하고 반복
성공 시, 정상 MBR를 복구 Reboot 후 정상 MBR로 진행할 수 있도록 하는 것 같다.
(귀찮아서 여기까진 생략)
* 결론
EXE, DLL보다 더 어려운 구조를 가지고 있으며 사실 핵심은 악성 MBR 부분인 것 같다. 분석의 한계가 있는 Boot Loader 부분이라 더미코드와 실제코드의 조합이 상당히 짜증났다.
구조 자체는 복구가 가능한 랜섬웨어. XOR로 인코딩하여 모든 정보를 가지고 있다. 좀 허술한 랜섬웨어인데, 일반인들한테는 아주 죽을맛인 랜섬웨어가 되시겠다.
그리고 암호 쪽은 암호 전문가가 아니라서 더 이상 분석은 생략하도록 하겠다.
댓글 없음
댓글 쓰기