아이 AI 수업은 도구 말고 누가 가르치는지부터 보자

이미지
아이들 AI 수업은 지금 누가 가르치고 있을까요. 얼마 전에 아이 학교 참관수업을 보고 왔습니다. 요즘은 수업 시간에 AI로 글을 쓰는 것 같은 걸 가르치는 모양입니다. 보고 나오면서 든 생각은 하나였습니다. 개발자 입장에서 보면 이건 아니다 싶었습니다. 처음 드는 생각도 아닙니다. 스크래치가 유행할 때도, 마인크래프트로 코딩을 가르친다고 할 때도 저는 비슷한 생각을 했습니다. 도구 이름은 그때마다 바뀌었는데, 개발자 눈에 비어 보이는 것은 매번 같았습니다. 도구가 문제였다면 도구가 바뀔 때 뭔가 달라졌어야 합니다. 세 번을 바꿔도 같은 것이 비어 있으니 도구보다 앞에 원인이 있다고 봤고, 그걸 따라가다 보니 가르치는 사람이 어디서 배워서 교실에 들어오는지에 닿았습니다. 범위를 먼저 그어 두겠습니다. 참관수업이 몇 학년 무슨 수업이었는지, 누가 가르쳤는지는 적지 않습니다. 이 글은 교실 하나나 강사 한 사람을 두고 하는 이야기가 아닙니다. 제도에 관한 사실은 원문을 열어서 확인한 것만 쓰고, 확인하지 못한 것은 제 판단이라고 밝혀 두겠습니다. 인용한 자료는 전부 2026년 9월 27일에 확인했습니다. 디지털새싹 강사는 이틀짜리 양성과정을 거쳐 교실에 들어갑니다 배경부터 짧게 적겠습니다. 소프트웨어 교육은 중학교가 2018학년도 신입생부터, 초등학교 5·6학년이 2019년부터 필수가 됐습니다( 한국일보, 2017-12-27 ). 그리고 2025년부터 적용되는 2022 개정 교육과정에서 정보 수업 시간이 초등은 17시간에서 34시간 이상으로, 중학교는 34시간에서 68시간 이상으로 늘었습니다( 정책브리핑, 2022-08-29 ). 정규 교과 안의 정보 수업은 학교 선생님이 맡습니다. 교과 밖으로 들어오는 AI 교육은 경로가 다릅니다. 대표적인 게 교육부의 디지털새싹입니다. 올해는 대학과 공공·민간기관을 대상으로 운영기관 공모를 해서 45곳을 골랐고, 이 기관들이 267종의 프로그램을 들고 학교로 직접 찾아가 8~12차시 이상씩 수업을 합니다. AI ...

리버스한 C를 러스트로 옮겼더니 완벽하게 돌아가던데요?

리버스 엔지니어링으로 뽑아낸 C 소스를 빌드해 봤습니다. 한 번에 된 건 아닙니다. 디컴파일 출력은 원래 컴파일러에 그냥 넣을 수 있는 물건이 아니라서, 오류를 보고 고치고 다시 넣기를 여러 차례 주고받았습니다.

그러고 나서 한 번 더 갔습니다. 그 C 를 러스트로 포팅했고, 포팅본도 빌드가 됐고, 나온 산출물이 완벽하게 동작했습니다.

먼저 범위를 정확히 적어두겠습니다. 무슨 물건을 뜯는지는 알고 시작했습니다. 뜯기로 정한 사람이 저였으니 그 프로그램이 무엇을 하는지는 처음부터 알고 있었습니다. 몰랐던 건 그 안쪽입니다. 열어보면 이게 무슨 역할인지 감이 안 오는 덩어리가 여럿 나오는데, 그런 건 비슷한 모양의 소스를 찾아다니면서 이런 구조면 대개 이런 물건이더라를 대조하는 식으로 메웠습니다.

그렇게 하고도 끝내 정체가 안 잡힌 덩어리가 남았습니다. 그건 이해하지 못한 채로 그냥 옮겼습니다.

그런데도 빌드가 통과했고 산출물이 돌아갔습니다. 제가 눈여겨본 게 여기입니다.

무엇을 뜯었는지는 적지 않겠습니다. 이 글에 필요한 건 대상이 아니라 체인이고, 대상을 적는 순간 글의 성격이 바뀝니다. 제가 눈여겨본 것도 결과물이 아니었습니다. 나온 C 가 얼마나 사람이 쓴 것처럼 생겼는지는 여기서 별로 중요하지 않습니다. 예전 같으면 여기 있어야 할 며칠이 통째로 비어 있었습니다.

