콘텐츠로 건너뛰기

Music Player

ReadMe.txt

This MP3 Player is limited to 1 minutes.
You have to play more than one minute.

There are exist several 1-minute-check-routine.
After bypassing every check routine, you will see the perfect flag.

MP3 Player 프로그램이 있는데 1분 듣기만 가능하지만,
1분 듣기 제한을 우회하면 flag를 획득할 수 있다고 한다.

Analysis

.실제로 재생해서 1분이 넘어가면,
“1분 미리듣기만 가능합니다.”라고 창이 뜨면서, 재생이 중단된다.

rtcMsgBox 함수를 통해 저 메시지 창을 띄우는데,

call하는 곳은 총 4군데이다.

00403B07,
00403C5F,
00403E22,
004045D8,

4군데를 전부 다 브레이크포인트를 걸어서
어디서 “1분 미리듣기만 가능합니다.” 창이 뜨는지 확인해본 결과,

sub_4044C0 함수의 004045D8에서 호출하는 것을 확인할 수 있었다.

sub_4044C0

// bad sp value at call has been detected, the output may be wrong!
int __stdcall sub_4044C0(int a1)
{
  int v1; // eax
  int v2; // edi
  int v3; // eax
  int v4; // eax
  double v5; // st7
  char v6; // fps
  int v10; // ebx
  int v11; // eax
  int *v12; // edi
  int v13; // eax
  int v14; // eax
  int v15; // edi
  int v16; // eax
  int v17; // eax
  int v18; // eax
  int v19; // eax
  int v20; // eax
  _DWORD v22[3]; // [esp-Ch] [ebp-CCh] BYREF
  int v23; // [esp+0h] [ebp-C0h]
  double v24; // [esp+4h] [ebp-BCh]
  int v25; // [esp+Ch] [ebp-B4h]
  int v26; // [esp+10h] [ebp-B0h]
  int v27; // [esp+14h] [ebp-ACh]
  int v28; // [esp+18h] [ebp-A8h]
  int v29[9]; // [esp+1Ch] [ebp-A4h] BYREF
  int v30[2]; // [esp+40h] [ebp-80h] BYREF
  int *v31; // [esp+48h] [ebp-78h]
  int v32; // [esp+4Ch] [ebp-74h]
  int v33[2]; // [esp+50h] [ebp-70h] BYREF
  int *v34; // [esp+58h] [ebp-68h]
  int v35; // [esp+5Ch] [ebp-64h]
  int v36[2]; // [esp+60h] [ebp-60h] BYREF
  int v37; // [esp+68h] [ebp-58h]
  int v38; // [esp+6Ch] [ebp-54h]
  int v39[2]; // [esp+70h] [ebp-50h] BYREF
  int v40; // [esp+78h] [ebp-48h]
  int v41; // [esp+7Ch] [ebp-44h]
  int v42[2]; // [esp+80h] [ebp-40h] BYREF
  int v43; // [esp+88h] [ebp-38h]
  int v44; // [esp+8Ch] [ebp-34h]
  int v45; // [esp+90h] [ebp-30h] BYREF
  int *v46; // [esp+94h] [ebp-2Ch]
  int *v47; // [esp+98h] [ebp-28h]
  int *v48; // [esp+9Ch] [ebp-24h]
  int v49; // [esp+A0h] [ebp-20h] BYREF
  int v50; // [esp+A4h] [ebp-1Ch] BYREF
  int v51; // [esp+A8h] [ebp-18h]
  _DWORD *v52; // [esp+B4h] [ebp-Ch]
  int *v53; // [esp+B8h] [ebp-8h]
  int v54; // [esp+BCh] [ebp-4h]
  _DWORD *v55; // [esp+C8h] [ebp+8h]

  v52 = v22;
  v53 = dword_4011D8;
  v54 = a1 & 1;
  v55 = (_DWORD *)(a1 & 0xFFFFFFFE);
  (*(void (__stdcall **)(_DWORD *, _DWORD, _DWORD, _DWORD, int, _DWORD, _DWORD, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int *, int, int, int, int *, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int *, int *, int *))(*v55 + 4))(
    v55,
    v22[0],
    v22[1],
    v22[2],
    v23,
    LODWORD(v24),
    HIDWORD(v24),
    v25,
    v26,
    v27,
    v28,
    v29[0],
    v29[1],
    v29[2],
    v29[3],
    v29[4],
    v29[5],
    v29[6],
    v29[7],
    v29[8],
    v30[0],
    v30[1],
    v31,
    v32,
    v33[0],
    v33[1],
    v34,
    v35,
    v36[0],
    v36[1],
    v37,
    v38,
    v39[0],
    v39[1],
    v40,
    v41,
    v42[0],
    v42[1],
    v43,
    v44,
    v45,
    v46,
    v47,
    v48);
  v1 = v55[13];
  v50 = 0;
  v49 = 0;
  v45 = 0;
  v42[0] = 0;
  v39[0] = 0;
  v36[0] = 0;
  v33[0] = 0;
  v30[0] = 0;
  v29[0] = 0;
  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;
}

else 문에서 “1분 미리듣기만 가능합니다.” 창이 뜨는데,
else 문이 아닌 항상 if문으로 가도록 패치해주면 될 것이다.

40456B에 있는 명령어에 의해 if문으로 분기되므로,
항상 분기되도록 jmp loc_4045FE로 명령어를 패치해주자.

그리고 다시 스텝을 밟다보면, 0x4046B8에 있는 명령어에 의해 충돌이 발생한다.

4046B9 함수에 있는 저 call 명령어를

nop으로 패치시켜주면 된다.

FLAG

LIstenCare

태그:

답글 남기기