ZINO
HomeResearchBlogTagsPlaygroundStackPortfolio
⌘K

레드팀 · AI 자동화 · 시스템 엔지니어링

직접 만들고, 실험하고, 기록한다

개인정보처리방침·이용약관

© 2026 ZINO LAB

← 태그 목록

Tag Archive

#reversing

이 태그가 포함된 글을 최신순으로 모아봤어요.

79
posts
BLOG2026-08-01
[🥇 Gold 2] 화면에 한 자리씩 뜨는 실행파일 — DreamHack captain-hook 풀이
blog

[🥇 Gold 2] 화면에 한 자리씩 뜨는 실행파일 — DreamHack captain-hook 풀이

클릭할 때마다 16진수 한 자리를 그리는 창. 그 18,432자리를 다 모으면 UPX로 패킹된 또 하나의 실행파일이 나온다. 풀어서 실행하면 flag가 뜨는데 굵기 10.0 펜으로 그은 가림막이 글자를 덮는다. 상수 하나를 0.3으로 바꾸면 덮개가 걷힌다.
#dreamhack#ctf#reversing+5
 
BLOG2026-07-28
[🥈 Silver 1] 상쇄되는 연산 14개와 미끼 키 2개 — DreamHack Ransom 풀이
blog

[🥈 Silver 1] 상쇄되는 연산 14개와 미끼 키 2개 — DreamHack Ransom 풀이

UPX 로 포장된 32비트 PE 가 flag.txt 를 두 번 암호화한다. 진짜 main 은 암호화 함수를 둘 부르는데, 하나만 보고 뒤집으면 아무것도 안 나온다. 2단계는 고정 인덱스 연산이 15개나 붙어 있지만 14개가 역연산 짝이라 상쇄되고 실질은 하나뿐이고, .rdata 의 키처럼 생긴 문자열 셋 중 둘은 스택에 복사만 되고 읽히지 않는 미끼다. 키가 파일보다 한 바이트 짧아 마지막 바이트는 초기화 안 된 스택을 읽는다.
#dreamhack#ctf#reversing+7
BLOG2026-07-28
[🥉 Bronze 3] 리크도 셸도 없이 strcmp 하나를 오라클로 바꾸기 — DreamHack checkflag 풀이
blog

[🥉 Bronze 3] 리크도 셸도 없이 strcmp 하나를 오라클로 바꾸기 — DreamHack checkflag 풀이

플래그를 맞히면 Correct 를, 틀리면 Failed 를 돌려주는 것이 전부인 바이너리다. 리크할 포인터도 덮을 리턴 주소도 없다. 그런데 main 이 flag 를 담는 버퍼와 우리가 입력하는 버퍼를 같은 스택 영역에 0x40 간격으로 나란히 놓고, read 는 그보다 훨씬 긴 0xc8 바이트를 받는다. 0x40+d 바이트를 보내면 flag 앞 d 글자가 내 바이트로 덮이고, 그 상태의 strcmp 는 "flag 의 남은 뒷부분이 이거 맞아?" 라는 접미사 오라클이 된다. 뒤에서부터 한 글자씩 물어보면 16글자가 16초 만에 나온다.
#dreamhack#ctf#pwnable+7
BLOG2026-07-27
[🥇 Gold 4] RC4인 줄 알았던 암호와, 파일 하나에 숨어 있던 세 조각 — DreamHack Enc-JPG 풀이
blog

[🥇 Gold 4] RC4인 줄 알았던 암호와, 파일 하나에 숨어 있던 세 조각 — DreamHack Enc-JPG 풀이

배포물은 윈도우 실행파일 Enc 와 깨진 flag.jpg 둘뿐이다. Enc 를 뜯어보면 RC4 처럼 생긴 루틴이 나오는데 표준과 세 군데가 다르다 — 키 확장이 나머지가 아니라 AND, 키스트림이 S 박스를 한 번 더 타고, 무엇보다 0x400 청크마다 S 박스를 새로 만든다. 마지막 하나를 놓치면 JPEG 헤더만 살아나고 나머지가 전부 깨진다. 제대로 복호하면 파일 한 개 안에 JPEG 둘과 평문과 PNG 가 이어붙어 있고, 플래그는 그 셋에 나뉘어 들어 있다. 마지막 조각은 PNG 의 IDAT 길이 필드 자리에 덮여 있어서 그림으로는 영영 안 보인다.
#dreamhack#ctf#reversing+7
BLOG2026-07-26
[🥈 Silver 2] Rust 크랙미의 비트 치환을 거꾸로 돌려 64글자 입력 복원 — DreamHack CrabME 풀이
blog

[🥈 Silver 2] Rust 크랙미의 비트 치환을 거꾸로 돌려 64글자 입력 복원 — DreamHack CrabME 풀이

Rust 로 빌드된 크랙미. 입력은 64글자 소문자 hex 로 고정돼 있고, 두 글자씩 묶은 32바이트를 flagchecker 가 "비트 치환 → XOR 0x63 → +0x22" 세 단계로 바꿔 .rodata 에 박힌 상수 32개와 비교한다. 세 단계가 전부 바이트 단위 전단사라 브루트포스 없이 그대로 역산되고, 나온 64글자가 곧 플래그 본문이다. objdump 로 치환표를 유도하고 gdb 로 런타임에서 같은 값이 나오는지 확인했다.
#dreamhack#ctf#reversing+6
BLOG2026-07-25
[🥈 Silver 1] main 이라는 이름의 거짓말 — DreamHack Honest 풀이
blog

[🥈 Silver 1] main 이라는 이름의 거짓말 — DreamHack Honest 풀이

바이너리 하나만 주는 리버싱 문제. 64개 함수가 전역 counter 를 하나씩 올리며 입력을 한 글자씩 검사하는 단순한 구조인데, 그대로 역산하면 Wrong! 이 나온다. ELF 진입점을 따라가 보면 _start 가 __libc_start_main 에 넘기는 함수가 main 심볼이 아니라 verify_func_28 이다. 시작 칸이 55칸 어긋나 정답 문자열이 통째로 회전해 있었다.
#dreamhack#ctf#reversing+5
BLOG2026-07-25
[🥉 Bronze 2] 버튼 8.9억 번 대신 APK 속 SEED-128 CBC 를 직접 푼다 — DreamHack [CodeEngn] MobileApp L03 풀이
blog

[🥉 Bronze 2] 버튼 8.9억 번 대신 APK 속 SEED-128 CBC 를 직접 푼다 — DreamHack [CodeEngn] MobileApp L03 풀이

SmartApp L03.apk 는 버튼을 randomRange(44444) 번 눌러야 키값을 보여준다. 평균 8.9억 번이라 UI 로는 절대 못 본다. jadx 로 뜯어보면 그 값은 입력과 무관하게 Security.DecryptStr() 이 하드코딩 hex 를 SEED-128 CBC 로 복호한 결과였고, key 와 IV 는 dex 의 fill-array-data 페이로드에 상수로 박혀 있다. apktool 없이 classes.dex 에서 상수를 직접 긁어내 openssl seed-cbc 로 복호하면 키값 CodeEngn_4_Ever 가 나온다.
#dreamhack#ctf#reversing
BLOG2026-07-25
[🥈 Silver 2] 해킹당한 VM 이미지에서 채굴기가 삼킨 플래그 되찾기 — DreamHack Infested Terran 풀이
blog

[🥈 Silver 2] 해킹당한 VM 이미지에서 채굴기가 삼킨 플래그 되찾기 — DreamHack Infested Terran 풀이

bzImage + rootfs.ext2 두 파일만 주는 문제. 이미지를 마운트하지 않고 열어보면 buildroot 바이너리들 사이에 glibc로 빌드된 두 개가 섞여 있다. /home/ubuntu/.helper는 표준입력으로 받은 값을 argv[0]="[kworker]"로 위장해 /usr/bin/sl에 넘기고, sl은 입력 36글자를 힙에 흩뿌린 뒤 누적합을 하드코딩된 값과 비교한다. 검증식이 삼각행렬이라 이웃한 두 항을 빼는 것만으로 한 글자씩 그대로 복원된다.
#dreamhack#ctf#reversing+7
BLOG2026-07-24
[🥈 Silver 2] 리버싱 대신 메모리를 읽는다 — DreamHack just read memory 풀이
blog

[🥈 Silver 2] 리버싱 대신 메모리를 읽는다 — DreamHack just read memory 풀이

