안녕하세요. 이번에는 우리가 코드엔진에서 풀어본 문제 중 하나인 bagic의 03문제인 프로그램을 만들어보겠습니다. 일단 동작을 봅시다.

실행시 메세지박스가 하나 띄워집니다. 확인과 취소 버튼이 있는데 확인 버튼을 누른시 아래와 같은 창이 뜹니다. 반대로 취소 버튼을 누르면 그냥 종료가 되버립니다.

동작을 확인했으니 일단 겉만 만들어봅시다. 제가 작성한 소스를 보여드리겠습니다.

저번 포스팅에서 왠만한 api함수들은 전부 설명드렸으니 새로 나온 함수 혹은 코드만 설명하겠습니다.

- #include "resource.h"

리소스가 선언되어 있는 헤더파일을 불러옵니다. 이것은 나중에 설명드리겠습니다.

- #define REGCODE 0

  #define REGISTRIEREN 1

  #define Exit 2

edit컨트롤과 버튼컨트롤을 제어하기 위해 정의된 상수입니다.

- HWND regcode;

edit컨트롤의 핸들을 저장시킬 변수를 전역변수로 선언하였습니다.

WinMain을 살펴봅시다.

- int Message_result;

MessageBox함수의 결과값을 저장시킬 변수입니다.

- HBRUSH bgBrush;

윈도우의 배경색을 저장할 용도로 HBRUSH 형의 변수를 선언하였습니다.

- bgBrush = CreateSolidBrush(RGB(238,238,238));

HBRUSH형의 변수에 CreateSolidBrush의 결과값을 집어넣습니다. CreateSolidBrush의 정의를 봅시다. MSDN을 참고합시다.

컬러를 인자로 받아 HBRUSH 형태로 반환합니다. 즉 이 함수는 단색 브러쉬를 만드는 함수입니다. 윈도우의 배경색을 주기 위해 사용하였습니다.

- wndclass.hbrBackground = bgBrush;

윈도우의 배경색을 아까 만든 bgBrush로 지정합니다. 여기서는 RGB(238,238,238)로 만들었으므로 회색 비슷한 색깔로 지정됩니다.

- wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);

윈도우의 메뉴를 IDR_MENU1로 지정합니다. 이 IDR_MENU1는 리소스를 의미합니다. 이 리소스 작성법은 나중에 설명드리겠습니다.

그리고 MAKEINTRESOURCE함수가 사용되었습니다. MSDN을 참고합시다.

정수형을 받아서 문자열로 반환합니다. 즉 이 함수는 리소스번호를 받아 리소스 문자열로 반환시킵니다. 우리가 만든 메뉴 리소스인 IDR_MENU1이 헤더파일에 상수로 정의되어 있으므로 이 IDR_MENU1 번호를 문자열로 변환시켜 lpszMenuName에 넣어주었습니다.

 - wndclass.lpszClassName = L"a";

클래스 이름을 지정합니다. 여기서는 그냥 a로 주었습니다.

- Message_result = MessageBox(NULL,L"Entferne diesen Nag, oder bekomme das richtige Passwort heraus !",L"Nag Meldung",MB_ICONQUESTION | MB_OKCANCEL);

MessageBox함수를 사용하여 메세지박스를 띄웁니다. 이 메세지박스는 프로그램을 실행할 때 제일 먼저 나오는 메세지박스입니다.

여기서 눈여겨보실 부분은 마지막 인자부분인데 ,MB_ICONQUESTION | MB_OKCANCEL입니다. MB_ICONQUESTION은 질문 아이콘을 메세지박스에 띄우는 인자입니다. 즉

빨간박스의 저 물음표 아이콘을 띄운다는 뜻입니다. 

MB_OKCANCEL은 OK버튼과 CANCEL버튼을 만든다는 뜻입니다. MessageBox함수는 각각의 버튼을 눌렀을 때 반환되는 결과값이 다릅니다.

- if(Message_result == 1)

Message_result는 MessageBox함수의 결과값이죠. 즉 어떤 버튼을 눌렀느냐에 따라 if문을 실행하냐 안하냐를 결정합니다. 결과값이 1이면 확인버튼을 눌렀다는 뜻이므로 조건문이 참이 되어 if문 안의 코드를 실행합니다. 만약 취소를 눌렀다면 if문을 실행하지 않고 else문을 실행합니다.

