보안/리버스 엔지니어링

C언어 리버싱(1)

Seonyoung Jeong 2022. 9. 29. 20:38

함수의 기본 구조

push ebp
mov ebp, esp
push ecx
....
....
....
mov esp, ebp
pop ebp
retn

모든 함수는 위의 세줄로 시작하고 아래 세줄로 끝난다!!

 

함수의 호출규약

함수의 호출규약에는 대표적으로 _cdecl, _stdcall, _fastcall, _thiscall 총 네가지가 있다.

이 호출규약을 보면 지금 보는 이 함수의 역할이 무엇인지, 파라미터가 어떤 구조로 넘어가는지를 알 수 있다.

이것만 알아도 리버싱 50퍼는 한 것으로 볼 수 있다.

디스어셈블된 코드의 특징을 살펴 어떤 호출규약을 사용하는지 파악할 수 있다.

 

_cdecl 방식

cdecl 호출방식의 주요 특징은 바로 이거다.

함수 호출문 바로 아래에 add esp, 8과 같은 어셈코드가 있다. 사용한 스택을 정리하는 코드인데 cdecl은 함수 호출문 바로 아래에 있음.

일단 아래 코드로 살펴보자.

int _cdecl sum(int a, int b)
{
	int c=a+b;
    return c;
}

int main(int argc, char* argv[])
{
	sum(1, 2);
    return 0;
}

함수 호출 규약을 cdecl로 한 c코드가 있다. 위의 코드를 ollydbg에서 리버싱해봤다.

우선 이게 main문이다. main문은 여기서 한번만 볼거임.

맨 윗줄에서 함수의 시작과 함께 지역변수 공간확보하는거 보여서 이걸로 main 시작임을 알 수 있음. 그리고 맨 뒷줄에 세개로 함수가 저기서 끝나는걸 알 수 있다.

 

그럼 이제 sum 함수를 살펴보자. main에서 보면 우선 파라미터 두개를 스택에 넣는 걸 확인할 수 있다.

그 아래에 바로 call.리버싱.003F1172라는 함수호출문이 보인다. ('리버싱'은 내가 설정한 실행파일 이름임!)

그리고 그 아래에 cdecl 주요 특징인 스택 정리문이 보인다. 이걸 통해 cdecl 방식임을 알 수 있다.

 

_stdcall

_stdcall방식의 주요 특징은 바로 스택 정리를 함수 종료 전 함수 내에서 한다는 것이다.

아까는 함수가 종료되고 main으로 다시 돌아와서 했는데, 이번에는 main으로 돌아가기 전, 함수 리턴되기 전에 하는 거임.

캡처 왜 이따구로했지,,,,,? 암튼 아까와 같은 main 문이다. 근데 다른 점이 있는 main문이다. 함수 호출문 바로 아래에 스택 정리 구문이 없어졌다. 아까 말했다시피 stdcall은 스택 정리를 함수 안에서 한다고 했잖음? 정리하는 구문이 함수 안에 있겠지? 그걸 확인하러 가보자.

이게 바로 sum함수다. 가운데에 박스 친 부분이 바로 int c = a+b; 부분이다.

sum함수에서 a, b는 파라미터고 스택에 거꾸로 쌓이니까 EBP+4 위치에는 리턴주소가, EBP+8에는 int a가, EBP+C에는 int b가 있다. 이해 안되면 이전 게시글 보고오기!!!!

암튼 그래서 EAX에 a를 더해놓고, EBP+C에 있는 b를 더하고, 더한 값을 EBP-8, 바로 지역변수 위치에 올려놓는다.

그리고 리턴을 하는거임.

근데 아까 _cdecl방식과는 좀 다른게 있음. 아까는 그냥 RETN이었는데 여기서는 RETN 8을 함. 이게 바로 스택 정리하는 거. 

 

여기서 주의할 점!!!!!! 만약 호출문 아래에도 스택정리구문이 없고, 함수 내에도 그냥 RETN이라면 이건 파라미터가 없는 함수의 _stdcall 호출 방식으로 생각하면 된다!!!!!

 

_fastcall

fastcall이라는 이름을 보면 알겠지만 빠른 속도의 함수 호출 규약이다. 그래서 인자를 따로 push하지 않는다.

위의 두가지는 main에서 push 2, push 1했는데 여기서는 그러지 않고 그냥 ecx, edx 레지스터를 이용해서 연산한다.

레지스터가 속도가 빠르니까!

main문이다. 아까는 push로 스택에 들어갔던 1, 2가 이번에는 레지스터로 들어가는걸 확인할 수 있다.

여기서는 함수 호출 후가 아니라 함수 호출 전에 변수가 어떻게 들어가는지를 확인하면 fastcall이라는걸 알 수 있다.

 

fastcall의 특징을 살리기 위해 보통 인자가 2개 이하면서 이용 빈도가 높은 함수에 쓰이는 편이다.

 

_thiscall

_thiscall 방식은 주로 C++ 클래스에서 이용된다. 주요 특징은 객체 포인터를 ecx로 전달하는 점이다.

이건 C++내용이니 일단 여기서는 이것만 알고 패스!!