입력을 한 글자씩 연결리스트 노드와 비교하고, 다 맞으면 플래그를 뱉는 바이너리. 노드 값은 루프 인덱스가 아니라 세 헬퍼 함수가 공유하는 초기화 안 된 스택 슬롯과 전역 cnt 로 만든 숨은 점화식(cnt = 27*cnt + 103)에서 나온다. 그 점화식을 파이썬으로 접어 정답 입력을 복원할 수도 있지만, 문제 이름대로 gdb 로 리스트 생성 직후에 멈춰 힙을 그대로 읽어내는 게 더 빠르다.
#dreamhack#ctf#reversing+5
BLOG2026-07-24
[🥈 Silver 4] 3×10¹⁷번 세제곱을 안 기다리고 접기 — DreamHack power cube 풀이
blog

[🥈 Silver 4] 3×10¹⁷번 세제곱을 안 기다리고 접기 — DreamHack power cube 풀이

실행하면 "You've waited so long...."만 남기고 영원히 안 끝나는 바이너리 하나. 하는 일은 64비트 정수를 세제곱만 3.1×10¹⁷번 반복한 뒤 그 결과를 SHA256해서 플래그로 찍는 게 전부다. 세제곱을 쌓으면 지수가 3의 거듭제곱으로 자란다는 것과, 2⁶⁴ 곱셈군의 지수가 2⁶²라는 카마이클 정리 하나로 그 반복을 한 줄 pow로 접는다.
#dreamhack#ctf#reversing+5
BLOG2026-07-23
[🥉 Bronze 1] 바이너리가 자기 자신을 읽어 만든 열쇠 — DreamHack SelfRead 풀이
blog

[🥉 Bronze 1] 바이너리가 자기 자신을 읽어 만든 열쇠 — DreamHack SelfRead 풀이

SelfRead는 정답 문자열을 어디에도 저장해 두지 않는다. 대신 실행하면서 /proc/self/exe로 자기 ELF를 다시 읽어, 정해진 오프셋의 바이트 배치에서 열쇠를 즉석에서 만들어 낸다. 그 조립 규칙을 거꾸로 따라가면 서버 없이 파일만으로 flag를 계산할 수 있다.
#dreamhack#ctf#reversing+4
 
BLOG2026-07-22
[🥉 Bronze 2] 3중으로 막아둔 안드로이드 버튼, 그래도 정적분석으로 뚫는다 — DreamHack [CodeEngn] MobileApp L02 풀이
blog

[🥉 Bronze 2] 3중으로 막아둔 안드로이드 버튼, 그래도 정적분석으로 뚫는다 — DreamHack [CodeEngn] MobileApp L02 풀이

L01과 같은 SEED-128 CBC + Java BigInteger 부호 인코딩 구조지만, 이번엔 유효 블록이 2개라 CBC 체이닝까지 풀어야 한다. onCreate()의 실행 조건은 참조비교(==)·하드코딩 상수· 불가능한 과거 시각 3중으로 막혀 있어, 앱을 실행해서는 절대 답이 안 나오게 설계됐다.
#dreamhack#ctf#reversing+6
BLOG2026-07-21
[🥇 Gold 2] .rela.tivity — 재배치 테이블이 곧 암호문이었다, DreamHack Theori of Relativity 풀이 (2번 보류 끝에 해결)
blog

[🥇 Gold 2] .rela.tivity — 재배치 테이블이 곧 암호문이었다, DreamHack Theori of Relativity 풀이 (2번 보류 끝에 해결)

키 문자열을 입력하면 맞는지 틀렸는지만 알려주는 짧은 리버싱 문제. 검증에 쓰이는 104바이트 "키 블록"이 파일에 적힌 값과 실행 중인 값이 완전히 다른데, 그 값을 바꾸는 함수를 아무리 찾아도 안 나온다 — 지난 두 세션은 여기서 막혔다. 진짜 범인은 함수가 아니라 ".rela.tivity"라는 이름의 가짜 재배치 섹션과 그걸 진짜로 처리해버리는 ELF 로더 자신이었다. 게다가 마지막 관문에서는 "디버거로 관찰하는 행위 자체가 관찰 대상을 바꿔버리는" 진짜 물리학 농담 같은 함정까지 기다리고 있었다.
#dreamhack#ctf#reversing+5
BLOG2026-07-21
[🥈 Silver 4] 10²²년짜리 잠을 0.3초로 줄이기 — DreamHack Long Sleep 풀이 (2번 보류 끝에 해결)
blog

[🥈 Silver 4] 10²²년짜리 잠을 0.3초로 줄이기 — DreamHack Long Sleep 풀이 (2번 보류 끝에 해결)

실행하면 "flag를 생성 중"이라며 뜸을 들이다가 점점 더 길게 잠드는 바이너리. sleep()이 아니라 libc 심볼도 없는 raw syscall이라 LD_PRELOAD 후킹이 안 통하고, 파일을 패치하거나 소프트웨어 브레이크포인트를 걸면 곧바로 "integrity_check(): file has been corrupted"가 뜬다. 지난 두 세션은 이 두 방어가 서로 "같은 것"을 검사한다고 착각해 막혔다. 사실은 완전히 다른 두 층이었다 — 하나는 코드 바이트 자체의 무결성, 하나는 그저 실행 속도. 코드는 그대로 두고 syscall 인자값만 바꾸면 두 방어 다 건드리지 않고 지나간다.
#dreamhack#ctf#reversing+4
BLOG2026-07-21
[🥇 Gold 1] 27455번 반복되는 누산도 결국 선형이다 — DreamHack multipoint 풀이 (2번 보류 끝에 해결)
blog

[🥇 Gold 1] 27455번 반복되는 누산도 결국 선형이다 — DreamHack multipoint 풀이 (2번 보류 끝에 해결)

38바이트 목표값과 memcmp하는 39바이트 입력 검증기. 검증 알고리즘 자체는 단순한 곱셈-누산 루프인데, 몇 개의 레코드를 도는지가 파일 안의 데이터로 결정되는 구조라 정적으로 훑다가 "말이 안 되게 크다"는 판단을 내려 두 세션 연속 보류했던 문제다. 사실은 판단이 틀렸던 것뿐이었다 — 정말로 27455번 반복하는 게 맞았고, 그 반복이 아무리 많아도 각 항의 계수가 전부 컴파일타임 상수라는 사실을 놓치지 않으면 전체가 GF(251) 위의 선형 방정식 하나로 정리된다.
#dreamhack#ctf#reversing+4
BLOG2026-07-21
[🥇 Gold 2] 코드가 스스로를 만들어낼 땐, 만드는 코드부터 읽어야 한다 — DreamHack You shall not pass 풀이
blog

[🥇 Gold 2] 코드가 스스로를 만들어낼 땐, 만드는 코드부터 읽어야 한다 — DreamHack You shall not pass 풀이

.NET/Mono 어셈블리 하나가 주어지는 리버싱 문제. ikdasm으로 Main()을 열어봐도 정작 flag를 검증하는 로직이 안 보인다 — 이 프로그램은 System.Reflection.Emit으로 검증 메서드를 실행 도중에 직접 조립해서 리플렉션으로 호출한다. "OpCode를 스택에 올리고 Emit을 부르는" 패턴이 100번 가까이 반복되는 걸 정규식 파서로 순서대로 추출해 실제 만들어질 메서드의 명령 시퀀스를 복원했다. 복원한 로직은 인접한 두 값의 순환 합을 32개의 목표값과 비교하는 방정식 시스템이었고, 이를 Z3로 직접 풀어 얻은 flag를 실제 mono 런타임에 넣어 "correct!"까지 확인했다.
#dreamhack#ctf#reversing
BLOG2026-07-21
[🥈 Silver 4] "L번 도니 한 번씩만 더해졌겠지"는 틀린 직관이었다 — DreamHack pybycodetes 풀이
blog

[🥈 Silver 4] "L번 도니 한 번씩만 더해졌겠지"는 틀린 직관이었다 — DreamHack pybycodetes 풀이

