글 수 217
-아직 작성중이랄까[.......................]
-뭐 일단 올리고 수정하죠 ㅡㅡㅋ
-본 게시물은 어셈블리 기초 부분입니다.
-디버거로 게임을 열었을 시 게임 프로그램 코드는 주로 어셈블리로 보일텐데 그 어셈블리를 알아야
-대사의 흐름을 쫒는데 용이하겠죠.
;이 부분은 프로그램 소스를 짜기 전 미리 선언하는 부분이다.
;따라서 이 강좌에서는 신경을 쓸 필요는 없다.
.586
.model flat, stdcall
option casemap:none
;메모리 영역에 이 프로그램이 사용할 영역을 생성하겠다고
;선언하고 있다. 두개의 메모리 공간을 만드는데
;각 공간의 크기는 20바이트이다.
;마찬가지로이 강좌에서 이 부분에 대해 큰 신경을 쓸 필요는 없다.
.data
work byte 20 DUP(0)
work_2 byte 20 DUP(0)
;여기서부터가 프로그램이 구동하기 위한 실행코드가 적혀있는
;부분이다. 살펴볼 곳은 여기서부터이다.
.code
;C언어에 대한 기초지식이 있다면 알고있겠지만 프로그램에는 무조건
;하나 이상의 함수가 존재해야만 한다. 일반적으로 프로그램에 반드시
;존재해야하는 함수를 main함수라고 한다.
;어셈블리어에서도 이것은 공통적으로 적용되어진다.
main proc
;이 루틴은 어셈블리어에서 말하는 함수 프롤로그라고 하는 루틴이다.
;이 루틴이 존재한다고 하는 것은 다시말해 그 지점부터 함수가 시작된다고
;하는 것과 마찬가지 이다. 그 동작원리는 굳이 알 필요는 없겠지만
;함수가 어디서부터 시작하는 가를 보려면 우선 이와 동일한 루틴을 찾는 것이
;좋은 방법이다. 그렇다고 해서 무조건 함수가 이런 루틴으로 시작하는 것은
;결코 아니다.
push ebp
mov ebp,esp
;이 루틴은 mov명령어에 대해 간단히 설명하고자 짜넣은 루틴이다.
;이 루틴을 통해 데이터가 이동하는 가장 기본적인 방식에 대해 알 수 있게 된다.
mov al,10h ;EAX의 1바이트 공간 AL에 16진수 10을 넣고있다.
;디버거를 포함한 모든 프로그램에서는 숫자, 혹은 문자는
;그 모두가 16진수값으로서 인식되어진다. 명심하고 또 명심하자.
;(10h의 h는 16진수를 의미하는 HEXA의 약어이다.)
mov ah,11h ;EAX의 1바이트 공간 AH에 16진수 11을 넣고있다.
;EAX는 4바이트 공간이다. 이것이 분할되어 사용될 수 있는데
;그 분할되는 구동원리는 아래와 같다.
;00000000 EAX(4바이트)
; 0000 AX(2바이트)
; 00 AH(1바이트)
; 00 AL(1바이트)
;따라서 지금 EAX에는 최초 AL에 10이 들어갔고
;AH에 11이 들어가있으니
;EAX=00001110이 되는 것이다. 다시말해
;0000 11 10 식으로 분할된다.
mov ax,1000h ;AX에 1000이 들어가고 있다.
;현재 EAX에는 0001110이 들어가 있지만 여기에 AX가 1000으로
;들어가면 현재 AX의 값 1110위로 1000이 덮어씌어지는 것을 확인할 수 있다.
mov eax,10000000h ;EAX에 다시 10000000을 넣고 있다. 이번에도 마찬가지로 기존에 존재하던 데이터
;00001000위로 10000000이 덮어씌어지는 것을 확인 할 수 있다.
mov eax,20000000h ;재차 복습이다. 마찬가지로 10000000위로 20000000이 덮어씌어지는 것을 확인 할 수
;있다.
mov ecx,01111111h ;ECX에 01111111이 들어간다.
add eax,ecx ;add명령이다. 이것은 두 값의 합을 목적지 오퍼렌드로 저장하게 된다.
;즉 EAX와 ECX의 합을 EAX로 저장한다는 거다.
;따라서 현재 EAX의 값 20000000은 ECX와 더해져 211111111이 된다.
;반대개념으로 sub라는 명령어가 존재한다. 이것은 add랑 다르게 뺄셈을 수행하지만 그 외의
;구동 방법은 add와 동일하다.
push 00401000h ;PUSH 명령은 스택에 피연산자의 데이터를 집어넣는 연산이다.
;스택 역시 일종의 메모리 공간인데 이 스택이라는 메모리 영역을 통해 많은 데이터의
;백업, 혹은 연산값이 들어가게 된다.
;스택은 기본적으로 FILO(First In Last Out)구조를 가지고 있다.
;사진을 보면 ESP는 현재 0012FFC0을 가리키고 있다. 거기서 우리는 00401000 이라는
;4바이트의 값을 PUSH해 넣었고 그 결과 ESP는 0012FFBC로 올라가게 된다.
;즉 0012FFC0-4가 되는 것이다. 이것은 스택의 구조가 4바이트씩 분단되어지기 때문이다.
;만약 4바이트값이 아닌 push 1을 때려넣는다 해도 같은 4바이트가 소모되어진다.
;여튼 결론적으로 PUSH 00401000을 실행한 결과는
;sub ESP,4
;mov [ESP],00401000
;이 된다.
;그런데 여기서 어째서 빼는데 올라가냐는 질문이 분명 생길 것이다.
;그것은 스택메모리의 시작이 가장 높은 메모리에서 낮은 방향으로 내려가기 때문이다.
;디버거는 이것을 거꾸로 뒤집어 우리가 보기 좋게 표시할 뿐이다.
;본래대로라면 ∪의 형태가 아닌 ∩의 형태가 올바르다.(설마 집합기호 생각하는 사람 없겠지)
;스택은 높은 주소에서 낮은 주소로 흐른다. 절대 착각하지 말자.
;이 루틴은 mov와 lea라고 하는 명령어에 대한 차이를 설명하고 있다.
;mov라는 값이 일반적으로 값을 가져온다면 lea는 주소를 가져오게된다.
;ESP라고 하는 레지스터는 메모리의 주소를 가리키는 레지스터다.
;따라서 사용시에는 무조건 [ ] 를 사용해 주어야 한다. [ ]라고 하는 것은 C에서 말하는 포인터, 즉 주소 연산을 의미한다.
;ESP의 값이 00400000라는 메모리 주소를 가리키고 00400000의 메모리 주소에는 01020304라는 값이 있다고 하자.
;ESP는 00400000이라고 하는 값이 들어가 있다. 그런데 이 ESP라는 녀석은 무조건 [ ]가 사용되어야 한다고 했다.
;그럼 [ESP]가 되는데 이것이 의미하는 것은 바로 00400000에 존재하는 01020304를 가리키고 있는 것이다.
;종합해 설명하면 ESP=00400000이지만 [ESP]=[00400000]=01020304가 된다고 하는 것이다.
;그렇다면 ESP가 가리키는 주소의 값이 아닌 ESP자체의 주소 값을 가져오려면 어떻게 해야할까?
;바로 LEA 명령을 통해 해당 피연산자의 주소를 직접 가져오는 것이다.
;아래의 예제를 진행하며 살펴보도록 하자.
;혹여 언급하지만 [ESP]가 아닌 단순히 ESP만을 사용해 연산된다면 그것은 즉 프로그램 에러와 동일하다.
mov ebx,[esp] ;현재 ESP값은 0012FFBC이다. 그리고 그 곳에는 지금 방금전 PUSH해 넣은
;00401000이 존재한다.
;ESP라고 하는 레지스터는 재차 말하지만 메모리의 주소를 가리키는 레지스터이다.
;ESP와 EBP가 스택에 관여하는 레지스터인건 지난 강좌에서 언급했었다.
;역시 재차 설명하지만 ESP는 스택의 최상의 주소를 가리키고 있다.
;즉 실제 사용중인 스택 영역의 가장 최상위의 끝단을 의미 하는데 거기에 우리는 아까
;PUSH명령을 통해 00401000을 넣었고
;그 결과 자동으로 [ESP]는 한층 더 올라가 00401000을 가리키고 있는 것이다.
;따라서 EBX에는 00401000이 들어가게 된다.
lea edx,[esp] ;LEA명령은 MOV와 다른 동작을 보여주게 된다.
;분명 [ESP]에는 00401000이 존재하게 되지만 LEA를 사용할 경우 [ESP]를 가져오는 게 아닌
;ESP를 가져오게 된다. 다시말해 현재 ESP는 0012FFBC가 들어가있다. 거기엔 아까
;PUSH해 놓은 00401000이 존재하지만 이번엔 0012FFBC를 가져와 EDX로 넣는다고 하는 것이다.
;그런이유로 EDX에는 0012FFBC가 들어가게 된다.
;이것은 ESP나 ESI, EBP의 포인터 레지스터 외에도 각종 주소 연산에서도 사용되는 명령어이다.
lea ebx,[402000] ;이것도 같은 맥락에서 보자 단지 ESP나 EBP가 아닌 402000을 말하고 있을 뿐이다.
;메모리를 확인해보면 메모리 영역 [402000]에는 0으로 채워져있을 뿐이지만 LEA명령어에 의해
;EBX에는 00402000을 가져오게 되는 것이다.
mov eax,11111111h ;EAX에 11111111을 채워넣는다.
mov dword ptr ds:[ebx],eax ;이것이 주소를 이용하는 본격적인 연산의 시작이다.
;DWORD PTR DS:[EBX]라고 하는 기다란 피연산자를 처음으로 보게 될 것이다.
;이것을 설명하려면 우선 아래의 주석을 보도록 하자
;BYTE : 1바이트(00)
;WORD : 2바이트(0000)
;DWORD : 4바이트(00000000)
;QWORD : 8바이트(0000000000000000)
;상기의 주석들이 어셈블리어에서 사용되는 데이터의 단위라고 할 수 있다.
;일단 PTR은 포인터연산을 생각하면 되지만 깊이 생각할 필요는 없다.
;그리고 DS는 데이터 세그먼트(Data Segment)의 약자이다. 이전의 강좌에도 말했지만 프로그램이 사용할
;메모리 영역을 말하고 있는 것이다.
;즉 DWORD PTR DS:[EBX]라고 하는 것은 메모리 영역의 EBX 주소로 시작하는 4바이트 공간이라고 해석할 수 있다.
;요컨데 현재 EBX에는 00402000이 들어가있다.
;00402000으로 시작하는 메모리 주소에 4바이트 길이 만큼, EAX의 값을 넣는다고 하는 것이다.
;실행해본다면 메모리 공간 00402000에 현재 EAX에 들어가있는 값 11111111이 들어가는 것을 확인할 수 있다.
;어지간하면 게임내에서 대사가 이런식으로 들어가진 않는다. 하지만 그렇다고 해서 절대로 그런건 아니다.
;잘 봐두도록 하자.
push 6c6b6a69h ;이번엔 스택에 모종의 값을 넣고있다.
push 68676665h ;아까 언급했지만 재차 언급하자면 프로그램, 즉 기계어란 녀석은 문장이나 숫자를
;결코 우리가 이해하는대로 보고있지 않다. 모든것을 16진수 HEXA값으로 보고있는데
;문자 역시 마찬가지다.
push 64636261h ;HEXA값 61은 소문자 알파뱃 a를 의미한다. 이것을 61 62 63 64...식으로 12개까지 쭈욱 쓰면
;abcdefghijkl이 된다.(영문의 경우는 1바이트가 문자 하나지만 한글이나 일본어의 경우는
;기본적으로 문자의 크기는 2바이트다. 즉 00 00의 형태라고 하는 것이다)
;한가지 봐둘 것이 있다.
;사람의 시점으로는 61626364가 정방향일 것이다. 즉 abcd라는 올바른 문자가 나온다고 하는 것이다.
;하지만 컴퓨터의 시점으로는 64636261이 정방향이다. 이렇게 거꾸로 나열해 줘야 문자가 abcd라고 온전히 나온단 거다.
;만약 61626364를 그대로 읽어들이게 된다면 컴퓨터는 dcba라고 출력하게된다. 기억하자 ㅡㅡㅋ
mov ecx,3 ;ecx에 3을 넣고 있다. 이것이 어째서 연산되었냐 하면 아무 의미없이
;숫자가 들어간 것은 아니다. 밑의 REP연산에서 다시금 참조하도록 하자.
lea esi,[ESP] ;[ESP]의 주소값을 ESI로 넣는데 현재 ESP는 64636261이 들어간 주소
;0012FFB0이 들어가 있다.
lea edi,[402000] ;그리고 EDI에는 아까처럼 00402000이 들어가게 된다.
rep movs dword ptr es:[edi],dword ptr ds:[esi]
;다시 처음 보는 명령어가 존재한다. REP라는 녀석은 반복을 의미한다.
;해당 명령을 정해진 수 만큼 반복하라는 의미인데 기억하는가?
;전 강좌에서 레지스터중 반복횟수에 사용되는 레지스터가 있다고 했다.
;바로 ECX라고 하는 녀석이다. 전 강좌에서 잊어버렸더라도 재차 확인하도록 하자.
;ECX라는 녀석은 REP 외에도 각종 반복횟수에 관여하는데 반복이라 하면 ECX가 빠질 수 없다. 명심하도록 하자.
;이 ECX에서는 위에서 보듯 MOV ECX,3이라는 명령에 의해 3이 들어가 있다.
;그리고 이어지는 MOVS. 이녀석은 MOV String의 약자로서 문자열을 복사한다고 하는 것이다.
;그 유형은 위에 명령어에 나온 그대로 인데 보다 자세히 보면
; MOVS DWORD PTR ES : [EDI], DWORD PTR DS : [ESI]
; 문자열 이동명령 (이동할 크기)옮길 목적지,(이동할 크기)옮겨갈 문장의 원본이 존재하는 영역
;이런식으로 된다. 이미 위의 과정을 어느정도 이해했다면 이 말이 무슨 의미인지 알 수 있을 것이다.
;자 그럼 이제 종합해 명령어를 분석해보자.
;ECX에는 3이 들어와있다. 따라서 REP에 의해 해당 명령어는 3회 실행되게 된다.
;MOVS에 의해 [ESI]로부터 [EDI]로 문장이 이동하게 된다. 물론 [ESI]에는 어떠한 값들이 존재하고 있다.
;재차 나오는 DWORD PTR이다. 위에서 설명했던 어셈블리어의 데이터 단위를 생각해보자. 이것은 4바이트크기를 의미한다고 했다.
;그렇다면 슬슬 감이 오지 않는가? [ESI]부터 4바이트씩 복사해 [EDI]로 갖다 놓는데 그것을 3회 반복한다 라는 것이다.
;4곱하기 3은 12이다. 이쯤되면 결론이 보일것이다. [ESI]의 주소부터 시작해 12바이트 크기를 [EDI]로 갖다 놓는 다는 것이다.
;그런데 여기서 질문이 생길 것이다.
;[ESI]부터 [EDI]로 4바이트를 움직이는 명령이 3회 반복되는데 그럼 같은 명령을 3번 반복하는게 아닌가 하는 의문이다.
;결론부터 말하자면 그럴일은 없다.
;해당 명령이 수행되면 ESI와 EDI는 자동으로 명령문이 지정한 문장의 크기(DWORD)만큼 더해지게된다.
;즉 명령이 한번 실행될때마다 자동적으로 ESI와 EDI는 다음에 복사할 지점과 다음 복사될 지점을 자동으로 계산한다고 하는 점이다.
;걱정하지 말고 알고는 있자.
;그렇다면 저 명령은 지금 현상에서는 어떻게 구동될까?
;ESI에는 현재 ESP의 주소가 들어가 있다. 거기엔 ESP부터 ESP+8까지 쭈욱 총 12바이트를 차지하면서
;abcdefghijkl(6162636465666768696a6b6c)가 들어있다.
;EDI에는 현재 00402000이 들어있다. 이것을 [EDI]라고 하면 메모리 주소 00402000이 된다.
;ECX에는 3이 들어있다. 그렇다면 4바이트씩 3회에 걸쳐서 [ESI]의 abcd...문자열을 [EDI]로 최종적으로는 12바이트를 움직이게 되는 것이다.
;따라서 [ESP]~[ESP+8]까지의 12바이트 크기 메모리에 들어있는 a~l까지의 12문자는 [EDI]의 메모리 주소 00402000으로 복사하게 된다.
;그 과정을 보도록 하자.
;이것을 이해한다면 스택이란 것도 결국 크게 다를바 없는 메모리의 영역이란 것을 알 수 있게된다.
;뿐만 아니라 REP MOVS는 게임에서 대화를 이동할때 주로 사용하는 명령이기도 하다.
;유심히 보도록 하자.
pop eax ;POP명령이다. 이것은 PUSH명령과는 완전히 반대라고 생각하면 된다.
pop ebx ;PUSH가 스택에 데이터를 넣었다면 POP은 스택에서 데이터를 뺀다.
pop ecx ;정확히 말하면 POP명령의 레지스터 혹은 메모리 주소로 현재 ESP가 가지고 있는 데이터를
pop edx ;옮긴 뒤 ESP주소를 4바이트 만큼 증감시킨다. 즉 ESP=ESP+4가 된다.
;따라서 POP EAX는 현재 [ESP]에 들어가있는 데이터를 EAX로 넣고 ESP가 한층 내려가는 것을 확인 할 수 있다.
;이렇게 4개의 레지스터에 POP하게 되면 최종적으로 ESP는 4층이 내려오게 되니 ESP=ESP+16이 된다.
jmp test0 ;드디어 나오는 점프 명령이다.
;JMP는 피연산자 주소를 향해 문자 그대로 무조건 점프하게 된다
;따라서 test0으로 프로시저가 이동하게 된다. TEST0으로 쫒아가보자.
test6: ;문장 복사 루틴이 끝나고 나온 JMP TEST6에 의해 이곳으로 오게 됐을 것이다.
call g2m ;다시 처음 보는 명령어의 등장이다. 이것은 CALL명령이라 하는 것이다.
;오퍼랜드에 존재하는 주소를 호출하게 되는데 이것은 현재 위치한 함수에서 새로운 함수를 호출해 오는 것이다.
;이것이 실행될 경우 프로시저는 해당 주소로 이동하게 되는데 그러면 JMP명령과 다를 바 없을 것이다.
;CALL은 JMP와는 조금 다른 구동방식을 보인다.
;레지스터중 EIP라는 녀석이 존재하는 것은 지난 강좌에서 보았을 것이다. 이녀석은 언제나 다음에 실행해야할 명령어 주소를 가리키고 있다.
;이 CALL이란 녀석은 JMP랑 다르게 EIP를 이용해 되돌아올 주소를 저장한다. 즉 갔다가 다시 되돌아 온다고 하는 것이다.
;CALL이 동작하는 자세한 구동방식은 아래와 같다.
;
; PUSH EIP
; JMP G2m
;
;CALL명령을 맞닥트리게 되면 CALL다음에 수행할 명령의 주소를 스택에 넣게 된다.
;그리고 CALL이 호출하고 있는 주소로 점프한다. 이것이 CALL이 갔다가 되돌아 올수 있는 이유다.
;돌아올 주소는 무조건 CALL다음에 실행되는 주소이다. CALL이 위치하는 주소로 되돌아오면 도로 CALL이 실행되어버리기 때문이다.
;따라서 CALL다음에 존재하는 주소가 CALL이 실행되고 돌아와 곧바로 실행되는 것이다.
;이것이 어떻게 돌아오는 지는 이제 G2m의 주소를 향해 쫒아가보도록 하자. 저 맨 아래에 위치해 있다.
jmp testexit ;CALL이 끝나 RETN하게 되면 이곳으로 오게 된다. 그리고 이곳을 실행하면
;TESTEXIT로 가게 된다. 다시 밑으로 쭈욱 내려가도록 하자.
test0: ;최초의 JMP TEST0을 통해 이곳으로 왔다.
;이 루틴부터는 이제 핵심중 하나인 조건 분기에 대해 알아보도록 하자.
;조건 분기라 하는 것은 조건에 따라 수행하느냐 안하느냐의 이야기다.
;인공지능이나 필터라 하는 것은 바로 이런 조건분기를 통해 수행되어지는 것이다.
mov eax,10h ;eax에 10을 넣고있다.
mov ebx,20h ;ebx에 20을 넣고있다.
mov ecx,10h ;ecx에 10을 넣고있다.
cmp eax,ebx ;이것이 바로 비교 명령이다. eax와 ebx를 비교해 어느쪽이 크거나 작은지 혹은 같은지를
;판별하게 되는데 그 결과를 바로 상태 플래그란 곳에 저장한다.
;이것은 지금 자세하게 알 필요는 없다. 기본적으로 어떻게 돌아가는 지를 집중적으로
;보기로 하자
jl test2 ;JL은 목적지 오퍼랜드가 소스 오퍼랜드보다 작을 경우 발동된다.
;현재 EAX에는 10이 들어가있고 EBX에는 20이 들어가있다.
;따라서 조건이 맞아 떨어지므로 TEST2로 점프하게 된다.
;만일 조건이 맞지 않는다면 이 명령은 완전히 무시되고 바로 다음 명령어가 실행되게 된다.
test1: ;JG TEST1을 통해 이곳으로 왔을 것이다.
cmp eax,ecx ;이번에는 EAX와 ECX를 비교하는데 둘다 같은 10이 들어있다.
je test3 ;짐작했겠지만 두 값이 같을 경우 발동하는 명령어이다.
;TEST3으로 쫒아가도록 하자.
test2: ;JL TEST2를 통해 이곳으로 왔을 것이다.
cmp ebx,eax ;이곳에서는 EBX와 EAX를 비교하고 있다. EBX에는 20이 EAX에는 10이 들어가있다.
jg test1 ;JG라는 명령어는 목적지 오퍼랜드가 소스 오퍼랜드보다 클 경우 발동된다.
;EBX가 EAX보다 크기때문에 해당 명령역시 실행되어져 TEST1로 점프하게 된다.
;조건분기를 끝마쳐 도달하는 곳은 이곳이다. 분명 JE TEST3을 통해 이곳으로 도달했을 것이다.
;이 루틴에서는 지금까지 보았던 각종 명령어가 구체적으로 어떤식으로 조합되어 사용되는지 알아보도록 하자.
test3: ;JE TEST3을 통해 왔을 것이다.
lea eax,[402014] ;EAX로 [402014]의 주소가 들어가게 되므로 EAX에는 402014가 세팅되어진다.
lea ebx,[402000] ;EBX로 [402000]의 주소가 들어가게 되므로 EBX에는 402000이 세팅되어진다.
test4:
mov cl,byte ptr ds:[ebx] ;CL(ECX의 1바이트 공간)에 EBX가 가리키는 주소의 1바이트 값을 옮긴다.
;EBX가 가리키는 402000에는 아까 우리가 옮겼던 a~l이 존재한다.
;그중 한 바이트라면 61 즉 a를 cl로 옮긴다고 하는 것이다.
cmp cl,0 ;복사해온 CL을 0과 비교하고 있다. 처음은 물론 같지 않다
je test5 ;그 둘이 같으면 TEST5로 점프하라고 하고있다.
mov byte ptr ds:[eax],cl ;CL을 EAX가 가리키는 주소 402014로 한바이트를 이동하라고 하고있다.
;즉 CL에 들어있는 값 61(알파뱃 a)를 402014로 옮기라고 하는 것이다.
inc eax ;처음 보는 명령어다. 이것은 increase의 약어인데 실행시 해당 오퍼랜드의
;값을 1 상승 시킨다. 따라서 EAX의 402014는 1이 더해져 402015가 된다.
inc ebx ;마찬가지로 EBX의 값이 1상승되어 EBX는 402001이 되어진다.
;그러면 EBX는 현재 a다음의 값 b를 가리키고 있다.
;여담이지만 반대되는 명령어로 dec(decrease)라는 명령어도 존재한다.
;짐작 하듯이 그것은 1이 감소하는 효과를 낸다.
jmp test4 ;그리고 TEST4로 무조건 점프 하게된다.
;여기까지 차례대로 와 다시 TEST4를 살펴보기 전에 잠시 이 주석을보도록 하자.
;이것은 반복하고있다 연산을 다 처리해 끝으로 가면 도로 JMP TEST4로 되돌아 가도록 하고있다.
;그럼 이것을 빠져나가는 연산은 어디있는걸까?
;이미 눈치채고있겠지만 CMP CL,0에서 이어지는 JE TEST5가 바로 이 무한 반복을 빠져나가는 열쇠일 것이다.
;생각해보자 이 연산은 MOV CL,BYTE PTR DS:[EBX]와 MOV BYTE PTR DS:[EAX],CL을 통해 한문자씩 계속해 옮겨질 것이고
;INC EAX와 INC EBX에 의해 계속해 다음 복사할 문자와 다음 복사할 위치를 가리키게 될 것이다.
;그런데 옮기려는 CL에 들어가는 값이 00이면 거기서 문장을 종료하고 빠져나가라고 하고있다.
;올리디버거를 이용해 메모리창을 봐 현재 원본 문자열이 존재하는 00402000을 보도록 하자.
;문자가 쭈욱 이어지다가 마지막의 l다음을 보면 바로 00으로 펼쳐져 있을 것이다.
;결국 문자를 전부 복사하면 이 반복문을 빠져나가라고 하는 것이다.
;다시말해 이 루틴은 모든 문자열을 메모리 주소 00402014로 한 문자씩 쭈욱 복사한 다음에 그것을 전부 복사하면
;빠져나간다는 것과 마찬가지 의미다.
;CL이 0을 가지게 되면 JMP에 의해 TEST5로 점프하게 될테니 모든 루프를 다 돌았다면 TEST5로 가보도록 하자.
test5: ;문자를 옮기는 루틴을 지나면 이곳으로 오게 될 것이다.
jmp test6 ;이번 루틴은 간단히 TEST6으로 이동하라고 말하고 있다.
;TEST6은 이 점프문을 배우기 이전에 존재한다. 위로 쭈욱 올라가보자.
testexit: ;CALL과 RETN을 끝내고 나면 이곳으로 도달하게 될 것이다.
;이곳은 제일 처음 시작했던 함수 프롤로그를 정리하는 구간이다.
;이 루틴을 거치게 되면 사실상 구동중인 프로그램은 종료된다.
;여기까지 잘 찾아왓다. 그동안 위아래로 왔다갔다 하느라 고생했겠지만
;어느정도 이해할때까지 강좌2와 강좌3을 계속해 반복해 줬으면 한다.
mov esp, ebp
pop ebp
retn
main endp ;종료 끝.
;G2m함수를 실행한다고 선언하는 부분이다.
g2m proc
nop ;어셈블리 명령을 보면 저 NOP란 명령을 자주 보게 될것이다.
nop ;그것이 내포하는 의미는 아무것도 없다. 그냥 무시하고 쭉 내려가면 된다.
retn ;그리고 새로나오는 명령인 RETN이다.
;이것은 Return의 약어이다. 돌아간다는 의미에서 아까 CALL이 백업한 돌아갈 주소가 연상되지 않는가?
;이녀석은 CALL에 의해 백업된 주소로 되돌아간다. 현재 ESP에는 아까 CALL이 백업한 CALL다음에 실행될 명령어의 주소가 백업되어져있다.
;이녀석이 구동하는 원리는 아래와 같다.
;
; pop eip
;
;EIP는 다음 실행해야할 주소가 들어가는 레지스터란 것을 이미 앞전에 배웠을 것이다.
;즉 스택에 백업해둔 돌아갈 주소를 EIP로 넣게되면.
;다음 실행해야할 명령주소가 CALL 지점의 다음 명령문이 되는 것이다.
;다시 위로 올라가도록 하자.
;여기는 코드 종결을 이야기 하는 부분이다.
g2m endp
end main
------------------------
이걸 써놓고 느낀건데 이보다 더 기초 내용이 선행되어야 하는게 아니었나 하는 생각이 듭니다[...............]
그래서 그동안 ㅈㅈ치고있었죠.
사실 이정도만 알아도. 코드파인드에 필요한 지식은 전부 클리어 된것과 마찬가지긴 합니다.
추가로 첨부합니다.
이게 아직도 남아있었군요 -_-;;
위의 코드를 실제 실행파일로 만들어 올리 디버거로 확인할 시 어떤 흐름으로 프로그램 코드가 진행되는지를 gif로
만들어 보았습니다.
이걸 프레임 단위로 볼 수 있으면 좋겠는데 말이죠. 하여간 위의 게시글과 병행해 보시는 것을 추천합니다. ㅡㅡㅋ
->추가로 첨언하면 MOV CL,BYTE PTR DS:[EBX]로 시작하는 반복문이 한번 반복한 후 진행하다 갑자기 워프해버리는
부분이 보일텐데. 귀찮아서 루프 탈출해 버렸어요[....]
->어케 탈출하는지는 본문을 봐주세요 ㅡㅡㅋ
오오 ~ 정말 감사드립니다.
코드 추출할려고 정말 막무가네로 뒤적뒤적 거렸던 기억이.. ㅠ_ㅠ
많은 도움이 될꺼 같습니다.