[Reversing] 6. 정적 분석과 동적 분석의 활용

Wonder_Land🛕·2022년 8월 26일
0

[Reversing]

목록 보기
6/6
post-thumbnail

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.


  1. 서론
  2. 정적 분석
  3. 동적 분석
  4. Q&A
  5. 마치며

1. 서론

이번에는 제가 참고한 강좌에서 제공하는 파일을 정적분석과 동적분석으로 분석해보는 활동입니다.


2. 정적 분석

IDA를 통해 정적 분석을 진행하겠습니다.

1) main함수 찾기

정적 분석은 주로 main함수를 찾고, 이를 분석하면서 시작합니다.

바이너리에서 어떤 함수를 찾는 방법은 크게 두 가지가 있습니다.

  1. 프로그램의 시작 지점인 '진입점(Entry Point, EP)'부터 분석을 시작하여 원하는 함수를 찾을 때까지 탐색하는 방법

  2. 대상 함수의 특성이나 프로그램의 여러 외적인 정보를 이용하여 탐색하는 방법

1번 방법은 바이너리의 규모가 조금만 커져도 분석에 소요되는 시간이 급증합니다.

따라서 이번에는 2번 방법으로 main함수를 찾아보겠습니다.

※ main함수를 찾는 일반적인 방법

일반적으로 main함수는 C계열 언어에서 프로그래머가 작성한 코드 중 가장 먼저 실행되는 함수입니다.

그래서 리버싱을 처음 공부할 때는,
프로그램에서 가장 먼저 실행되는 코드가 main함수라고 착각하기 쉽습니다.

그러나 사실 운영체제는 바이너리를 실행할 때, 바이너리에 명시된 진입점부터 프로그램을 실행합니다.

진입점으로 main함수인게 불가능한 것은 아니지만, 일반적으로는 그렇지 않습니다.

진입점과 main함수 사이를 채우는 것은 컴파일러의 몫입니다.
대부분 컴파일러는 둘 사이에 여러 함수를 삽입하여 바이너리가 실행될 환경을 먼저 구성하고,
그 뒤에 main함수를 호출합니다.

main함수를 쉽게 찾고 싶다면,
컴파일러가 작성하는 진입점에 위치하는 함수에 익숙해져야 합니다.

이 함수를 여러 번 분석하다 보면, 나중에는 이 함수를 보고,
main함수가 어디서 호출될지를 쉽게 찾을 수 있습니다.


2) 문자열 검색

프로그램을 정적 분석할 떄, 많이 사용되는 정보 중 하나가 프로그램에 포함된 문자열입니다.

프로그래머는 디버깅 메시지를 출력하거나, 로그 파일을 생성하는 등의 목적으로 여러 문자열을 프로그램에 포함시킵니다.

이때 사용되는 문자열들은 특성상 유용한 정보를 제공할 때가 많습니다.

예를 들어, 프로그램의 로그와 관련된 문자열들은 로그를 생상하는 함수의 이름과 인자가 적혀있을 수도 있습니다.

따라서, 이 문자열이 어떤 함수에서 사용되는지 찾는다면, 원하는 함수를 쉽게 찾거나,
함수의 이름과 인자를 통해 기능을 유추할 수도 있을 것입니다.

IDA는 바이너리에 포함된 문자열을 쉽게 찾을 수 있도록 '문자열 탐색' 기능을 제공하고 있습니다.

문자열 탐색 기능을 실행하면,
바이너리에 포함된 문자열들이 나열됩니다.

이 문자열들 중에서, 프로그래머가 넣었을법한 내용을 찾습니다.


3) 상호 참조

정적 분석을 하다가, 어떤 수상한 값이나 함수를 찾았다면, 우리는 이를 참조하는 함수를 분석하고자 할 것입니다.

많은 정적 분석 도구들은 '상호 참조(Cross Reference, XRef)'라는 기능을 통해 이를 지원합니다.


4) main함수 분석