실행 파일이 아니라 파이썬 dis.dis() 바이트코드 덤프 두 장(main.txt, hard2rev.txt)과 출력 결과(output.txt)만 주어지는 리버싱 문제. opcode를 그대로 읽어 파이썬 소스로 재구성하면, flag를 바이트 XOR 한 뒤 값 하나에 +36을 하고 배열 전체를 j//2번 뒤집는 걸 반복하는 함수가 나온다. "루프가 L번 도니까 각 원소에 정확히 한 번씩만 더해졌겠지"라는 첫 직관은 틀렸다 — 반복되는 반전 때문에 같은 값이 여러 번 걸리기도, 아예 안 걸리기도 한다. 실제 값 대신 "위치 라벨" 배열을 똑같은 알고리즘으로 굴려 정확한 횟수와 최종 위치를 추적하고, 그 결과를 역산해 flag를 복원했다.
#dreamhack#ctf#reversing+3
BLOG2026-07-21
[🥈 Silver 4] ctype 비트 하나를 잘못 짚으면 퍼뮤테이션 전체가 어긋난다 — DreamHack Permpkin 풀이
blog

[🥈 Silver 4] ctype 비트 하나를 잘못 짚으면 퍼뮤테이션 전체가 어긋난다 — DreamHack Permpkin 풀이

chall 바이너리는 입력을 받지 않는다. 대신 미리 정해둔 sample 문자열을 스스로 뒤섞어 rev1.txt/rev2.txt로 보여줄 뿐이다. 진짜 풀어야 할 대상은 같은 방식으로 미리 변환돼 주어진 flag1.txt/flag2.txt. 바이너리 안의 23자리 hex 문자열을 ctype 기반으로 숫자 배열(퍼뮤테이션+XOR 키)로 바꾸는데, 그 변환 도중 하필 값이 0이 되는 지점이 있어서 이후 strlen()이 배열 길이를 23에서 13으로 끊어버린다. 이 함정을 처음엔 못 보고 엉뚱한 ctype 비트를 가정해 틀렸다가, gdb로 실제 프로세스의 중간 메모리 상태를 직접 찍어보고 나서야 알고리즘을 100% 검증하고 뒤집을 수 있었다.
#dreamhack#ctf#reversing+4
BLOG2026-07-21
[🥈 Silver 4] 화면에 아무것도 안 뜨는 바이너리에서 flag 뽑아내기 — DreamHack fake 풀이
blog

[🥈 Silver 4] 화면에 아무것도 안 뜨는 바이너리에서 flag 뽑아내기 — DreamHack fake 풀이

실행해도 아무것도 출력하지 않는 ELF 바이너리 하나. strace로 확인해보니 read/write 계열 syscall이 단 한 번도 없다 — 임포트된 함수조차 malloc/free뿐이다. objdump로 뜯어보니 main()은 스택에 movabs 즉시값으로 23바이트 시드 버퍼를 조립해 malloc한 뒤, reverse → 인접 바이트 교환 → XOR라는 세 단계 변환만 거쳐 곧바로 free해버린다. 화면에 찍히는 건 없지만, 그 변환 결과를 파이썬으로 그대로 재현하고 gdb로 free() 직전 힙 버퍼를 직접 들여다보는 것으로 flag를 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-21
[🥈 Silver 4] 프로그램이 정말로 자기 이름에게 물었다 — DreamHack What is your name? 풀이
blog

[🥈 Silver 4] 프로그램이 정말로 자기 이름에게 물었다 — DreamHack What is your name? 풀이

zip 안에 exe가 11개, 전부 파일명이 무작위 3글자로 뒤섞여 있다. 실행해도 콘솔에 아무것도 안 뜨고, sha256도 전부 다르다. cmp -l로 두 파일을 겹쳐보니 123392바이트 중 딱 2바이트만 다르다는 걸 발견하면서 실마리가 풀렸다. 각 exe는 GetModuleFileNameA로 자기 자신의 파일명을 읽어 한 글자씩 밀어서 출력할 뿐이었고, 그 결과를 순서대로 이어붙이면 그대로 flag가 된다 — "What is your name?"이 은유가 아니라, 프로그램이 진짜로 자기 자신의 이름을 묻는 질문이었다.
#dreamhack#ctf#reversing+4
BLOG2026-07-21
[🥈 Silver 2] 반복문을 없애도 제약은 사라지지 않는다 — DreamHack Call more functions 풀이
blog

[🥈 Silver 2] 반복문을 없애도 제약은 사라지지 않는다 — DreamHack Call more functions 풀이

64바이트 입력을 검증하는 함수 하나를 열어보니 반복문도, 분기문도 없다. 대신 똑같은 헬퍼 함수 네 개(push, peek, xor, compare)를 319번이나 이어서 호출하는, 완전히 펼쳐진(unrolled) 코드가 전부다. 사람 눈으로 319개의 호출을 하나하나 손으로 추적하는 대신, objdump 출력을 그대로 파싱해서 이 호출 순서를 Z3 SMT 솔버로 "재생"시켰다. 어떤 값이 정답인지 스스로 계산할 필요 없이, 검증 로직 자체를 제약 조건 덩어리로 옮겨놓기만 하면 풀린다.
#dreamhack#ctf#reversing+5
BLOG2026-07-21
[🥈 Silver 4] srand() 없는 rand()는 이미 예언된 미래다 — DreamHack randzzz 풀이
blog

[🥈 Silver 4] srand() 없는 rand()는 이미 예언된 미래다 — DreamHack randzzz 풀이

"난수를 맞혀보라"는 리버싱 문제. 그런데 바이너리를 뜯어보면 어디에도 srand()가 없다 — glibc의 rand()는 시드를 명시적으로 주지 않으면 항상 똑같은 순서로 값을 뱉는 결정론적 함수이므로, 이 프로그램이 뭘 물어볼지는 실행하기도 전에 전부 계산할 수 있다. 문제는 그 다음이다. 두 번의 검증이 서로 다른 정답을 요구하는데 입력은 한 번뿐이라, 한 번의 정상 실행으로는 절대 flag 전체를 볼 수 없다. GDB로 실행 중인 프로세스의 스택 메모리를 직접 고쳐써서 두 검증을 동시에 통과시킨 끝에야 완전한 flag가 나왔다.
#dreamhack#ctf#reversing+5
BLOG2026-07-21
[🥈 Silver 3] 시리얼 검증 함수 5개는 사실 연립방정식 5개였다 — DreamHack verify 풀이
blog

[🥈 Silver 3] 시리얼 검증 함수 5개는 사실 연립방정식 5개였다 — DreamHack verify 풀이

스트립된 keygen류 리버싱 바이너리. "DH 뒤에 8자리-10자리-10자리-8자리 형식의 hex 세그먼트"를 가진 시리얼을 입력받아 형식을 검사하고, 4조각을 각각 64비트 정수로 바꾼 뒤 5개의 검증 함수를 체인으로 통과시킨다. 함수를 하나씩 디스어셈블해보면 그 5개가 전부 덧셈과 XOR로 이루어진 식이라는 게 드러난다 — a+b, b+c, c+d, a+d, b^c^d. 겉보기엔 복잡한 검증 로직이지만 결국 미지수 4개짜리 연립방정식 5개일 뿐이고, numpy로 66초 만에 유일해를 찾아낸다.
#dreamhack#ctf#reversing+5
BLOG2026-07-21
[🥈 Silver 2] MD5를 잘게 썰면 "충돌 저항성"이라는 말이 무색해진다 — DreamHack hash-browns 풀이
blog

[🥈 Silver 2] MD5를 잘게 썰면 "충돌 저항성"이라는 말이 무색해진다 — DreamHack hash-browns 풀이

스트립된 리버싱 바이너리 하나가 전부다. 입력을 받아 27바이트로 자르고, 그걸 다시 3바이트씩 9조각으로 쪼갠 뒤 각 조각의 MD5를 계산해 바이너리 안에 박혀 있는 16바이트 값과 비교한다. 27바이트 전체를 한 번에 맞히려 들면 무차별대입은 우주 나이보다 오래 걸리지만, 3바이트짜리 조각 하나의 MD5 역상은 파이썬으로 0.2초면 찾는다. 해시 함수 자체는 멀쩡한데, "작은 조각으로 쪼개 각각 검증한다"는 설계 하나가 전체 키공간을 9개의 훨씬 작은 문제로 무너뜨린다.
#dreamhack#ctf#reversing+5
BLOG2026-07-20
[🌱 Sprout] 문제 만든 사람이 엔디언을 깜빡했다 — DreamHack littlevsbig 풀이
blog

[🌱 Sprout] 문제 만든 사람이 엔디언을 깜빡했다 — DreamHack littlevsbig 풀이

