728x90

위와 같은 exe 파일 하나가 있고 메세지 박스에는 Wrong이 적혀있다. 임의의 값 혹은 아무값을 입력하지 않고도 Check를 누르면 프로그램이 종료되고, 

WinDbg로 trace해보면, Access violation 오류로 종료되는 것을 확인할 수 있다.

 

어떤 함수가 이를 일으키는지 확인해보자.

 

우선, exe 파일 시작인 winmain에선 DialogBoxParamA를 호출하여, 대화 박스를 호출하고, 4번째 인자는 대화 상자 프로시저에 대한 포인터이므로, 이 부분을 디버깅하면

이와 같은 사실(주석 확인)을 알 수 있었다.

우리가 박스에 입력하여(10진수로) check 버튼을 누르면, 그 값이 dword_4084D0에 16진수 값으로 저장된다.

 

 

그리고 함수를 살펴보면, 

else if ( (unsigned __int16)a4 == 0x3EB )     // 메세지 입력하면 항상 여기로
  {
    dword_4084D0 = GetDlgItemInt(hDlg, 1002, 0, 0);
    sub_40466F(a1);
    sub_404689();
    *(_DWORD *)sub_40466F = -1013972794;
    sub_40466F(&loc_40469F);
    sub_40466F(v6);
    *(_DWORD *)sub_40466F = 1768;
    return 1;
  }

현재, dword_4084d0에 사용자가 입력한 값이 들어간 상태이다. 

이후, 

1. sub_40466f를 a1을 인자로 실행

edi에 있는 a1은 0x00110F02, 무슨 값인진 모른다.

'sub_40466f' 함수

해당 함수는, 사용자가 입력한 값에 어떠한 hex값을 더한 후, inc 연산까지 하고 return 하는 함수이다.

현재 a1인자가 어떤 역할을 하는지 모르겠고, 이는 ida가 해석한 것이니 잘못 표현됐을 가능성을 열어두고 넘어가자.

 

 

2. sub_404689 함수 실행

'sub_404689' 함수

해당 함수 또한 사용자가 입력한 값을 inc하고 return하는 연산이다.

 

 

3. sub_40466f 함수 포인터 지정

sub_40466f 함수 포인터에 0c39000c6 mov 한 후, sub_40466f call, 즉 opcode "c6 00 90 c3"(리틀엔디안에 따라 역순으로) 을 실행하는것이며, 

https://shell-storm.org/online/Online-Assembler-and-Disassembler/?opcodes=c6+00+90+c3&arch=x86-32&endianness=little&baddr=0x00000000&dis_with_addr=True&dis_with_raw=True&dis_with_ins=True#disassembly

해당 opcode는 eax레지스터에 있는 주소에 0x90 바이트 (nop)를 삽입하고, ret를 한다.

 

해당 text영역과 stack view를 연동시켜보면, 

 

스택에 이전에 넣은 opcode가 있는것을 확인할 수 있고, opcode 내용대로  

eax에 있는 60160a9d 주소에 0x90 바이트 쓰기를 하다 엑세스 위반을 한 것을 알 수 있다.

 

이제 사용자의 입력이 처리되는 과정을 동적 디버깅으로 확인해보겠다.

 

사용자 입력에 1을 입력하면, 

GetDIgItemInt의 결과값, 즉 사용자 입력값인 1이 eax에 들어있는 것이 보이고, 해당 값을 dword_4084d0에 저장한다.

이후 sub_40466f 함수를 실행하면, 

loc_40467a를 호출하고, 

현재 eip가 코드로 정의되지 않은(정의는 ida가 함) 부분을 가르키고 있고, 이를 즉각 변환해준다고 한다.

dword_406014+2에 619060eb를 저장하고, inc를 두번 실행하여 (스택에 해당 주소를 넣었는지 두번 실행되는걸 동적 디버깅중 확인하였다) 현재 dword_4084d0의 값이 (사용자 입력 1) + 1 + 1 = 3인걸 확인하였다.

