ida에서 __usercall로 해석되는 함수의 경우는 2~3개 정도의 인자를 불특정 레지스터에 저장하고 사용하기 때문에
C에서 제공하는 calling convention으로 함수를 정의할 수 없었습니다.
(있다면 댓글로 자비좀 베풀어주세요.. )

어쩔 수 없이 naked형태로 후킹 함수를 구현하였습니다.


후킹 대상인 __usercall 함수는 4개의 인자를 사용하고 있습니다.
int __usercall 대상함수(int a1@<edx>, int a2@<esi>, int a3, int a4)
호출 시 레지스터는 유지하고, a3, a4만 push합니다.
호출이 끝나면 push한 만큼 esp를 복원해주고(hook함수 종료 시 mov esp, ebp를 해주니 복원할 필요가 없을듯하나 습관적으로 해주는것이 좋을듯) , 리턴값이 담긴 eax를 백업합니다.
esi와 edx는 __usercall 함수 내부에서 retn전에 pop을 해주기 때문에 따로 백업하지 않아도 됩니다.

이후에 각종 파라미터를 빼내는 함수를 호출하는데, __stdcall로 선언했으므로 따로 스택정리는 하지 않았습니다. 

 pTrampoline는 jmp 다음 명령어를 가리키도록 해주면 됩니다.



다른 컴파일러로 개발할 일 생기면 추가 예정


 

 Visual C

 

GCC 

 

 Bit

32 

64 

32 

64 

 INT 

 4

 LONG

 4

 FLOAT

 4

8

 DOUBLE

 4

8

8

8


멀티 소스로 개발시 LONG형이 문제가 될 수 있다. 특히 long 으로 리턴받는 api사용 시 주의.


8바이트를 쓰고자 한다면, int64_t / __int64_t 로 선언하면 어느 쪽에서도 확실하게 8바이트를 사용할 수 있다.

ScanBase64Function.dll


메모리에서 base64의 인코딩 및 디코딩 테이블을 검색한 후, 해당 테이블 주소를 text섹션에서 참조하는 코드 주소를 찾아냅니다.

char MimeBase64[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };   char(혹은 int) DecodeMimeBase64[256] = { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 };  


사용 방법 : 플러그인 메뉴에서 Scan을 클릭하면, 검색 결과를 로그에 출력합니다.

속도가 매우 느립니다. (-_-)


실행 결과 예시: 

jscript.dll의 0x3F1E8630에서 base64 테이블을 검색하였습니다. encode용도일 가능성이 있습니다.

해당 주소를 6개의 코드에서 참조하고 있습니다.


개선해야할 내용

- 테이블을 사용하지 않는 BASE64 함수 검색

- encode / decode 용도 정확하게 구분하기

- 패커 (더미다 등) 적용시 확인해보지 못하였음




개발환경 : Visual Studio 2013 / Windows 7 x64

Active X, 후킹모듈과 같은 브라우저용 DLL을 개발하다보면 로그 처리 및 타 프로세스와 연동할 일이 생기는데 기본적으로 브라우저는 낮은 Integrity를 가지고 있으므로 temp폴더 정도의 자원만 사용할 수 있습니다.

제가 만든 프로그램 (PIPESERVER.exe - High Integrity)에서 생성한 파이프는 브라우저(iexplore.exe - Low Integrity)에서 사용할 수 없습니다.

이를 해결하기 위해 각종 핸들(PIPE 등)을 생성할 때 SECURITY 관련 속성을 인자로 주어야 합니다. 

아래 예제 소스는 RPC 파이프를 이용하여 브라우저 DLL에서 EXE의 함수를 호출하는 것을 보여줍니다.

(아래 참조사이트들을 참고하여 작성하였습니다.)

# EXE (서버) Side


# DLL (클라이언트) Side

주의하여야 할 점은 RpcStringBindingCompose 함수의 인자로 local을 의미하는 "\\\\."을 사용하였는데 일반적인 경우는 "localhost"를 사용해도 상관 없지만 브라우저 DLL의 경우 반드시 "\\\\."을 사용해야 정상 동작합니다.


도움이 되는 참조 사이트

RPC 기본 샘플 예제

http://www.codeproject.com/Articles/4837/Introduction-to-RPC-Part

VISTA에서 Active X와 통신

https://www.blackhat.com/presentations/bh-usa-08/Kim/BH_US_08_Kim_Vista_and_ActiveX_control_WhitePaper.pdf



함수가 호출 될 당시의 콜스택을 확인합니다.
DEPTH를 5로 설정하면 5 DEPTH까지 호출한 주소를 출력합니다.
만약 디버거 없이 동적으로 콜스택을 확인하고 싶다면 이 방법을 쓰면 됩니다.
(후킹용 DLL 에서 유용할 것이라 생각됩니다.)

이 예제는 TestFunction() 이 호출될 당시의 콜스택을 보여줍니다. (최적화 안함)


샘플을 돌린 결과입니다. 출력된 호출 주소의 바로 윗줄이 실제 호출 주소입니다.



빌드 환경 : Visual Studio 2010
필요 헤더 : windows.h
윈도우즈 환경에서 Crypto API를 이용하여 손쉽게 MD5를 구할 수 있습니다.



windows 용으로 포팅한 zlib입니다. 

zlib-x86..zip

visual studio 2010 에서 테스트 하였습니다.

zconf.h 와 zlib.h 을 프로젝트에 추가한 후,

소스에 zlib.h을 include합니다.

라이브러리를 추가 종속성에 추가해 주세요.

 

빌드 시 아래와 같이 에러가 나는 경우, 특정 기본 라이브러리 무시에서 libcmt.lib을 입력해 주세요.

 

 

일반적으로 Ring 3에서는 fs:[0] 과 같은 명령으로 세그먼트 디스크립터가 가리키는 메모리에 접근할 수 있지만, 세그먼트 디스크립터에는 접근할 수 없습니다.

그러나 OllyDbg에서는 아래 그림과 같이 해당 인덱스가 가리키는 세그먼트 디스크립터의 베이스 주소를 표시해주고 있습니다.

Ring 3에서는 일반적인 방법으로는 베이스 주소를 구할 수 없으며, 약간의 트릭이 필요합니다. GetThreadSelectorEntry라는 API를 사용해서 LDT_ENTRY 구조체를 채운 다음, BaseHi + BaseMid + BaseLow 를 통해 베이스 주소를 만들 수 있습니다. LDT_ENTRY는 WinNT.h에 정의되어 있으므로 따로 선언할 필요가 없습니다. (아래 예시는 이해를 돕기위해)

 

위의 방법은 이메일을 통해 올리디버거 제작자인 Oleh Yuschuk에게 문의해서 알아낸 방법이며, 자기가 알기로는 이에 해당하는 64비트 API는 없다고 하네요.

그러나 현재 개발 중인 64비트 올리디버거 스크린샷을 보면 Base Address가 일부 표시되고 있으니 다른 방식으로 구하는 방법이 있는 것 같습니다.

FS 의 경우 비교적 쉽게 구할 수 있는데, FS의 디스크립터가 TEB를 표시하고 있으므로 NtCurrentTeb() 혹은 mov eax, fs:[0x18] 로 베이스 주소를 구할 수 있습니다.

 

+ Recent posts