소스에는 int_arr[0] == 0x64726d68, int_arr[1] == 0x636b3a29라는 비교식이 그대로 노출돼 있다. 16진수를 사람 눈으로 그대로 읽으면 "drmhck:)" — "dreamhack :)"을 줄인 문자열처럼 보인다. 그런데 그 문자열을 그대로 입력하면 프로그램은 거부한다. x86은 little-endian이라 4바이트 정수를 메모리에 거꾸로 저장하기 때문에, 실제로 입력해야 하는 바이트열은 각 4바이트 워드 단위로 뒤집힌 "hmrd):kc"다. GDB로 스택 위 바이트를 직접 찍어 그 반전을 눈으로 확인했다.
#dreamhack#ctf#misc+3
BLOG2026-07-20
[🥉 Bronze 1] 두 번의 XOR은 상쇄되지 않았다 — DreamHack ez_rev 풀이
blog

[🥉 Bronze 1] 두 번의 XOR은 상쇄되지 않았다 — DreamHack ez_rev 풀이

"함수가 너무 많아요"라는 문제 설명과 달리 실제로 입력을 건드리는 함수는 셋뿐이다. 회전 두 번과 XOR 두 번이 교차로 걸려 있는데, 같은 키로 XOR을 두 번 하니 그냥 상쇄될 거라 생각하고 지름길을 탔다가 완전히 다른 문자열이 나왔다. 원인은 두 XOR 사이에 낀 회전 하나가 인덱스 정렬 자체를 바꿔버린다는 점이었다.
#dreamhack#ctf#reversing+2
BLOG2026-07-20
[🥉 Bronze 1] 키는 GOT 뒤에 숨어 있었다 — DreamHack basic_CrackMe 풀이
blog

[🥉 Bronze 1] 키는 GOT 뒤에 숨어 있었다 — DreamHack basic_CrackMe 풀이

32비트 PIE ELF와 Windows PE가 세트로 나오는 리버싱 문제. 겉보기엔 흔한 XOR 크랙미인데 막상 열어보면 XOR 한 번이 아니라 자릿수마다 제곱·뺄셈·덧셈이 섞인 int64 배열 연산이라 손으로 따라가면 금방 헷갈린다. 진짜 함정은 알고리즘이 아니라 키 위치 — 키 문자열은 코드 어디에도 안 보이고 GOT 슬롯에 꽂힌 포인터 하나가 .rodata를 가리키고 있었다.
#dreamhack#ctf#reversing+3
BLOG2026-07-20
[🥉 Bronze 1] 위치 무관 바이트 변환은 256개짜리 표 하나로 뒤집힌다 — DreamHack ezmix 풀이
blog

[🥉 Bronze 1] 위치 무관 바이트 변환은 256개짜리 표 하나로 뒤집힌다 — DreamHack ezmix 풀이

stripped 바이너리가 program.bin이라는 2바이트짜리 opcode 프로그램을 해석해 우리가 입력한 문자열을 ADD/XOR/ROR 256번 연속으로 뒤섞는다. 언뜻 복잡해 보이지만 세 연산 전부 버퍼의 모든 바이트에 같은 인자를 똑같이 적용할 뿐이라, 입력 위치와 무관하게 0~255 전체에 대해 한 번만 정방향 표를 만들면 역표는 자동으로 나온다. output.bin 36바이트에 역표를 그대로 대입해 flag를 즉시 복원했다.
#dreamhack#ctf#reversing+3
BLOG2026-07-20
[🥉 Bronze 1] 64바이트를 6개 함수가 나눠 검증하는 crackme — DreamHack mix-compare 풀이
blog

[🥉 Bronze 1] 64바이트를 6개 함수가 나눠 검증하는 crackme — DreamHack mix-compare 풀이

64바이트 입력을 check → check_not → check_add → check_dec → check_mul → check_la 여섯 함수가 10바이트 안팎씩 나눠 맡아 각자 다른 산술 변환(NOT·덧셈·뺄셈·곱셈·역방향 덧셈)으로 검증한다. 비교 대상 배열은 .data에 평문 정수로 그대로 박혀 있어서, 각 구간의 변환을 거꾸로 풀기만 하면 64바이트 전체가 그대로 복원된다.
#dreamhack#ctf#reversing+3
BLOG2026-07-19
[🥉 Bronze 1] 홈브루 RSA, 모듈러스가 32비트면 그냥 나눠본다 — DreamHack public 풀이
blog

[🥉 Bronze 1] 홈브루 RSA, 모듈러스가 32비트면 그냥 나눠본다 — DreamHack public 풀이

키 생성 시드도, p·q도 안 주고 공개 모듈러스 n1과 공개지수 n2만 준다. 그런데 그 n1이 32비트급(약 42억)이라 시험 나눗셈만으로 즉시 소인수분해된다. objdump로 뜯은 키생성 로직과 gdb 런타임 검증으로 알고리즘을 확정하고 개인키를 복원해 플래그를 복호했다.
#dreamhack#ctf#reversing+5
 
BLOG2026-07-19
[🥈 Bronze 2] 함수 주소일까, 호출 지점일까 — DreamHack [CodeEngn] Malware L08 풀이
blog

[🥈 Bronze 2] 함수 주소일까, 호출 지점일까 — DreamHack [CodeEngn] Malware L08 풀이

다른 악성코드를 실행해 추가 감염을 일으키는 함수의 주소를 찾으라는 문제. 그래프 전체가 ShellExecuteExA로 대상 파일을 실행하는 하나의 함수인데, 그 함수 자체의 시작 주소를 냈다가 오답을 받았다. 함수 안에 ShellExecuteExA 호출이 두 곳이라는 걸 알고도 어느 쪽인지 헷갈렸는데, 결국 "함수의 주소"가 가리키는 건 실행 API를 호출하는 그 지점이었다.
#dreamhack#ctf#reversing+5
BLOG2026-07-19
[🥈 Bronze 2] 미끼인 줄 알았던 디코드가 사실 정답이었다 — DreamHack 아 문제 이름 뭘로하지 풀이
blog

[🥈 Bronze 2] 미끼인 줄 알았던 디코드가 사실 정답이었다 — DreamHack 아 문제 이름 뭘로하지 풀이

39바이트 암호문을 XOR 0xAA로 풀면 그럴듯한 DH{} 문자열이 바로 나온다. 너무 쉬워서 미끼라고 판단하고, 사용자 입력을 변형하는 복잡한 커스텀 인코딩 함수를 통째로 역산했다. GDB로 대조하다 32비트 시프트 카운트 마스킹 때문에 특정 자리가 항상 0이 되는 버그를 발견했고, 그 버그를 쫓아가다 결국 처음 봤던 그 디코드 문자열이 진짜 정답이었다는 걸 알게 됐다.
#dreamhack#ctf#reversing+4
BLOG2026-07-19
[🥉 Bronze 4] strncmp(18) 부분비교 + 시간기반 토큰 — DreamHack angry_darim 풀이
blog

[🥉 Bronze 4] strncmp(18) 부분비교 + 시간기반 토큰 — DreamHack angry_darim 풀이

time()으로 매초 바뀌는 "동적 토큰"을 strncmp로 딱 18바이트만 비교하는 바이너리. 로컬에서는 실행 순간의 epoch를 그대로 넣어 우회하지만, 정작 DreamHack에 내는 flag는 그 시간 요소를 통째로 들어내야 하는 정적 문자열이었다.
#dreamhack#ctf#pwnable+5
BLOG2026-07-19
[🥈 Bronze 2] 난독화된 전역 변수를 GDB로 그대로 읽어내기 — DreamHack Stop before stops! 풀이
blog

[🥈 Bronze 2] 난독화된 전역 변수를 GDB로 그대로 읽어내기 — DreamHack Stop before stops! 풀이

C++ 바이너리인데 함수·전역변수 이름이 전부 무작위 문자열로 바뀌어 있다. 정적 분석으로 로직은 다 읽히는데(24자 비밀번호를 비교해 맞으면 flag를 출력), 실제 비교 대상 문자열은 실행 시점에 조용히 채워지는 전역 std::string이라 정적 디스어셈블리만으론 값을 알 수 없다. GDB로 static initializer가 끝난 직후 raw 메모리를 읽어 값을 그대로 꺼냈다.
#dreamhack#ctf#reversing+5
BLOG2026-07-19
[🥉 Bronze 3] 뮤텍스가 두 개인 악성코드에서 Thread Mutex 골라내기 — DreamHack [CodeEngn] Malware L06 풀이
blog

