본문 바로가기

About../Security

네이버 만화 캡춰 방지

네이버 만화 캡쳐 방지 분석

   

이 문서에는 네이버 만화를 볼 때 화면 캡쳐 프로그램이 동작을 하지 않게 되는데

어떠한 방식으로 구현이 되었는지에 대해 간단하게 리버싱하는 방법에 대해 적을까합니다.

( 지금까지는 CrackMe를 크랙해왔지만 실제로 리버싱하는 경우는 이번이 처음이라고 할 수 있겠습니다. )

   

갑자기 화면 캡쳐 방지에 대해서 리버싱을 하려고 하면 상당히 어렵다고 생각 할 수 있지만

네이버쪽에서 사용하는 방지방법은 간단한 방법이므로 이에 대해 설명할까 합니다.

( 불법적인 문제가 있는것에 한해서 뚫는 방법에 대해서 직접적으로 언급하지는 않을 것 입니다. )

그러면 실제로 어떤식으로 막히는지 확인하기 위해 사이트에 접속하여 무료 맛보기를 클릭합니다.

( 죄송하지만 제가 돈이 없어서 유료로 열 수가 없습니다.ㅠㅠ )

   

클릭을 하면 새로운 창이 뜨면서 만화 뷰어 ActiveX를 설치하게 되는데 이 때부터 화면 캡쳐가 되지 않게 됩니다.

만화 뷰어가 실행 된 상태에서 오픈 캡쳐를 실행해보면 실행은 되지만 바로 종료가 되게 됩니다.

   

자!..이제부터 리버싱을 시작할 준비를 합니다. 하지만 아무 생각없이 디스어셈블링하면 안됩니다.

오픈 캡쳐가 실행되었다가 종료되었다면 초보적인 프로그래머라도 어떠한 방식으로 하였는지 유추 할 수 있기 때문입니다.

바로 생각나는 것은 FindWindow함수를 이용해 ClassName이나 Caption을 검색하거나 또는 ProcessName를 검색하여

오픈 캡쳐나 다른 캡쳐 프로그램이라면 해당 프로세스를 종료시켜버린다라고 생각날 수 있을 것 입니다.

( 만약에 이러한 생각이 나지 않는다면 낭패.. 아니 오히려 이러한 생각이 안 날 수도 있습니다.

설마 이렇게 허접한 방식으로 했을까??라는 생각도 할 수 있기 때문에.. )

여기서 제가 말씀드리고 싶은 것은 어떠한 대상을 리버싱하는 것은 그 프로그램이 어떠한 방식으로 구현되었는지

알지 못 하기 때문이며, 그러한 기능을 내 프로그램에 넣고 싶거나 또는 그 방식만을 알고 싶을 때 입니다.

이러한 경우 무턱되고 디버거를 열어 코드를 하나하나씩 리버싱을 하는 짓을 해서는 안됩니다.

리버싱을 하기전에 리버싱하는 대상의 기능이 어떤식으로 구현되었을지 모를지라도 그 기능을 사용하기 위해

사용되는 API ( 자주 사용하지 않는 API일수록 좋습니다. )를 스스로 유추해보는 것.

즉, 아마 이러한 함수를 써서 이러이러한 방식으로 했을꺼 같다라는 생각을 가지고 리버싱을 해야 합니다.

물론 유추자체가 불가능한 경우라면 그 대상의 기능이 디스어셈블링 된 코드에서 찾아서 하나씩 분석을 해야 할 것 이며

그 기능이 있는 코드를 찾는데에도 시간이 걸리게 됩니다.

현재 FindWindow, ProcessName을 이용해 화면 캡쳐 방지를 할 것이라는 가정을 세웠으며 이러한 가정을 통해서 리버싱을 해보겠습니다.

( 계속적으로 강조하지만 이러한 가정이 없다면 아무리 간단한 기능이라도 리버싱하기가 힘들어집니다.

가정을 잘 유추하려면 많은 프로그램을 만들어보고 분석해 볼 것을 권장합니다. )

   

올리 디버거를 실행시키고 iexplore.exe를 엽니다. ( Attach를 시켜도 되나 Open을 할 것을 권장합니다. )

   

   

iexplore.exe에 디스어셈블링 된 코드를 보실 수 있으며 F9를 눌러 실행 시킨 후 네이버 만화 사이트로 이동합니다.

중간중간에 Exception이 발생 수 있으나 F9를 눌러 무시하고 계속 실행하시면 됩니다.

   

만화 사이트까지 이동하였다면 무료 맛보기를 클릭합니다.

이제 새로운 창이 나타나면서 화면 캡쳐가 되지 않게 됩니다.

여기서 중요한 부분은 화면 캡쳐 방지 루틴이 ActiveX에 있을지 아니면 ActiveX에서 화면 캡쳐 방지 모듈을 따로 실행하는지 대해서 알아보아야 합니다.

