Post

DLL, DLL mapping과 API 주소 결정 방식

DLL은 자신을 필요로 하는 프로그램이 실행될 때, 프로그램과 별도로 최초 한 번만 physical memory에 loading 된 후 프로세스에 mapping 되는 방식으로 사용된다. OS는 dll별로 usage count를 유지하고 있으며 usage count가 0이 되면 이 dll을 참조하는 프로세스가 없다는 의미이므로 dll을 메모리에서 unloading한다.

* 프로세스에서 DLL에 접근할 때는 VA를 사용하기 때문에, 같은 physical address를 참조하더라도 pointer 주소는 다 다르게 나온다. (Image base 대로 mapping 됐다면 같을 수도 있지만.)

e.g.

*nix에서 PLT, GOT를 사용하는 것 처럼, mapping(import)된 DLL의 API를 호출 할 때 IAT 를 사용하게 된다. notepad에서 user32.SendMessageW를 호출하는 상황을 예로 들면, 이런식이다.

SendMessageW의 IAT 주소 0xCC1234는 RVA 주소이며 Base + VA = RVA 이므로, notepad의 Base인 0x00CC0000

SendMessageW의 VA 0x1234를 더해서 구한다.

CC1234로 이동해 보면 SendMessageW의 RVA가 나온다. Data가 PEView에 적혀있는 값과 다른데 이는 user32 Base가 ASLR 때문에 달라졌기 때문이다. * PEView의 IAT항목 Data = user32’s Image Base + dll’s EAT func RVA 로 계산된다.

IAT는 runtime에 dll’s EAT를 참고하여 계산된다.

user32의 Base는 0x75CD0000이고, SendMessageW의 EAT Data값은 0x1764C다. SendMessageW의 RVA는 75CD0000 + 1764C = 75CE764C

이 값이 IAT에 채워진다.

이런 식으로 IAT는 compile time에 입력된 EAT와 compile time 또는 runtime에 결정되는 Base를 더해 결정된다. ASLR이 적용되면 Base가 runtime에 결정되기 때문에 주소를 특정할 수 없다. 따라서 IAT 주소를 compile time에 미리 계산해서 공격에 사용하는 식은 불가능하다. * ImageBase가 중복되지 않도록 정해져있는 windows 기본 dll들도 ASLR이 적용된다.

따라서 ASLR을 우회하거나 ASLR이 적용되지 않는 module을 이용한다. ASLR이 적용되지 않는 module은, IAT주소가 항상 고정이기 때문에 간단히 shellcode에서 사용할 수 있다. 그냥 PEView 보고 그 주소 쓰면 된다.

IAT에 없는 API를 호출할 때는 이런 방식을 사용한다.

windows dll의 API도 현재 module의 IAT에 없다면 마찬가지다.

LoadLibrary를 이용해 Base 주소를 구한다. (runtime에 동적으로 결정되니까) 그리곤 Base를 GetProcAddress의 인자로 넘겨 function이나 variable의 포인터를 얻는다. 이런식으로 구하면 IAT section의 주소가 아니라, loading된 dll의 직접적인 API 주소를 얻게된다. (== *IAT)

IAT에 있는 API라면 그냥 이렇게 호출한다.

1
2
MOV EAX, DWORD PTR DS: [<&DLL\_NAME.API\_NAME>]
CALL EAX

Note ) mapping 된 dll의 위치 == LoadLibrary( XXX.dll ) 했을 때 output == Module view의 Base

This post is licensed under CC BY 4.0 by the author.