[🥉 Bronze 3] 뮤텍스가 두 개인 악성코드에서 Thread Mutex 골라내기 — DreamHack [CodeEngn] Malware L06 풀이

같은 악성코드(bagla downloader) 그래프를 다루는 CodeEngn 시리즈의 또 다른 문제. 이번엔 뮤텍스가 두 개 나온다 — 프로그램 시작 시 만드는 프로세스 단일실행 가드용, 그리고 CreateThread 직후 만드는 두 번째 뮤텍스. 문제가 묻는 "Thread Mutex"는 스레드 생성과 바로 붙어 있는 후자다.
#dreamhack#ctf#reversing+5
BLOG2026-07-19
[🥉 Bronze 1] 스트립된 PIE 바이너리의 커스텀 RLE 역산 — DreamHack secret message 풀이
blog

[🥉 Bronze 1] 스트립된 PIE 바이너리의 커스텀 RLE 역산 — DreamHack secret message 풀이

드림이가 이미지를 자기가 만든 인코딩으로 압축해뒀다. 스트립된 PIE ELF를 objdump로 뜯어 fopen/fgetc/fputc 호출만으로 커스텀 RLE 상태머신을 복원하고, gdb 런타임 검증까지 거쳐 원본 비트맵을 되살린 풀이.
#dreamhack#ctf#reversing+3
BLOG2026-07-19
[🥉 Bronze 4] 가짜 함수 199개 속에서 flag 찾기 — DreamHack please, please, please 풀이
blog

[🥉 Bronze 4] 가짜 함수 199개 속에서 flag 찾기 — DreamHack please, please, please 풀이

stripped PIE 바이너리 안에 거의 동일한 함수가 199개 박혀 있다. 전부 "please~"만 출력하는 죽은 코드고, 실제 main도 puts 한 줄만 부르고 끝난다. objdump로 199개 중 유일하게 다른 함수를 찾아내면 "Hhu,"라는 또 다른 미끼가 나오는데, 정작 flag는 strings 한 번이면 바로 보이는 DH{NOGADA}였다.
#dreamhack#ctf#reversing+5
BLOG2026-07-19
[🥉 Bronze 4] IDA 그래프 한 장으로 끝내는 악성코드 재접속 주기 — DreamHack [CodeEngn] Malware L07 풀이
blog

[🥉 Bronze 4] IDA 그래프 한 장으로 끝내는 악성코드 재접속 주기 — DreamHack [CodeEngn] Malware L07 풀이

실행 파일 대신 IDA 그래프 캡쳐 한 장(07.pdf)만 주어진다. 악성코드가 C2 서버 접속에 실패했을 때 몇 ms 간격으로 재시도하는지를 묻는데, 그래프 안의 재시도 루프를 따라가면 `push 96000h` → `call ds:Sleep`가 답이다. 16진수 0x96000을 10진수로 환산한 614400이 flag가 된다.
#dreamhack#ctf#reversing+5
BLOG2026-07-17
[🥉 Bronze 4] 심볼 없는 STM32F4 펌웨어에서 진짜 키 찾기 — DreamHack Please Discover my Discovery key 풀이
blog

[🥉 Bronze 4] 심볼 없는 STM32F4 펌웨어에서 진짜 키 찾기 — DreamHack Please Discover my Discovery key 풀이

769바이트짜리 stripped Cortex-M 펌웨어 하나가 전부다. strings 를 걸면 "진짜 키"를 묻는 미끼 문자열들이 우수수 나오는데, 정작 보드가 UART 로 뱉는 값은 판독 불가능한 바이트다. Thumb 코드를 디스어셈블하고 Unicorn 으로 돌려보면 그 바이트가 두 base64 문자열의 XOR 임이 드러나고, 되돌리면 숨은 메시지가 나온다.
#dreamhack#ctf#misc+6
BLOG2026-07-16
[🥉 Bronze 3] stripped Rust 바이너리에서 스택 key 를 뽑아 XOR 역산 — DreamHack Rusty Gyul 풀이
blog

[🥉 Bronze 3] stripped Rust 바이너리에서 스택 key 를 뽑아 XOR 역산 — DreamHack Rusty Gyul 풀이

심볼을 모두 걷어낸 Rust ELF 하나가 "플래그를 맞춰봐"라고 한다. ltrace 에는 strcmp 하나 안 잡히고 main 은 껍데기뿐이다. 진짜 검사 루틴을 어셈블리에서 찾아 들어가 보면, 입력을 trim 한 뒤 스택에 실어둔 32바이트 key 와 한 바이트씩 XOR 해서 전부 0x55 인지만 본다. key 를 그대로 뽑아 0x55 로 되XOR 하면 플래그가 나온다.
#dreamhack#ctf#reversing+5
BLOG2026-07-16
[🥉 Bronze 3] 안드로이드 APK 속 SEED-128 CBC 키값 복원 — DreamHack [CodeEngn] MobileApp L01 풀이
blog

[🥉 Bronze 3] 안드로이드 APK 속 SEED-128 CBC 키값 복원 — DreamHack [CodeEngn] MobileApp L01 풀이

SmartApp L01.apk 를 jadx 로 뜯어보니 버튼을 누르면 Security.DecryptStr() 가 하드코딩된 hex 문자열을 SEED-128 CBC 로 복호해 "Key" 로 띄운다. 암호문은 Java BigInteger 의 부호 있는 2의 보수로 인코딩돼 있고, key 와 IV 가 정적 배열에 그대로 박혀 있다. openssl 로 그대로 복호해 키값 H3ll0 C0de3ngn 를 얻는다.
#dreamhack#ctf#reversing+6
BLOG2026-07-15
[🥈 Silver 4] add·rotate·xor 블록암호를 정적 역산 — DreamHack My ARX Cipher 풀이
blog

[🥈 Silver 4] add·rotate·xor 블록암호를 정적 역산 — DreamHack My ARX Cipher 풀이

stripped ELF 하나가 12바이트 key로 flag.enc를 만들어 놓았다. 블록 함수를 objdump로 뜯어 4바이트(16bit 워드 v0·v1)에 rotate-add-xor-swap을 3라운드 도는 ARX 구조임을 밝히고, 각 연산을 역으로 되감아 flag를 복원한다. gdb 런타임과 재암호화 라운드트립으로 이중 검증.
#dreamhack#ctf#reversing+5
BLOG2026-07-20
[🥉 Bronze 2] 부분 문자열로 흩어 놓은 배치 난독화 되돌리기 — DreamHack Batch Checker 풀이
blog

[🥉 Bronze 2] 부분 문자열로 흩어 놓은 배치 난독화 되돌리기 — DreamHack Batch Checker 풀이

검사 로직을 윈도우 배치 파일로 난독화한 리버싱 문제. 100자짜리 무의미한 이름의 변수 37개를 정의한 뒤 %VAR:~offset,1% 부분 문자열 문법으로 한 글자씩 뽑아 7,463자짜리 명령 한 줄을 조립한다. 이 치환을 프로그램으로 풀면 원래의 비교식이 드러나고, 비교 대상이 곧 플래그다. "실행만 하면 된다"는 문제 설명과 달리, 실행하지 않고 정적으로 풀어야 안전하다.
#dreamhack#ctf#reversing+3
BLOG2026-07-14
[🥉 Bronze 2] Duff's device로 펼친 위치별 XOR 되돌리기 — DreamHack legacyopt 풀이
blog

[🥉 Bronze 2] Duff's device로 펼친 위치별 XOR 되돌리기 — DreamHack legacyopt 풀이

입력을 Duff's device로 펼친 루프에서 위치별 상수와 XOR해 hex로 출력하는 인코더 문제. 각 출력 바이트가 그 자리 입력에만 달려 있어, 같은 길이의 알려진 입력을 한 번 넣어 위치별 XOR 상수를 캐낸 뒤 주어진 출력에 그대로 적용하면 플래그가 복원된다. 재인코딩이 원본과 일치함까지 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-14
[🥉 Bronze 3] XOR·덧셈·비트회전을 거꾸로 되짚다 — DreamHack nub_rev01 풀이
blog

[🥉 Bronze 3] XOR·덧셈·비트회전을 거꾸로 되짚다 — DreamHack nub_rev01 풀이