이후 계속 실행하면, 

또 함수를 생성해준다고 하고, 

위와 같은 연산을 한다. 또한, dword_4084d0 값을 확인해보면 

이와 같이 값이 바뀌었는데, ida에서 보여주지 못한 부분이 있다 생각하고 추측하면, 

이전에 mov로 dword_406014+2에 저장한 619060eb와 관련이 있다 생각한다.

이전 inc연산을 두번 함

최종적으로, eax가 사용자가 입력한 1에 601605d0을 더한 값이 되는것을 확인했다.

디버깅을 하면서, ida가 잡지 못한 부분이 있어 (619060eb를 저장하는 부분) 확실하지 않지만, 사용자의 입력이 덧셈으로만 조작된다 가정하면 

사용자 입력 + 601605d0 = EAX이다.

 

최종적으로, 우린 correct의 출력을 막는 401071을 nop로 씌우면 되므로, eax값을 401071로 만들어야 한다.

 

사용자 입력 + 0x601605d0 = 0x401071

사용자 입력 = 0x401071 - 0x601605d0 = FFFFFFFFA02A0AA1

32비트 프로그램이므로, 뒤의 4바이트만 주소에 들어가면 되므로, 

a02a0aa1을 넣어주면, 뒤 4바이트가 401071이 되는것을 확인하였고, 사용자는 정수로 입력해야 하므로 (GetDIgItemInt이므로) 이를 정수로 변환하면, 

2687109793 을 입력하면 된다.

정답!

 

728x90

'reversing > reversing.kr' 카테고리의 다른 글

[reversing.kr] Music Player  (1) 2024.02.15
[reversing.kr] Easy Keygen  (0) 2021.04.14
[reversing.kr] Easy_CrackMe  (0) 2020.11.29
728x90

exe파일 실행시, 음악 플레이어가 나온다.

테스트 오디오를 재생할 시, 1분 제한이 걸리며 시간이 지나면, 

해당 팝업창이 띄워지며 1분 이후로는 재생이 되지 않는다.

 

우선, 팝업창이 띄워지므로 messagebox api를 호출하는 부분을 모두 trace 해본다.

sub_4038d0, sub_4044c0 에서 호출하는 것을 확인하였다.

호출하는 모든 부분에 breakpoint를 설정한다.

이후, 프로그램을 디버깅하여 어떤 위치에서 "1분 미리듣기만 가능합니다" 라는 message box를 호출하는지 알아보자.

 

sub_4044c0

위 위치에서 호출하는것을 확인하였고, 

해당 함수의 코드를 살펴보면, 

// bad sp value at call has been detected, the output may be wrong!
int __stdcall sub_4044C0(int a1)
{
  //선언부분 생략
  if ( !v1 )
    _vbaNew2(dword_40186C, v55 + 13);
  v2 = v55[13];
  v3 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v2 + 68))(v2, v29);
  __asm { fnclex }
  if ( v3 < 0 )
    _vbaHresultCheckObj(v3, v2, dword_40276C, 68);
  v51 = v29[0];
  if ( v29[0] < 60000 )
  {
    if ( v29[0] != -1 )
    {
      (*(void (__stdcall **)(_DWORD *, int))(*v55 + 1784))(v55, v29[0]);
      v24 = (double)v51;
      v5 = v24;
      if ( dword_407000 )
        adj_fdiv_m64(0, 1079574528);
      else
        v5 = v24 / 100.0;
      if ( (v6 & 0xD) != 0 )
        _vbaFPException(v55);
      v10 = _vbaFpI4(v49, v50, v5);
      if ( v10 > 600 )
        _vbaStrCopy(v55 + 14, L"LI");
      if ( !v10 )
        v10 = 1;
      v11 = (*(int (__stdcall **)(_DWORD *))(*v55 + 796))(v55);
      v12 = (int *)_vbaObjSet(&v49, v11);
      v23 = *v12;
      v13 = _vbaI2I4(v10);
      v14 = (*(int (__stdcall **)(int *, int))(v23 + 188))(v12, v13);
      __asm { fnclex }
      if ( v14 < 0 )
        _vbaHresultCheckObj(v14, v12, dword_402B58, 188);
      _vbaFreeObj(&v49);
    }
    if ( !v55[13] )
      _vbaNew2(dword_40186C, v55 + 13);
    v15 = v55[13];
    v16 = (*(int (__stdcall **)(int, int *))(*(_DWORD *)v15 + 68))(v15, v29);
    __asm { fnclex }
    if ( v16 < 0 )
      _vbaHresultCheckObj(v16, v15, dword_40276C, 68);
    if ( v29[0] > 60010 )
    {
      v34 = dword_402BDC;
      v33[0] = 8;
      rtcVarBstrFromAnsi(&v45, 114);
      v30[0] = 8;
      v31 = dword_402BE4;
      v17 = _vbaVarCat(v42, &v45, v33);
      v18 = _vbaVarCat(v39, v30, v17);
      v19 = _vbaStrVarMove(v18);
      v20 = _vbaStrMove(&v50, v19);
      _vbaStrCopy(v55 + 16, v20);
      _vbaFreeStr(&v50);
      v48 = v39;
      v47 = v42;
      v46 = &v45;
      ((void (__cdecl *)(int))_vbaFreeVarList)(3);
    }
  }
  else
  {
    v4 = (*(int (__stdcall **)(_DWORD *))(*v55 + 1800))(v55);
    if ( v4 < 0 )
      _vbaHresultCheckObj(v4, v55, dword_4025C0, 1800);
    v37 = -2147352572;
    v40 = -2147352572;
    v43 = -2147352572;
    v36[0] = 10;
    v39[0] = 10;
    v42[0] = 10;
    v34 = dword_402BAC;
    v33[0] = 8;
    _vbaVarDup(&v45, v33, v49, v50);
    v48 = v36;
    v47 = v39;
    v46 = v42;
    v45 = 64;
    ((void (__stdcall *)(int *))rtcMsgBox)(&v45);
    v48 = v36;
    v47 = v39;
    v46 = v42;
    v45 = (int)&v45;
    ((void (__cdecl *)(int))_vbaFreeVarList)(4);
  }
  v54 = 0;
  v48 = (int *)&loc_4047CF;
  (*(void (__stdcall **)(_DWORD *))(*v55 + 8))(v55);
  return v54;
}

  if ( v29[0] < 60000 ) , ~~. else, ~~

구조로 이루어져 있는것을 보아, 해당 조건문은

 

  if ( v29[0] < 60000 ):

         "파일을 재생한지 1분이 지나지 않은 경우"

else

          "1분이 지난 경우"

 

이와 같은 구조로 이루어져 있는것을 확인할 수 있다. 또한 특이점으론, 

 

  if ( v29[0] < 60000 ):

         "파일을 재생한지 1분이 지나지 않은 경우"

        if if ( v29[0] > 60010 ): <<???

else

          "1분이 지난 경우"

 

이와 같이 전제 조건에 맞지 않는 조건문이 안에 들어있는것을 확인하여, 해당 부분 로직을 실행하기 위해 바이너리 패치를 하였다.

패치 전
패치 후

이후, 바이너리를 다시 실행해보면, 

노래는 1분 이후로 재생되지만, 어떠한 동작을 하는 도중 runtime error가 발생한다.

해당 error를 유발하는 함수를 찾기위해, conditional breakpoint를 설정하여 디버깅하였다.

여기서 get_reg_value는 IDC의 ida api로, ida 공식 홈페이지에서 확인할 수 있다.

또한, 'v29[0]'이 60010 초과일때, 또다른 로직이 실행되므로, 해당값 이상일 경우 break하도록 설정하였다.

 

v29[0]이 60073인 시점에 break하였고, 프로시저 하나하나 실행해보면, 

