2015. 1. 26. 20:04ㆍSecurity ★ Development/Reversing
리버싱의 일부분으로 특정 api에 대해 정보를 찾는 방법을 알아보겠습니다.
여기서는 ntdll.dll에 있는 윈도우 네이티브 api인 generic table api를 대상으로 합니다.
그럼 generic table api에 포함되는 적절한 함수를 찾아야합니다. 이 때 필요한 것이 generic table api가 구현되어 있는 ntdll.dll과 dumpbin입니다.
dumpbin을 위와 같이 사용하면 ntdll.dll의 함수목록이 나열되어있는 txt파일을 생성할 수 있습니다.
여기서 generic table api 중 하나인 RtlNumberGenericTableElements 함수를 볼 수 있습니다.
api를 조사하는데 가장 좋은 출발점은 사용된 구조체를 살펴보는 것입니다. geneic table api와 관련된 여러 함수 중 관련 구조체를 보기 가장 쉬울 것 같은 함수로 RtlInitializeGenericTable이 있습니다. 이름을 보면 무언가를 초기화한다는 것을 알 수 있습니다. 이를 디스어셈블러인 OllyDbg로 디스어셈블 합니다.
함수의 호출 규약에서 가장 먼저 봐야할 것이 종료를 나타내는 RET 명령인데 여기서는 RETN 14로 되어있습니다. RET 명령이 정수의 오퍼랜드와 함께 사용되었는데 이는 오퍼랜드 값만큼 스택을 정리하고 반환한다는 것과 함수가 스택을 정리한다는 것은 이 함수가 cdecl 함수가 아니라는 것을 의미합니다. 함수의 호출자가 스택을 정리하는 경우가 cdecl 함수이기 때문입니다.
또한 전달받은 파라미터는 EDX, ECX를 이용한느데 여기서는 EDX와 ECX의 값을 함수 내부에서 초기화하고 있기 때문에 _fastcall 함수가 아니라는 것을 알 수 있습니다.
그리고 c++ 함수는 클래스 이름과 함수에 전달되는 파라미터의 타입에 따라 내부적으로 이름이 항상 바뀌기 때문에 익스포트 디렉토리를 통해 알고 있는 이 함수는 c++ 멤버 함수가 아닙니다.
그럼 이 함수는 stdcall 함수가 됩니다.
RET 명령의 오퍼랜드로 사용된 14를 통해 함수에 전달된 파라미터의 값을 알 수 있는데 14를 10진수로 표현하면 20이 됩니다. 이는 20바이트를 뜻하고 파라미터로는 최소 4바이트 이상이 올 수 있기 때문에 최대 5개의 파라미터를 가질 수 있습니다.
먼저 ebp 값을 스택에 넣는데 이는 함수에 전달된 파라미터라는 것을 의미합니다.
MOV EAX, DWORD PTR SS:[EBP+8]
- ebp+8위치의 값을 eax에 저장합니다.
XOR EDX, EDX
- EDX 레지스터를 0으로 만듭니다. mov edx, 0 과 같은 작업을 하지만 코드의 길이가 더 작습니다.
LEA ECX, DWORD PTR DS:[EAX+4]
- ECX=EAX+4와 같은 의미입니다.
MOV DWORD PTR DS:[EAX], EDX
MOV DWORD PTR DS:[ECX+4], ECX
MOV DWORD PTR DS:[ECX], ECX
MOV DWORD PTR DS:[EAX+C], ECX
- edx에는 0이 들어있습니다. eax는 파라미터로 받은 값이 들어 있고 위 코드를 통해 이 파라미터는 구조체라는 것과 이 함수 내에서 초기화된다는 것을 알 수 있습니다.
레지스터의 구조체 내 위치를 하나씩 보면
EDX는 0
EAX는 EBP+8이므로 구조체의 시작위치, 첫번째 멤버입니다.
ECX는 EAX+4였으므로 4를 하나의 멤버크기로 간주하고 2번째 멤버의 주소라는 것을 알 수 있고 ECX+4는 3번째 멤버의 위치가 됩니다. EAX가 첫번째이므로 EAX+C는 4번째 멤버라는 것을 알 수 있습니다.
모든 값들이 하나의 포인터를 가리키고 있습니다.
MOV ECX, DWORD PTR SS : [EBP+C]
MOV DWORD PTR DS : [EAX+18], ECX
MOV ECX, DWORD PTR SS : [EBP+10]
MOV DWORD PTR DS : [EAX+1C], ECX
- 첫 줄은 파라미터의 2번째 값을 ECX에 넣고 다시 EAX+18 위치에 넣습니다. EAX+18은 7번째 멤버입니다. 다음라인은 3번째 파라미터를 8번째 멤버에 넣는 구문입니다.
MOV EXC, DWORD PTR : SS[EBP+14]
MOV DWORD PTR DS : [EAX+20], ECX
MOV ECX, DWORD PTR SS : [EBP+18]
MOV DWORD PTR DS : [EAX+14], EDX
MOV DWORD PTR DS : [EAX+10], EDX
MOV DWORD PTR DS : [EAX+24], ECX
- 앞의 줄들과 같이 각 멤버를 초기화하고 있습니다.
구조체의 마지막 멤버의 오프셋이 24이므로 총 크기는 28, 10진수로 보면 40바이트가 될 것이라는 것을 짐작할 수 있습니다. 멤버의 크기를 4바이트로 보고있으므로 총 멤버의 수는 10개로 예상됩니다.
이를 통해 알게된 구조체의 예상 모습입니다.
struct TABLE{
UNKNOWN Member1;
UNKNOWN_PTR Member2; //다른 멤버의 포인터의 값을 넣는 것을 확인했습니다.
UNKNOWN_PTR Member3;
UNKNOWN_PTR Member4;
UNKNOWN Member5;
UNKNOWN Member6;
UNKNOWN Member7;
UNKNOWN Memeber8;
UNKNOWN Member9;
UNKNOWN Member10;
}
'Security ★ Development > Reversing' 카테고리의 다른 글
리버스엔지니어링 문제 1 (0) | 2015.03.22 |
---|---|
보안 취약점 (0) | 2015.03.16 |
무분기 로직 (0) | 2015.03.09 |
api 리버싱 3 - RtlGetElementGenericTable (0) | 2015.03.07 |
api 리버싱 2 - RtlNumberGenericTableElements, RtlIsGenericTableEmpty (0) | 2015.01.27 |