입력 한 글자씩 XOR·덧셈·비트회전을 걸어 스택에 박힌 상수와 비교하는 리버싱 크랙미. 세 연산이 모두 가역이라, 비교 대상(target)에서 역순으로 회전을 풀고 덧셈을 빼고 XOR를 되돌리면 정답 입력이 그대로 복원된다. 복원한 문자열을 바이너리에 넣어 Correct로 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 브론즈 2] 입력할 수 없는 정답 — DreamHack My Favorite Fruit 풀이
blog

[🥉 브론즈 2] 입력할 수 없는 정답 — DreamHack My Favorite Fruit 풀이

과일 이름을 물어보는 챗봇 바이너리다. 다섯 과일을 다 맞히면 전역 버퍼를 flag 로 복원해 출력하는데, scanf 포맷이 %9s 라 열 글자짜리 strawberry 는 아홉 글자로 잘려 절대 매칭되지 않는다. 그래서 런타임으로는 flag 가 나오지 않는다. 하지만 버퍼를 만드는 연산이 XOR 라 순서와 무관하므로, 다섯 과일 키를 정적으로 한꺼번에 XOR 해 flag 를 복원했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 Bronze 3] smali에 흩어 둔 상수로 조립되는 플래그 — DreamHack Summer Fan 풀이
blog

[🥉 Bronze 3] smali에 흩어 둔 상수로 조립되는 플래그 — DreamHack Summer Fan 풀이

APK를 apktool로 뜯어 smali를 읽는 안드로이드 리버싱 문제. 플래그는 32개 정수 리스트 FLAG와 한 글자씩 쪼개 놓은 문자열 KEY를 XOR·뺄셈으로 조합해 만든다. gen(i) 보정항까지 그대로 재현하면 BISC 형식의 플래그가 복원된다.
#dreamhack#ctf#reversing+4
 
BLOG2026-07-13
[🥉 Bronze 3] 원형 리스트 인코더를 블랙박스로 뒤집다 — DreamHack tiny-ouroboroi 풀이
blog

[🥉 Bronze 3] 원형 리스트 인코더를 블랙박스로 뒤집다 — DreamHack tiny-ouroboroi 풀이

입력 각 글자를 그 글자값 길이의 원형 연결리스트로 만들어 회전·토글한 뒤 비트를 모아 한 바이트씩 출력하는 인코더 문제. 출력의 각 바이트가 오직 그 자리 글자와 위치에만 의존하므로, 함수를 다 역산할 필요 없이 후보 글자를 통째로 넣어 위치별 매핑을 만들면 플래그가 복원된다.
#dreamhack#ctf#reversing+3
BLOG2026-07-13
[🥉 Bronze 4] 입력을 XOR해 비교하는 검사식 뒤집기 — DreamHack Happy New Year! 풀이
blog

[🥉 Bronze 4] 입력을 XOR해 비교하는 검사식 뒤집기 — DreamHack Happy New Year! 풀이

59자 입력을 코드에 박힌 상수 버퍼로 XOR한 뒤 고정 문자열과 strcmp하는 리버싱 문제. 비교식을 뒤집으면 정답 입력 = buf ^ s2 이고, 그 입력이 그대로 플래그 본문이 된다. 상수 버퍼를 재구성해 XOR 한 번으로 새해 인사 문장을 복원했다.
#dreamhack#ctf#reversing+3
BLOG2026-07-13
[🥉 Bronze 4] 비교식을 뒤집어 키를 복원하다 — DreamHack Easy Assembly 풀이
blog

[🥉 Bronze 4] 비교식을 뒤집어 키를 복원하다 — DreamHack Easy Assembly 풀이

손으로 짠 어셈블리 크랙미. check_password가 입력을 len과 enc_flag로 XOR해 0인지 검사하는데, 이 비교식을 뒤집으면 정답 키 = enc_flag ^ len 이다. 키 길이가 48이라 enc_flag를 0x30으로 XOR하면 플래그가 바로 나오고, 그 키를 넣으면 "You nailed it!"으로 확인된다.
#dreamhack#ctf#reversing+3
BLOG2026-07-13
[🥉 Bronze 4] 만들어 놓고 버린 반환값에서 플래그를 낚아채다 — DreamHack Check Return Value 풀이
blog

[🥉 Bronze 4] 만들어 놓고 버린 반환값에서 플래그를 낚아채다 — DreamHack Check Return Value 풀이

main이 플래그를 버퍼에 조립해 반환하지만, 그 반환값을 쓰지 않고 곧바로 다른 문자열로 덮어써 출력하지 않는 리버싱 문제. 반환값을 덮어쓰기 직전 지점에 gdb 브레이크포인트를 걸고 $rax를 문자열로 읽으면 플래그가 그대로 나온다.
#dreamhack#ctf#reversing+3
BLOG2026-07-13
[🥉 브론즈 3] XOR 한 줄로 잠근 랜섬웨어, 키를 거꾸로 뽑아내다 — DreamHack darimchal_001 풀이
blog

[🥉 브론즈 3] XOR 한 줄로 잠근 랜섬웨어, 키를 거꾸로 뽑아내다 — DreamHack darimchal_001 풀이

입력한 복호화 키를 고정 KEY와 XOR해 JOKER 상수와 strncmp하는 랜섬웨어 콘셉트 문제. XOR은 자기역원이라 JOKER ⊕ KEY를 그대로 계산하면 통과 비밀번호 pa55uc0_가 나온다. 마지막에 플래그를 DH{} 없이·언더바를 빼고 pa55uc0로 내야 정답으로 인정되는 함정이 있었다.
#dreamhack#ctf#crypto+4
BLOG2026-07-13
[🥈 실버 3] 커스텀 base64를 알려진 평문으로 복원하다 — DreamHack baseball 풀이
blog

[🥈 실버 3] 커스텀 base64를 알려진 평문으로 복원하다 — DreamHack baseball 풀이

baseball 바이너리는 table 파일의 64글자를 알파벳으로 쓰는 커스텀 base64 인코더다. 정작 table은 주어지지 않지만, 평문 text_in과 그 인코딩 text_out이라는 알려진 쌍이 있다. 표준 base64의 위치별 6비트 값에 text_out 문자를 대응시켜 알파벳을 복원하고, 같은 알파벳으로 flag_out을 복호했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 브론즈 4] 암호화된 이미지를 되살리다 — DreamHack Recover 풀이
blog

[🥉 브론즈 4] 암호화된 이미지를 되살리다 — DreamHack Recover 풀이

chall 바이너리가 flag.png를 바이트마다 암호화해 encrypted 파일로 저장한다. 암호화는 4바이트 키 deadbeef로 XOR한 뒤 0x13을 더하는 단순 가역 연산이라, 순서를 반대로 되돌리면 원본이 복구된다. 복구본의 첫 8바이트가 PNG 시그니처와 일치하는 걸 확인하고, 되살린 이미지 속 플래그를 읽었다.
#dreamhack#ctf#reversing+4
 
BLOG2026-07-13
[🥉 브론즈 4] 928개 함수에 흩어진 글자 모으기 — DreamHack Collect Me 풀이
blog

[🥉 브론즈 4] 928개 함수에 흩어진 글자 모으기 — DreamHack Collect Me 풀이

func_0부터 func_927까지 928개 함수가 각각 char 지역변수 하나에 한 바이트씩 담고 있다. main은 이 함수들을 호출하지 않고 안내만 출력하므로, 실행이 아니라 objdump로 각 함수의 즉시값을 번호순으로 긁어 928자 문자열을 조립했다. 그 Lorem ipsum 본문 속에 플래그가 숨어 있었다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 브론즈 3] 출력되지 않는 flag를 함수 인자에서 훔치다 — DreamHack Check Function Argument 풀이
blog

[🥉 브론즈 3] 출력되지 않는 flag를 함수 인자에서 훔치다 — DreamHack Check Function Argument 풀이

main이 어떤 함수를 flag 문자열 주소를 인자로 넘겨 호출하지만, 그 함수는 flag를 출력하지 않는다. 게다가 flag는 바이너리에 평문으로 없고 생성자가 런타임에 조립한다. x86-64 호출 규약상 첫 인자는 rdi이므로, 그 함수 호출 지점에 GDB 브레이크를 걸고 rdi를 읽어 flag를 그대로 가로챘다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 브론즈 3] 세 단계 변환을 거꾸로 되돌리다 — DreamHack r-xor-t 풀이
blog

