문맥교환(컨텍스트 스위칭)을 연습해 보았다.


main.c

#include <stdio.h>

typedef struct _context
{
  int  efl;
  int  eip;
  int  edi;
  int  esi;
  int  ebp;
  int  esp;
  int  ebx;
  int  edx;
  int  ecx;
  int  eax;
} CONTEXT;

void STST( CONTEXT *stpReg );  // CPU의 정보를 메모리에 저장
void LDST( CONTEXT *stpReg );  // 메모리에 저장된 CPU 정보를 재등록
void PrintRegister( CONTEXT *stpReg );

int main()
{
  CONTEXT  stReg =  { 0, };
  PrintRegister( &stReg );

  STST( &stReg );
  PrintRegister( &stReg );

  getchar();

  LDST( &stReg );

  printf( "Kernel Panic\n" );
  return 0;
}

void PrintRegister( CONTEXT *stpReg )
{
  printf( "┌──────RegisterStatus──────┐\n" );
  printf( "│EAX : 0x%08X\t", stpReg->eax );
  printf( "ECX : 0x%08X│\n", stpReg->ecx );
  printf( "│EDX : 0x%08X\t", stpReg->edx );
  printf( "EBX : 0x%08X│\n", stpReg->ebx );
  printf( "│ESP : 0x%08X\t", stpReg->esp );
  printf( "EBP : 0x%08X│\n", stpReg->ebp );
  printf( "│ESI : 0x%08X\t", stpReg->esi );
  printf( "EDI : 0x%08X│\n", stpReg->edi );
  printf( "│EIP : 0x%08X\t", stpReg->eip );
  printf( "EFL : 0x%08X│\n", stpReg->efl );
  printf( "└───────────────────┘\n\n" );
}



monitor.asm

.386

.MODEL FLAT


PUBLIC _STST ; Store Status

PUBLIC _LDST ; Load Status


.CODE

_STST PROC NEAR32

PUSH EBP

MOV EBP, ESP


PUSHFD


AND DWORD PTR [EBP-4], 0FFFFFEFFh


MOV ESP, [EBP+8]


ADD ESP, 40


PUSHAD


ADD ESP, 16

;Old EBP, RA, 인자, Main과의 경계

MOV EAX, EBP

ADD EAX, 8


PUSH EAX ; 메인 ESP


PUSH [EBP] ; Old EBP


SUB ESP, 8


PUSH [EBP+4] ; EIP(Return Address)

PUSH [EBP-4] ; EFL


MOV ESP, EBP

POP EBP

RET

_STST ENDP


_LDST PROC NEAR32


MOV ESP, [ESP+4] ; ESP = &Context


POPFD ; EFL = Context.EFL

; 플래그 레지스터에 값을 넣음


POP EAX ; EAX = Context.EIP

; 예전 EIP를 EAX 저장


MOV EBX, ESP ; EBX = Current ESP

; 현재 ESP 백업


MOV ESP, [ESP+12] ; ESP = Old ESP

; 과거 ESP로 점프


PUSH EAX ; Save Old EIP

; 과거 ESP에 EAX를 푸쉬하여 RA덮어씀


MOV ESP, EBX ; ESP = Current ESP

; 백업해둔 ESP를 다시 복원


POPAD ; ESP를 제외한 모든 값을 복원


MOV ESP, [ESP-20] ; Old ESP값을 복원

SUB ESP, 4 ; ESP를 RA위로 올려주어야 한다(RET이 POP하기때문)


RET

_LDST ENDP

END



결과


실행 순서

1

CPU 정보 구조체 선언 및 초기화

2

구조체 내용 출력

3

구조체에 CPU 정보 저장

4

구조체 내용 출력

5

구조체에 저장된 정보를 CPU 레지스터에 넣음

(STST의 RA가 가르키는 곳, 즉 4로 되돌아감)

6

(실행되지 않음)

Kernel Panic 출력


 실제로는 STST가 수행된 직후 상황을 강제로 재구현하는 방식이다. 원래라면 LDST를 실행한 후 printf를 실행하여 Kernel Panic이 출력되며 종료되어야 하나, LDST로 CPU정보를 STST에서 저장한 값으로 바꾸어버렸기 때문에 무한루프를 돌게 된다.



 pushad로 넣은 값을 읽어들일 때 popad를 쓸 수 있는데, popad는 ESP의 값을 쌩깐다는 점에 유의한다.(ESP의 위치는 이동하지만 값은 유지되어있다) POP은 ESP에서 이루어지는데 ESP가 도중에 변경되면 지장을 줄 수 있다는 문제때문인 듯 하다.


 끝에 SUB ESP, 4를 해주는 이유는 RA의 위로 이동하여야 제대로 RET명령이 POP한 주소로 이동되는 방식으로 수행되기 때문이다.

+ Recent posts