- hwnd = CreateWindow(L"a",L"DCD VB5-CrackMe 1.0",WS_SYSMENU | WS_VISIBLE,73,73,253,181,NULL,NULL,hInstance,NULL);

윈도우를 생성합니다. 여기서는 윈도우 스타일에 WS_SYSMENU 인자를 주었습니다. 이 인자는 닫기 창만 존재하는 윈도우를 의미합니다. 덧붙여서 이 스타일이 적용된 윈도우는 크기를 바꿀 수 없습니다.

- else

return 0

메세지박스의 결과값이 1이 아닐 경우 프로그램을 종료합니다.

나머지 소스코드는 앞시간에 전부 설명드렸으니 따로 설명드리진 않겠습니다.

자 이제는 리소스를 만들어봅시다. 리소스 파일부분에서 rc파일을 만듭니다.

이제 만들었으니 편집을 할 차례입니다. 저는 resedit를 사용하겠습니다.

아 그리고 앞 포스팅에서 깜박하고 빼먹은 부분이 있는데 resedit를 사용하시는 분들은 resedit의 경로를 따로 지정해주어야 합니다.

Options의 Preferences...를 누릅시다.

General의 경로를 추가해주는데 어떤 경로로 설정하냐면 Windows.h 파일이 들어있는 경로로 설정해주면 됩니다.

경로를 지정해주었으면 아까 만든 리소스 파일을 열어봅시다. 보통 리소스파일은 생성시 소스파일과 동일한 위치에 생성됩니다.

해당 rc파일을 성공적으로 여셨다면 메뉴 리소스를 추가합시다.

IDR_MENU1이라는 리소스가 생성되었습니다. 우측에 보시면 Type here이 보이시죠? 이 부분이 바로 윈도우의 메뉴부분입니다. 03.exe에 맞게 직접 메뉴를 지정해줍시다.

지정을 다하시고 나서 해당 메뉴를 누르면 밑에 하위항목이 보입니다. 03.exe에는 하위항목이 Exit밖에 없으므로 Exit를 적어줍시다.

메뉴가 다 작성되었습니다. 이제 메뉴의 설정을 변경해봅시다.

메뉴 ?er을 누르고 속성부분에서 Popup부분을 False로 설정해줍니다. 이 Popup 부분은 이 메뉴가 하위항목이 있냐 없냐를 설정해주는 부분입니다. Programm 메뉴는 하위항목 Exit가 있기 때문에 이 부분을 True로 설정해주어야 하지만 ?er은 하위항목을 표시하지 않기 때문에 이 Popup부분을 False로 고쳐주어야 합니다.

여기까지가 메뉴였습니다. 이번에는 아이콘을 추가합시다. 아이콘 추출법과 추가하는 방법은 앞시간에 올렸으므로 따로 설명드리지 않겠습니다.

다 만들었으면 저장해줍시다. 그리고 비주얼 스투디오로 돌아와 리소스의 헤더파일을 추가합시다.

resource.h 파일을 찾아 추가해주시면 됩니다. 이 헤더파일 안에는 우리가 만든 리소스들이 상수로 정의되어있습니다. 이렇게 추가된 resource.h 파일을 사용하기 위해서는 소스코드이 위에 include "resource.h" 를 추가해주어야 합니다. 

여기까지 다되셨으면 우리가 만든 리소스를 사용하기 위해 소스코드를 약간 수정해줍시다. 소스코드에서는 메뉴가 추가되어있지만 아이콘을 따로 추가가 되어있지 않습니다. 그러므로 아이콘을 새로 추가해 줍시다. 다음과 같이 수정합시다.

 - wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); -> 기존코드

 - wndclass.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1));

-> 수정코드

LoadIcon의 인자를 변경해줍니다. 기존에 정의되어 있는 아이콘을 사용할 경우에는 첫 인자 값을 NULL로 주지만 그 외의 아이콘을 사용할 경우에는 첫 인자에 hInstance를 줍니다. 두번째 인자는 우리가 만든 아이콘을 문자열 형태로 넣어주시면 됩니다.

자 이제 코드 설명이 전부 끝났습니다. 한번 실행을 시켜 봅시다.

그럴듯하게 만들어졌습니다. 다음포스팅에서는 동작을 구현해봅시다.

이것으로 포스팅을 마치겠습니다.

Posted by englishmath
,

안녕하십니까. 드디어 길고 길던 abex crack me2 제작이 끝을 보이기 시작합니다. 자 한 번 해봅시다.