이제 main함수를 찾았으므로, 디컴파일 해보겠습니다.

다음은 IDA에 의해 디컴파일된 코드입니다.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  Sleep(0x3E8u);
  qword_14001DBE0 = (__int64)"Hello, world!\n";
  sub_140001060("Hello, world!\n");
  return 0;
}

(1) 인자 분석

IDA는 argc, argv, envp로 3개의 인자를 받는다고 해석했습니다.

(2) 동작

  1. Sleep함수를 호출하여 1초 대기합니다.

  2. qword_14001DBE0"Hello, world!\n"문자열의 주소를 넣습니다.

  3. sub_140001060에 `"Hello, world!\n"를 인자로 전달하여 호출합니다.

  4. 0을 반환합니다.

(3) 반환값

0을 반환합니다.


5) sub_14001060함수 분석

다음은 sub_14001060함수의 디컴파일된 코드입니다.

__int64 sub_140001060(__int64 a1, ...)
{
  FILE *v1; // rax
  va_list va; // [rsp+58h] [rbp+10h] BYREF

  va_start(va, a1);
  v1 = _acrt_iob_func(1u);
  return (unsigned int)sub_140001010(v1, a1, 0i64, (__int64 *)va);
}

먼저 va_start함수를 통해 가변 인자를 처리하는 함수임을 알 수 있습니다.

_acrt_iob_func(1u)함수는 스트림을 가져올 때 사용되는 함수인데,
인자로 들어가는 1stdout을 의미합니다.

즉, 문자열을 인자로 받고 stdout 스트림을 내부적으로 사용하는 가변 함수입니다.

따라서, printf함수로 추정할 수 있습니다.


3. 동적 분석

1) main함수 진입

(1) 중단점(Break Point, F2) 및 실행(Run, F9)

동적 분석은 프로그램을 실행하면서 분석하는 방법입니다.

그런데, 사실 우리는 프로그램 중 아주 일부분의 동작에만 관심이 있습니다.

이 활동에서는, main함수가 유일한 관심 대상입니다.

그런데, 이러한 상황에서 진입점에서부터 main함수까지 한 줄씩 실행시켜 main함수에 도달해야하는 것은 효율적이지 않습니다.

그래서 대부분의 디버거는 우리가 원하는 지점까지 실행시킬 수 있는 중단점실행이라는 기능을 제공합니다.

중단점을 특정 주소에 지정하고, 실행 명령을 내리면,
프로그램은 중단점까지 멈추지 않고 실행됩니다.

  1. main함수에 중단점을 설정합니다.

  2. 디버깅을 시작해 main함수까지 실행시킵니다.


(2) 한 단계씩 실행(Step Over, F8)

중단점과 실행이 필요한 실행 과정을 생략하는 기능이라면,
Step Over은 관심이 있는 부분의 코드를 정밀하게 분석하는 기능입니다.


(3) 함수 내부로 진입하기(Step Into, F7)

Step Over 기능은 함수의 내부로 진입하지 않습니다.

그런데 어떤 함수를 분석하다 보면, 그 함수가 호출하는 다른 함수까지 정밀하게 분석해야할 때가 있습니다.

이 때는 Step Into를 사용합니다.


2) Appendix, 실행 중인 프로세스 조작하기

(1) Sleep Forever

기존코드에서는 Sleep(delay=1000)를 호출하여 1초 동안 프로세스를 정지시켰습니다.

이번에는 delay1000000으로 조작하여 프로세스를 정지시킬 수 있습니다.


4. Q&A

-


5. 마치며

오늘은 정적 분서과 동적 분석을 IDA를 통해 직접 해보았습니다.

사실 처음 써보다 보니, 단축키나 보는 법이 어색하네요😂

계속 쓰면서 익숙해져야 겠습니다.

[Reference] : 위 글은 다음 내용을 제가 공부한 후, 인용∙참고∙정리하여 만들어진 게시글입니다.

profile
아무것도 모르는 컴공 학생의 Wonder_Land

0개의 댓글