이 체인을 끝까지 돌려본 이유도 거창하지 않습니다. 요즘 도구로 어디까지 되는지가 궁금해서 한 번 끝을 봤습니다. 중간에 멈추면 어차피 그럴듯한 텍스트를 본 것에 불과하니, 맞는지 틀리는지 판정이 날 때까지는 가야 한다고 봤습니다. 그 판정을 빌드가 해 줍니다.

먼저 경계를 긋겠습니다. 제가 확인한 것은 이 체인 하나입니다. 임의의 바이너리에서 같은 결과가 나오는지, 수십만 줄짜리 상용 코드베이스에서도 같은지는 확인하지 않았습니다. 그래서 이 글은 이제 전부 뚫린다는 이야기가 아닙니다. 제가 하려는 이야기는 우리가 오랫동안 어떤 값 하나에 기대어 설계를 해 왔는데 그 값이 예전 같지 않다는 겁니다.

어두운 작업대 위에 왼쪽에는 금속 장치 하나가 분해되어 부품이 줄지어 놓여 있고, 오른쪽에는 같은 형태의 장치가 다른 재질로 조립되어 서 있는 장면

전부 이해하지 못했는데 빌드가 통과했습니다

모델이 그럴듯한 C 를 뱉었다는 말과 그 C 가 빌드됐다는 말은 질이 다릅니다. 이 차이를 먼저 짚고 가야 뒤의 이야기가 성립합니다.

컴파일러는 문장의 분위기를 봐주지 않습니다. 타입이 안 맞으면 멈추고, 선언이 없으면 멈추고, 구조체 크기가 어긋나면 그 자리에서 냅니다. 그러니까 빌드가 통과했다는 것은 재구성이 문법만 맞은 게 아니라 의미까지 맞았다는 뜻입니다. 어느 값이 포인터고 어느 값이 정수인지, 이 구조체에 필드가 몇 개고 어떤 순서로 놓이는지, 이 함수가 무엇을 받아서 무엇을 돌려주는지가 전부 맞아떨어졌을 때만 나오는 결과입니다.

리버스 산출물이 그대로 빌드되는 일이 흔하지 않다는 것도 같이 적어두는 게 맞겠습니다. 디컴파일러 출력은 보통 컴파일러에 그냥 넣을 수 있는 물건이 아닙니다. 헤더가 없고, 호출하는 함수의 선언이 비어 있고, 도구가 자기 편의로 만들어낸 타입이 섞여 들어옵니다. 이걸 빌드되는 상태로 만들려면 그 빈자리를 자기 판단으로 메워야 하고, 메우는 작업은 이해한 만큼만 됩니다. 그래서 빌드가 통과했다는 것은 적어도 그만큼은 이해가 됐다는 뜻입니다.

두 번째 빌드가 더 셉니다.

러스트로 포팅한다는 것은 C 에서 암묵적으로 넘어가던 것들을 전부 명시적으로 적어야 한다는 뜻입니다. 이 값의 소유권이 어디 있는지, 이 참조가 얼마나 사는지, 널이 될 수 있는 자리인지 아닌지. C 에서는 그냥 포인터 하나였던 것이 러스트에서는 여러 선택지 중 하나를 골라 적어야 합니다. 그걸 골라 적었는데 빌드가 됐고 산출물이 돌아갔습니다. 원본의 의미가 다른 언어의 타입 체계로 옮겨간 뒤에도 보존됐다는 뜻입니다.

러스트 쪽에서 한 가지가 더 나옵니다. 컴파일러가 걸러주는 항목이 C 보다 훨씬 많다는 것은, 원본의 의미를 잘못 읽었을 때 그게 드러날 확률도 그만큼 높다는 뜻입니다. C 는 틀린 채로도 곧잘 빌드됩니다. 러스트는 바로 멈춥니다. 통과한 검사가 더 깐깐한 검사였다는 것이 두 번째 빌드의 값입니다.

두 번의 빌드가 각각 검산 역할을 했습니다. 한 번이면 운이 좋았다고 할 수 있는데, 서로 다른 검사 체계를 두 번 통과하면 그건 운이 아닙니다.

다만 여기서 정직하게 갈라야 할 것이 있습니다. 빌드가 증명하는 것은 충분히 이해했다는 것이지 전부 이해했다는 것이 아닙니다. 앞에 적은 대로 정체가 안 잡힌 덩어리가 남아 있었고, 그것들은 이해되지 않은 채로 옮겨졌고, 옮겨진 채로 동작했습니다.

처음에는 이게 이 체인의 흠이라고 봤습니다. 지금은 반대로 보고 있습니다. 방어하는 쪽에서는 이게 더 곤란합니다.