150, 159번 줄의 'vbaHresultCheckObj' 함수를 호출할 시, floating point 에러가 발생하는 것을 알 수 있다.

위 조건문을 건너뛰도록 바이너리 패치를 하면, 

 

위와 같이 flag가 messagebox의 제목으로 나오는것을 볼 수 있다.

728x90

'reversing > reversing.kr' 카테고리의 다른 글

[reversing.kr] Replace  (0) 2024.02.16
[reversing.kr] Easy Keygen  (0) 2021.04.14
[reversing.kr] Easy_CrackMe  (0) 2020.11.29
728x90

exe파일과 txt파일 하나씩 있네요. 

이름 입력 형식이 자유니 이름에 따라 serial넘버가 만들어지는것 같네요. 자세한건 디버거로 확인해봅시당

 

v6배열을 확인해보면 0 -> 16, 1 -> 32, 2 -> 48 이렇게 배정되어  있고 루프로 3개씩 반복되니 v7[0] ^ 16, v7[1] ^ 32, v7[2] ^ 48, v7[3] ^ 0x16 이렇게 반복되네요.

sprintf에서 Buffer의 주소와 연산 값을 넣어주고, 입력값(v7)과 string compare한 후에 correct와 wrong으로 분리됩니다.

정적 디버깅으로 코드의 흐름을 대충 알아봤고 이제 동적 디버깅으로 자세히 알아봅시다.

 

우선 연산값을 보기 위해 for문 안에 breakpoint를 하나 걸어주고, 하나하나씩 실행해보며 확인해보겠습니다.

 

우선 첫 변수 name의 입력값은 AAAA로 넣어주었습니다. 입력값이 stack에 잘 들어가는것이 보입니다. 굿

 

 

입력값 "AAAA"의 1번 루프

 

ecx와 edx에 어떤 값을 넣어주는것을 확인할 수 있습니다.

각각 0x10, 0x41이 들어가있습니다. 0x41은 입력값 'A'의 ascii값이고 0x10은 v6[0]값이라고 생각할 수 있습니다. 

또한 다음으로 ecx, edx의 xor값을 ecx에 넣어줍니다. 

잘 들어가있네요 

 

xor된 값을 넣어주고 compare연산 후 루프로 돌아갑니다. 

compare연산은 ebp와 ecx에서 진행하는데 각각 1, 4로 1은 현재 진행한 문자 개수, 4는 총 문자의 개수로 생각할 수 있습니다. 확인은 입력값을 바꿔보면서 할 수 있는데, 과정은 노가다이므로 과감히 생략합니다! 

 

입력값 "AAAA"의 2번 루프

ecx값이 전엔 0x10이였지만 지금은 0x20인 걸 알수있죠. 배열 v6의 값인건 확실합니다. 이후 4번의 루프(입력이 4글자이므로) 끝나고 stack을 확인해보면, 

 

0x0019fe08에 \x42\x42\x42\x42, 즉 "AAAA"가 들어있음을 알 수 있고

0x0019fe6c엔 \x35\x31 (0x51), \x36\x31 (0x61), \x37\x31(0x71), \x35\x31 (0x51) 이 들어가있음을 알 수 있습니다.

(문자열 연산이므로 바이트코드로 들어가지 않아서 ascii로 계산합니다. 뒤에서 2개씩 끊어서 ascii로 변환해보면 값을 알 수 있습니다.)

 

serical을 입력하면 다음과 같은 방식처럼 하나하나 compare연산을 합니다. jnz연산은 총 3번이 있는데 

1, 2번째의 연산은 입력값(serical)과 연산된 값을 비교하는 연산입니다.

(A를 비교한다 하면 A가 ascii값이 0x41이고 4와 1을 하나씩 비교합니다)

3번째 연산은 길이를 계산하여 다하면 루프에서 탈출시켜줍니다. 

 

 

이렇게 연산이 끝나고, correct와 wrong으로 갈라집니다..

 

 

역연산은 어렵지 않습니다. xor은 xor로 상쇄되기 때문이죠! 

 

