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