문제 파일을 다운받고, 디컴파일 해보면 ..
31번째줄에 check1 이라는 함수가 있다.
해당 함수에서 '참'이 반환되는 입력값이 플래그일 것이기 때문에,
이 함수를 분석해보겠다.
변수 v5에 할당되는 배열을 알아내기 위해 해당 주소값을 찾아가보았다.
그 결과 다음과 같은 배열을 얻게 되었다.char v5[80] = { 0xA6, 0x62, 0x55, 0xA8, 0x90, 0xAC, 0xBA, 0x0A, 0xE5, 0x3B, 0x07, 0xDD, 0x09, 0x03, 0x8B, 0x49, 0x8D, 0x73, 0x01, 0xCD, 0xF2, 0xCF, 0xAB, 0xB0, 0xBE, 0x12, 0x43, 0xD8, 0x55, 0xBD, 0xBB, 0x0E, 0xC1, 0x18, 0xCF, 0x5A, 0xD0, 0x60, 0xB7, 0xF3, 0xE9, 0xB6, 0x4D, 0xD7, 0x14, 0x0B, 0x0A, 0x0F, 0xC1, 0x05, 0x13, 0x77, 0x45, 0x7A, 0x20, 0x28, 0x79, 0x80, 0xCB, 0x9E, 0xF9, 0xBF, 0x55, 0x21, 0x4D, 0x17, 0x11, 0x5D, 0x21, 0x77, 0x8F, 0xD1, 0xAA, 0x02, 0xE2, 0x7E, 0xFD, 0xAA, 0x15, 0x2C };해당 배열을 방금 check1 함수에서 문자열을 검사하는 역할을 하는 조건문에 역연산하면 된다.
if ( *(_BYTE *)(a1 + v2) - ((v1 ^ 0xA6) + 4 * (_BYTE)v2) != v3 ) | a1 | 검사 대상 문자열의 주소 | v1 | 반복마다 `+93`씩 증가 | v2 | 0부터 79까지 반복 증가 | v5[5] | 80바이트 16진수 배열이 조건문을 이해하기 쉽도록 C언어 기반으로 다시 작성해보았다.
if (a1[i] - ((v1 ^ 0xA6) + 4 * v2 != v3))이제 해당 식을 이용하여 플래그를 출력하기 위한 역연산 코드를 구상해보겠다.
I | a1[i] - ((v1 ^ 0xA6) + 4 * v2 != v3) II | a1[v2] = v3 + ((v1 ^ 0xA6) + 4 * v2)해당 역연산식을 이용하여 플래그를 출력하는 프로그램을 작성해보았다.
#include <stdio.h> int main() { char v5[80] = { 0xA6, 0x62, 0x55, 0xA8, 0x90, 0xAC, 0xDB, 0x0A, 0xE5, 0x3B, 0x07, 0xDD, 0x09, 0x03, 0x8B, 0x49, 0x8D, 0x73, 0x01, 0xCD, 0x26, 0xFF, 0xBC, 0x0A, 0xEB, 0x2B, 0x31, 0x84, 0x5D, 0xD5, 0xBB, 0xE8, 0xC1, 0x8D, 0xF1, 0xAC, 0x05, 0x0D, 0x76, 0x3B, 0x9F, 0x6E, 0xDB, 0x74, 0x4D, 0xB1, 0xA0, 0xF0, 0xC1, 0x05, 0x13, 0x77, 0x45, 0x7A, 0x20, 0x28, 0x79, 0x80, 0xCB, 0x9E, 0xF9, 0xBF, 0x55, 0x21, 0x4D, 0x17, 0x11, 0x5D, 0x21, 0x77, 0x8F, 0xD1, 0xAA, 0x02, 0xE2, 0x7E, 0xFD, 0xAA, 0x15, 0x2C }; int a = 0; for (int i = 0; i < 80; i++) { v5[i] = v5[i] + ((a ^ 0xA6) + 4 * i); a += 93; } for (int i = 0; i < 80; i++) { printf("%c", v5[i]); } putchar('\n'); // 줄바꿈 추가 (선택 사항) return 0; }그 결과 정상적인 플래그가 출력되었다.
문제 파일을 다운받고, 디컴파일 해보면 ..
74번째줄에 check2 이라는 함수가 있다.
요번에도 마찬가지로 해당 함수를 분석해보겠다.
저번에 이어서 다시 한 번 함수 rol이 나왔다.
rol은 왼쪽으로 비트를 회전시키는 함수이다.ror (오른쪽으로 비트 회전) | 오른쪽으로 밀린 비트는 왼쪽 끝으로 돌아옴 rol (왼쪽으로 비트 회전) | 왼쪽으로 밀린 비트는 오른쪽 끝으로 돌아옴함수 rol은 ((3 * i) ^ a1[i])와 (i % 7 + 1)을 연산한 후 반환한다.
그 후 v3 + i 와 일치하는지 검사한다.이 식을 역연산하기 위해 ror을 사용하여 회전시킨 비트 수 만큼
다시 복호화하는 코드를 구상해보았다.#include <stdio.h> unsigned ror(unsigned char a1, int a2) { return (a1 >> a2) | (a1 << (8 - a2)); } int main() { unsigned long long v3[21] = { v3[0] = 0x8EB40ECFC6FB8998LL, v3[1] = 0x21BCB25368270A01LL, v3[2] = 0x39C4F2B5D689171BLL, v3[3] = 0x93194406854821B0LL, v3[4] = 0x87E1D8EC10954864LL, v3[5] = 0xFB5C6BB58B321FCFLL, v3[6] = 0xFF243BEAEFC7C9EBLL, v3[7] = 0xCF657E5B3DF6A7F7LL, v3[8] = 0x1A6B4F25151AB41ELL, v3[9] = 0xA51A5B452E37A804LL, v3[10] = 0xF5BBE6775823F07ALL, v3[11] = 0xCF967B98E6BB5269LL, v3[12] = 0x58AA852A2DACB056LL, v3[13] = 0x164B41C02935DAB7LL, v3[14] = 0xEAB47A4B333990ELL, v3[15] = 0x7C1E27C74602F16DLL, v3[16] = 0xBFFBCFE57B3C8E46LL, v3[17] = 0x4F0697EF797E9D1FLL, v3[18] = 0x91EA4F3AB5E8E1FCLL, v3[19] = 0xE15D189C62F77DE3LL, v3[20] = 0x44F1B74B444E535CLL }; unsigned char * byte = (unsigned char *) v3; for (int i = 0; i < 168; i++) { printf("%c", ror(byte[i], i % 7 + 1) ^ (3 * i)); } return 0; }함수 ror을 선언한 후, 변수 v3의 값들을 byte 단위로 나누어준다.
그 후 역연산하여 출력하는 코드이다.실행시키면 플래그가 나온다.
문제 파일을 다운받고, 디컴파일 해보면 ..
31번째줄에 check3 이라는 함수가 있다.
해당 함수를 분석해보겠다.
입력 값 a1[v3]은 unsigned char로 처리되며, 그 값을 sbox[]에 인덱스로 넣어 나온 값이 v2와 같아야한다.
perm[] 배열은 어떤 순서로 a1의 값을 검사할 것 인지를 지정한다.
enc3[] 배열은 sbox[a1[perm[i]]]과 일치해야하는 값이다.변수에 들어있는 값들이 너무 많으므로 따로 더 생각하지 않고,
바로 역연산하는 코드를 작성하였다.#include <stdio.h> unsigned char sbox[] = { 0xE8, 0x76, 0xDC, 0x0F, 0xCC, 0x4A, 0xF0, 0x16, 0x78, 0x42, 0x4D, 0xAD, 0x5B, 0x9F, 0x2E, 0xBD, 0x96, 0xAB, 0x8E, 0xAF, 0x13, 0x68, 0x9E, 0xD6, 0xB1, 0x72, 0xB0, 0x58, 0x2F, 0x31, 0xC6, 0x47, 0x21, 0x5D, 0x9C, 0x26, 0x22, 0x25, 0xB2, 0xFA, 0x30, 0x92, 0x90, 0xF5, 0xBC, 0x48, 0x2C, 0xE6, 0x12, 0xE4, 0x6B, 0x35, 0xDE, 0xA1, 0xFD, 0x03, 0x19, 0x5E, 0x51, 0x85, 0x46, 0x1A, 0x0A, 0x71, 0x0B, 0x01, 0x59, 0xEC, 0xF6, 0xCE, 0x7B, 0x9A, 0xC5, 0xDD, 0x6A, 0xF7, 0xCF, 0xC3, 0x94, 0xD1, 0x8B, 0x0D, 0x2B, 0xCB, 0x7A, 0x60, 0xA6, 0x53, 0xDF, 0x06, 0x3B, 0x63, 0xEB, 0xB6, 0x37, 0x54, 0xD0, 0xDB, 0x18, 0x34, 0x67, 0x64, 0x1F, 0x3C, 0x69, 0x49, 0xA2, 0x83, 0x45, 0xB4, 0x3E, 0x55, 0xFB, 0x86, 0x41, 0xA5, 0x93, 0x6E, 0xC2, 0x9D, 0x08, 0xC7, 0x07, 0xF9, 0x5A, 0x09, 0xA8, 0xE1, 0x3A, 0x05, 0x56, 0xD2, 0x39, 0xED, 0x1E, 0x73, 0x84, 0x70, 0xAC, 0xE3, 0x4F, 0x33, 0xAA, 0xF8, 0xD4, 0xA9, 0xB9, 0x2A, 0x8C, 0x79, 0x97, 0x20, 0x88, 0x11, 0x7C, 0x15, 0x14, 0xFC, 0xB3, 0x0E, 0x3D, 0x4E, 0xF4, 0xC4, 0x04, 0x7D, 0x52, 0x99, 0xBB, 0xCD, 0x8A, 0x29, 0x5F, 0xD9, 0x32, 0x9B, 0xB5, 0xA3, 0x4C, 0xD8, 0x1B, 0x81, 0x2D, 0x80, 0xFE, 0xBA, 0xC8, 0x00, 0x74, 0xD7, 0x6F, 0x4B, 0xDA, 0x57, 0xF2, 0x44, 0xC0, 0xF3, 0xC9, 0x1D, 0xB8, 0xD5, 0x02, 0x8D, 0x40, 0x87, 0x77, 0xD3, 0x61, 0x62, 0x10, 0x98, 0xFF, 0xAE, 0xCA, 0x28, 0xA0, 0x27, 0x1C, 0x0C, 0xC1, 0x17, 0x7E, 0x82, 0xEE, 0x38, 0x5C, 0x66, 0xE7, 0xE5, 0x6D, 0x95, 0xBF, 0xEF, 0x89, 0x8F, 0xBE, 0x3F, 0x23, 0x43, 0x24, 0x75, 0xA4, 0xEA, 0xE2, 0x65, 0x7F, 0xA7, 0x36, 0x50, 0xB7, 0xE0, 0x91, 0xF1, 0xE9, 0x6C }; unsigned char perm[] = { 0x2A, 0x29, 0x5B, 0x09, 0x41, 0x32, 0x01, 0x46, 0x0F, 0x4E, 0x49, 0x0A, 0x37, 0x38, 0x48, 0x2D, 0x30, 0x5C, 0x4C, 0x25, 0x1E, 0x15, 0x20, 0x60, 0x50, 0x31, 0x53, 0x1A, 0x57, 0x21, 0x08, 0x2F, 0x3B, 0x3F, 0x4A, 0x2C, 0x62, 0x34, 0x55, 0x0C, 0x24, 0x17, 0x27, 0x28, 0x12, 0x42, 0x3D, 0x3C, 0x07, 0x22, 0x63, 0x2E, 0x02, 0x33, 0x10, 0x26, 0x3A, 0x44, 0x16, 0x3E, 0x18, 0x05, 0x06, 0x43, 0x52, 0x13, 0x4F, 0x2B, 0x5A, 0x14, 0x00, 0x5F, 0x39, 0x5D, 0x35, 0x59, 0x19, 0x47, 0x54, 0x4D, 0x40, 0x1D, 0x1B, 0x58, 0x61, 0x04, 0x36, 0x4B, 0x0B, 0x45, 0x56, 0x0D, 0x11, 0x1C, 0x1F, 0x23, 0x5E, 0x03, 0x0E, 0x51 }; unsigned char encr[] = { 0x83, 0x67, 0x34, 0x06, 0x94, 0x3C, 0xDB, 0x83, 0x34, 0x3C, 0xE4, 0x3C, 0x34, 0x49, 0x9A, 0x2B, 0x6B, 0x9A, 0x6B, 0x9D, 0x3C, 0x6A, 0x53, 0x0D, 0x6B, 0x60, 0xDD, 0x35, 0x18, 0x45, 0x53, 0xDB, 0x34, 0x18, 0x69, 0xC5, 0x1A, 0x53, 0xE4, 0x9A, 0xCB, 0x3B, 0xCB, 0xCB, 0x35, 0x07, 0xA6, 0xB4, 0xCB, 0x6E, 0x09, 0x69, 0xC7, 0x64, 0xC7, 0x3C, 0x83, 0x53, 0xA5, 0x69, 0xEC, 0x03, 0xF9, 0x06, 0x93, 0x18, 0x6A, 0xDD, 0xFB, 0x35, 0xCF, 0xF6, 0x59, 0x08, 0x5E, 0x2B, 0x59, 0xDD, 0x9A, 0x60, 0xDF, 0xC3, 0x06, 0xC5, 0x55, 0x41, 0xE4, 0xDB, 0x67, 0xA1, 0xE4, 0x69, 0x59, 0xDF, 0x18, 0x3B, 0xA1, 0x64, 0xFB, 0xA6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; int main() { char answer[100] = {0}; for (int i = 0; i < 100; i++) { unsigned char char_1 = encr[i]; int char_2 = perm[i]; for (int j = 0; j < 256; j++) { if (sbox[j] == char_1) { answer[char_2] = j; break; } } } answer[100] = 0; printf("%s\n", answer); return 0; }answer은 정답 문자열을 저장하기 위한 문자열 변수다.
for문을 통해 i의 값을 상승시키며 char_1과 char_2의 값을 계속 업데이트 해준다.
sbox[i]의 값이 char_1. 즉, encr[i] 값과 동일할 경우 answer에 char2. 즉, answer에 인덱스 perm[i]에 i를 저장한다.
마지막에 Null 문자를 삽입하는 것을 잊지말자.다음 프로그램을 실행하여 플래그 값을 얻을 수 있다.
문제 파일을 다운받고, 디컴파일 해보면 ..
플래그를 찾아달라는 말 밖에 없다 ..
그래서 Shift + F12 단축키를 사용하여 문자열 변수를 확인해보았다.
그 결과 다음과 같은 플래그 값을 찾을 수 있었다.
이것을 보고 알 수 있는 것이 어떠한 비밀번호가 6자리이며 알맞은 비밀번호를 입력했을 때,
플래그를 알아낼 수 있다는 것을 추측해 볼 수 있다.
여기다가 비밀번호를 입력하면 되는 것 같다.
F12 관리자 창을 이용해 Source 탭에서 JS 소스를 분석해보자.
function _0x9a220(..., ...) return alert('Wrong')함수 _0x9a220가 작동한다는 것과 잘못된 값이 입력되었을 때,
'Wrong'이라는 알람이 뜨는 것 외에는
난독화 때문에 알 수 있는 정보가 별로 없는것 같다.따라서 이 문제는 무작정 모든 생년월일을 입력해보는
무차별 대입 공격(brute-force)을 이용해서 해결해야하는 것 같다.Console 탭에서 생일을 무작정 대입해보는 소스를 작성해보았다.
window.alert = function (val) { console.log("fail"); return 1; }; function tryInput(val) { const format = /^([0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1]))$/; if (format.test(val)) { _0x9a220(val); } } for (let i = 500000; i <= 991231; i++) { tryInput(i); }이 코드를 실행시켜놓고 잠시 딴 짓을 하다오니 페이지가 넘어가져 있었고,
플래그를 알아낼 수 있었다.
문제 파일을 다운받고, 디컴파일 해보면 ..
다수의 함수를 실행한다.
문제 이름이 fake 이듯, 이 중에서 아무런 역할도 하지 않는 함수를 찾아내는 것이 중요한 문제다.
일단 변수 ptr를 채워주는 함수 sub_140B는 가짜일리 없으니 확인해봤다.
확인 결과 다음과 같이 변수 v3를 선언하고 선언하고 if문에서는 변수 ptr 에 따라 만약 참이라면 ptr[1]에 값을 대입한다. for문은 23번 반복 실행되며 ptr[i]에 v3[i]를 넣고 *ptr의 인덱스에 0을 넣어서 문자열 끝을 표시하고 ptr을 반환한다. 뒤에 if문에서 '참'이 아니라면 ptr의 동적할당을 해제하고 '거짓'을 반환한다.
v2와 v3에 연산값을 넣고 있지만 주요한 변수 ptr 값이 바뀌진 않기 때문에 넘기겠다.
밑에 함수 sub_1189도 확인해보면
해당 함수를 실행했을 때도 마찬가지로 주요한 변화는 없다.
해당 함수들을 확인해보면 다음과 같은 프로그램을 구상하여 플래그를 출력할 수 있다.#include <stdio.h> int main() { unsigned char data[23] = { 0x4A, 0x5B, 0x5B, 0x5F, 0x56, 0x68, 0x54, 0x59, 0x50, 0x44, 0x06, 0x52, 0x45, 0x04, 0x41, 0x68, 0x65, 0x7A, 0x74, 0x4C, 0x64, 0x73, 0x7F }; for (int i = 0; i < 23 / 2; i++) { unsigned char tmp = data[i]; data[i] = data[22 - i]; data[22 - i] = tmp; } for (int i = 0; i + 1 < 23; i += 2) { unsigned char tmp = data[i]; data[i] = data[i + 1]; data[i + 1] = tmp; } for (int i = 0; i < 23; i++) { data[i] ^= 55; } printf("%s\n", data); return 0; }첫 번째 for문에서 값을 뒤집고, 마지막 for문에서 Xor 연산을 실행한다.
해당 프로그램을 실행시키면 플래그가 출력된다.