코드 가상화 개인 공부 [0x04]

2017. 12. 20. 23:280x03 Reversing Theory

728x90

이번에는 질문을 하나 던지고 시작할까합니다.


역 공학(Reverse Engineering)을 막으려면 어떻게 해야할까요? 


프로그램을 안만들면 되지? (X)

프로그램은 만들어야 먹고 삽니다 ^ , ^ 


프로그램은 0과 1로 이루어져 있기 때문에 "막을 순" 없습니다.


그럼 질문 자체가 잘못되었다는 것 이겠죠?


역공학을 최대한 지연시키려면 혹은 분석 할 맘이 안들게 하려면? 


==> 안티디버깅이라는 방법을 사용하면 됩니다. 


그렇다면 어떠한 안티디버깅 방법이 있는지 궁금할 수 밖에 없을 것입니다.


지금 부터 하나하나 살펴보겠습니다. 

설명을 할 때는 반말로 적겠습니다.  (양해 바랍니다..)


Start ~~ ♪


안티 디버깅 기술에는 크게 4가지가 존재한다. 

예전에 안티 디버깅에 대해 간단하게 공부를 하고 PDF 형식으로 올려두었다. 


이 논문을 참고하면서 아직까지 몰랐던 것이 많다는 것을 알게 되었다.


일반 디스어셈블러,  타켓 디스어셈블러, 일반 안티디버깅, 타켓 안티디버깅으로 해당 논문에서는 분류 시켜 두었다.


하나 하나 살펴보면서 이해를 해보자.


일반 디스어셈블러

Context modification 

Context가 익숙하지 않은 분들이 있을 것 같아 간단하게 설명할까 한다. 

context를 모르면 해당 기법을 이해하기 어려울 것이라 판단되기 때문이다.


context는 우리말로 풀면 "문맥"이라고 한다.


문맥을 말하기 전에 교착상태(Dead Lock)에 대해서도 언급하려 한다.

우리의 프로그램이 실행상태가 되면 '프로세스'가 되었다고 표현한다.  

사람들도 어떤 일을 할 때 자원이 필요하듯이 (ex. 벽에 못을 박기 위해 망치를 쓰는 행위) 프로세스도 자신을 구동시킬 수 있는 자원이 필요하다. 이 자원을 통솔하는 녀석은 바로 CPU이다. 

CPU가 만일 한정 된 자원을 소유하고 있다면(ex. 프로세스는 2개인데 자원은 1개다) 서로 서로 싸우지 마라고 순서를 지정해 준다. 

프로세스 별로 스케줄을 부여해주는 거다. 


쉽게 접근하기 위해 군대에서의 근무시간으로 생각해볼 수 있겠다. 


A조가 근무에 나서게 되면, B조는 생활관에서 편하게 휴식을 취할 것이다. 

A조의 근무 시간이 끝나게 되면 비로소 B조는 교대를 하고 B조가 근무를 서게 된다. 


근무 = 일,      A,B조 = 프로세스,     근무시간 = 자원,   간부 = CPU라고 생각하면 된다. 


만약 어느 간부가 병사들의 스케줄을 겹치게 해두었다고 가정하자.

스케줄이 겹쳤다는 것은 근무시간을 의미하겠다.  A와 B조 두 팀다 간부(CPU)가 명령했기 때문에 따르기 위해 근무지로 향한다.

그럼 근무지에는 A와 B팀 둘 다 머물게 된다.  그럼 두 팀은 실갱이를 벌일 것이다. 내 근무 시간인데 왜 너가 와 있니? 라고 .. 

하지만, 드라마틱이 아니라 현실적인 군대였다면 "아 씨 니가 왔으면 나 안올걸 나 잘걸 이라고 할 것 같다...근무는 정신적으로 힘들당.. 


실갱이를 벌이다 보면 정작 해야하는 일(근무)을 대충 하거나 하지 못하게 되어버린다. 