[🥉 브론즈 3] 세 단계 변환을 거꾸로 되돌리다 — DreamHack r-xor-t 풀이

입력 64자를 add·배열 뒤집기·xor 세 단계로 변환해 고정된 target과 strncmp하는 리버싱 문제. 세 연산이 모두 가역이라, target에서 xor 3을 풀고 배열을 다시 뒤집고 add 13을 되돌리면 올바른 입력이 그대로 복원된다. 그 입력이 곧 플래그 본문이고, 바이너리에 넣어 Nice!로 확인했다.
#dreamhack#ctf#reversing+4
 
BLOG2026-07-13
[🥉 Bronze 1] 죽은 코드 속 Caesar 복호기를 되살리다 — DreamHack Small Counter 풀이
blog

[🥉 Bronze 1] 죽은 코드 속 Caesar 복호기를 되살리다 — DreamHack Small Counter 풀이

바이너리는 실행해도 카운터만 세고 끝난다. flag를 만드는 flag_gen은 counter==5일 때만 불리는데 루프가 0에서 끝나 절대 도달하지 않는 죽은 코드다. flag_gen이 알파벳 테이블로 Caesar 복호를 한다는 걸 읽어내고, 암호문 IM 접두가 DH 로 풀리는 shift=5를 확정한 뒤 그 로직을 재현해 flag를 복원했다. GDB로 실제 flag_gen을 직접 호출해 결과가 정확히 일치함을 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 Bronze 3] 메뉴판이 곧 정답 — DreamHack amocafe 풀이
blog

[🥉 Bronze 3] 메뉴판이 곧 정답 — DreamHack amocafe 풀이

카페 사이트가 좋아하는 메뉴라며 이상한 문자열을 보여주고, 그 "메뉴 번호"를 맞히면 flag를 준다. 알고 보면 그 문자열은 flag에서 잘라낸 19자리 숫자(org)의 16진수를 살짝 가린 것이다. nibble a는 "10", b는 "_"로만 바뀌어 있어 그대로 되짚으면 풀린다.
#dreamhack#ctf#web+4
 
BLOG2026-07-13
[🥉 Bronze 2] "1년 뒤에 알려줄게"를 1바이트로 앞당기기 — DreamHack Simple Patch Me 풀이
blog

[🥉 Bronze 2] "1년 뒤에 알려줄게"를 1바이트로 앞당기기 — DreamHack Simple Patch Me 풀이

한 시간씩 sleep하며 8760번(1년)을 세야 flag를 보여주는 바이너리다. 카운터 초기값 0을 8760으로 바꾸는 4바이트 패치 하나로 대기 루프를 건너뛴다. flag는 그 카운터 값으로 만든 키의 XOR이라, 값이 고정이니 정적으로도 계산된다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 Bronze 3] 7번 주무른 입력을 거꾸로 되감기 — DreamHack Simple Crack Me 2 풀이
blog

[🥉 Bronze 3] 7번 주무른 입력을 거꾸로 되감기 — DreamHack Simple Crack Me 2 풀이

32바이트 입력에 XOR·덧셈·뺄셈을 일곱 번 섞어 목표값과 비교하는 crackme다. 섞는 연산이 전부 바이트 단위 가역이라, 목표값에서 같은 연산을 역순으로 풀면 입력이 그대로 복원된다. 디스어셈블로 순서·키·상수를 읽어 되감았다.
#dreamhack#ctf#reversing+4
BLOG2026-07-13
[🥉 Bronze 2] dreamhack.exe에 심겨야 열리는 DLL — DreamHack Inject ME!!! 풀이
blog

[🥉 Bronze 2] dreamhack.exe에 심겨야 열리는 DLL — DreamHack Inject ME!!! 풀이

익스포트도 없는 DLL이 DllMain에서 호스트 프로세스명을 검사한다. 이름이 dreamhack.exe여야 flag를 계산하는데, 그 값은 프로세스명 첫 4바이트를 WELL512 PRNG 시드로 삼아 만든다. 프로세스명이 고정이라 flag는 실행 없이 정적으로 복원된다.
#dreamhack#ctf#reversing+5
BLOG2026-07-08
[🥉 Bronze 2] AES S-box 블록 변환을 역산해 입력 복원 — DreamHack rev-basic-9 풀이
blog

[🥉 Bronze 2] AES S-box 블록 변환을 역산해 입력 복원 — DreamHack rev-basic-9 풀이

chall9.exe 는 입력을 8바이트 블록마다 16라운드 변환(AES S-box + ror + 덧셈 피드백)한 뒤 하드코딩된 target 과 memcmp 한다. S-box 가 전단사라 각 라운드가 가역이므로, 블록별로 역산하면 정답 입력이 그대로 복원된다. wine 으로 "Correct" 까지 확인.
#dreamhack#ctf#reversing+4
BLOG2026-07-07
[🥈 Silver 4] 암호문 배열을 걷어내니 로직은 열 줄도 안 됐다 — DreamHack Secure Mail 풀이
blog

[🥈 Silver 4] 암호문 배열을 걷어내니 로직은 열 줄도 안 됐다 — DreamHack Secure Mail 풀이

1.1MB짜리 난독화된 JS라 겁먹었지만, 진짜 로직은 223KB짜리 암호문 배열을 걷어내면 열 줄이 안 됐다. 생년월일 후보의 MD5를 키/IV로 쓰는 AES-128-CBC로 첨부 메일을 복호화하는 구조라, 원본 코드를 그대로 브루트포스하면 한 번에 2.3초씩 걸려 하루가 넘게 걸린다. 알고리즘만 뽑아 Node 네이티브 AES로 다시 짜서 3만 5천 개 생년월일을 45초에 끝냈다.
#dreamhack#ctf#reversing+5
BLOG2026-07-06
[🌱 새싹] cmp 명령어 하나가 곧 플래그 — DreamHack Simple Crack Me 풀이
blog

[🌱 새싹] cmp 명령어 하나가 곧 플래그 — DreamHack Simple Crack Me 풀이

정적 링크·strip된 ELF 한 개. scanf로 정수 하나를 받아 상수와 비교하는 게 로직의 전부다. objdump로 main을 까면 cmp eax,0x13371337 한 줄이 곧바로 나온다 — 그 16진수를 10진수로 바꿔 DH{}로 감싸면 플래그. gdb로 그 비교가 실행 시점에 실제로 어떻게 일어나는지까지 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-07-05
[🌱 Sprout] 뒤집고, 뒤집고, XOR 한 번 — DreamHack simple-operation 풀이
blog

[🌱 Sprout] 뒤집고, 뒤집고, XOR 한 번 — DreamHack simple-operation 풀이

난수를 받아 XOR 하고, 8자리 hex 문자열로 만든 다음 뒤집어서 상수와 비교하는 아주 짧은 체크 루틴. 서버가 알려주는 난수 하나만 있으면 그 변환을 그대로 거꾸로 밟아 정답을 계산할 수 있다.
#dreamhack#ctf#reversing+2
 
BLOG2026-07-05
[🥉 Bronze 4] 왼쪽으로 돌린 비트를 오른쪽으로 되돌리기 — DreamHack rev-basic-7 풀이
blog

[🥉 Bronze 4] 왼쪽으로 돌린 비트를 오른쪽으로 되돌리기 — DreamHack rev-basic-7 풀이

입력 바이트를 (i mod 8)만큼 왼쪽으로 회전시키고 i와 XOR한 값을 31바이트 고정 테이블과 비교하는 x86-64 PE. 회전은 반대 방향으로 같은 양만큼 돌리면 정확히 원상복구되는 연산이라, 검증식을 그대로 뒤에서부터 읽어 ROR로 되돌렸다. 디스어셈블로 검증 로직을 읽고, 표를 바이너리에서 직접 추출해 역산하고, wine으로 Correct를 확인한 과정.
#dreamhack#ctf#reversing+7
BLOG2026-07-03
[🥉 Bronze 4] 곱셈 치환을 모듈러 역원으로 되돌리기 — DreamHack rev-basic-8 풀이
blog

[🥉 Bronze 4] 곱셈 치환을 모듈러 역원으로 되돌리기 — DreamHack rev-basic-8 풀이

