340 likes | 494 Views
Hacker Debugging Uncovered Chapter 9:Hashing and How to Overcome it Chapter 10:Popular Protection Mechanisms Used in Demo Versions. 2008.3.10 이동현. Chapter 9: Hashing and How to Overcome it. Intent. 1980 년대 후반 복잡한 패스워드 암호화 에 대한 응용프로그램 개발자의 첫 번째 시도 . 그러나 최신의 디버거와 디스어셈블러로 무장한 크래커 들에겐 불충분 .
E N D
Hacker Debugging UncoveredChapter 9:Hashing and How to Overcome itChapter 10:Popular Protection Mechanisms Used in Demo Versions 2008.3.10 이동현
Intent • 1980년대 후반 복잡한 패스워드 암호화 에 대한 응용프로그램 개발자의 첫 번째 시도. • 그러나 최신의 디버거와 디스어셈블러로 무장한 크래커 들에겐 불충분. • 해시함수의 사용 시도 조차 불충분. • 크래커들은 유효한 패스워드 입력을 위해 실제 패스워드를 찾는 대신 결과 해쉬값을 취함. • Listing 9.1. The evolution of protection mechanisms • if ((s==ch) != "KPNC") cout << "password fail" << endl; //Old variant • if (hashe(&pasw[0]) != 0x77) cout << "password fail" << endl; //New variant • 둘 다 하나의 조건 분기를 변경 함으로서 충분하다.( 크랙 가능 )
부실한 보호 메커니즘의 이해 • 보안 개발자들의 부주의함을 이해하기 위해서 이들을 크랙 해보자. • 모순되게도, 대부분의 프로그램들이 이런 순진한 방식으로 보호가 되어있다. • 왜 프로그래머들은 좋은 의도를 가진 해커들의 조언을 듣고 자신의 실수를 고치려고 하지 않는지 참 수수께끼다. • 잘 디자인 되고 구현된 보호 메커니즘은 비용이 많이 든다. 그리고 잠재적 고객의 구입을 증가시키지도 않는다. • 어떠한 보호 메커니즘도 뚫린다. 유명하면 유명할수록 더 빠르게.
Crack02.exe • 기존방법으로 스트링을 찾아보자.. 안 보인다.. • 음.. 좀더 자세히 살펴보면.. 리소스 안에 위치하고 있군.. • 그렇다면 LoadString 로 스트링을 불러왔겠군. • crack02.exe 를 dependency 로 추적. • 음? 이놈이 LoadString 를 임포트 하지 않네? • 의존하는 dll을을 추적해보자.. CString 가 호출하는군.. • 음 MFC42.dll 의 임포트 테이블을 덤프 해보니 심볼은 없고 Ordinal 만 있다. • 맵 파일을 참고하자. MFC42.map • LoadStringA 의 주소로 툴을 이용해서 가보자. • 코드의 생김이 함수 호출과 비슷하다. • 자 이주소 0x5F404042 - 0x5F400000(loaded address) = 0x4042 ( map 파일 확인.. ) • 모든 것은 IDA-Pro 를 이용함으로써 쉽게 해결. • 이로서 Protection mechanism 의 시작지점을 찾았다. • 마지막 점프 지점을 무조건 점프 명령으로 교체함으로써 손쉽게 Crack 되었다.
Crack03.exe • Crack02 는 너무나 쉽게 뚫린다.( 단지 한 바이트의 수정으로 ) • 이 Protection Mechanism 을 개선 해보자. • 패스워드를 프로그램 자체내부에 암호화 해서 내장한다. • 툴을 이용해서 실행파일에 암호화된 패스워드를 저장하는 것도 가능하지만 직접 소스 코드에 암호화된 패스워드를 넣자.
Crack03.exe 계속 • 마지막 jz 를 무조건 분기로 교체. • 결과적으로 어떤 패스워드를 넣던지 프로그램은 받아 들인다. • 그러나 정상적으로 동작하지 않음.
Crack03.exe 계속 • 입력된 패스워드가 사용되는 곳에 집중하자. • 401291 번지가 핵심 SecretString[pTxt++] = SecretString[pTxt] ^ Password[pPsw++];
Crack03.exe - 계속 • SecretString[pTxt++] = SecretString[pTxt] ^ Password[pPsw++]; • 잘 알려진 Vernam cipher 이다. • 소스 참고 • if (mult(hashe(&pasw[0]),hashe(&pasw[0]))!=504) s0.LoadString(IDS_WRONG); • 이 암호 체크 로직을 분석해 내야 한다.
Chapter 10:Popular Protection Mechanisms Used in Demo Versions
어떤 것들? • Limiting the Functionality • Limiting the Term of Use • Limiting the Number of Startups • The Nag Screen • The Key File
Limiting the Functionality • Cracking the program that requires registration doesn’t cause any difficulties. • The demon version VS fully functional version. • Physically missing. • Cracking is impossible. • Reconstruction of the missing code? • You should study the algorithm of interaction between the remaining and the missing code. • Usually the required code is physically present but never gains control.
Crack0d.exe • 아주 간단한 시도 • 프로그램 제작 시 리소스 편집기에서 활성화 옵션을 비활성화 한 경우 • 리소스 편집기로 해당 exe 파일을 열어 해당 컨터롤 활성화.
Crack0e.exe • Crack01 보다 까다로운 경우. • 컨터롤의 활성화 여부와 상관없이 내부 플래그를 이용해서 등록 여부를 검증. • 외부에서 리소스 편집기로 컨터롤만 활성화 시키는 걸로는 정상적인 동작을 수행할 수 없다. • 리소스 편집기로 컨터롤 활성화 불가능. • 프로그램 시작 시 API 를 이용한 비 활성화. • Customizer 등으로 활성화 시도 후 아래 절차 시도.
Crack0e.exe - 계속 • 시도 • “This is an illegal.. “ 메시지 검색. • 프로그램을 제어하는 변수를 찾는다. • Esi 는 클래스의 인스턴스이고, 원하는 변수도 같은 방법으로 초기화. • 컴파일러의 최적화 등의 경우 아주 어려운 작업이나, 많은 경우 효과적이다. • Esi + • 제어 변수를 찾아서 수정 후 수행. • 성공!!60 • 어떤 보호 알고리즘의 분석 없이 한 바이트의 수정만으로 Crack 성공. • 기존의 보호 알고리즘 등은 모두 정상적으로 동작한다. • 광범위하게 알려지고 사용되어지는 방법. • 엄청난 양의 코드를 분석하고 회피를 위해 점프하는 등의 기법에 비해 훨씬 쉽다.
Crack0f.exe • Crack0e 예제의 경우 하나의 제어 변수만 존재 했었다. • 그러나 이러한 제어변수가 여러 개로 구성되고, return SomeResult*(!FlagReg1 ^ FlagReg2); 와 같은 로직으로 체크가 된다면 기존의 시도는 실패한다. • 다행히 프로그래머들은 게을러서 이러한 바이트들을 신중하게 관리하지 않는다.
Crack0f.exe - 계속 • 첫번째 시도. • EnableWindow 를 찾는다. • 두군 데의 컨터롤 변수를 수정한다. • 버튼은 활성화 되었으나 정상 동작하지 않음. • 또 다른 원인이 어딘가 존재한다. • 다행히 컴파일러는 이런 조각들은 가까이 배치하는 경향이 있다. – 이는 Cracker 의 일을 줄여준다. • 주위의 다른 변수의 값을 변경해 보았다. • 빙고~ . 제대로 동작하는 결과 출력. • 보안 알고리즘은 세 개의 플래그를 사용하는 로직 이었다. • S0.SetAt(0, s0[0]*(!RegFlag_1^RegFlag_3)); • 제 4, 5의 제어 변수가 없다고 어떻게 장담하는가? • 보통 제어 변수는 전역 변수이고, 잘 설계된 OOP 언어에서는 이런 변수는 거의 없다. • 모든 건 개발자가 게으르다는 데 있다.
Crack10.exe • 존재하는 기능을 막아둔 프로그램의 Crack 은 상대적으로 쉽다. • 이전과는 달리 demo 버전의 프로그램에서는 특정 기능의 코드가 물리적으로 존재하지 않을 때 어떻게 하는가? • 또한 컴파일러는 사용되지 않는 코드는 제거해 버린다. • 결국 최종 실행파일에 해당 기능의 코드가 존재할 가능성은 없다. • 보안개발자가 행복해지고, Cracker 가 불행해 지는 순간이다. • 그러나 이 프로그램의 경우 해당 텍스트 버퍼에 접근이 가능하다면 새로운 기능을 추가 하는건 어렵지 않다. BOOL CCRACK10Doc::OnSaveDocument(LPCTSTR lpszPathName) { // TODO: Add your specialized code here and/or call the base class AfxMessageBox("This is a limited version. Please purchase fully functional one"); return 0; // AfxMessageBox("OK"); // return CDocument::OnSaveDocument(lpszPathName); }
Crack10.exe - 계속 • 문제점 • PE파일에 새로 작성한 코드를 삽입하는 일은 아주 어렵다. • 이전에 축적된 DOS 와 이전 Windows 에서의 지식은 쓸모가 없다. • 그러나, Windows 는 DLL을 통한 코드의 수정이라는 방법이 있다. • 고려 사항들. • 프로그램에 Import 된 함수들을 탐색 할 수 있어야 한다. – IDA 의 rdata 섹션. • 새로운 코드를 파일에 삽입해야 한다. • 다행히도 실행파일(DLL)내에는 충분한 공간들이 존재. • 컴파일러는 최적화를 통해서 함수 경계에 0x10바이트의 경계를 둠. • 원하는 함수를 호출하기 위해선 GetProcAddress 함수가 필요. • 이 함수가 import 되어 있지 않기 때문에 Import table 의 수정이 필요하다. • Import table 의 수정. • 컴파일러는 일반적으로 PE파일 내에 많은 여유공간을 만든다. – 결국 추가 코드 삽입이 어렵지 않음. • 이를 위해 PE file Format 의 숙지 필요.
Crack10.exe - 계속 • Import section 을 찾는다. • Import section 의 구조. – 참고1, 참고2 • 이 section 의 첫번째 데이터는 IMAGE_IMPORT_DESCRIPTER 의 배열. • 첫 두 바이트는 RVA 에 대한 포인터.(IMAGE_THUNK_DATA) • .. 좀더 자세한 내용은 참고를 참고. • 툴을 이용하면 쉽게 진행할 수 있지만, 이러한 과정을 손으로 해보자. • 해커는 결국 그들의 머리와 손에 의존한다. 결국 이런 스킬 들이 필요하다. • PEKPNXE by Kris Kaspersky. • PE file format 의 학습. • http://hdp.null2root.org/reversing/PE_analysis_anesra.pdf • http://en.wikipedia.org/wiki/Portable_Executable
Limiting the Term of Use • 많이 쓰이는 데모 버전 용 제한 기법 • 총 실행 횟수 제한. • 정해진 기간 동안만 동작. • 레지스트리나 외부 파일에 정보를 저장할 필요가 있다. • 여러 가지 시도들. • 레지스트리 수정. • 시스템 시간 변경. • 시간 시간의 수정.
Crack05.exe • 처음 실행 후 20일 동안 유효하다. • 재 설치는 전혀 도움이 되지 않는다. • 첫 수행 시간은 어디에 기록 될까? • 레지스트리? • 이는 레지스트리 모니터 툴을 이용해서 쉽게 확인이 가능하다. • Sysinternals 의 Regmon 사용. • Vista 는 Process monitor 사용. • 레지스트리를 수정 하는건 불편하다. • Log 를 통한 Protection mechanism 의 분석. • 먼저 HKEY_CURRENT_USER\SOFTWARE\CRACK05 라는 이름의 레지스트리 키를 찾는다. • 만약 키가 존재 하지 않는다면 이 키를 생성하고 현재 시간을 세팅 한다. • 키가 존재한다면, 현재 시간과 처음 실행된 시간을 비교해서 수행 여부를 결정한다.
Crack05.exe - 계속 • 프로그램이 항상 처음 수행된 효과가 나도록 코드를 수정한다. • IDA 를 이용 RegCreateKeyExA 를 호출하는 곳을 찾는다. • 리턴 값을 비교하는 곳을 찾아서 Jnz 명령을 nop 두개로 교체. • LONG WINAPI RegCreateKeyEx( … ); • Return Value • If the function succeeds, the return value is ERROR_SUCCESS. • If the function fails, the return value is a nonzero error code defined in Winerror.h. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTEM flag to get a generic description of the error. • IDA-pro 를 이용한 disassemble 결과 그래프의 분기 관찰.
Crack06.exe • 레지스트리가 아닌 파일에 저장 될 때 • Filemon 을 사용 로그를 분석해서 어떤 파일을 사용하는지 파악 한다. • Vista 의 경우 process monitor • 앞 예제와 비슷한 코스로 Crack 시도.
Limiting the Number of Startups • 앞 예에서와 마찬가지로 카운터를 저장할 필요가 있음. • Filemon, regmon 의 로그를 통해서 Protection algorithm 분석 • 일반적인 응용프로그램은 수많은 레지스트리 키를 생성한다. 이중 어떤 것인 카운터 인가? • 만능열쇠는 없다. • 연속적 프로그램 실행의 결과 log 를 통해서 변경되는 부분을 발견할 수 있다.( 카운터가 증가 되거나)
Crack09.exe • 카운터를 찾기가 힘들다. • 변경되는 데이터가 어떤 때는 증가되고, 어떤 때는 감소 되는등 임의적이다. • 진실을 밝히기 위해 debugger 와 disassembler 를 동원한다. • IDA-pro 를 통한 분석. • aCount1 의 이름을 찾고, • xRef 로 참조 하는 곳으로 이동. • Dec 부분을 nop 로 교체. RegQueryValueEx(hKey,"Count1",0,&TYPE,(LPBYTE) &Count1,&res); RegQueryValueEx(hKey,"Count2",0,&TYPE,(LPBYTE) &Count2,&res); Count2 = Count2 ^ Count1; Count2--; printf("Count %x \n",Count2); if (!Count2) return 0; srand((unsigned)time( NULL ) ); Count1 = (unsigned) rand(); Count2 = Count2 ^ Count1; RegSetValueEx(hKey,"Count1",0,REG_DWORD,(CONST BYTE *) &Count1,4); RegSetValueEx(hKey,"Count2",0,REG_DWORD,(CONST BYTE *) &Count2,4);
The Nag Screen • 프로그램 중간 중간에 등록을 하란 메시지 박스로 귀찮게 함. • 지속적인 귀찮음 유발로 정식 제품을 구매하도록 유도. • 광고 등을 출력 하기도 한다. • Nag screen 을 보고 참고 사용하거나, 정식 구매자가 되거나 사용자의 선택이다. • 그러나 대부분의 사용자는 비용을 지불하기를 꺼린다. • 이는 구매력이 없어서라기 보다는 정신적인 문제이다. • Cracker 의 입장에선 이는 Protection mechanism 이 아니다. • Nag screen 이 뜨지 않도록 코드를 수정하는 것은 어렵지 않다. • 두 가지 일반적 방법. • Key generator 의 사용. • Nag screen 을 표시하는 코드의 수정. • 후자는 권장하지 않는다. 전자가 쉽고 심플하다.
Crack0b.exe • 프로그램 수행 중 주기적으로 표시되는 nag screen. • 이때는 모든 기능 정지. • 첫 번째 시도. • dialog를 표시하는 부분의 코드를 수정. • 어떻게 이 부분을 찾을 것인가? • Dialog 를 생성하는 부분에 breakpoint 를 잡는 방법은 적절치 않다. • MFC42.dll 내부에서 일어나는 일이라.. • Cdialog::DoModal 을 시도하자. • Domodal 의 ordinal 은 0x9d2 • Breakpoint to mfc42!ORD 09D2 • 저자는 제대로 breakpoint 를 잡기 위해서 12시간 소모.
Crack0b.exe - 계속 • Dialog::domodal 를 호출하는 코드를 수정 하는 건 무식한 방법. • 위 함수를 호출하게 되는 조건을 처리하는 분기 문을 무력화 시킨다. • 원하는 대로 동작한다. • 끝인가? • Cracker 들은 호기심이 많다. • 당신이 진정한 cracker 이라면 내부 protection mechanism 이 어떻게 동작하고 있는지 관심을 가져야 한다.
Crack0b.exe - 계속 • 좀더 관찰 후 프로그램이 SetTimer 로 타이머를 생성후 • 매 타이머 이벤트 마다 다이얼로그를 생성한다. • SetTimer 호출을 무력화 시킨다. • 함수 호출 뿐만 아니라 스택 관련 코드도 같이 수정. • 그냥 타이머의 시간을 아주 큰 수로 설정해서 통과를 시켜 보았다.
Crack0c.exe • 시간과 상관없이 생성되는 nag screen. • Spyxx 등으로 살펴봐도 WM_TIMER 메시지는 발생하지 않는다. • 메시지 프로세싱 루프에서 현재 시간을 체크하거나, 독립된 쓰레드에서 전담해서 시간을 체크. • 일반적으로 후자의 방법을 많이 사용. • VC++ 의 process viewer 를이용해서 관찰. • 시간 체크 전용 쓰레드의 루프 코드를 무력화. • HKEY_CURRENT_USER\Software\crack0c\Regit 의 생성으로 한방에
The Key File • 가장 이상적인 방법. • 두 가지의 접근 방법. • 새로운 유효 키 파일의 생성. • Protection mechanism 을 무력화 시키는 코드 수정. • 일반적으로 첫 번째 방법 선호. • 일반적으로 key 파일은 유져 이름과 등록 번호가 저장된다. • 혹은 물리적으로 빠진 코드를 이 key 파일에 넣기도 한다. • 이건 정말 쉽고도 크래킹을 현실적으로 불가능하게 만드는 방법이다. • 여기서 불가능 하다는 말은 정식 제품을 구입하는 비용보다 크랙에 들어가는 비용이 더 크다는 의미 이다.
Crack11.exe • File 의 존재 여부로 체크. • Disassempler 로 확인. • findFirstFileA를 호출하는 부분 발견. • File monitor 로 확인. • 단순히 파일의 존재 여부 뿐 아니라, 생성 시간, 파일 사이즈 등을 비교할 가능성 있음. • FindFirstFile 함수는 해당 함수의 존재 여부 뿐만 아니라 _WIN32_FIND_DATA 의 정보도 리턴한다. typedef struct _WIN32_FIND_DATA {DWORD dwFileAttributes;FILETIME ftCreationTime;FILETIME ftLastAccessTime;FILETIME ftLastWriteTime;DWORD nFileSizeHigh;DWORD nFileSizeLow;DWORD dwReserved0;DWORD dwReserved1;TCHAR cFileName[MAX_PATH];TCHAR cAlternateFileName[14]; } WIN32_FIND_DATA, *PWIN32_FIND_DATA, *LPWIN32_FIND_DATA;
Crack11.exe - 계속 • 실제 Protection mechanism 을 분석 전체 알고리즘을 무력화 시키는 부분은 아주 많은 assembly 언어대한 스킬이 필요. • 쉬운 접근 방법 시도. • IDA-Pro 의 disassemple 결과를 확인 하면 파일을 찾은 후 분기 하는 부분을 확인. • 이 분기를 강제로 성공한 코드 영역으로 점프 하도록 코드를 수정.