소스를 한 번 봅시다.

허허 양이 좀 많습니다. 코드를 살펴봅시다.

- #define name 10

  #define serial 20

  #define check 0

  #define about 1

  #define quit 2

자 우선 define을 이용해 상수를 정의해줍시다.

여기에 정의된 상수값은 각각 컨트롤의 식별자로 사용하기 위해 정의하였습니다. 

name,serial -> edit

check,about,quit -> button

즉 각각의 id를 의미하는데 쉽게 말하면 button을 눌렀을 때 어떤 button이 눌러졌는지 구분해야 하지 않겠습니까? 그걸 위해 버튼의 식별자를 정의하였습니다. 그리고 에디트의 식별자도 정의해놓았는데 이 식별자는 실제로는 쓸 데가 없습니다만 쓸일이 없다고 정의를 하지 않으니 알고리즘이 꼬여 버려서 결국 정의해주었습니다.

- LPTSTR Serial(LPTSTR NameStr);

NameStr이란 유니코드문자열을 받아 유니코드문자열로 반환하는 함수입니다.

즉 NameStr에 저장된 name(값)을 읽어서 그 값에 맞는 시리얼을 제작해 반환해주는 함수라고 보시면 됩니다. 이 함수는 뒤쪽에 구현되어 있습니다.

- HWND hName,hSerial;

이름과 시리얼을 읽어올 edit 들의 핸들을 저장시키기 위해 선언하였습니다. 이 핸들을 WinMain의 지역변수가 아닌 전역변수로 선언시킨 이유는 WndProc함수에서 읽어오기 위함입니다.

- CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,

10,57,165,18,hWnd,NULL,hInstance,NULL);

- CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,

10,113,165,18,hWnd,NULL,hInstance,NULL);

-> 기존코드들

- hName = CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,10,57,165,18,hWnd,(HMENU) name,hInstance,NULL);

- hSerial = CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,10,113,165,18,hWnd,(HMENU) serial,hInstance,NULL);

->수정코드들

edit를 생성하는 윈도우의 핸들을 저장시키기 위해 수정하였습니다. 그런데? 인자 하나가 다른 부분이 있지요? 인자 8번째의 값이 다릅니다. 기존코드에서는 NULL값을 주었습니다만 수정코드에서는 (HMENU) name과 (HMENU) serial을 주는 것을 알 수 있습니다. 이 8번째 인자는 해당 윈도우의 식별자를 받는 인자입니다. 우리가 앞에서 선언한 상수들을 사용하였지요. 즉 첫번째 edit의 식별자는 name, 두번째 edit의 식별자는 serial입니다. 물론 여기서는 edit의 식별자를 사용할 필요가 없지만 버튼들의 동작구현을 위해 선언하였습니다. 아 그리고 이 인자는 반환값을 HMENU로 받기 때문에 앞에 (HMENU)를 주어 HMENU로 넘겨주었습니다.

- CreateWindow(L"button",L"Check",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,40,65,25,hWnd,NULL,hInstance,NULL);

  CreateWindow(L"button",L"About",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,80,65,25,hWnd,NULL,hInstance,NULL);

  CreateWindow(L"button",L"Quit",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,120,65,25,hWnd,NULL,hInstance,NULL);

-> 기존코드들

- CreateWindow(L"button",L"Check",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,40,65,25,hWnd,(HMENU) check,hInstance,NULL);

  CreateWindow(L"button",L"About",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,80,65,25,hWnd,(HMENU) about,hInstance,NULL);

  CreateWindow(L"button",L"Quit",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,200,120,65,25,hWnd,(HMENU) quit,hInstance,NULL);

-> 수정코드들

역시 마찬가지로 각 버튼들에게 식별자를 주었습니다.

WinMain의 설명은 끝났군요. 이제 WndProc로 넘어갑시다.

- TCHAR SerialStr[50] = L"", NameStr[50] = L"",AnswerSerial[50] = L"";

TCHAR 형의 문자열 변수를 세개 선언하고 초기화 시켜주었습니다. 크기는 전부 50으로 주었습니다. 이 문자열 변수들은 사용자가 입력한 시리얼,이름 그리고 이름을 기반으로 생성된 올바른 시리얼 값을 유니코드로 저장시키기 위해 선언하였습니다.

- int result;

사용자가 입력한 시리얼과 올바른 시리얼 값을 비교한 값이 저장될 변수입니다.

- case WM_COMMAND:

메세지가 WM_COMMAND일 때의 처리를 하기 위해 만들었습니다. 이 WM_COMMAND메세지는 윈도우에서 각각의 메뉴(컨트롤 등)을 누를 때 발생합니다.

- switch(wParam)

위에서 WM_COMMAND메세지가 메뉴를 누를 때 발생한다고 하였지요? 이 WM_COMMAND메세지가 발생하였을 때 어떤 메뉴를 눌렀는가에 대한 정보가 추가적으로 WParam(세번째 인자)로 들어갑니다.  그러므로 각 메뉴에 대한 처리를 하기위해 switch함수를 사용하였습니다.

 - case check:

wParam의 값이 check 일때의 처리입니다. 이 check는 앞에 우리가 선언한 상수이자 check버튼의 식별자 값입니다. 즉 check버튼을 눌렀을 때의 처리를 위해 선언해주었습니다. 

- if(GetWindowText(hName,NameStr,5) < 4)

MessageBox(hWnd,L"Please enter at least 4 chars as name!",L"Error",MB_OK);

GetWindowText가 나왔군요. 정의를 한 번 봅시다.

해당 핸들의 내용을 읽어옵니다. 인자를 하나씩 살펴봅시다.

*hwnd

내용을 가져올 윈도우의 핸들을 의미합니다.

*lpString

내용을 가져와서 저장시킬 유니코드 문자열을 의미합니다.

*nMaxCount

윈도우에서 내용을 가져올 때 얼마만큼 가져올 것인지를 묻습니다.

즉 이함수는 edit에서 사용자가 입력한 값을 가져오기 위해 사용하였습니다. 

핸들값이 hName이므로 첫번째 edit 윈도우의 값 중 5글자를 읽어와 NameStr에 저장시키고 읽어온 문자의 개수를 반환합니다. 5글자를 읽어오라고 하였지만 한 글자는 null값을 읽어오므로 실제로 들고오는 값은 4글자입니다. 그리고 문자의 개수를 반환할 때에는 null값은 카운트 하지 않으므로 4라는 값을 반환합니다.

쉽게 얘기하면 읽어온 글자만큼 반환합니다. 참고로 읽어올 글자가 없을 경우 0을 반환합니다.

즉 GetWindowText함수를 사용하여 읽어온 입력된 이름 글자수가 4개 미만일 경우 messeagebox함수를 출력하여 이름을 4글자 이상 입력하라고 사용자에게 알립니다.

else문을 봅시다.

- else

이름 글자수가 4개 이상인 경우는 위의 if문의 조건이 거짓이 되어 else문을 실행하게 됩니다. 그 때의 처리구문입니다.

- GetWindowText(hSerial,SerialStr,100);

hSerial에서 100만큼의 글자를 읽어들여 SerialStr에 저장시킵니다. 왜 여기서는 100글자를 읽어오냐고 하니 시리얼 칸에 입력된 값을 전부 가져오기 위함입니다. 물론 시리얼 칸에 입력된 값이 100글자를 넘어가면 다 가져오진 못하지만 그래도 왠만한 시리얼 값을 전부 들고오기 위해 100을 넣어주었습니다.

추가로 위의 Name은 입력한 값을 전부 읽어오지 않고 4만큼만 읽어오게끔 코딩을 해주었는데 이유는 기존의 abex crack me2가 이름의 값을 4만큼 읽어들여 그 4글자로 시리얼을 만드므로 이에 맞춰주기 위해 이름을 4글자만 읽어들였습니다.

- strcpy(AnswerSerial,Serial(NameStr));

strcpy함수는 문자열을 복사하는 함수이죠. Serial함수를 호출한 후 이 반환값을 AnswerSerial에 복사시킨다는 뜻입니다. 여기서 이 Serial함수는 제가 임의로 만든 시리얼 생성 함수입니다. 

- result = lstrcmp(AnswerSerial,SerialStr);

시리얼키가 저장된 AnswerSerial과 사용자가 입력한 시리얼이 저장된 SerialStr을 비교하여 결과갑을 result에 저장됩니다. 일반 strcmp가 아닌 lstrcmp를 사용하였는데 이유는 유니코드 문자열을 비교하기 위해서입니다.

이 lstrcmp의 반환값은 두 문자열이 같을 시 0을 반환합니다. 

-  if(result == 0)

MessageBox(hWnd,L"Yep, this key is right!",L"Congratulations",MB_OK);

   else 

