본문 바로가기

My Study/Programming&Theory

EXPORT Table에서 함수 주소 얻기

http://ezbeat.tistory.com/274
위 글에 있는 파일을 분석해본 결과 사용할 함수 주소들을 프로그램 실행 시에 얻어와 호출을 하고 있습니다.

저도 위 내용을 한번 구현해 보려고 했습니다. 일단 구현에 성공은 한 것 같아서 이렇게 글을 올려봅니다.
API함수는 하나도 사용하지 않습니다.
=========================================
제가 얻어올 함수는 CreateFileW, WinExec 두 함수의 주소를 얻어와보겠습니다.
해당 함수들은 kernel32.dll 모듈에 존재하므로 먼저 kernel32.dll 모듈의 BaseAddress를 얻어오겠습니다.
모듈의 BaseAddress는 LDR_MODULE 에서 가져옵니다.
그림으로 보시겠습니다.

위 그림은 notepad.exe를 켰을 때 봐본 내용입니다. 저 모듈이 전부는 아니지만 그림 크기상 3개만 그렸습니다.
이제 코드를 다시 설명해 보면 첫번째 LDR_MODULE에서 FullDllName이 "kernel32.dll" 인지 확인 후 맞으면 BaseAddress를 가져오고 아니면 NextModule로 가서 그 다음 모듈의 LDR_MODULE의 FullDllName확인 .. 쭉쭉 이런식 입니다.

인자로 들어온 모듈의 이름의 BaseAddress를 가져옵니다.
저렇게 복잡하게 인라인 어셈으로 구현해봤지만 API함수 하나면 끝납니다.
GetModuleHandle(L"kernel32.dll");

이제 원하는 모듈의 BaseAddress를 얻어왔으니 해당 모듈의 export table로 가서 원하는 함수의 주소를 가져오겠습니다.
먼저 코드를 보시겠습니다.
코드가 쫌 보기 힘들거 같군요.
크게 생각하면 어려운 코드가 아닙니다.

해당 모듈의 BaseAddress부터 export table까지 접근해 가는 과정입니다. 포인터 장난이죠.
그리고 각 주소들은 RVA로 되어 있으므로 그 값을 그대로 사용했다간 오류가 납니다. VA로 바꿔준 후 사용해야 합니다.

그리고 while문이 작동하는 원리입니다.
1. export table의 AddressOfNames테이블에서 찾고자 하는 함수 이름을 찾는다. ( 그 때마다 index값 증가 )
2. 찾았으면 그 때의 index값을 가지고 AddressOfFunctions테이블에서 함수 RVA 주소를 가져온다.
3. 가져온 RVA를 VA로 변경 시켜주고 주소 담는 배열에 넣어준다.
위 과정을 찾고자 하는 모든 함수 주소를 찾을 때 까지 반복합니다. 이 때 찾았던 함수는 더이상 반복문을 돌면서 찾을 필요가 없으므로 반복문에서 제외 시켜버리면서 돕니다. 제외시키는 방법은 찾을 함수 이름이 담겨있는 배열을 0부터가 아닌 1부터 돌게 하는 것입니다. 그리고 다 찾으면 빠져나오는 것이죠. 다행이 export table의 있는 함수들이 알파벳 순서대로 있어서 편했습니다.

코드는 이게 전부 입니다.
이제 main함수를 보시겠습니다.
얻어온 함수 주소를 출력했습니다. 그리고 얻어온 함수 주소 중에 WinExec는 한번 사용해봤습니다.

실행 결과입니다.

정상적으로 함수 주소를 얻어왔고 WinExec함수도 정상적으로 호출이 됬습니다.

지금까지 API함수를 사용하지 않고 export table에서 원하는 함수의 주소를 얻어와 사용하는 방법에 대해서 알아보았습니다.
만약 위 코드를 딱 한줄로 쓸수 있다면..?
GetProcAddress(GetModuleHandle(L"kernel32.dll"),"WinExec");

실제로 위 코드를 가지고 정적 분석 방해를 할 수 있는데 그 때는 찾을 함수 이름 테이블에 함수 이름을 넣는게 아니라 인코딩 시켜서 인코딩한 값을 넣고 숫자를 비교해서 함수 주소를 얻어오는 형태로 바꿔야 할 것입니다.
인코딩하는 루틴만 구하면 쉽게 끝날 것 같군요.

지금까지 분석해본 바이러스에서 사용하던 코드를 재구현 시켜봤습니다.