군대에서는 근무라는 일이 중요한 과업인데 이 과업을 소홀히 하게 되면 해당 부대에 차질이 생기게 된다.


잡담이 길었다. 


본격적으로 문맥의 세계에 빠져보자.


일단 문맥이 무엇일까?  

한글 : 서로 이어져 있는 문장의 앞뒤 관계 [구글 사전]


그렇다면, 프로그램에서 이어져 있는 문장의 앞뒤 관계는 무엇일까?


프로세스가 가지고 있는 모든 정보가 된다.


그 정보에는 무엇이 있을까? stack, heap, cpu 내 레지스터 값 등이 있을 것이다. 


추가적으로 PCB라는 것도 있다. Process Control Block이라고 하는데 여기에 프로세스들에 대한 정보가 잔뜩 잔뜩 담겨있다. 


그렇다면, 프로세스 제어 블록에는 무엇이 있는걸까? 


1. 프로세스 번호 

---- 각각의 프로세스는 저마다의 번호를 가지고 있다. 현실세계에서 동명이인이 가능하지만, 그 동명이인이 부모님이 같거나 생일이 같거나(이 같은 경우는 Birthday Paradox 로 가능성은 있지만  암튼 부모님이 같을 순 없는 것 처럼 프로세스의 아이디는 중복 될 수 없다. 

여기서 말하는 프로세스 아이디는 회원가입 시 사용하는 아이디라고 보긴 힘들다. 

회원가입 시 사용하는 아이디라고 생각하면 헷갈릴 수 있기 때문이다. 

물론 회원가입 때도 중복되는 이름은 가입이 불가능한 조건이 부여가 된다. 

하지만, 프로세스 아이디는 실행 할 때 마다 그 아이디 값이 변경 되기 때문에 회원가입에서의 아이디라고 생각하지 마라고 하는 것이다.


2. 프로세스 상태

사람도 사람관계를 맺으러 사회에 외출을 할 때가 있고, 집에서 푹 쉴 때도 있다. 프로세스 역시 마찬가지인 것이다.

프로세스도 24시간 편의점 처럼 운영을 할 순 없는 노릇이다. 필요한 자원이 오지 않았는데 굳이 CPU 할당량을 차지하면서 있을 필요가 없다.

프로세스의 상태는 크게 4가지로 나누어 진다. 준비, 실행, 대기, 보류 이다. 

이것에 대해 설명을 하면 너무 블로그 포스팅 양이 길어지므로 공부 후 만들어 둔 자료를 사진으로 대신해봤다.

그림 사진 같은 경우는 대학 3학년 때 배운 운영체제 책에서 가져 왔다. 


항상 느끼는 거지만, 복잡한 프로그램의 흐름을 간략하게 도식화 해서 표현해주시는 분들은 멋지다. 



 생성 부분에 대해서 간략하게 적어보면 사용자가 작업을 요청하면 그 요청된 정보는 커널에 등록 된다.

 PCB가 만들어져 프로세스가 만들어진 다음, 준비나 보류 상태로 되기 위해서 잠시 거치는 곳이다.

 프로세스 생성 후 메모리 공간을 검사하여 충분한 공간이 있으면 메모리를 할당하면서 준비상태로 바꾸어 주고, 그렇지 않으면 보류 준비 상태가 된다.



준비 부분은 다음과 같은 특징을 지니고 있다. CPU에게 컨택 되기 위해 즉, CPU에게 할당 받기 위해 기다린다.

준비 큐를 이용하여 여러 프로세스들을 저장한다.

준비 상태의 프로세스들은 순서에 따라 CPU를 할당 받으면 실행 상태가 된다.

모든 프로세스는 순서가 짜여 있는데 그 순서를 정하는 것은 "CPU 스케줄링" 이라 한다.

준비 상태는 가게에 곧 들어갈 수 있는 손님이라고 생각하자.

손님은 큐 구조로 들어가기 때문이다. 새치기는 아주 아주 나쁜 것 !! 



이제 실행부분이다.

우리의 프로세스는 CPU의 컨택, 즉 CPU에게 할당 받으면 실행상태로 변환한다.

하지만 실행 상태의 프로세스는 무적은 아니다. CPU 스케줄링 정책에 의해 CPU를 뺏길 수 있다.

CPU를 선점 당하면 해당 프로세스는 다시 준비상태로 돌아간다.

시간 할당량(Time Quantum)이 소진되어 뺏길 때 "시간 종료(Time Out)"라고 표현한다.

실행 상태의 프로세스가 입출력이 필요하게 되어 시스템 호출을 하게 되면 입출력 처리가 끝날 때까지 대기상태로 바뀌게 되고 CPU는 준비 상태의 프로세스들 중에서 하나를 선택하여 그 프로세스 에서 자신(CPU)을 할당하게 된다.

실행 상태는 가게에 입장한 손님으로 생각하자.


 

프로세스가 실행되다가 다른 아이가 입출력 처리를 요청하거나, 바로 확보 될 수 없는 자원을 요청하게 되면 

CPU를 양도하고, 요청한 일(=다른 프로세스가 행하는 일)이 완료되기를 기다리면서 대기하게 된다.

대기 상태는 준비 손님보다 더 늦게 와서 웨이팅이 좀 뒤에 있는 손님이라고 생각하자.




프로세스가 종료 될 때 아주 잠시 거치는 곳이다.

프로세스에 할당 되었던 모든 자원들이 회수되고, PCB만 커널에 남아 있는 상태가 된다.

운영체제가 시스템에 남겨져 있는 이 프로세스의 흔적들을 최종 정리 후  PCB를 삭제하면 비로소 프로세스는 완전히 사라지게 된다.

종료 상태는 식사를 다 하고 집으로 향하는 손님이라고 생각하자.  



3. 프로세스 우선순위

일상 생활에서 스케줄 표를 짜는 것을 상상해 보자.
우리는 스케줄 표를 짤 때 우리가 모르는 사이에 '우선적으로 처리할 것'으로 분류할 때가 있다.
프로세스 영역에서도 마찬가지이다. 스케줄링 시 사용되는 우선순위를 파악하기 위해 프로세스 우선순위라는 영역이 존재해야한다.

4. 프로그램 카운터

PC(Program Counter)는 다음에 실행 될 명령어의 주소 값이 들어있다. 
프로그램은 주소로 이루어져 있는데 주소를 모르면 다음에 수행해야 할 공간을 찾아가지 못한다.
왜 우리도 주소가 없으면 목적지에 찾아갈 수 없지 않는가


5. 메모리 포인터

프로그램과 데이터가 저장되어 있는 메모리 블록 위치공유되는 메모리 블록들에 대한 포인터를 담고 있다.


6. 문맥 데이터

문맥 교환(Context Swtiching) - 곧 우리가 배울 수 있는 부분이다.
문맥 교환 시 CPU 레지스터 값들을 저장해주는 영역이다.


7. 계정 정보

계정 정보라고 해서 프로세스에 계정이 있는 것이 아니라 CPU를 사용한 시간 등의 정보를 담고 있다.


8. 입출력 정보

진행 중인 입출력 요구 등의 정보를 담고 있다.


9. 할당 받은 자원들에 대한 목록

할당 받은 자원들의 정보가 담겨 있다. 




공부 할게 엄청 나게 많은 보안이다 후아 틈틈히 복습 안하면 다 까묵긋다^^ 





드디어 Context Switching에 대해 알아볼 시간이 왔다. 안티 디버깅 설명하다가 문맥교환까지 와버렸다. 

해당 글은 순전히 나의 공부를 위한 것이기 때문에 순서는 뒤죽박죽 될 수도 있다. 여기에 적는 내용들은 잘 아는것도 있지만, 기억이 나지 않는 부분들도 상기시키기 위해서이기 때문이다. 무엇보다 내 블로그다. 여기서 만큼은 내가 곧 법이지 않을까한다. (^,^)


후딱 복습하고 본질인 안티디버깅으로 넘어가보자 !! 


문맥을 교환한다.. 좀 전에 프로그램에서의 문맥은 프로세스에 대한 정보라고 했다. 그리고 CPU 내 레지스터, 스택, 힙이 있다. 

전혀 다른 프로세스 A,B가 있다고 가정하자. 

프로세스 A 그 내부에서도 조차 함수마다 각각의 스택이 존재하는 세상인데, 당연히 프로세스 A와 프로세스 B는 다른 세계에 존재할 것이다.

그렇기 때문에 프로세스 A에서 프로세스 B로 갈 때 A의 스택,힙, 프로세스 정보를 들고 가버리면 프로세스 B는 이게 뭐지..? 싶을 거다. 

왜 필요하지도 않는 놈이 나한테 오는거야!! 라고 .. 


이러한 사태를 방지하기 위해서 프로세스 A가 프로세스 B로 교환 될 때 현재 상태를 PCB에 저장하고 다른 프로세스의 상태를 사용하게 되는 것이다. 


그렇다면, 하나의 프로세스에서 함수가 끝나면 그 함수를 호출 한 다음 주소로 복귀하는 것처럼 A에서 B갔다가 다시 A로 오려면 A의 정보가 담겨 있어야 하는게 당연해지는 논리가 되겠다. 


우린 이제 언제 "문맥 교환"이 일어나는지도 살펴보자.


프로세스 상태를 위에서 설명했다.


프로세스가 준비 -> 실행  || 실행 -> 준비 || 실행 -> 대기 상태가 될 때 문맥교환이 일어나게 되는 것이다.


이러한 교환이 일어나는 곳은 멀티태스킹, 인터럽트 핸들링, 사용자 모드 -> 커널 모드 전환 등이 있다. 


소설을 한번 써보겠다.

A 프로세스에 문맥교환 요청이 들어왔다.

그렇다면 프로세스의 제어는 사용자 모드에서 "커널 모드"로 넘어간다. 

이 말은 쉽게 생각하면 커널 모드 속에서 떠돌아다니면서 쉬겠다고 생각하면 된다.


난 이렇게 예시를 만들어서 이해를 했었다. 이 예시가 정확하지 않을 수 있지만, 내 기준에선 이해가 잘되는 예제다. 


사용자모드 = 열심히 활동하는 공간 (일터)

커널모드 = 휴식 공간(집) 


그렇다면, A프로세스는 집으로 돌아가고 B프로세스가 교대자가 되는 상황이 벌어지게 될 것이다. 


A프로세스는 집으로 그냥 돌아가는 것이 아니라 업무 하고 있던 내용을 저장하고 집으로 돌아가야 담 업무의 진행이 매끄러울 것이다.


여기서의 내용 저장이 PCB에 프로세스 정보를 저장한다고 보면 된다.

B 프로세스는 이제 CPU를 선점 받았으니 열심히 일을 할 것이고, B가 퇴근할 때도 자신의 PCB를 보관할 것이다.


신나게 적다가 본래 무엇을 적으려고 했는지 까먹었다. ㅋㅋㅋㅋ 

위에 올려보니 Context modification에 대해서 적고 있었다는 것을 알아차렸다. 

문맥교환을 설명했으니 다이렉트로 설명할거다. (손가락이 너무 아프다 ^^;;)

Context Modification

문맥교환이 일어나는 시점에 레지스터 조작을 통상적이지 않은 순서로 진행하여 디버거를 혼란 시키는 행위이다. 

음 통상적이다. 곰곰히 생각해봤는데, 원래의 개념적 행위를 하지 않는것 (== 탐지 혹은 분석을 혼란시킴) 이라는 결론을 내려보았을 때 

레지스터 순서가 갑자기 뒤바뀐다라고 이해해도 되려나... 



.

다음 안티디버깅은 다음 장에서 적겠다.  

누워서 내가 쓴 글을 복습할 때 엄청난 스크롤 압박이 있을 것 같다.