우선 작업 관리자를 열어봅니다.

   

   

새로 추가 된 프로세스가 있는지 찾아보면 화면 캡쳐 방지와 관련 된 모듈은 없다는것을 확인 할 수 있습니다.

( 사실 원칙적으로 네이버 만화 뷰어가 떠있으므로 저 화면 자체도 캡쳐가 불가능하므로 캡쳐가 가능하도록 수정해 놓은 상태로 캡쳐하고 있습니다. )

그렇다면 ActiveX일것이고 캡쳐 방지 모듈이 어떠한 ActiveX인지 찾아내야 합니다.

이럴 경우 실행중인 프로세스의 목록을 모두 보여주고 어떠한 모듈이 로드되었는지 분석 할 수 있는 도구인

ProcessExplorer를 사용하게 됩니다. ( http://technet.microsoft.com/en-us/sysinternals/default.aspx 에서 받으실 수 있습니다. )

   

위와 같이 바로 발견 할 수 있습니다. 어느 회사에서 만든지까지 적혀있습니다.

두 개의 ActiveX가 있는데 파일 이름만 본다면 NHNComicCore.dll에서 그 역할을 할꺼 같습니다

   

   

올리디버거를 열고 위에 메뉴를 선택합니다. 모듈 이름이 짤려서 나오게 되는데 메뉴를 위에 처럼 선택 후

코드가 나오면 다시 오른쪽 버튼을 눌러 Copy to executable - Selection를 선택하면 모듈 전체 경로를 볼 수 있습니다.

오른쪽 메뉴인 Search for - All intermodular calls를 눌러 import된 API목록을 봅니다.

웁스...그런데 FindWindow API가 검색되지 않습니다. 즉 API를 안 썼다는 말이 되겠죠. ( 물론 동적일 수도 있지만 무시하겠습니다. )

하지만 제 감으로 생각하기엔 아무리 봐도 Caption이나 ClassName를 이용해 검사를 할꺼 같습니다.

그렇다면 GetWindowText나 GetClassName으로 브레이크 포인터를 모두 걸어보겠습니다.

오른쪽 메뉴인 Set breakpoint on every call to xxxxxxx 를 누르시면 해당 함수를 사용하는 모든 코드에 브레이크 포인트를 걸 수 있습니다.

역시나 브레이크 포인트를 걸자마자 바로 반응하는 것을 볼 수 있습니다.

   

   

코드를 보시면 GetWindowTextA와 GetClassNameA를 쓰고 있는데 F9를 누를때 마다

계속해서 현재 열려있는 윈도우 핸들에 대해 값을 가져오고 있는 것을 볼 수 있습니다.

이쯤 왔다면 코드를 해석할 필요도 없이 어떠한 방법으로 하는지 대략적으로 느낌이 오실 것 입니다.

그래도 조금만 간략하게 분석해보면..

068625F2 8B1D 44A29106 MOV EBX,DWORD PTR DS:[<&USER32.PostMessa>; USER32.PostMessageA
068625F8 56 PUSH ESI ; 여기서부터 루프가 시작됩니다.
068625F9 FF15 B4A29106 CALL DWORD PTR DS:[<&USER32.IsWindow>] ; USER32.IsWindow
068625FF 85C0 TEST EAX,EAX
06862601 0F84 50010000 JE NHNCom_1.06862757 ; IsWindow 실패 시 0x06862757로 점프
06862607 B9 19000000 MOV ECX,19
0686260C 33C0 XOR EAX,EAX
0686260E 8D7C24 14 LEA EDI,DWORD PTR SS:[ESP+14]
06862612 6A 63 PUSH 63
06862614 F3:AB REP STOS DWORD PTR ES:[EDI]
06862616 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+18]
0686261A 50 PUSH EAX
0686261B 56 PUSH ESI
0686261C FF15 FCA19106 CALL DWORD PTR DS:[<&USER32.GetWindowTex>; WindowText의 값을 가져옵니다.
06862622 8D4C24 78 LEA ECX,DWORD PTR SS:[ESP+78]
06862626 6A 63 PUSH 63
06862628 51 PUSH ECX
06862629 56 PUSH ESI
0686262A FF15 00A29106 CALL DWORD PTR DS:[<&USER32.GetClassName>; ClassName를 가져옵니다.
06862630 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14] ; WindowText값을 EDX에 저장합니다.
06862634 52 PUSH EDX
06862635 E8 0A5F0300 CALL NHNCom_1.06898544
0686263A 83C4 04 ADD ESP,4
0686263D 85F6 TEST ESI,ESI
0686263F 0F84 F0000000 JE NHNCom_1.06862735
06862645 A1 A0F19206 MOV EAX,DWORD PTR DS:[692F1A0]
0686264A 33ED XOR EBP,EBP
0686264C 85C0 TEST EAX,EAX
0686264E 7E 5F JLE SHORT NHNCom_1.068626AF
06862650 33FF XOR EDI,EDI
06862652 A1 00129606 MOV EAX,DWORD PTR DS:[6961200] ; 여러 스크린 캡쳐 프로그램의 Title값을 문자열 배열로 갖고 있으며 배열의 시작주소를 가져옵니다.
06862657 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14]
0686265B 8D0C07 LEA ECX,DWORD PTR DS:[EDI+EAX] ; 배열에서 비교할 스크린 캡쳐 프로그램 Title을 가져옵니다.
0686265E 51 PUSH ECX
0686265F 52 PUSH EDX
06862660 E8 8B560300 CALL NHNCom_1.06897CF0 ; GetWindowText로 가져온 값과 배열에 값을 서로 비교합니다.
06862665 83C4 08 ADD ESP,8
06862668 85C0 TEST EAX,EAX
0686266A 74 36 JE SHORT NHNCom_1.068626A2
0686266C 8B8424 E0000000 MOV EAX,DWORD PTR SS:[ESP+E0]
06862673 50 PUSH EAX ; 프로세스 이름이 들어갑니다.
06862674 56 PUSH ESI
06862675 E8 F6000000 CALL NHNCom_1.06862770
0686267A 83C4 08 ADD ESP,8
0686267D 85C0 TEST EAX,EAX
0686267F 75 21 JNZ SHORT NHNCom_1.068626A2
06862681 50 PUSH EAX
06862682 50 PUSH EAX
06862683 6A 10 PUSH 10 ; WM_CLOSE
06862685 56 PUSH ESI ; 핸들값
06862686 FFD3 CALL EBX ; PostMessage를 호출합니다.
06862688 6A 00 PUSH 0
0686268A 6A 00 PUSH 0
0686268C 6A 02 PUSH 2 ; WM_DESTROY
0686268E 56 PUSH ESI
0686268F FFD3 CALL EBX ; PostMessage를 호출합니다.
06862691 6A 00 PUSH 0
06862693 6A 00 PUSH 0
06862695 6A 12 PUSH 12 ; WM_QUIT
06862697 56 PUSH ESI
06862698 FFD3 CALL EBX ; PostMessage를 호출합니다.
0686269A C74424 10 010000>MOV DWORD PTR SS:[ESP+10],1
068626A2 A1 A0F19206 MOV EAX,DWORD PTR DS:[692F1A0]
068626A7 45 INC EBP
068626A8 83C7 64 ADD EDI,64
068626AB 3BE8 CMP EBP,EAX
068626AD ^7C A3 JL SHORT NHNCom_1.06862652
068626AF A1 A4F19206 MOV EAX,DWORD PTR DS:[692F1A4]
068626B4 33FF XOR EDI,EDI
068626B6 85C0 TEST EAX,EAX
068626B8 7E 38 JLE SHORT NHNCom_1.068626F2
068626BA 8B0CBD 00EE9206 MOV ECX,DWORD PTR DS:[EDI*4+692EE00]
068626C1 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14]
068626C5 51 PUSH ECX
068626C6 52 PUSH EDX
068626C7 E8 A40B0400 CALL NHNCom_1.068A3270
068626CC 83C4 08 ADD ESP,8
068626CF 85C0 TEST EAX,EAX
068626D1 75 15 JNZ SHORT NHNCom_1.068626E8
068626D3 8B8424 E0000000 MOV EAX,DWORD PTR SS:[ESP+E0]
068626DA 50 PUSH EAX
068626DB 56 PUSH ESI
068626DC E8 8F000000 CALL NHNCom_1.06862770
068626E1 83C4 08 ADD ESP,8
068626E4 85C0 TEST EAX,EAX
068626E6 74 73 JE SHORT NHNCom_1.0686275B
068626E8 A1 A4F19206 MOV EAX,DWORD PTR DS:[692F1A4]
068626ED 47 INC EDI
068626EE 3BF8 CMP EDI,EAX
068626F0 ^7C C8 JL SHORT NHNCom_1.068626BA
068626F2 A1 A8F19206 MOV EAX,DWORD PTR DS:[692F1A8]
068626F7 33FF XOR EDI,EDI
068626F9 85C0 TEST EAX,EAX
068626FB 7E 38 JLE SHORT NHNCom_1.06862735
068626FD 8B0CBD 40EE9206 MOV ECX,DWORD PTR DS:[EDI*4+692EE40]
06862704 8D5424 78 LEA EDX,DWORD PTR SS:[ESP+78]
06862708 51 PUSH ECX
06862709 52 PUSH EDX
0686270A E8 610B0400 CALL NHNCom_1.068A3270
0686270F 83C4 08 ADD ESP,8
06862712 85C0 TEST EAX,EAX
06862714 75 15 JNZ SHORT NHNCom_1.0686272B
06862716 8B8424 E0000000 MOV EAX,DWORD PTR SS:[ESP+E0]
0686271D 50 PUSH EAX
0686271E 56 PUSH ESI
0686271F E8 4C000000 CALL NHNCom_1.06862770
06862724 83C4 08 ADD ESP,8
06862727 85C0 TEST EAX,EAX
06862729 74 30 JE SHORT NHNCom_1.0686275B
0686272B A1 A8F19206 MOV EAX,DWORD PTR DS:[692F1A8]
06862730 47 INC EDI
06862731 3BF8 CMP EDI,EAX
06862733 ^7C C8 JL SHORT NHNCom_1.068626FD
06862735 6A 02 PUSH 2
06862737 56 PUSH ESI
06862738 FF15 B0A29106 CALL DWORD PTR DS:[<&USER32.GetWindow>] ; GetWindow(윈도우 핸들, GW_HWNDNEXT);
0686273E 8BF0 MOV ESI,EAX
06862740 85F6 TEST ESI,ESI
06862742 ^0F85 B0FEFFFF JNZ NHNCom_1.068625F8 ; 다음 윈도우를 읽어왔다면 계속 루프를 돕니다.
06862748 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10]
0686274C 5F POP EDI
0686274D 5E POP ESI
0686274E 5D POP EBP
0686274F 5B POP EBX
06862750 81C4 CC000000 ADD ESP,0CC
06862756 C3 RETN
06862757 33F6 XOR ESI,ESI
06862759 ^EB DA JMP SHORT NHNCom_1.06862735
0686275B 5F POP EDI
0686275C 5E POP ESI
0686275D 5D POP EBP
0686275E B8 02000000 MOV EAX,2
06862763 5B POP EBX
06862764 81C4 CC000000 ADD ESP,0CC
0686276A C3 RETN                
        

   