MessageBox(hWnd,L"Nope, this serial is wrong",L"Wrong serial!",MB_OK);

lstrcmp의 반환값을 받은 이 result의 값이 0일 경우 성공문자열을 출력하고 아닐 경우 실패 문자열을 출력합니다. result의 값이 0이라는 것은 사용자가 입력한 시리얼값과 알고리즘에 의해 생성된 정답 시리얼 값이 같다는 뜻이지요.

여기까지가 체크버튼을 눌렀을 때의 처리입니다. 이제 다른 버튼을 처리합시다.

- case about:

MessageBox(hWnd,L"abex' 2nd crackme\ncoded on 30th Nobember 2016",L"About",MB_OK);

about 버튼을 눌렀을 시의 처리입니다. 이 프로그램에 관한 메세지박스를 띄웁니다. abex crack me2의 about를 누르면 문자열 (엔터) 문자열 이런식으로 메세지박스가 띄워집니다. 그래서 우리도 문자열 중간에 \n을 줌으로써 개행이 되도록 하였습니다. 문자열 내용은 조금 바꿨습니다.

- case quit:

PostQuitMessage(0);

quit버튼을 눌렀을 때의 처리입니다. quit 버튼은 종료를 하는 버튼이므로 프로그램 종료를 위해 PostQuitMessage함수를 호출하였습니다.

일반적으로 이 PostQuitMessage함수는 WM_DESTROY에서 처리를 하는 것이 맞습니다. 하지만 이 WM_DESTROY메세지는 WM_CLOSE메세지가 DefWindowProc로 인해 처리가 되었을 때 발생하는 메세지입니다. WM_CLOSE.. 즉 윈도우 창이 닫혔을 때 발생하는 메세지인데 이 윈도우는 알다시피 팝업창이기 때문에 닫는 창이 없습니다. 즉 WM_CLOSE가 발생하지 않습니다. 그렇기에 WM_DESTROY에서 PostQuitMessage처리를 하지 않고 quit버튼을 눌렀을 때 닫히도록 코딩하였습니다.

자 이제 제가 만든 Serial 함수를 살펴봅시다. 그전에 잠깐 시리얼 알고리즘을 설명하겠습니다.

abex crack me2를 리버싱 해보신 분은 아시겠지만 시리얼 키를 만드는 알고리즘이 단순합니다. 4글자의 이름을 입력받은 후 각 글자를 하나씩 뽑아와서 그 글자의 16진수 값과 16진수 64를 더한 값을 시리얼로 사용합니다. 

ex) a(0x61) + 0x64 = 0xC5(시리얼)

Serial 함수를 살펴봅시다.

 - int i,j,Key;

for문에 쓰일 i,j 변수와 16진수 덧셈연산의 결과값을 저장할 Key값을 int로 선언합니다.

- int k = 0;

for문에 쓰일 k변수를 선언 후 0으로 초기화 시킵니다. 다만 위의 i,j하고는 조금 다른 의미로 쓰입니다. 

- TCHAR Key2[50] = L"", Key3[50] = L"";

Key2와 Key3 문자열 변수를 선언합니다. Key2는 한 글자의 시리얼값이 저장되고 Key3은 각 글자의 Key2값을 저장하기 위해 사용합니다.

- for(i=0; i<lstrlen(NameStr); i++)

이름을 시리얼로 바꾸기 위한 for문입니다. NameStr에 들어있는 글자수만큼 반복하도록 구현하였습니다.

- Key = NameStr[i] + 0x64;

사용자가 입력한 이름값 즉 NameStr에서 1글자를 뽑아와서 그 글자의 값과 16진수 64를 더해서 Key에 저장합니다. 다만 이렇게 연산을 할 경우에는 10진수로 연산이 됩니다. 

예를 들어 a를 가져왔을 때 a + 0x64를 연산하지요. 이럴 때 a의 10진수 값(아스키코드)인 97과 0x64의 10진수 값인 100을 더한다는 뜻입니다. 

즉 Key값에는 197이 들어가게 됩니다. 이 197의 16진수 값은 C5이므로 이 197을 16진수로 바꿀 필요가 있습니다.

- wsprintf(Key2, L"%X", Key);

앞에서 말한 정수값 Key를 16진수로 저장시키기 위한 코드입니다. wsprintf함수가 사용되었지요? 원래 sprintf를 써야 하는데 유니코드로 저장시켜야 하므로 wsprintf를 사용하였습니다. 

