x64 Lotto
A lottery check decompiled in IDA, where the comparison can be satisfied without guessing the numbers.
로또 번호를 맞춰야하는 프로그래밍인거 같다. 먼저 아이다 64 로 열어서 파일을 디컴파일링해 파일을 정적 분석을해보자. 밑에는 아이다 F5 디컴파일링으로 가져온 소스코드이다: do { wprintf(L”\n\t\tL O T T O\t\t\n\n”); wprintf(L”Input the number: ”); wscanf_s(L”%d %d %d %d
%d %d", &v13, &v14, &v15, &v16, &v17, &v18); wsystem(L"cls"); Sleep(0x1F4u); for ( i = 0i64; i < 6; v19[i -
1] = rand() % 100 ) ++i; v2 = 1; v3 = 0; v4 = 0i64; byte_1400035F0 = 1; while ( v19[v4] == *(int *)((char
*)&v13 + v4 * 4) ) { ++v4; ++v3; if ( v4 >= 6 ) goto LABEL_9; } v2 = 0; byte_1400035F0 = 0; LABEL_9: ; }
사용자로부터 6개의 정수 입력을 받아 저장한 뒤, rand() % 100으로 무작위 6개 숫자를 생성합니다. 생성된 랜덤 숫자 배열(v19)과 사용자가 입력한 숫자(v13~v18)를 앞에서부터 순차 비교합니다. 6개가 모두 동일하면 성공 플래그(v2=1, byte_1400035F0=1) 유지, 하나라도 다르면 즉시 실패로 설정합니다.
while ( v3 != 6 ); v5 = byte_140003021; v23[1] = 92; v23[0] = 184; v23[2] = 139; v23[5] = 184; v23[3] =
107; v6 = 0i64; v23[4] = 66; v23[6] = 56; v23[7] = 237; v23[8] = 219; v23[9] = 91; v23[10] = 129; v23[11] =
41; v23[12] = 160; v23[13] = 126; v23[14] = 80; v23[15] = 140; v23[16] = 27; v23[17] = 134; v23[18] =
245; v23[19] = 2; v23[20] = 85; v23[21] = 33; v23[22] = 12; v23[23] = 14; v23[24] = 242; v24 = 0; do { v7 =
byte_140003021[v6 - 1]; v6 += 5i64; *((_WORD *)&v20 + v6 + 1) ^= (unsigned __int8)(v7 - 12); *((_WORD
*)&v21 + v6) ^= (unsigned __int8)(byte_140003021[v6 - 5] - 12); *((_WORD *)&v21 + v6 + 1) ^= (unsigned
__int8)(byte_140003021[v6 - 4] - 12); v23[v6 - 2] ^= (unsigned __int8)(byte_140003021[v6 - 3] - 12);
v23[v6 - 1] ^= (unsigned __int8)(byte_140003021[v6 - 2] - 12); } while ( v6 < 25 ); if ( v2 ) { v8 = 0; v9 = v23;
do { v10 = *v9++; v11 = v8++ + (v10 ^ 0xF); *(v9 - 1) = v11; } while ( v8 < 25 ); v24 = 0; wprintf(L"%s\n",
v23); } wprintf(L"\n", v5); return 1i64; }
여러 변수를 고정된 값으로 초기화한다. 0x140003021에 있는 데이터를 가져와서, 초기화한 변수들과 순서대로 XOR 연 산을 수행해 값을 변경한다. v1 == 1 (앞에서 로또 숫자를 모두 맞춘 경우)이면, 변형된 값들에 다시 0xF를 XOR한 뒤 문 자열로 출력한다. 이 출력 결과가 flag이다. 우리의 입력값들이 flag 를 만들어내는데 아무런 영향을 주지 않고 오직 v23 이랑 byte_140003021들의 값들만을 사용해 플래그가 생겨난다.

그럼 x64dbg 에 다시 들어가서 확인해보자. 메인함수로가 로또 번호를 받는곳으로 문자열 찾기를 이용해 옴겨간다. CLS 문자열이 있으니까 거기 브레이크 포인트를 걸어주자. 코드에서 성공 여부를 결정하는 cmp 부분들에 브레이크 포인 트를 걸고, 값들을 분기해준다. 성공 여부를 결정하는 코드인지 아닌지 아는 방법은 그후 jne 가 처음으로 다시 이동시키 면 성공여부를 결정하는 코드인걸 알수있다
첫번째로 밑에 있는 cmp edx, 6 부분에 걸어준다. 여기서 만약 edx 가 6과 같지 않으면 다시 시작 부분으로 옴겨간다. rdx 부분을 6으로 분기후 F8 해준다.

두번째로 test r8b, r8b 부분에 하드웨어 브레이크를 걸어준다. 도착하면 R8 를 1로 분기해서 다시 처음으로 돌아가지 않 게 해준다.

숫자를 틀리면:
• ZF = 0
• jne → 실패 루트
맞추면:
ZF = 1
• 성공 루트
부가 설명. 왜 CLS 에 첫번째 BP 를 거는가? “사용자 입력/출력의 시작점이기 때문”이다 흐름을 보면 EntryPoint CRT 초 기화 (__scrt_common_main_seh) → 사용자 코드 진입 (wmain) 화면 정리 (system(“cls”)) UI 출력 (wprintf(“LOTTO”)) 입력 (wscanf_s) cls는 CRT가 아니라 ‘문제 출제자가 작성한 로직’의 첫 행동이다 그래서 리버 싱에서는: cls / printf / scanf가 연속으로 나오면 → “아, 여기부터 사용자 main이구나” 라고 판단한다.

참고로 여기 wprintf 문 (플래그를 프린트한다) 에 bp 를 걸어야지 성공했을때 플래그를 얻을수있다! 끝에 답은:
