zj3t

email: euntaejang@gmail.com

Latest Posts


PE파일 정보를 나타내 주는 프로그램을 만들고있습니다. 천천히 하루에 1시간~2시간씩 투자해서 만들고있는데 그냥 함수로 구현을 했어야하는데 그러질 안아 노가다가 되었습니다.

다만들면 배포해보겠습니다.


말을 추가해보자면,,,
예전에 저도 PE view와 패커를 만들고 싶어서 엄청검색하고 어케만들지 몰라 해매서 결국 포기하고 다음으로 미루었었습니다. 그래서인지 만들어 보고싶은, 어떻게 만드는지 알고싶은 분들이 많을 것이라 생각됩니다. 저는 이 계정으로 항상 접속해있으니 어떻게 만드는지 모르시는 분들은 메일 주시면 제가 아는한 아주 자세히 알려드릴 수 있으니 메일 부탁드립니다.

euntaejang@gmail.com

EAT: 라이브러리 파일에서 제공하는 함수를 다른 프로그램에서 가져다 쓸 수 있도록 해주는 핵심적은 메커니증!!

즉 EAT를 통해서만 해당 라이브러리에서 익스포트하는 함수의 시작 주소를 정확히 구할 수 있다. (따라서 EXPORT TABLE은 dll과 같이 함수를 제공해 주는 라이브러리에만 존재)

typedef struct _IMAGE_EXPORT_DIRECTORY{
DWORD Characteristics;
DWORD TimeDateStamp;
WORD  MajorVersion;
DWORD MinorVersion;
DWORD Name; //예)kernel32.dll등 라이브러리의 시작 주소
DWORD Base;
DWORD NumberOfFunctions;        //실제 EXPORT 함수의 개수
DWORD NumberOfNames; //EXPORT 함수 중에서 이름을 가지는 함수 개수
DWORD AddressOfFunctions; //EXPORT 함수 주소 배열
DWORD AddressOfNames; //함수 이름 주소 배열
DWORD AddressOfNameOrdinals; //Ordinal 주소 배열
}IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY

라이브리에서 함수 주소를 얻는 API: GetProcAddress()
GetProcAddress() 함수는 EAT를 참조해서 원하는 API의 주소를 구해줌

GetProcAddress() 동작 원리

1.AddressOfNames 멤버를 이용해 '함수 이름 배열'로 갑니다.
2.문자열 비교(strcmp)를 통하여 원하는 함수 이름을 찾습니다.(이 때 배열의 인덱스는 n이라 가정)
3.AddressOfNameOrdinals 멤버를 이용하여 'ordinal 배열'로 간다.
4.'ordinal 배열'에서 인덱스 n으로 해당 ordinal값을 찾음
5.AddressOfFunctions 멤버를 이용하여 '함수 주소 배열(EAT)'로 찾아간다.
6.EAT에서 4단계에서 구한 ordinal을 배열 인덱스로 하여 원하는 함수의 시작 주소를 얻는다.

IMAGE_IMPORT_DESCRIPTOR 구조체의 위치(PE 바디에 위치)

>IMAGE_OPTIONAL_HEADER32.DataDirectory[1].Virtual Address =IMAGE_IMPORT_DESCRIPTOR 구조체 배열의 시작 주소


RVA가 7604 이다. 이는 .text섹션에 포함
RAW를 구하는 공식: RAW-Pointer to Raw Data = RVA - Virtual Address
text섹션 헤더의 Pointer to Raw Data=400
                     Virtual Address =1000

따라서.

RAW=7604-1000+400=6A04

어떻게 text 섹션에 포함된 줄 알았냐하면 text섹션을 봤을 떄 RVA가 1000이고 Virtual Size가 7748이라 7604가 이 안에 포함되기 때문입니다.


00007990: OriginalFirstThunk
FFFFFFFF:  TimeDateStamp
FFFFFFFF:  ForwarderChain
00007AAC: Name
000012C4: FirstThunk


-Name(임포트 함수가 소속된 라이브러리 파일의 이름 문자열 포인터)
RVA:7AAC RAW: ????

7AAC 또한 text섹션에 포함되어 있으므로 아까와 공식은 같다.

RAW=7AAC-1000+400=6EAC

따라서 RAW:6EAC이다.


comdlg.dll IID구조체의 첫번째 원소의 라이브러리 이름이다. 그 밑 두번째 원소의 라이브러리 이름도 이와 같은 방법으로 찾을 수 있다.

-OriginalFirstThunk(INT)

RVA:7990 RWA:6D90


저 빨간 박스가 INT이고, 각은 박스하나하나가 저번포스팅에서 언급한 IMAGE_IMPORT_BY_NAME구조체의 시작 주소입니다. 예를 들어 저기서 00007A7A를 보면 

RVA:7A7A RAW:6E7A


임포트하는 API의 이름을 볼 수있었습니다.

하나 더 보자면

RVA:7A5E RAW:6E5E

역시 임포트하는 API의 이름을 볼 수 있었습니다.

-FirstThunk

RVA:12C4 RAW:6C4


저 부분이 comdig32.dll 라이브러리에 해당하는 IAT 배열 영역이다. INT와 마찬가지로 구조체 배열로 되어있어 NULL로 끝난다.

지금 보이는 이 763249006, 763185CE 등은 의미 없는 값으로, 프로그램이 메모리에 로딩될 때 이 값은 정확한 주소 값(API의 주솟값 INT의 값)으로 대체됩니다.


ollydbg로 확인할 수 있다.


-출처: 리버싱핵심원리
















IMAGE_IMPORT_DESCRIPTOR=IMPORT Directory Table


typedef struct _IMAGE_IMPORT_DESCRIPTOR{
union{
DWORD characteristics;
DWORD OriginalFirstThunk; //INT address(RVA)
};
DWORD TimeDateStamp;
DWORD ForwarderChain;
  DWORD Name;
DWORD FirstThunk; //IAT address (RVA)
}IMAGE_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_IMPORT_BY_NAME{
WORD Hint;
BYTE Name[1];
}IMAGE_IMPORT_BY_NAME;

-IAT에 값 입력 과정


1.IMAGE_IMPORT_DESCRIPTOR(IID)의 Name 멤버를 읽어서 라이브러리의 이름 문자열(ex:kernel32.dll)을 얻습니다.
2.해당 라이브러리를 로딩 ->LoadLibrary("kernel32.dll")
3.IMAGE_IMPORT_DESCRIPTOR의 OriginalFirstThunk 멤버를 읽어서 INT(IMPORT_NAME_TABLE)주소를 얻음
4.INT에서 배열의 값을 하나씩 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻음
5.IMAGE_IMPORT_BY_NAME의 Hint 또는 Name 항목을 이용하여 해당 함수의 시작 주소를 얻습니다.
   ->GetProcAddress("GetCurrentThreadld")
6.IDD의 FirstThunk 멤버를 읽어와 IAT 주소를 얻는다.
7.해당 IAT 배열 값에 위에서 구한 함수 주소를 입력한다.
8.INT가 끝날 때까지(NULL을 만날 때까지) 위 4~7과정을 반복

-출처: 리버싱핵심원리(www.reversecore.com)
섹션추가와 추가한 섹션의 간단한 활용을 포스팅 해보겠습니다.






PUSH 404000 이 아닌 것을 확인해주세요 중요합니다!!!


성공!!

질문은 댓글이나 쪽지 부탁드려요~~~


-부가설명

원래 저 붉은색 박스 때문에 다음 추가할 섹션의 파일시작점은 1000이상이 되여야합니다. 하지만 여기서는 추가해서 어떤 기능을 하는지 간단히 보여주는 것이 목적이기에 따로 HxD로 바이트를 추가하지 않고 사용하는 섹션을 공유했습니다.





PE file format을 공부하려고 섹션을 빼고 넣고하는 작업을 하다 흥미로울거 같다 생각해봤는데 잘되서 포스팅하게 되었습니다.

ASLR기능은 간단히 말해서 파일에 메모리에 올라가 실행될때 그 주소를 랜덤으로 바꿔주는 기능입니다.

이 ASLR 기법은 .reloc 섹션의 재배치 기능에 의해서 된다고 일단 간단히 알고있으면 될것 같습이다.

<그림.1> 프로그램 실행화면

<그림.2>ASLR이 적용됬을 때

<그림.3>Stud_PE를 이용해 ASLR을 제거했을 때


Stud_PE라는 툴로 간단하게 ASLR기능을 끌 수가 있습니다. 하지만 .reloc 섹션을 지워 ASLR을 끄는 법을 포스팅 하겠습니다.







<순서>
1. .reloc의 Section_Header를 0으로 만듬
2. .reloc의 body부분을 0으로 만듬
3. IMAGE_NT_HEADER의 IMAGE_FILE_HEADER의 Number of Section을 수정
4. 이 파일의 SectionAlignment가 1000이고 .reloc섹션의 vitualaddress가 1000보다 작으
   므로 size of image의 값을 1000빼준다.

<결과>

Entry Point에 정확히 매핑된 것을 확인 할 수가있다.

동적할당은 체크되어있지만 ASLR이 적용되지 않는다.

보통 Stud_PE로 ASLR 기능을 끄지만 한번쯤 이렇게 실습해보면 PE에 대한 이해도가 점점 높아질 거에요~~

고길님 블로그에 있는 패커 제작 실습을 따라해보았습니다. 이런식으로 섹션을 합쳐본 적은 첨이여서 머리를 많이 굴렸던것 같습니다. 한번 따라하고 났더니 PE에 대한 지식이 조금은 상승한 것 같습니다.

밑에는 원본과 수정본의 비교와 수정본을 실행한 화면입니다.





패커를 제작하려면 섹션에 관한 지식은 필수라고 생각됩니다.
섹션은 사용자가 추가할 수도있고 삭제할 수도 있는 특징이 있습니다. 그중 일반적으로 많이 접해볼 수 있는 섹션에 대한 설명입니다.

.text    = 프로그램의 Code 관련 정보가 들어 있는 섹션.
.reloc  =  Relocate 정보를 담고 있는 섹션.
.rdata : Const로 선언된 변수, 변수로 사용되지 않는 String 
.data  : Const를 제외한 전역 변수 , Static으로 선언된 지역 변수 
.idata  = IAT에 관련된 정보들이 존재 하는 섹션.
.edata = EAT에 관련된 정보들이 존재 하는 섹션.



IMAGE_IMPORT_DESCRIPTOR의 구조체 비열의 시작 주소(RVA)=
IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress의 값

그림출처: http://blog.naver.com/kyw11541/220418191690
올해 목표가 간단한 패커를 만들어 보는 것이였습니다.
꾸준히 지식 몇개씩 검색을 통해서 얻는중 좋은 블로그를 발견해서 포스팅을 하게 되었습니다.
http://gogil.kr

Reversing.kr 사이트를 만드신 분이라고 들었습니다. 블로그 글들을 봤는데 나이가 어리시던데 정말 지식이 많으시더라구요.

이 분이 패커 제작에 대한 글을 올려서 그것을 퍼왔습니다. (물론 다는 아니고 PE 정보를 간단히 정리한 글이 있어서 퍼왔습니다.)

PE구조


IMAGE_DOS_HEADER

e_magic : IMAGE_DOS_SIGNATURE = 'MZ'
e_lfanew : IMAGE_NT_HEADERS를 가리키는 주소

IMAGE_NT_HEADERS

Signature : IMAGE_NT_SIGNATURE = 'PE\0\0'
IMAGE_FILE_HEADER
NumberOfSections : 섹션의 개수
SizeOfOptionalHeader : IMAGE_OPTIONAL_HEADER32의 크기. 보통 0xE0
Characteristics : 파일의 속성 ex) exe = IMAGE_FILE_EXECUTABLE_IMAGE, dll = IMAGE_FILE_DLL
IMAGE_OPTIONAL_HEADER32
Magic : IMAGE_OPTIONAL_HEADER32 = 0x10B, IMAGE_OPTIONAL_HEADER64 = 0x20B
SizeOfCode : 코드의 전체 크기
SizeOfInitializedData : 초기화 되어있는 섹션들의 총 합
SizeOfUninitializedData : 초기화 되지 않은 섹션들의 총 합
AddressOfEntryPoint : 프로그램 코드 시작점 (RVA)
BaseOfCode : 코드 영역의 시작 위치 (RVA)
BaseOfData : 데이터 영역의 시작 위치 (RVA)
ImageBase : 메모리에 매핑될 주소
SectionAlignment : 메모리에서 섹션의 최소 단위
FileAlignment : 파일에서 섹션의 최소 단위
SizeOfImage : 메모리에 차지할 크기 ←날 삽질하게 만든 녀석. 아래에서 자세히 다루겠다SizeOfHeader : DOS_HEADER + DOS_STUB + NT_HEADERS + SECTION_HEADER의 합
CheckSum : 무결성 검사. imagehlp.dll - MapFileAndCheckSum() API로 체크함
Subsystem : GUI=IMAGE_SUBSYSTEM_WINDOWS_GUI, CUI=IMAGE_SUBSYSTEM_WINDOWS_CUI
SizeOfStackReserve : 사용할 스택의 크기 지정
SizeOfStackCommit : 예약할 스택
SizeOfHeapReserve : 사용할 힙 크기 지정
SizeOfHeapCommit : 예약할 힙
NumberOfRvaAndSizes : DataDirectory의 개수
DataDirectory[0] = EXPORT Directory
DataDirectory[1] = IMPORT Directory
DataDirectory[2] = RESOURCE Directory
...
DataDirectory[C] = IAT Directory
...
DataDirectory[F]

IMAGE_SECTION_HEADER

Name : 섹션의 이름
VirtualSize : 메모리에서 섹션의 크기
VirtualAddress : 메모리에서 섹션의 시작 위치 (RVA) (SectionAlignment의 배수)
SizeOfRawData : 파일에서 섹션의 크기
PointerToRawData : 파일에서 섹션의 시작 위치 (FileAlgnment의 배수. 일치하지 않으면 버림(내림)함)
Characteristics : 섹션의 속성
IMAGE_OPTIONAL_HEADER32->DataDirectory[1]->VirtualAddress가 가리키는 RVA에는 IMAGE_IMPORT_DESCRIPTOR가 존재한다.
IMAGE_IMPORT_DESCRIPTOR
OriginalFirstThunk : Import Name Table의 주소 (RVA)
Name : DLL 이름 버퍼의 주소 (RVA)
FirstThunk : Import Address Table의 주소 (RVA)

출처: http://gogil.kr/13
안녕하세요.
오랫만의 포스팅입니다~
저번에 포스팅은 Global API Hooking에서 Injdll.exe로 API를 후킹하여
야후, 네이트, 다음, 네이버를 접속하면  www.reversecore.com(리버스 코어)로 우회되도록 우회 될 URL이 고정 되어있었습니다.

이번에는 파일을 변경하여 원하는 곳으로 우회할 수 있도록 고정하는 방법을 포스팅해보겠습니다.
아주 약간의 PE 구조에 대한 지식만 필요로하며 DLL 폴더에있는 PE구조를 변경시켜 직접 Injection을 하는 방법보다는 훨씬 쉽습니다.

그럼 시작하겠습니다.
우선 IDA로 redirect.dll 을 엽니다.
redirect.dll 은 Injll.exe에서 사용되는 dll 파일입니다. 즉 우회 될 사이트와 우회를 시킬 사이트의 정보가 담겨있습니다.



여기 네이버, 다음, 네이트, 야후가 보입니다. 우회시킬 사이트들이 나열 되어있습니다. 그리고 그 밑으로 조금 더 내려보면 www.reversecore.com, 우회 URL이 쓰여있습니다.
PUSH로 URL을 넣고 함수를 CALL합니다. 그럼 www.reversecore.com은 어디 있나 찾아보았습니다.


여기 URL 들이 나열 되어있습니다. 위에서 본 URL들은 모두 이쪽에서 가져온 것을 확인할 수 있습니다.

ollydbg로도 확인해보니 AC98에서 가져 오는 것을 확인할 수 있었습니다.
그렇다면 우회 URL을 바꾸는 방법은 크게 두 가지를 나눠 볼 수 있습니다.

1. AC98에 있는 URL을 단순히 원하는 URL로 바꾸는 방법
2. NULL 지역을 찾아 그곳에 원하는 URL을 입력한다음 PUSH 주소(1AB3)를 URL이 입력된 주소로 바꾸는 방법

1번을 해보려했는데 보시는 것처럼 공간이 없습니다. 짧은 URL은 괜찮지만 긴 URL은 뒤의 data를 침범하기 때문에 프로그램이 제대로 실행되지 않을 수도 있습니다.

따라서 2번방법으로 실습해 보겠습니다.

이제 PEview와 HxD로 확인해 보겠습니다.


이렇게 바꾸어 주었습니다. 우회될 URL은 제 블로그 주소입니다.


이 부분이 | PUSH 우회 URL |  주소 입니다. URL 을 가리키는 주소는 AC98(리틀 엔디안)으로 쓰여있는 것을 확인 할 수 있었습니다. 이 주소를 우리는 NULL 지역에 쓴 새로운 URL이 있는 주소로 바꾸어 주어야 합니다.


File Offset도 확인이 됬습니다. 이제 헥스에디터로 찾아가 URL이 참조되는 주소를 바꿔주면 될 것 같습니다.


바꾸어 주었습니다. redirect.dll -> JMPOEP.dll 이름 변경을 한 뒤 실행을 해보겠습니다.


PID를 확인했습니다. 왜 부모 프로세스는 Hooking이 안되는지 아직도 모르겠습니다. 어떤 보호기법인거 같은데 자세히는 모르겠습니다. 따라서 자식 프로세스의 PID를 확인했습니다.




부모 프로세스의 인젝션은 실패,
자식 프로세스의 인젝션은 성공

자식 프로세스가 이제 원하는 URL로 우회가 되는지 확인해 보겠습니다.




시작페이지 daum에서 naver로 이동을 하니 제 블로그로 우회되었습니다. 이를 응용한다면 네이버, 다음, 네이트, 야후가 아닌 유해사이트들 다른 사이트로 돌리도록 할 수도 있습니다.

저번 dll을 자체적으로 인젝션하는 건 조금 어려웠지만 이렇게 PE Header를 건드리지 않고 body 부분에서만 데이터를 바꿔주는 것은 매우 쉽습니다. 앞으로 PE를 가지고 노는 것을 많이 포스팅 해보겠습니다.




혹시 redirect.dll 파일에 대한 저작권을 침해했다면 글 지우겠습니다.