1. 커널 오브젝트와 핸들

- 커널 오브젝트 : Windows 운영체제에서 리소스(프로세스, 스레드, 파일 등)를 관리하기 위해 필요한 정보를 모아둔 데이터 블록.


- 핸들 : 커널 오브젝트에 접근하기 위한 숫자.


- 커널 오브젝트에 직접적인 접근이 불가능하기 때문에 핸들을 인자로 전달하는 함수 호출을 이용해서 접근한다.



2. 운영체제에 종속적인 커널 오브젝트

- 커널 오브젝트의 생성 주체는 운영체제 이기 때문에 프로세스가 아닌 운영체제에 종속적이다.


- 운영체제에 종속적이기 때문에 여러 프로세스에서 하나의 커널 오브젝트에 접근이 가능하다.


- 운영체제에 의해서 생성되었기 때문에 소멸 역시 운영체제에 의해서 결정된다.



3. Usage Count

- 커널 오브젝트에 접근 가능한 대상의 수


- 커널 오브젝트에 존재한다.


- 커널 오브젝트에 접근 가능 대상이 늘어날 때마다 증가하고,  CloseHandle 함수가 호출될 때 감소한다.


- 프로세스가 종료되는 시점에도 감소한다.



4. CloseHandle 함수

- 핸들을 반환하는 함수이지만, Usage Count와 연관 지어 설명하자면 "인자로 전달된 핸들이 가리키는 커널 오브젝트를 더 이상 참조하지 않겠다. 즉, 필요가 없다."라는 의미이다.


- 따라서 CloseHandle 함수가 호출된다면 해당 커널 오브젝트의 Usage Count 값이 1 감소하게 된다.



5. 프로세스의 Usage Count

- 프로세스는 생성과 동시에 Usage Count가 1이 아닌 2로 세팅된다.


- 바탕화면에서 더블 클릭 해 실행된 프로세스는 '바탕화면'이라는 프로세스에 의해서 실행된 것이다.


- "바탕화면 = 부모 , 실행된 프로세스 = 자식" 이렇게 성립된다.



6. 종료 코드

- 자식 프로세스에서 exit(1), return 1 등을 종료 코드를 사용한다.


- 부모 프로세스에서는 GetExitCodeProcess 함수를 이용해 자식 프로세스의 종료 코드를 얻어온다.


- exit(), return을 이용해서 전달되는 값을 통해서 부모 프로세스에서 적절한 조치를 취하도록 프로그래밍할 수 있다.


ex)

exit(-1);이라면 부모 프로세스에서 _tprintf(_T("에러 발생!"));


GetExitCodeProcess 함수의 반환 값 중 STILL_ACTIVE는 자식 프로세스가 실행 중인 상태를 의미한다.



7. 부모 프로세스가 자식 프로세스 핸들을 곧바로 반환하는 이유

- 만약 부모 프로세스에서 수많은 자식 프로세스를 생성하고, 자식 프로세스의 리소스가 필요 없다고 가정하자.


- 부모 프로세스가 CloseHandle 함수를 호출하지 않으면 자식 프로세스가 종료되었음에도 불구하고 Usage Count는 0이 되지 않는다.


- 즉, 쓸모없는 커널 오브젝트가 소멸되지 않고 계속해서 남아 자원을 낭비하게 된다.


- 자식 프로세스의 종료 코드는 자식 프로세스의 커널 오브젝트에 저장되므로, 부모 프로세스에서 자식 프로세스의 종료 코드를 얻어오기 전에 CloseHandle 함수를 호출해버린다면, 자식 프로세스의 종료 코드를 얻어 올 수 없게 된다.

UPack 으로 실행 압축된 파일은 여러가지 특징을 가진다.


1. MZ헤더와 PE헤더를 겹쳐 쓴다.

- IMAGE_DOS_HEADER.e_lfanew 값을 10으로 설정


2. SizeOfOptionalHeader

- 값을 148로 변경 (PE32에서 크기는 E0)

- IMAGE_OPTIONAL_HAEDER 시작 옵셋에 SizeOfOptionalHeader 값을 더한 위치부터 IMAGE_SECTION_HEADER가 나타난다.

- 실제 구조체 크기를 제외한 공간에 디코딩에 필요한 코드를 넣기위함


3. NumberOfRvaAndSizes 

- 값을 A로 설정 (원래는 10)

- 나머지 항목에 디코딩 코드를 넣기위함


4. 섹션 겹쳐쓰기

- 첫 번째 섹션과 세 번째 섹션의 파일 시작 옵셋과 파일에서의 크기가 완전히 동일하다.

- 그러나 메모리에서의 시작 RVA와 크기(VirtualSize)는 다르다.

- 두 번째 섹션에 압축된 원본파일이 있고, 첫 번째 섹션에 원본파일을 압축해제한다.


5. PointerToRawData

- 값을 10으로 설정

- 일반적으로 PointerToRawData값은 FileAlignment값의 배수가 되어야한다.

- PE로더는 PointerToRawData값이 FileAlignment값의 배수가 아니면 강제로 배수에 맟춰서 인식한다.

- 그러나 PE유틸리티들은 PointerToRawData값을 그대로 신뢰했기 때문에 에러가 발생했었다.


6. 특이한 Import Table 구성

- IMAGE_IMPORT_DESCRIPTOR 구조체 배열중 첫 번째 구조체는 정상, 그 뒤로는 두 번째 구조체도 아니고, 끝을 알리는 NULL 구조체도 아니다.

- 하지만 메모리에 로딩시, Import Table의 마지막 2바이트부터 Import Table이 속하는 세 번째 섹션에 매핑되지 않고 NULL로 채워진다.

- 즉 첫 번째 구조체는 정상 IID 구조체가 되고, 나머지는 NULL이 채워지기 때문에 구조체의 끝을 알리는 NULL 구조체가 된다.

- 쉽게 말하면 파일과 프로세스의 섹션 범위가 달라서 파일에서는 Import Table이 아닌것 처럼 보이지만 메모리 로딩시 정상 Import Table이 된다.

정상적인 실행파일에 섹션을 하나 추가 하기 위한 과정은 아래와 같다.


1. NumberOfSections의 값을 변경(기존 5에서 6으로)

2. SizeOfImage 값을 변경(기존 11000 에서 12000으로)

3. 추가할 섹션의 헤더정보 추가

4. 섹션 Body 추가


(1. NumberOfSections 변경)



(2. SizeOfImage 변경)



(3. 섹션 헤더 정보 추가)

여기서 중요한 부분은 다음이다.

RVA

- 다른 섹션 헤더들의 RVA값을 보면 차례대로 이어지는 것을 확인 할 수 있다.

- 만약 RVA값을 이어지게 주지않으면 실행되지 않는다.


Size Of Raw Data

- Pointer To Raw Data 값에서 Size Of Raw Data의 크기 만큼의 파일 공간이 존재 해야 실행된다.


Pointer To Raw Data

- 이값은 다른섹션의 Body 부분과 곂쳐도 실행이 된다.

- ex).reloc의 섹션 부분이 "C000~파일끝"이지만 .ABC의 PointerToRawData 값을 "C800"으로 설정해도 실행이 된다.

- 존재하지 않는 옵셋으로 설정할 경우 실행 되지 않는다.



(4. 섹션 바디 내용 추가)



+ Recent posts