입력 각 바이트에 0xfb를 곱하고 하위 1바이트만 남긴 값을 21바이트 고정 테이블과 비교하는 x86-64 PE. 0xfb가 mod 256에서 홀수라 곱셈의 역원이 존재한다는 점을 이용해 검증 테이블을 그대로 역산했다. 마지막 21번째 비교가 사실은 문자열 종결 NUL 검증이라는 것도 함께 정리.
#dreamhack#ctf#reversing+4
BLOG2026-07-01
[🥉 Bronze 4] AES S-box를 거꾸로 — DreamHack rev-basic-6 풀이
blog

[🥉 Bronze 4] AES S-box를 거꾸로 — DreamHack rev-basic-6 풀이

입력을 한 글자씩 256바이트 치환표로 바꿔 고정된 18바이트와 비교하는 x86-64 PE. 그 치환표가 AES S-box라는 걸 알아채면 끝난다. S-box를 한 번 뒤집어 기대값을 역치환하면 입력이 그대로 나온다. 디스어셈블로 검증 로직을 읽고, 표를 추출해 역산하고, wine으로 Correct를 확인한 과정.
#dreamhack#ctf#reversing+5
BLOG2026-07-01
[🥉 Bronze 4] 정답을 흘리는 NULL 종단 — DreamHack rev-basic-5 풀이
blog

[🥉 Bronze 4] 정답을 흘리는 NULL 종단 — DreamHack rev-basic-5 풀이

입력의 인접한 두 바이트를 더해 .data 테이블과 비교하는 x86-64 PE. 식만 보면 미지수가 하나 남는 부정방정식이지만, 입력 버퍼가 0으로 초기화돼 문자열이 NULL로 끝난다는 점과 테이블 마지막 값이 0이라는 점이 오른쪽 끝을 고정한다. 거기서 왼쪽으로 도미노처럼 역산하면 입력이 그대로 나온다.
#dreamhack#ctf#reversing+4
BLOG2026-06-25
[🥈 Silver 4] 검은 가림막을 ret 한 줄로 걷어내다 — DreamHack patch 풀이
blog

[🥈 Silver 4] 검은 가림막을 ret 한 줄로 걷어내다 — DreamHack patch 풀이

GDI+ 라인 드로잉으로 flag를 그리는 Windows GUI 프로그램. flag 글자를 검정으로 그리기 전에, 같은 검정으로 두꺼운 선을 겹쳐 그려 가운데를 덮는 "가림막"을 먼저 깐다. 검정 글자가 검정 가림막 위에 얹히니 안 보일 뿐이다. 가림막을 그리는 함수의 첫 바이트를 ret(0xC3)로 패치해 가림막을 통째로 없애면 flag가 흰 배경 위에 그대로 드러난다.
#dreamhack#ctf#reversing+4
BLOG2026-06-22
[🌱 새싹] 호박을 만 번 깎으면 떨어지는 플래그 — DreamHack Carve Party 풀이
blog

[🌱 새싹] 호박을 만 번 깎으면 떨어지는 플래그 — DreamHack Carve Party 풀이

서버도 백엔드도 없는 HTML 한 장짜리 문제. 호박을 10000번 클릭하면 canvas에 플래그가 그려지는데, 그 플래그 바이트는 클릭 100번마다 한 번씩 XOR 변환을 거친다. 키로 쓰이는 pie 변수가 함수 밖 전역이라 배치 사이에 값이 이어진다는 점만 잡으면, 손으로 만 번 누르는 대신 100배치를 그대로 시뮬레이션해서 플래그를 꺼낼 수 있다.
#dreamhack#ctf#web+4
BLOG2026-06-08
[🌱 새싹] 한 바이트의 니블을 맞바꾸는 검증 — DreamHack rev-basic-4 풀이
blog

[🌱 새싹] 한 바이트의 니블을 맞바꾸는 검증 — DreamHack rev-basic-4 풀이

rev-basic-3이 XOR·산술을 끼웠다면 4번은 비트로 논다. 각 글자의 상위 4비트와 하위 4비트를 맞바꾼(니블 스왑) 값이 .data 테이블과 같아야 통과한다. 니블 스왑은 두 번 하면 제자리라 테이블을 한 번 더 스왑하면 그대로 정답이 나온다.
#dreamhack#ctf#reversing+5
 
BLOG2026-06-08
[🌱 새싹] 비교 직전 XOR·산술이 끼어든 검증 루프 — DreamHack rev-basic-3 풀이
blog

[🌱 새싹] 비교 직전 XOR·산술이 끼어든 검증 루프 — DreamHack rev-basic-3 풀이

rev-basic-2까지는 정답이 평문으로 메모리에 박혀 있었다. 3번은 비교 직전에 글자를 한 번 주물러 둔다 — input[i]를 i로 XOR하고 2*i를 더한 값이 .data의 테이블과 같아야 통과한다. XOR도 덧셈도 역연산이 있으니, 테이블을 떠내 거꾸로 풀면 그대로 정답이 나온다.
#dreamhack#ctf#reversing+5
BLOG2026-06-04
[🌱 새싹] 정답이 4바이트씩 끊겨 박힌 배열 — DreamHack rev-basic-2 풀이
blog

[🌱 새싹] 정답이 4바이트씩 끊겨 박힌 배열 — DreamHack rev-basic-2 풀이

rev-basic-1이 글자마다 cmp를 펼친 코드였다면, 2번은 루프 하나로 정답 배열과 비교한다. 비교 대상이 byte가 아니라 .data의 4바이트 정수 배열이라 한 칸이 4바이트씩이다. 각 원소의 저바이트만 모으면 정답. objdump로 배열을 떠내 디코드하고 wine으로 Correct를 확인했다.
#dreamhack#ctf#reversing+4
BLOG2026-06-04
[🌱 새싹] 한 글자씩 cmp — 코드에 흩어진 상수 모으기 — DreamHack rev-basic-1 풀이
blog

[🌱 새싹] 한 글자씩 cmp — 코드에 흩어진 상수 모으기 — DreamHack rev-basic-1 풀이

rev-basic-0이 strcmp 한 방이었다면, 1번은 입력을 한 글자씩 cmp로 비교한다. 검증 함수에 흩어진 비교 상수 21개를 주소순으로 모아 ASCII로 디코드하면 정답이 나온다. objdump로 cmp 체인을 뽑고 wine으로 Correct를 확인한 과정.
#dreamhack#ctf#reversing+4
BLOG2026-06-04
[🌱 새싹] strcmp 하나가 전부 — DreamHack rev-basic-0 풀이
blog

[🌱 새싹] strcmp 하나가 전부 — DreamHack rev-basic-0 풀이

입력을 받아 Correct/Wrong을 출력하는 x86-64 PE. 검증 함수는 입력을 .rdata에 평문으로 박힌 문자열과 strcmp 비교할 뿐이다. 디스어셈블로 비교 대상 주소를 찾아 문자열을 그대로 꺼내 답을 구하고, wine으로 Correct를 확인한 과정.
#dreamhack#ctf#reversing+4
 
BLOG2026-05-29
[💎 Diamond 4] 난독화된 안드로이드 DRM을 통째로 빌려 쓰기 — DreamHack Dream DRM 풀이
blog

[💎 Diamond 4] 난독화된 안드로이드 DRM을 통째로 빌려 쓰기 — DreamHack Dream DRM 풀이

request_book의 id 파라미터 SQL Injection으로 서버의 임의 파일(/flag)을 암호문 상태로 읽어내고, 패킹된 native DRM 라이브러리를 NDK 하니스 + dlopen + 가짜 JNIEnv로 직접 호출해 복호화 오라클로 삼는 과정을 정리한다. 오프라인 크래킹·APK 재서명·MITM 등 막다른 길까지 전부 기록한 Theori 채용 CTF 다이아 4 풀이.
#dreamhack#ctf#web+7
BLOG2026-04-30
[⭐] Switch는 작동한다, 다만 모든 분기가 진실을 말하지 않을 뿐 — DreamHack Control-Flow: mirage 풀이
blog

[⭐] Switch는 작동한다, 다만 모든 분기가 진실을 말하지 않을 뿐 — DreamHack Control-Flow: mirage 풀이

44개 상태를 dispatch table로 돌리는 state machine, 그리고 PRNG 조건처럼 보이지만 실행 결과에 아무런 영향을 주지 않는 opaque predicate. 정적 분석만으로 jump table을 추출하고 변환 순서를 역추적해 플래그를 구하는 과정을 단계별로 정리한다.
#dreamhack#ctf#reversing+3
 
 
 
 
 
+9
 
 
 
 
 
 
 
 
+5