함수 이름에서 알 수 있듯이 이 sprintf함수는 출력시키는 함수입니다. 다만 일반 printf함수가 콘솔에 출력시키는 거라면 이 sprintf함수는 변수에 출력시킵니다.

즉 Key2란 변수에 %X, Key 형식으로 출력시킨다는 뜻입니다. %X는 16진수 출력을 의미하지요? 그리고 이에 대응하는 변수는 Key입니다. 

정리하면 Key값을 16진수로 변환시켜서 Key2에 출력합니다. 그러면 Key2에는 아까 16진수 연산의 결과값(정수) Key의 16진수 값이 문자열로 들어가집니다. 

- for(j=0; j<lstrlen(Key2); j++)

이 for문은 Key2에 들어있는 문자열의 길이만큼 반복합니다. Key2에 있는 16진수 값을 Key3에 저장시키기 위해서 사용하였습니다.

- Key3[k] = Key2[j];

Key2의 첫 글자부터 마지막 글자까지 Key3에 저장시키는 코드입니다. 이 때 Key3의 인덱스는 k변수를 사용하였는데 그 이유는 Key3의 인덱스는 for문이 시작되어도 초기화가 되면 안되기 때문입니다. 

- k++;

글자를 받았으니 k값을 하나 증가시킵니다.

- return Key3;

최종적으로 for문이 모두 처리되면 시리얼이 저장된 Key3을 리턴합니다. 이 리턴된 Key3은 lstrcpy함수에서 사용됩니다.

자 이제 진짜 다 끝났... 으면 좋겠지만 마지막 부분이 남았습니다. 바로 아이콘입니다.

현재 프로그램의 아이콘을 보면 

기본 아이콘으로 되어있지요? abex crack me2 아이콘을 볼까요?

아이콘이 참 예쁘지요? 우리도 이렇게 예쁜 아이콘을 프로그램에 적용시켜 봅시다.

먼저 리소스 해커로 abex crack me2의 아이콘 리소스를 가져옵시다.

저장시킵시다.

저장시키면 ico확장자를 가진 파일이 생성됩니다. 이 파일을 프로젝트 안에 넣어버립시다. 위치는 딱히 상관없지만 나중에 관리하기 용이하도록 해주기 위합니다.

생성이 된 것을 확인하였으면 리소스 파일을 생성합시다.

이 리소스라는 것은 메뉴,아이콘,커서 등의 데이터를 의미합니다. 우리는 아이콘 리소스를 사용해야 하므로 리소스 파일을 새로 생성시켜 주어야 합니다.

사실 제가 쓰고 있는 visual c++ 2010 express 프로그램은 리소스 편집기를 제공하지 않습니다. 그렇기에 저는 리소스 편집을 다른 방법으로 할 예정입니다. 다른 visual프로그램을 쓰고 계신분은 리소스 편집기를 제공하므로 그걸 쓰는 것이 좋습니다. 

파일을 생성시킬 때 이름을 Icon.rc로 바꿔줍시다. 메뉴에는 리소스가 없지만 이렇게 확장자를 바꿔주는 것만으로도 리소스 파일을 생성시킬 수 있습니다.

리소스 파일이 생성되었습니다. 제가 쓰는 프로그램은 편집 기능이 지원되지 않는군요.

자 그러면 저는 ResEdit를 쓰겠습니다. 이 ResEdit 프로그램은 리소스를 편집하고 저장할 수 있는 도구입니다. 다른 버전의 visual studio는 리소스 편집기를 사용할 수 있으니 여러분들은 편하신 대로 하시면 됩니다.

RESEdit를 열고 아까 생성한 리소스 파일을 열어봅시다.

리소스 파일을 여셨다면 리소스를 추가해 봅시다.

오른쪽마우스 -> Add Resource... -> Icon을 누릅시다.


아까 저장한 ico 파일을 불러오기 위해 위의 버튼을 체크합시다.

파일을 가져옵시다.

이 ico 파일의 경로를 절대로 지정할거냐 상대로 지정할거냐고 나옵니다. 우리는 상대로 지정해줍시다.

자 아이콘 리소스를 추가하였습니다. 저장시킨 다음 visual studio로 돌아갑시다.

우리가 만든 리소스를 사용하기 위해 리소스 파일을 추가합시다. 추가 - 기존 항목을 누릅시다.