상대의 시간을 계산할 때 우리가 암묵적으로 깔던 전제가 상대는 이걸 전부 이해해야 한다는 것이었습니다. 그런데 공격하는 사람에게 필요한 건 전부가 아닙니다. 자기가 하려는 일에 닿는 경로만큼입니다. 인증 흐름을 우회하려는 사람은 화면 그리는 코드를 이해할 이유가 없고, 프로토콜을 재현하려는 사람은 그 안의 압축 루틴이 무슨 알고리즘인지 몰라도 됩니다. 모르는 덩어리는 모르는 채로 들고 가면 그만입니다.

그러니까 상대가 넘어야 하는 문턱은 완전한 이해가 아니라 필요한 만큼의 이해이고, 그 문턱은 원래도 우리가 생각하던 것보다 낮았습니다. 이번에 제가 본 건 그게 한 번 더 낮아졌다는 겁니다. 이 글의 나머지는 전부 여기에 얹혀 있습니다.

몇 달이 걸리던 건 디컴파일이 아니라 그다음이었습니다

디컴파일 자체는 원래 몇 분짜리 자동화였습니다. Ghidra 에 바이너리를 넣으면 C 비슷한 것이 나옵니다. 여기에 사람 시간이 크게 들어간 적은 없습니다.

몇 달이 걸리던 것은 그 출력을 읽는 일입니다. FUN_00401a30 이라는 이름과 undefined4 * 라는 타입과 DAT_00478c10 이라는 상수를 늘어놓고 이게 무엇을 하는 코드인지 복원하는 일, 그게 그 몇 달입니다.

그 일에는 성격이 다른 네 가지가 겹쳐 있습니다. 이름을 복원하는 것. 타입을 복원하는 것. 어디부터 어디까지가 하나의 기능인지 경계를 긋는 것. 그리고 이 루프가 왜 이 모양인지를 보고 이건 체크섬 계산이구나로 건너뛰는 것.

앞의 셋이 전부 텍스트 패턴 인식입니다. 이 이름의 함수가 이런 인자를 받아 이런 호출을 하면 대개 무슨 역할이더라, 이 접근 패턴이면 이 오프셋은 길이 필드더라. 사람이 그걸 해내는 근거도 지식이 아니라 그동안 본 코드의 양입니다. 그리고 그건 모델이 가장 잘하는 종류의 일입니다.

제가 쓴 방법도 정확히 그것이었습니다. 정체를 모르겠는 덩어리가 나오면 비슷한 모양의 소스를 찾아다니면서 대조했습니다. 이 구조에 이 순서로 호출이 붙으면 대개 이런 물건이더라, 하는 식입니다. 사람이 리버스할 때 원래 쓰던 방법이 이건데, 예전에는 그 비슷한 소스를 내 머릿속이나 내가 뒤질 수 있는 범위에서만 찾을 수 있었습니다. 대조할 모수가 달라지면 같은 방법의 속도가 달라집니다.

그래서 앞의 셋은 제가 돌린 체인에서도 대체로 해결되어 나왔습니다. 전부는 아니었고, 끝내 안 풀린 게 앞서 말한 그 덩어리들입니다.

그 셋에 사람이 쓰던 시간이 어떤 종류였는지도 짚어둘 만합니다. 어렵다기보다 지루한 시간입니다. 함수 하나를 읽고 이름을 붙이고 옆 함수로 넘어가고, 며칠 뒤에 앞서 붙인 이름이 틀렸다는 걸 알고 되돌아오는 식입니다. 머리를 쓰는 구간은 짧고 그 사이를 잇는 반복이 깁니다. 사람이 지쳐서 놓치는 것도 대개 여기였습니다.

마지막 하나, 의미 도약은 여전히 사람 쪽이 낫다고 보고 있습니다. 다만 앞의 셋이 사라지면 사람은 남은 하나에 시간을 온전히 씁니다. 압축된 것은 리버스의 난이도가 아니라 이해에 도달하기까지의 단계입니다.

표적을 먼저 고르지 않아도 되면 순서가 바뀝니다

한 표적을 이해하는 데 몇 달이 들면 일의 순서가 정해져 있습니다. 가치 있는 것을 먼저 고르고, 고른 것을 팝니다. 조사 비용이 비싸니까 조사 앞에 선별이 와야 합니다.

몇 시간이면 그 순서가 뒤집힙니다. 전부 분석하고 나온 결과를 보고 고릅니다. 표적 선정이 공격의 맨 앞에서 맨 뒤로 갑니다.

