저도 프로그래밍은 조금 배웠지만, 디버깅에 대해선 아는게 없어서 밑의 글을 읽고 도움이 참 되었습니다.
다만, 프로그래밍 경험이 없는 분들의 입장에선 조금 이해하기 어렵단 생각이 드네요.ㅎ
특히 첫번째 강좌와 두번째 강좌의 레벨차이가 꽤 난다는 생각이 듭니다...
그래서 밑의 강좌의 보충 설명, 즉 vol 1.5 정도로 가볼까 생각중입니다만 잘 될 수 있으면 좋겠네요ㅋ
하지만 저도 일단은 초보라서 다소 설명에 틀린 부분이나 미흡한 부분이 있을 수 있으니 태클 많이 걸어주세요ㅎ
그럼 우선 컴퓨터가 어떻게 돌아가는지 살짝 살펴본 후, 프로그래밍의 원리를 설명할게요ㅎ
1. CPU
CPU 는 뭐 워낙 컴퓨터의 기본이라 많이들 들어보셨겠지만, 간단히 말하자면 고성능 계산기입니다...
그런데 워낙 고성능이라 사칙연산이 아니라 일종의 논리계산이란 것을 합니다.
이산수학이나 논리 등을 배우신 분들은 익숙하실 수도 있는데요,
A 가 참이고 B 도 참이면 A 와 B 둘다 참이므로 A and B 는 참입니다.
하지만 둘중 하나나 둘 다 거짓이면 A and B는 거짓이 되어버리겠죠 ??
참을 1 로 표현하고 거짓을 0으로 표현한다면 1 and 1 은 참(1)이고 그 외에는 모두 거짓(0)이 되겠습니다.
눈치 채신분들 계시겠지만 즉 컴퓨터는 이진수 계산을 하기 때문에 1은 참으로 0을 거짓으로 해서
모든 논리 계산을 한답니다. 논리계산이 뭐 이것저것 많아서 다 쓰기도 그렇지만
기본적으로 and 이외에 or, not, nand, xor 정도가 있습니다....
컴퓨터의 사칙연산은 사실은 논리연산의 응용입니다. and 는 논리 곱이라고도 하는데 잘 생각해보시면 이해가 되실 듯;
컴퓨터는 이렇듯 기본적으로 계산기이기 때문에 computer 라는 단어도 직역하자면 계산기라는 뜻이됩니다.
더 아시고 싶으신 분들께서는 트랜지스터에 대한 간단한 책 한번 읽어보세요. 그럼 도움이 될 것입니다...
2. 메모리
그럼 CPU는 계산을 하는데, 계산하는 숫자들은 어디서 와서 어디로 가느냐....
그것이 메모리입니다.
CPU는 계산하려 하는 정보들을 메모리에서 읽어서 할일을 한 후 메모리에 다시 저장시킵니다.
(사실 조금 더 복잡하긴 하지만, 적어도 지금은 이것 이상 알 필요가 없습니다.)
메모리는 블록으로 된 구조라고 생각하시면 쉬운데요, 한 블록이 각각 1byte 입니다.
그리고 각각의 블록에는 주소가 있는데요, 보통 16진수의 숫자로 표현됩니다....
컴퓨터는 2진수만 알아듣는데 왜 주소는 16진수로 표기하냐면요...
컴퓨터가 16진수를 알아들어서가 아니라, 사람들이 읽기 편해서 입니다.
다 아시겠지만 2진수는 저희가 평소에 사용하는 10진수보다도 꽤 불편하고 깁니다.
16진수 특성상 네 자리 2진수는 16진수 한 자리에 표현됩니다.
즉 0001 0001 0000 0010 이런 숫자도 16진수로는 1 1 0 2 이렇게 줄여지는거죠/ㅎ
무슨 숫자든 컴퓨터 내부에서는 다 2진수로 바뀌어서 사용됩니다.
그런데 CPU는 이런걸 어떻게 다 알고 척척 계산하는 걸까요...
사실 CPU는 알아서 하는게 아니라 처음부터 끝까지 프로그래머가 시키는 것만 합니다.
3. 프로그래머와 컴퓨터 언어, 그리고 코드
프로그래머는 다들 아시겠지만 컴퓨터 언어를 이용해 코드를 작성하는 사람입니다.
코드는 말하자면 컴퓨터에게 내리는 명령의 집합입니다.
즉 컴퓨터한테 이거해라 저거해라 시키는 거죠.
코드에 존재하는 함수란 녀석도 결국 반복되는 명령들의 그룹입니다.
사실 스크린에 글자 하나 출력하는 것에도 다양한 명령들이 필요합니다.
그래서 출력에 필요한 명령들만 묶어서 '출력함수'라고 명명하고 하나의 함수를 만든 것이죠...
코드도 컴퓨터 언어에 따라 바뀝니다. 컴퓨터 언어는 목적에 따라, 효율성에 따라 다양한 것들이 쓰이지만
유명한 것으로 C/++, Java, Lisp, Python, 그 외에도 이것저것 많이 있습니다.
프로그래밍을 통해 CPU는 결국 제 구실을 하게 됩니다. 메모리에 저장되어있던 단순한 0 과 1도
프로그래머에 의해 그 의미를 찾게 되는 것이죠...
그렇다면 이제 프로그래머는 코딩을 했습니다. 앞에서 G2M 님께서 설명했듯이,
컴퓨터는 코드 조차도 알아볼 수 없어서, 기계어로 일일이 번역을 해줘야 합니다.
다행이 프로그래밍 언어에는 그러한 번역기가 같이 있는데, 그것이 compiler 입니다.
4. Compiler
사실 보면 번역된 언어가 원문하고 완벽하게 똑같을 수가 없습니다. 똑같애서도 안되고요.
예를 들어 영어에서 a piece of cake 라는 표현이 있지요. 직역하면 케이크 한조각이겠지만,
문맥에 따라서 '굉장히 쉬운 일' 이라는 관용어로 의역할 수도 있습니다...
이렇듯 컴퓨터에게도 최적화라는 의역이 필요합니다.
아까 함수에 대한 언급이 있었는데요, 어떤 함수들은 꽤 덩치가 큽니다.
하지만 프로그래머들은 편의를 위해서 덩치 큰 함수들을 자주 쓰게 됩니다.
그렇지만 한번 두번 계속 쓰다보면 덩치가 커서 속도도 느려지고 효율이 떨어지죠...
그래서 컴파일러는 최적화를 하게 됩니다.
예를 들어 이런 함수가 있습니다.
주어진 수가 1이면 1을 내보내고,
주어진 수가 2이면 2를 내보내고,
주어진 수가 3이면 3을 내보내고,
.... 해서 10까지 있습니다.
그런데 프로그래머가 1을 입력했으면, 나머지 조건은 거들떠 볼 필요도 없겠죠?
그래서 컴파일러는 나머지를 버리고 첫번째만 CPU에게 전달합니다.
이것은 함수 중에서도 굉장히 단순한 함수이지만, 어떤 함수들은 정말 수백줄이 되는 것들도 있습니다.
그런 함수에서 단순히 한 두 기능 쓰고 넘어간다면 최적화를 시켜야겠죠?
(******사실 컴파일러에 대한 부분은 그냥 이해하기 쉬우라고 실제와 많이 다르게 썼습니다....
컴파일러 자체도 꼭 번역기라 할 수는 없고요 ... 뭐 조금 복잡합니다....
이 부분은 그냥 말이 그렇다는 것이지,,, 너무 진심으로 받아들이지는 마세요...
솔직히 그냥 그런거구나 이해만 하시면 실제 후킹 할때는 별 필요도 없는 부분입니다.******)
그럼 이제 마지막으로 디버깅에 대해서 간단히 설명해 보겠습니다.
5. 디버깅
디버깅은 G2M님의 설명대로 오류를 없애기 위한 것이 본 목적입니다.
단어 자체도 de+bug 라서 버그를 없앤다란 뜻이죠.
하지만 이것이 잘만 이용하면 프로그램이 어떻게 돌아가는 지 알 수 있습니다.
CPU가 하는 일을 메모리에서 일어나는 일을 통해 간접적으로 알 수 있거든요...
하지만 한계는 여기서 드러납니다.
앞에서 보여드렸듯이 함수에는 여러가지 조건들이 있습니다. 기본적으로 한 조건이 충족되면
함수는 끝나버리기 때문에, 단순히 CPU가 뭘했는지만 봐서는 함수의 전체 모습을 알 수가 없습니다.
게다가 최적화로 인해 함수의 많은 부분이 잘려버리게 됩니다.
후킹은 이래서 쉽지 않습니다...
솔직히 함수와 코드를 알 수 있다면 게임의 대사가 메모리 어디에 어느식으로 저장되는지 쉽게 추적 가능하기 때문에
후킹이 훨씬 쉬워지지만, 디버깅을 통한다면 일일이 메모리를 뒤져보고 규칙을 찾는 수 밖에 없습니다.
이정도 이해하셨으면 후에 디버깅의 원리에 대해서도 어느정도 이해가 가실거라 생각합니다...
간단하게 설명하려다 굉장히 길어졌네요...
어떻게 하면 쉽게 설명이 될까 생각하다가 이것저것 별걸 다 쓴것 같은 기분입니다.
얄팍한 지식으로 어떤건 좀 말도 안되게 쓴 것도 있지만, vol 1.5로서 이해만 된다면 대단한 문제는 아닐 듯 싶네요....?!
후킹에 관심있으신 분들께 도움이 됐으면 좋겠습니다.