리소스를 만들고 나면 그 리소스에 관한 내용을 담고 있는 헤더파일이 생성됩니다. 이 헤더파일은 rc파일과 동일한 경로에 생성됩니다.

헤더파일이 성공적으로 추가되었습니다. 이 헤더파일에는 앞서 만든 아이콘 리소스가 상수로 정의되어 있습니다. 헤더파일을 추가하였으면 소스코드 맨위에 헤더파일을 사용하기 위해 선언을 해 줍시다.

 - include "resource.h"

이 헤더파일을 선언하실 때 유의하실 부분이 있는데 경로를 잘 맞춰주어야 합니다.

소스코드를 수정합시다.

wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION); - > 기존코드

wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));

-> 수정코드

wndclass의 hIcon멤버를 위와 같이 수정하였습니다. 우리가 만든 리소스 아이콘을 사용하기 위함입니다. 세세한 설명은 다음 포스팅 때 설명드리겠습니다.

여기까지 다 되었으면 실행시켜봅시다.

작업표시줄을 보시면 아이콘이 등록된 것을 볼 수 있습니다. 파일을 한번 볼까요?

파일의 아이콘도 바뀌었습니다. 예~~

자 이제 진짜 완성되었습니다. 결과물 한번 봅시다.

네 이것으로 길고 긴 포스팅을 마치겠습니다. 감사합니다.

Posted by englishmath
,

 안녕하십니까. 저번 포스팅까지 작성했던 소스를 더 수정했습니다.

한번 보시지요.

바뀌거나 추가된 소스를 살펴 봅시다.

- hWnd = CreateWindow(wndclass.lpszClassName, L"abex 2nd crackme",       

WS_POPUPWINDOW, 820, 462,

        280, 155, NULL, NULL, wndclass.hInstance, NULL); -> 기존코드

- hWnd = CreateWindow(wndclass.lpszClassName, L"abex 2nd crackme",

        WS_POPUPWINDOW | WS_VISIBLE, 820, 462,

        280, 155, NULL, NULL, wndclass.hInstance, NULL); -> 수정코드

윈도우를 생성할 때 스타일 옵션에 하나를 더 주었습니다. WS_VISIBLE라는 옵션입니다. 이 WS_VISIBLE은 ShowWindow 기능을 제공합니다. 즉 ShowWindow 함수가 없어도 윈도우 창이 보인다는 뜻이지요. 그래서 ShowWindow를 지우고 WS_VISIBLE을 넣어주었습니다.  사실 이  WS_VISIBLE은 나중에 알게 되어서 미리 처음에 작성할 때 적용하지 못했습니다.

- CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,

10,57,165,18,hWnd,NULL,hInstance,NULL);

컨트롤을 만드는 코드입니다. 여기서 컨트롤이라 함은 사용자와의 인터페이스를 도와주는 윈도우 입니다. 즉 사용자로부터 입력을 받고 결과값을 보여주기 위해 사용합니다. 예로 버튼, 에디트 등이 있으며 이러한 컨트롤은 클래스가 이미 정의되어이 있습니다.

정의된 컨트롤을 쓰기 위해선 CreateWindow의 첫번째 인자인 클래스 이름을 바꿔주어야 합니다. 각각의 컨트롤은 정의된 클래스 이름이 있습니다.

button(버튼), static(텍스트), scrollbar(스크롤 바), edit(에디트) , listbox(리스트 박스), combobox(콤보 박스)

우리는 이중 사용자의 입력값을 받기 위해 edit라는 클래스를 사용하겠습니다. 원래 CreateWindow함수를 사용할려면 WinMain함수에서 보았듯이 윈도우 클래스를 선언하고.. 각각의 멤버를 정의하고... RegisterClass 함수를 이용해 윈도우클래스를 등록해야 합니다만 컨트롤은 미리 클래스가 등록되어 있기 때문에 그냥 갖다 쓰기만 하면 됩니다.

그다음 세번 째 인자인 윈도우 스타일을 봅시다. WS_CHILD가 적혀있습니다. 이 WS_CHILD 은 이 윈도우가 자식윈도우임을 나타냅니다. 즉 부모 윈도우가 꺼지면 이 자식 윈도우도 자동으로 꺼집니다. 만약 이 옵션이 없으면 이 윈도우는 독립된 윈도우로 인식하여 운영체제에 윈도우 창이 총 2개 띄워집니다. abex crack me2에서는 자식윈도우를 쓰고 있으니 우리도 자식 윈도우를 씁시다. WS_VISIBLE은 설명했으니 넘어갑시다. 