이 전환은 처음이 아닙니다. 포트 스캔이 같은 걸 겪었습니다. 예전에는 어디를 스캔할지가 먼저였습니다. 대상 대역을 정하고 며칠씩 돌렸으니까요. 인터넷 전체를 짧은 시간에 훑는 도구가 나오고 그 결과를 검색으로 뒤질 수 있게 된 다음부터는, 어디를 스캔할지라는 질문 자체가 없어졌습니다. 전부 훑어놓고 조건으로 거르는 쪽이 싸니까요.

여기서 무너지는 전제가 하나 있습니다. 우리는 작으니까 표적이 될 일이 없다는 전제입니다. 이 전제는 선별이 앞에 있을 때만 성립합니다. 선별이 뒤로 가면 기준도 바뀝니다. 자산 가치가 아니라 결과가 기준이 됩니다. 조사가 끝난 다음에 나온 목록에서 취약한 순서대로 고르면 되니까요. 규모가 작다는 사실이 목록에서 빠질 이유가 되지 않습니다.

방어하는 쪽에서 이 순서 변화가 무엇을 바꾸는지가 저한테는 더 큰 문제였습니다. 노출면을 관리할 때 우리가 쓰던 암묵적인 필터가 바로 저 선별이었습니다. 이건 규모가 작으니 뒤로, 이건 내부용이니 뒤로, 이건 옛날 거라 아무도 안 볼 테니 뒤로. 선별이 뒤로 가 있는 상대에게 이 순서는 아무 의미가 없습니다. 상대는 우리 자산 목록이 아니라 자기 스캔 결과를 정렬해서 봅니다.

패치 디핑에는 원래 기한이 붙어 있었습니다

n-day 라고 부르는 경제가 있습니다. 패치가 공개되면 그 패치를 이전 버전과 비교해서 무엇이 고쳐졌는지를 역산하고, 거기서 취약점을 복원하고, 익스플로잇을 만듭니다. 방어하는 쪽은 그 사이에 배포를 끝내는 것으로 이깁니다.

이 구조가 성립하는 근거가 창입니다. 역산에 시간이 걸린다는 것. 그런데 그 역산이 앞에서 말한 바로 그 이해 작업입니다. 패치 전후의 바이너리 차이를 놓고 이 분기가 왜 추가됐는지, 이 길이 검사가 무엇을 막으려는 건지를 복원하는 일이니까요. 이름 복원과 타입 복원과 경계 식별이 여기서도 똑같이 나옵니다.

창이 좁아지면 결과가 조직별로 갈립니다. 배포가 빠른 곳은 원래도 창 안에 들어갔으니 체감이 크지 않습니다. 배포가 느린 조직에게는 n-day 가 0-day 처럼 동작하기 시작합니다. 패치 공개일이 보호가 시작되는 날이 아니라 노출이 시작되는 날이 되는 것인데, 사실 원래도 그랬고 다만 그 사이의 유예가 눈에 안 보일 만큼 짧아지는 겁니다.

그리고 이 창은 조직이 통제할 수 있는 값이 아닙니다. 패치가 언제 공개되는지는 상류가 정하고, 역산에 며칠이 걸리는지는 상대가 정합니다. 우리 쪽에서 움직일 수 있는 것은 공개 시점부터 배포 완료까지의 구간 하나뿐입니다. 그 구간의 길이를 몇 년째 같은 값으로 두고 있었다면, 그 값을 정할 때 상대 쪽 시간을 얼마로 잡았는지부터 다시 봐야 합니다.

이건 새 위협이 아니라 기존 계산의 항 하나가 바뀐 겁니다. 패치 배포 주기를 정할 때 우리가 암묵적으로 넣고 있던 값이 무엇이었는지 꺼내 볼 때이기도 합니다.

같은 일을 하는 사람들의 분포가 달라졌습니다

한 가지는 분명히 하고 가겠습니다. 매크로는 LLM 보다 스무 해 먼저 있었습니다. 자동화된 어뷰징이 AI 때문에 생겼다는 말은 사실이 아닙니다. 바뀐 것은 존재가 아니라 모양입니다.

예전에는 이 일이 전문 업자 쪽으로 몰려 있었습니다. 프로그램을 만들 줄 아는 사람을 고용하거나 사 와야 했고, 그러려면 수지가 맞을 만큼 규모가 나와야 했습니다. 규모가 필요하다는 조건이 사실상의 진입 장벽이었습니다.

지금은 업자가 그대로 있고 그 뒤에 아주 긴 개인 꼬리가 붙습니다. 혼자서 자기 필요만큼 만들어 쓰는 사람들입니다.

문제는 이 둘에 대해 탐지가 정반대로 작동한다는 것입니다. 가장 잘 듣는 탐지 기법이 군집 분석인데, 업자는 계정을 여러 개 굴리면서 결제수단이 겹치고 배송지가 겹치고 접속 대역이 겹칩니다. 개체 하나하나는 정교해도 집단의 윤곽이 보입니다.

