본문 바로가기
코스웨어/12년 내장형하드웨어

LDST 자세한 정리

by 알 수 없는 사용자 2012. 9. 12.
728x90
반응형

●LDST (초기상태로 되돌아오는 어셈블리 함수)

어떠한 일을 수행하고 함수가 수행되기 전의 상태로 되돌리고 싶을 때 이 함수를 사용한다. 이것은 함수의 호출을 이용한 것인데 C언어로는 이해할수 없는 것이다. 초기상태로 되돌리고 싶으면 제일 처음 레지스터 상태를 저장한 곳이 있어야 하며 그곳의 정보를 LDST함수는 다시 CPU에 적재하는 것이다.

일단 소스를 보고 생각해보자.

 

▶ldst.asm

.386
.MODEL FLAT
PUBLIC _LDST

.CODE

_LDST      PROC      NEAR32
;===[1] popad 사용=======================================================================================
           mov esp, [esp + 4]

           popfd ; EFL input

           pop eax ; eax = eip(R/A) eip값을 eax에 백업
           mov ebx, [esp + 12] ; ebx = esp_main
           mov [ebx - 4], eax ; eip를 ebx-4자리에 넣음.

           popad

           mov esp, [esp - 20]
           sub esp, 4
;===[2] popad 사용안함=======================================================================================
           ;mov esp, [esp + 4]

           ;popfd

           ;pop eax ; eax = eip(R/A)
           ;mov ebx, [esp + 12] ; ebx = esp_main
           ;mov [ebx - 4], eax

           ;pop edi

           ;pop esi

           ;pop ebp

           ;add esp, 4

           ;pop ebx

           ;pop edx

           ;pop ecx

           ;pop eax

           ;mov esp, [esp - 20]
           ;sub esp, 4
           ret
_LDST      ENDP

END

popad를 사용한것을 중점적으로 살펴 볼 것이다.

☞초기상태

go함수가 호출되고 go함수안의 ldst함수가 호출된 상황의 그림이다. 여기서 주의해야 할 것은 entry code가

없어야 한다는 것이다. 그 이유는 ldst함수는 구조체에 저장된 정보의 위치로 돌아가기 위한 함수이므로 지금

ldst함수에서 레지스터에 어떤 값을 쓰든 간에 모두 필요없는 정보이기 때문이다.

인자로 context주소가 넘어왔으므로 그림에 go위에 context&가 저장되어 있다. (assem code : push &r)

다음은 ldst함수를 call 하여서 R/A가 위에 저장되어있다. (assem code : call ldst)

그리고 esp는 R/A를 가리키고 있다.

ldst의 목적은 구조체의 정보를 CPU로 옴기는 것이다. 어차피 go함수는 사용하지 않는 일시적인 것이어서

이것의 정보는 사용하지 않는다. ( 지워져도 상관없다. 또한 적재하지 않은 레지스터 공간은 마음대로 사용

가능하다.)

 

☞mov esp, [esp + 4]

지금 해야할 것은 필요한 정보를 가지고 있는 구조체의 주소를 어떻게 이용할 것인지 생각해보아야 한다.

자유롭게 이용 할 수 있는 esp이용한다. [esp + 4] : esp를 +4한 곳의 값이라는 뜻. 즉, Context &가 된다.

mov esp, [esp + 4] : 그 주소를 esp레지스터에 넣으니 esp는 우리가 필요한 정보를 가지는 구조체를 가리키게 된다.

 

☞popfd

pop명령어는 esp가 가리키는 곳의 값을 레지스터에 되돌리는 명령어이며, 수행후 esp는 떨어진다.

popfd는 efl를 사용할때 쓰는 명령어이다. efl과 eip는 assembly에 선언되지 않아 이것의 이름을 사용할 수없다 그래서 이 명령어를 사용하는 것이다.

efl을 CPU에 넣고 esp는 한칸 떨어져 있다.

 

☞pop eax