내부적으로 스크린 캡쳐 프로그램에 Title를 문자열 배열로 선언해두었으며 각 문자열 버퍼는 0x64크기를 가지게 됩니다.

06862657 8D5424 14 LEA EDX,DWORD PTR SS:[ESP+14] 에서 현재 떠있는 윈도우들의 Title문자열 값이 들어가는 것을

확인 할 수 있으며 LEA ECX,DWORD PTR DS:[EDI+EAX]에서 내부적으로 DB형식으로 갖고 있는 스크린 캡쳐 Title값을 가져오게 되며

해당 주소 밑에 코드부터 문자열을 서로 비교하게 됩니다.

다음과 같이 브레이크 포인트를 걸어놓으시면 쉽게 확인 할 수 있습니다.

   

주의하셔야 할 점은 분석을 하실 때 LEA ECX,DWORD PTR DS:[EDI+EAX]부분에서 "오픈 캡쳐"라는 단어가 나오지 않게 되는데

이것은 올리디버거에서 간혹 한글을 제대로 보여주지 못 해서 발생하는 문제입니다.

해당 코드에서 브레이크 포인트를 걸어놓고 지속적으로 F9를 눌러 EDI값이 0x258이 되면은 ECX는 0x058542D6라는 주소값을

가지게 되고 Ctrl+G를 눌러 메모리값을 따라가면 디스어셈블링 된 코드가 나오는데 이것은 실질적으로 문자열로 사용하기 때문에