개인은 반대입니다. 본인 명의에 본인 카드에 자기 집 주소에 집 회선입니다. 군집이 생길 재료가 아예 없습니다. 대신 개별 품질이 낮습니다. 타이밍이 거칠고 예외 처리가 없고 화면이 한 번 바뀌면 그대로 멈춰 섭니다.

운영하는 쪽에서 이게 왜 성가시냐면, 꼬리 쪽은 개체 수가 많은데 건당 이득이 작습니다. 업자 하나를 끊으면 눈에 보이는 양이 줄지만 꼬리는 끊어도 티가 잘 안 납니다. 그렇다고 두면 총량이 쌓입니다. 대응 비용은 건수를 따라가는데 효과는 건수를 안 따라가는 구조이고, 여기서 오탐이 한 번 나면 그 비용은 다시 사람 쪽으로 옵니다.

그래서 한 축으로 둘 다 잡으려고 하면 양쪽을 다 놓칩니다. 군집만 보면 개인이 안 걸리고, 개별 행위 품질만 보면 정교하게 만든 업자가 안 걸립니다. 두 종류가 같은 이름으로 불린다는 것, 탐지를 설계할 때 제일 불편한 게 이겁니다.

이미지 챌린지는 이제 사람 쪽에만 마찰을 줍니다

CAPTCHA 는 이미 정리가 끝난 이야기입니다. 신호등이 들어간 칸을 고르라는 그 화면은 비전 모델이 사람보다 빠르고 정확하게 풉니다. 이건 논쟁 중인 사안이 아니라 한참 전에 넘어간 선입니다.

그러니까 이미지 챌린지가 아직 주 방어선인 서비스가 있다면, 그 장치는 막고 싶은 쪽은 못 막고 정상 사용자에게만 마찰을 주고 있습니다. 흐릿한 사진을 세 번 틀려서 짜증이 나는 쪽은 언제나 사람입니다.

여기서 진짜 곤란한 건 방어가 뚫렸다는 사실보다, 뚫린 뒤에도 그 장치가 계속 잘 돌아가는 것처럼 보인다는 겁니다. 지표가 안 나빠 보이거든요. 챌린지 통과 비율은 여전히 높고 차단 건수도 잡히기는 합니다. 걸러지는 대상이 바뀌었다는 것은 그 숫자에 안 나옵니다.

무엇으로 대체하느냐는 따로 긴 이야기라 여기서는 열지 않겠습니다. 다만 이것도 앞의 이야기와 똑같습니다. 이 방어가 서 있던 근거는 사람만 할 수 있는 일이 있다는 것이었고, 그 근거가 옮겨간 것입니다.

암표 조항은 공연장 앞에서 파는 걸 전제로 쓰였습니다

이 글을 올린 날 뉴스에 앞 절 이야기의 실물이 하나 나왔습니다. 그래서 절을 하나 붙입니다. 경기북부경찰청 사이버범죄수사2대가 30대 남성 한 명을 업무방해 혐의로 구속 송치했습니다.

기간이 2018년 9월부터 올해 4월까지 7년 7개월입니다. 매크로 프로그램에 가족과 친척 명의 계정을 붙여 인기 공연과 스포츠 경기 티켓 8,967장을 사들였고, 판매 금액이 약 24억 원, 경찰은 그중 3분의 2 정도를 이익금으로 보고 있습니다. 2019년 잠실 BTS 콘서트 티켓은 11만 원에 사서 300만 원에, 2024년 토트넘 경기 티켓은 40만 원에 사서 165만 원에 되팔았습니다. 그 돈으로 경기도 아파트 한 채와 상가 두 채를 샀고, 경찰이 상가 두 채와 채권 등 12억 6천만 원을 기소 전 추징보전했습니다. 3월에 암표 카르텔 검거 보도를 보고 PC와 휴대전화를 포맷하고 신고해 둔 사업자까지 폐업했는데, 그 전에 인공지능에 경찰의 암표 수사 관련 내용을 물어본 것도 같이 나왔습니다.

앞 절에서 적은 업자 쪽 모양이 그대로입니다. 계정이 여러 개고 명의가 가족과 친척으로 묶여 있고 사업자까지 냈습니다. 경찰이 처음 실을 잡은 곳도 거래 단체 대화방이었습니다. 개체가 아니라 집단이 먼저 보이는 쪽입니다.

제가 눈여겨본 건 다른 데입니다. 7년 7개월에 8,967장에 24억인데 적용된 혐의가 업무방해 하나입니다.