그 다음은 eip를 저장해야 한다. 그러나 eip는 efl과 같이 직접사용이 불가능하며 간접적으로 사용해야 한다. 또한 eip는 R/A이기 때문에 이것을 바로 레지스터에 넣을 수는 없으며 ret때 eip값(R/A) 자동으로 pop되므로 이것을 자연스럽게 유도 할 수 있는 자리로 이동시켜 놓아야 한다. 우리가 함수호출을 할때 배운것은 R/A는 함수바로 위에 저장됨을 알고 있다. 우리는 여기서는 main함수로 돌아갈 것이므로 main함수위에 R/A(eip)가 들어가야 할 것이다.

eip를 적절한위치에 넣기전까지 eip를 임시로 eax에 저장하였다. 그리고 esp는 한칸 떨어져 있다.

 

☞mov ebx, [esp + 12]

이 코드는 eip의 위치를 찾기위해서 ebx를 임시로 사용한 코드이다. 자세히 살펴보자.esp는 pop eax코드로 인해 edi를 가리키고 있다. 그래서 [esp + 12]는 esp_main이다. mov ebx, [esp + 12] 은 esp_main을 ebx에 넣었다.

위 그림에서 중요한 것은 ebx가 esp_main을 가리키고 있다는 것이다. 이것은 eip의 위치를 지정하기 우해서 꼭 필요한 것이다. 왜냐하면 main위에는 어떻게 접근할 수 있는 방법이 없으며 오직 esp_main을 기준으로 접근할 수 밖에 없기때문이다.

 

☞mov [ebx - 4], eax

eax는 위 코드에서 eip를 넣었으므로 eip(R/A)이다.

ebx - 4는 ebx에서 한칸 올라간 위치이다. ebx는 esp_main이라고 했으니 main바로 위가 된다.

mov [ebx - 4], eax는 결국 main바로 위에 eip(R/A)를 넣는 것이다. 이 위치에 R/A가 있어야 ret할 때 이곳에 있는 eip(R/A)가 CPU의 eip레지스터로 pop되게된다.

 

☞popad

popad는 esp가 가리키고있는 위치의 정보를 순서대로 CPU로 되돌리는 명령어이다.

그림에서 보면 esp에서 값이 들억지 않았는데 이것은 popad명령어의 특징으로 esp에는 값을 쓸 수 없다. 또한 esp가 popad명령을 다 수행하고 난 뒤에 esp는 구조체 한칸 아래쪽으로 떨어져 있음을 알 수 있다.

 

☞mov esp, [esp - 20]

[esp - 20]에서 esp - 20은 esp를 5칸 위로 이동시킨다는 것을 말한다.(왜 5칸 위로 올라가는지 모른다면 다시 공부 하길 바란다.) 이 상태에 []를 하였으므로 이 위치의 값을 지칭하는 것이다. 즉, esp_main을 말한다. mov esp, [esp - 20]은 esp_main을 CPU의 esp레지스터에 넣는다느 말이다.

위 코드가 실행되면 초록색 화상표가 가리키는 곳이 esp의 위치가 된다. 구조체를 가리키고 있다가 이제는 main으로 돌아온 것이다.

 

☞sub esp, 4

정상적으로 종료를 하려면 R/A를 되돌려야 한다. 그런데 esp가 지금 esp_main을 가리키고 있으니 esp위치를 한칸 위를 가리키게 하기 위해서 sub esp, 4를 한 것이다.

 

☞ret

최종적으로 eip(R/A)를 eip레지스터로 되돌려야만 처음 상태로 돌아갈 수 있다. ret는 eip를 pop하는 명령어 이다.

그림에서 CPU를 보면 보든 레지스터에 값이 구조체의 값과 같아졌다. 완성!

 

▶실행결과

go명령어 실행이후 메모리 영역이 출력 되어서 초기 상태로 돌아간 것을 알수 있다. 성공!!

 

728x90