Code가 아닌 Data가 되게 됩니다.

   

코드는 분석할 필요가 없지만 만약에 분석을 하게 된다면 생각했던것보다 복잡 할 수도 있습니다.

하지만 GetWindowTextA 브레이크 포인트를 걸음으로써 분석을 하지 않아도 쉽게 리버싱이 가능하였습니다. ( 짐작이 가능. )

분석을 좀 더 하다보면 꼭 화면 캡쳐 프로그램뿐만 아니라 원격 제어 프로그램 또한 방지하려고 한다는 것과

캡쳐와 관련 된 프로그램의 DB가 상당량이 되는 것을 알 수 있습니다.

   

참고 삼아 말씀드리자면 다음 문서에서는 화면 캡쳐 방지 중 BitBlt를 훅하는 방식을 이용하는 방지 프로그램을

분석할 생각이었지만 해당 사이트가 네이버와 비슷한 방식으로 바뀌는 바람에 계획이 취소되었습니다.ㅠㅠ

   

지금까지 문서를 작성하면서 어느정도 초중급 정도에 내용만 올렸지만 다음 문서부터는 문서에 난이도가 조금씩 올라갈 것 입니다.

끝.

   

   문제시 삭제 하겠습니다.

Microsoft Office OneNote 2007을 사용하여 작성했습니다.
모든 노트 및 정보를 한 곳에서 볼 수 있습니다.