왜 그것뿐인지를 따라가 보면 조문이 서 있던 자리가 나옵니다. 경범죄처벌법의 암표매매 조항은 공연장이나 경기장처럼 사람을 입장시키는 곳에서 웃돈을 받고 되파는 행위를 전제로 쓰였습니다. 암표가 공연장 앞에서 손으로 넘기던 물건이던 시절의 문장입니다. 거래가 예매 사이트와 단체 대화방으로 옮겨가면서 그 전제가 없어졌는데, 조문은 그대로 남아 있었습니다.

앞에서 계속 하던 이야기와 같은 모양입니다. 값은 남아 있고 그 값을 받치던 전제만 사라진 것. 다른 점이라면 이쪽은 전제가 적혀 있기라도 했다는 겁니다. 「입장시키는 곳에서」라는 구절이 그 전제입니다.

값을 다시 계산한 쪽도 같이 볼 만합니다. 2024년 3월에 공연법에 매크로를 이용한 입장권 부정판매 조항이 들어갔습니다. 그런데 지난 8월 28일 시행분에서는 매크로를 썼는지를 요건에서 뺐습니다. 도구를 특정한 조문은 도구가 바뀌면 무력해진다는 이유였고, 반복성과 영리성만 보는 쪽으로 바꾸면서 판매 금액의 50배까지 과징금을 매길 수 있게 했습니다. 전제가 흔들린 걸 확인하고 값을 다시 적은 사례입니다. 두 해 만입니다.

여기까지는 법 쪽 이야기라 제가 손댈 자리가 아닙니다. 제가 손대는 건 예매 제한 쪽인데, 거기에도 같은 게 있었습니다.

업무방해가 붙은 근거가 되판 가격이 아니라 어떻게 샀느냐였습니다. 매크로와 여러 계정으로 예매 제한을 우회한 행위입니다. 그러니까 이 건에서 법이 붙잡은 것은 우리가 걸어둔 제한입니다. 1인 N매 제한이든 계정당 제한이든, 그 값들은 사람 하나가 계정 하나라는 전제 위에 서 있습니다. 가족과 친척 명의 계정이 붙는 순간 그 전제가 없어지는데, 제한 값은 여전히 계정을 세고 있습니다.

그런데 그 제한이 헛돈 것만은 아닙니다. 8,967장을 막지는 못했지만 우회했다는 사실이 기록으로 남았고, 그 기록이 혐의의 근거가 됐습니다. 제한을 걸 때 저는 막는 쪽만 생각하고 있었습니다. 막지 못했을 때 무엇이 남는지는 설계에 넣어본 적이 없습니다. 지금은 그쪽도 제한의 값에 들어간다고 보고 있습니다.

키 회전 주기는 그 시간보다 짧을 때만 방어입니다

제가 이 글을 쓰고 있는 이유가 여기입니다.

회전이 실제로 무엇을 사 주는지부터 다시 보면 이야기가 단순해집니다. 회전은 공격을 막지 않습니다. 이미 새어 나간 키의 유효기간에 상한을 씌우는 장치입니다. 그러니까 회전 주기는 상대가 그 키를 가지고 무언가를 해내는 데 걸리는 시간과 비교할 때만 값이 생깁니다.

키 회전이 방어로 작동하는 조건은 하나입니다. 회전 주기가 공격자가 그 체계를 이해하는 데 걸리는 시간보다 짧아야 합니다. 이해에 석 달이 걸린다면 90일 회전은 상대가 다 파악하기 전에 바닥을 갈아엎는 행위입니다. 이해가 일주일로 내려왔는데 회전 주기가 그대로 90일이면, 그건 방어가 아니라 가끔 키를 바꾸는 습관입니다. 이름은 같은데 하는 일이 달라졌습니다.

90일이라는 숫자가 어디서 나왔는지 기억하는 사람을 저는 거의 못 봤습니다. 규정에 적혀 있고 감사에서 묻고 통과하니까 남아 있습니다. 그런데 그 값도 처음에는 누군가가 이 정도면 그 사이에 다 이해하지는 못한다고 계산해서 적어 넣은 결과입니다. 계산의 전제가 바뀌면 결과도 다시 계산해야 하는데, 숫자만 남고 전제는 문서 어디에도 안 적혀 있는 경우가 대부분입니다.

같은 모양이 여럿입니다. 난독화 갱신 주기가 그렇고, 강제 업데이트 주기가 그렇고, 시그니처 갱신 주기가 그렇습니다. 전부 그 사이에 다 파악하지는 못한다는 문장 하나에 걸려 있던 값들입니다.