#include <stdio.h>
int main()
{
	unsigned int arr[8] = { 0x5B,0x13,0x49,0x77,0x13,0x5E,0x7D,0x13 };
	unsigned int ans[8] = { 0 };
	int num[3] = { 16,32,48 };
	int j = 0;
	for (int i = 0; i < 8; i++,j++)
	{
		if (j > 2)
			j = 0;
		ans[i] = arr[i] ^ num[j];
	}
	for (int i = 0; i < 8; i++)
		printf("%c", ans[i]);
}

 

 

 

(ida를 업데이트했더니 아주 이쁘게 잘 분석하네요.  아주 좋아요~)

728x90

'reversing > reversing.kr' 카테고리의 다른 글

[reversing.kr] Replace  (0) 2024.02.16
[reversing.kr] Music Player  (1) 2024.02.15
[reversing.kr] Easy_CrackMe  (0) 2020.11.29
728x90

파일을 실행시키면 다음과 같이 입력창이 나오고, 입력값을 어떤 문자열과 비교한다. 그 문자열을 알아내는 문제다.

 

우선, x32_dbg 로 보면  

이렇게 다양한 정보들이 나오는데, 

 

이 문제를 풀어가는 방법은, visual_studio 혹은 gdb를 사용하는것 처럼 breakpoint를 건 다음에 스택에 들어있는 인자와 어셈블리의 관계를 보면서 확인한다.

 

먼저 모든 모듈에서 '모듈간 참조'를 찾아보면,

이렇게 호출된 다양한 함수들이 나오고 GetDLgItemtext함수가 문자열을 받아오는 함수이다.

 

에디트 간에 문자열 복사하기

: Win32 프로그래밍 관련 전체 목차 http://blog.naver.com/tipsware/221059977193이 강좌는 아래에 링크...

blog.naver.com

다음으로 모든 모듈에서 '문자열 참조'를 찾아보면, 

 

여기서 패스워드가 틀렸을 경우 이동하는 주소를 알 수 있다.

 

Incorrect 일때 이동하는 주소는 401135 인 것을 알 수 있다.

 

따라서, 

 jne(jump not equal - 비교 결과가 다를 때 점프)를 401135로 뛰면 틀린것이니깐 jne를 했을때 값이 같아야 한다.

 

총 4개의 비교가 있고, 각각 break를 걸어서 확인해봤다.

 

STAGE 1.

esp + 5위치에 있는 값과 0x61, 즉 ascii로 'a'랑 같아야 한다.

 

입력이 esp + 4부터 들어가므로 두 번째 string은 'a'이다. (_a_____...)

 

STAGE 2.

ecs에 [esp + A]의 주소를 넣고, test라는 것을 하는데, eax값이 0이냐를 확인하는 구문이지만 디버깅해보면 당연히 넘어가고, 문자열 비교만 확인하면 된다.

1a23456789를 넣었을때, 앞의 1a는 자르고 '5y'랑 비교한다. (_a5y___...)

 

STAGE 3.

 

여러 분기점이 보이지만, string은 'R3versing'이고 해당 값을 cmp를 하는데 

string 값은 b1에 들어있으니깐 이제 내가 어디서부터 입력한 값이 d1에 들어가는지 확인하면 된다.

1a5y23456789를 넣었을때, 

5y 뒷부분은 3versing에 대응하고, 

다른 분기점을 보니 결국 뒷부분은 R3versing에 해당한다. (_a5yR3versing)

 

STAGE 4.

 

우리가 입력한 값은 stage 1에서 말했듯이 esp + 4부터 들어가므로, 첫글자는 E이다. (Ea5yR3versing)

 

728x90

'reversing > reversing.kr' 카테고리의 다른 글

[reversing.kr] Replace  (0) 2024.02.16
[reversing.kr] Music Player  (1) 2024.02.15
[reversing.kr] Easy Keygen  (0) 2021.04.14

+ Recent posts