그 다음 눈에 들어오는 것이 ES_AUTOHSCROLL 옵션입니다. 이 옵션은 윈도우 클래스 edit의 전용옵션입니다. 어떤 옵션이냐면 말 그대로 자동으로 스크롤을 지원한다는 뜻입니다. 좀더 쉽게 얘기하면 이 edit라는 컨트롤이 사용자의 입력값을 받지 않습니까? 그런데 이 edit는 사용자가 입력칸에 값을 꽉 채웠을 때 더 못쓰게 할거냐 아니면 스크롤을 이용해서 계속 쓰게 할거냐를 결정합니다. 우리는 계속 쓰게 할 것이므로 ES_AUTOHSCROLL 옵션을 추가로 주었습니다.

8번째 인자에는 hWnd가 들어가 있습니다. 이 인자는 부모윈도우를 인자로 받습니다. 자식윈도우이므로 당연히 부모윈도우가 인자로 들어가야 합니다. 여기서는 팝업윈도우로 만들어진 윈도우가 부모윈도우이므로 이 부모윈도우의 핸들값인 hWnd를 넣어주었습니다.

이 edit창은 이름을 입력받습니다.

그다음은 달라진 것이 하나 있군요. 부모 윈도우는 wndclass.hInstance인데 자식 윈도우는 hInstance입니다. 어.. 별 차이는 없으니 넘어갑시다.

- CreateWindow(L"edit",NULL,WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL,

10,113,165,18,hWnd,NULL,hInstance,NULL);

두 번째 edit 창을 만듭니다. 이 edit창은 시리얼을 입력받습니다.

- CreateWindow(L"button",L"Check",WS_CHILD | WS_VISIBLE |   BS_PUSHBUTTON, 200,40,65,25,hWnd,NULL,hInstance,NULL);

이번에는 버튼을 만듭니다. 클래스 이름을 button으로 줌으로써 button을 생성합니다.물론 이 버튼도 전용옵션이 있습니다. BS_PUSHBUTTON입니다. 이 옵션은 이 버튼을 누르는 버튼으로 생성한다는 뜻입니다. 그 외에도 체크박스, 라디오 버튼 등의 옵션이 있는데 이것은 나중에 추가로 설명하겠습니다.

이 button의 보여지는 값은 Check입니다. 이 Check는 두번째 인자를 의미합니다. 그 외에는 앞에서 설명했으니 넘어갑시다.

- CreateWindow(L"button",L"About",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 200,80,65,25,hWnd,NULL,hInstance,NULL);

About 푸쉬버튼을 만듭니다.

- CreateWindow(L"button",L"Quit",WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 200,120,65,25,hWnd,NULL,hInstance,NULL);

Quit 버튼을 만듭니다.

자 이제 WndProc함수를 살펴봅시다.

- HFONT font, font1;

font가 하나 더 필요하므로 font를 하나 더 선언하였습니다.

- font1 = CreateFont(12,0,0,0,400,0,0,0,HANGEUL_CHARSET,0,0,0,

DEFAULT_PITCH | FF_ROMAN,L"돋움");

폰트를 하나 더 생성합니다. 이 폰트는 앞의 font보다 크기가 작고, 진하게가 설정되지 않았으며(400), 글씨체가 돋움을 의미합니다.

- DeleteObject(font);

  SelectObject(hdc,font1);

처음에 font를 hdc에 등록한 후 제목을 출력하였으므로 이제 폰트를 바꾸기 위해 기존에 등록한 font를 지우고 새 폰트인 font1을 등록하였습니다. 

- TextOut(hdc, 10,40,L"Name:",lstrlen(L"Name:"));

  TextOut(hdc, 10,96,L"Serial:",lstrlen(L"Serial:"));

새로 등록한 폰트로 Name: 과 Serial: 을 출력시킵니다.

여기까지입니다. 생각보다 조금 짧네요. 결과를 한 번 봅시다.

오오오오 정말 그럴듯하게 만들어졌습니다. abex crack me2를 봅시다.

완전히 똑같지는 않지만 굉장히 비슷합니다. 

네 이것으로 포스팅을 마치겠습니다. 다음 포스팅에선 이제 버튼 동작을 시켜보겠습니다. 

점점 더 재밌어지는군요!. 아.. 아닌가요? 죄송합니다...

Posted by englishmath
,