그리고 이 값들은 대개 한 군데 모여 있지 않습니다. 회전 주기는 보안 규정에 있고, 강제 업데이트 주기는 릴리스 정책에 있고, 시그니처 갱신 주기는 운영 문서에 있습니다. 같은 전제 위에 서 있는 값들인데 한군데서 관리되지 않으니, 전제가 흔들렸을 때 한꺼번에 다시 볼 방법이 없습니다. 각각이 자기 문서 안에서는 여전히 멀쩡해 보입니다.

그럼 며칠로 바꿔야 하느냐고 물으면, 그건 제가 모릅니다. 제가 확인한 것은 한 번의 체인이고 그게 곧 새로운 값을 주지는 않습니다. 대상마다 다를 것이고, 무엇을 지키는 키인지에 따라서도 다를 겁니다. 확실한 건 예전 값을 정당화하던 근거가 이제 없다는 겁니다. 그 값이 여전히 맞을 수도 있지만, 맞다면 맞는 이유를 새로 대야 합니다.

난독화는 디컴파일러의 하류에 있습니다

여기서 방향이 한 번 뒤집힙니다.

모델은 원시 바이트를 읽지 않습니다. Ghidra 나 IDA 가 뱉어낸 C 비슷한 텍스트를 읽습니다. 그러니까 모델은 디컴파일러의 하류에 있고, 디컴파일러 출력을 망가뜨리는 기법이 모델 쪽에 더 크게 듭니다.

제어 흐름 평탄화가 그렇습니다. 원래의 분기 구조를 지우고 거대한 switch 한 덩어리와 상태 변수로 바꿔 놓으면, 디컴파일러 출력이 사람이 읽을 수 있는 모양을 잃습니다. 코드 가상화는 한 걸음 더 갑니다. 기계어 대신 자체 명령 집합을 두고 그것을 해석하는 루프를 넣으면, 디컴파일러가 보여주는 것은 원래 로직이 아니라 인터프리터입니다.

사람은 여기서 아래로 내려갑니다. 어셈블리를 직접 읽고, 디버거를 붙이고, 실행을 따라가고, 메모리를 들여다봅니다. 느리지만 길이 있습니다. 모델은 그 폴백이 약합니다. 읽을 텍스트가 무너지면 그다음 단계가 마땅치 않습니다.

이 차이가 나는 이유가 폴백의 유무라고 보고 있습니다. 사람이 리버스를 오래 한다는 것은 막혔을 때 내려갈 계단을 여럿 갖고 있다는 뜻입니다. 정적으로 안 보이면 동적으로 보고, 그것도 안 되면 입력을 바꿔가며 관찰합니다. 모델에게 주어지는 것은 대개 한 장의 텍스트이고, 그 텍스트가 무너지면 다음 계단이 마땅치 않습니다.

그래서 결론이 갈립니다. 난독화로 전문가를 막지 못하는 건 예전에도 지금도 같습니다. 대신 새로 유입된 저숙련 다수는 오히려 더 잘 걸러집니다. 난독화의 값이 0 이 된 게 아니라 겨냥하는 대상이 바뀐 것입니다.

다만 이 절은 제가 직접 확인한 범위 밖입니다. 평탄화되거나 가상화된 바이너리에 같은 체인을 돌려보지는 않았습니다. 도구가 놓인 순서를 보고 그렇게 판단하는 데까지입니다.

DRM 은 25년 전부터 이 전제로만 설계했습니다

DRM 업계는 이 전제를 사반세기 동안 깔고 일했습니다. 클라이언트는 전적으로 적대적이고, 공격자는 완전한 리버스 능력을 갖고 있고, 우리가 출하한 코드는 상대 손에 통째로 들어간다. 나머지 업계가 이제 막 들어서고 있는 방에 그쪽은 25년 전에 들어가 있었습니다.

이 전제를 받아들이는 순간 설계에서 무엇이 빠지는지가 더 중요합니다. 클라이언트가 보낸 값을 믿는 코드가 빠지고, 클라이언트 안에 판정 로직을 두는 구조가 빠지고, 이 파일은 사용자가 못 열 거라는 가정이 빠집니다. 그쪽 코드가 유난히 서버 왕복이 많은 것도 그래서입니다. 그게 편해서가 아니라 그 전제에서 남는 게 그것뿐이라서입니다.

거기서 도달한 답 몇 개가 우리 쪽에도 그대로 옮겨집니다.

출하하는 물건에 비밀을 넣지 않는 것이 하나입니다. 키가 있어야 한다면 서버가 발급하고 수명이 짧은 토큰으로 바꿉니다. 뽑히는 것 자체를 못 막으니 뽑혀도 오래 못 쓰게 만드는 방향입니다. 이해 시간이 짧아졌다는 것과 정면으로 맞는 설계이기도 합니다. 상대가 빨리 이해한들 손에 든 게 몇 분짜리라면 이해의 값이 떨어집니다.

폐기와 갱신을 설계에 내장하는 것이 그다음입니다. 언젠가 뚫린다는 전제에서 시작해서, 뚫린 개체만 끊어내는 경로를 처음부터 만들어 둡니다. 이 경로를 사고가 난 뒤에 급하게 만들면 선택지가 전체 중단밖에 안 남습니다.

숨기는 데 걸지 않고 증명하는 데 거는 것이 남은 하나입니다. 코드를 읽기 어렵게 만드는 데 예산을 쓰는 대신, 지금 이 실행 환경이 무엇인지를 증명하게 하고 그 증명을 서버가 판단하게 합니다. 읽히는 것을 전제로 하면 비밀을 지키는 쪽이 아니라 상대가 누구인지 확인하는 쪽으로 무게가 옮겨갑니다.

정직하게 덧붙이면 DRM 도 결국은 계속 뚫려 왔습니다. 다만 그쪽이 세운 목표가 애초에 불가능을 만드는 것이 아니었습니다. 뚫는 데 드는 비용과 시간을 조정하고, 뚫렸을 때 복구할 수 있게 만드는 것이 목표였습니다. 이해 시간이 짧아진 세계에서 남는 설계가 그 모양이라고 보고 있습니다.

같은 도구를 방어자가 더 좋은 입력으로, 더 자주 돌립니다

AI 로 코드를 분석할 때 결과 품질은 입력 품질을 그대로 따라갑니다. 이번에 체인을 돌리면서 그걸 반대쪽에서 봤습니다. 제가 쥔 입력이 빈약할수록 짐작으로 메운 대목이 늘었습니다. 끝까지 정체를 모른 덩어리가 남은 것도 결국 거기서 나온 결과입니다.

공격자의 입력이 무엇인지 보면 심볼이 없는 바이너리입니다. 최적화로 함수가 인라인돼 경계가 사라져 있고, 상수는 흩어져 있고, 주석은 애초에 없습니다. 방어자의 입력은 그 반대입니다. 소스가 있고 심볼이 있고 주석이 있고 커밋 이력이 있고 테스트가 있고, 필요하면 계측을 넣은 빌드를 만들어 돌려볼 수도 있습니다.

빈도도 다릅니다. 공격자는 한 번 뜯습니다. 방어자는 커밋마다 돌릴 수 있습니다.

그래서 이해 시간이 짧아진 것에 대한 대응은 난독화를 더 세게 거는 쪽이 아니라고 봅니다. 같은 도구를 내 소스에 먼저, 그리고 계속 돌리는 겁니다. 상대가 뜯어서 알아낼 것을 내가 먼저 알아내는 일에서는 내 입력이 훨씬 좋으니까요.

여기에 단서를 하나 붙이겠습니다. AI 는 정찰을 증폭하는 배율이 수정을 증폭하는 배율보다 큽니다. 취약한 곳을 찾는 일은 바로 늘어나는데, 찾은 것을 고쳐서 배포하는 일은 여전히 조직 속도에 묶여 있습니다. 릴리스 창이 있고 회귀 테스트가 있고 호환성 검토가 있고 승인 단계가 있습니다. 발견이 열 배가 되어도 배포가 열 배가 되지는 않습니다.

그래서 격차가 줄어드는 게 아니라 모양이 바뀝니다. 발견과 조치 사이에 쌓이는 목록이 길어지고, 그 목록을 어떤 순서로 처리하느냐가 예전보다 훨씬 많이 중요해집니다. 순서를 정하는 기준이 자산 가치 하나였다면 이제 그 옆에 이해 시간이 같이 놓입니다.

그 순서를 정하는 일은 결국 사람 몫으로 남습니다. 어느 것이 실제로 노출돼 있는지, 어느 것이 뚫렸을 때 되돌릴 수 있는지, 어느 것이 되돌릴 수 없는지를 아는 쪽은 우리고 상대가 아닙니다. 방어자가 가진 우위를 굳이 하나 꼽으라면 도구가 아니라 여기라고 봅니다.

제가 지금 열어보고 있는 건 난독화 설정이 아니라 회전 주기가 적혀 있는 파일들입니다. 90일이 왜 90일인지 아무도 답을 못 하는 것부터 보고 있습니다.

댓글

이 블로그의 인기 게시물

아이들은 우리 게임에 들어오지 못한다

AI 부업 강의 여섯 편이 "리스크가 제로죠"라고 말했다

AI가 썼다는 브라우저 300만 줄에서 112만 줄은 남의 코드였다