Payload encryption
난독화와 마찬가지로 페이로드를 저장할 때 백신이 알아차리지 못하게 하는 방법이다.
- 장점: 시그니처 기반 탐지를 우회할 수 있다
단, 행동으로 판단하는 휴리스틱 탐지에서는 소용없음... - 단점: 암호화된 데이터가 많을수록 엔트로피가 증가한다
따라서 엔트로피도 고려하면 걸릴수도 있다
XOR 암호화
가장 단순하고 가벼운 암호 기법
문자열 같은거는 걍 이런걸로 숨기자
암호 해독도 해당 키로 다시 XOR해주면 되는 만큼 쉽고 간편하다
원문 -XOR-> 암호문 -XOR-> 원문
1byte key인 bKey를 이용해 shellcode를 암호화 하기
Void XOR(IN PBYTE pShellcode, IN SIZE_T size, IN BYTE bKey){
for (int i = 0 ; i < size ; i++){
pShellcode[i] = pShellcode[i] ^ bKey;
}
}
-> bKey가 너무 작아 brute force로 금방 뚫릴듯...
임의 바이트의 Key를 입력받아 반복 암호화 하기
Void adv_XOR(IN PBYTE pShellcode, IN SIZE_T shellcodeSize, IN PBYTE key, IN SIZE_T keySize){
for (int i = 0, j = 0 ; i < shellcodeSize ; i++, j++){
if (j >= keySize) {
j = 0;
}
pShellcode[i] = pShellcode[i] ^ key[j];
}
}
RC4 암호화
빠르고 효율적인 스트림 암호의 일종
key와 256바이트 state(S-box)를 통해 평문과 동일한 길이의 key stream을 만들어 XOR로 암호화 함.
즉, keystream만 알면 XOR의 성질인
원문 -XOR-> 암호문 -XOR-> 원문
을 이용해 복호화가 가능하다!
// RC4 구조체 정의
typedef struct {
unsigned char state[256]; // S-box
int x, y; // <- 뽑은 위치 기억용
} RC4_CONTEXT;
S-box를 key를 이용해 뒤섞기
// 1. 키 초기화 (KSA)
void rc4_init(RC4_CONTEXT* ctx, const unsigned char* key, int key_len) {
for (int i = 0; i < 256; i++) ctx->state[i] = i;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + ctx->state[i] + key[i % key_len]) % 256;
// Swap
unsigned char temp = ctx->state[i];
ctx->state[i] = ctx->state[j];
ctx->state[j] = temp;
}
ctx->x = 0;
ctx->y = 0;
}
j = (j + ctx->state[i] + key[i % key_len]) % 256로 새로운 위치(j)를 계산하고
이걸 i번째 값과 Swap한다.
그리고 이걸 256번 반복하면 S-box 내용이 무작위가 된다.
키 스트림 생성
// 2. 키 스트림 생성 (PRGA)
void generate_keystream(RC4_CONTEXT* ctx, unsigned char* stream, int len) {
int x = ctx->x;
int y = ctx->y;
for (int i = 0; i < len; i++) {
x = (x + 1) % 256;
y = (y + ctx->state[x]) % 256;
// Swap
unsigned char temp = ctx->state[x];
ctx->state[x] = ctx->state[y];
ctx->state[y] = temp;
// 무작위 바이트 하나 추출해서 stream에 저장
int xor_index = (ctx->state[x] + ctx->state[y]) % 256;
stream[i] = ctx->state[xor_index];
}
ctx->x = x;
ctx->y = y;
}
S-box를 알기 어렵게 하기 위해 x, y로 다시 뒤섞고 무작위 바이트를 추출해 keystream에 저장한다
이 과정을 평문만큼 반복하면 keystream이 완성된다!
암호화
// 3. 암호화
void xor_cipher(unsigned char* data, unsigned char* keystream, int len) {
for (int i = 0; i < len; i++) {
data[i] ^= keystream[i]; // 원본과 키를 겹침
}
}
keystream과 평문을 XOR하여 암호화 한다.
int main() {
unsigned char key[] = "bigsilver";
unsigned char data[] = "you are cooked";
int data_len = sizeof(data);
RC4_CONTEXT ctx;
unsigned char* keystream = (unsigned char*)malloc(data_len);
printf("plain text: %s\n", data);
// --- 암호화 단계 ---
rc4_init(&ctx, key, sizeof(key) - 1);
rc4_generate_keystream(&ctx, keystream, data_len);
xor_cipher(data, keystream, data_len);
printf("encryted: %.*s\n", data_len, data);
// --- 복호화 단계 ---
rc4_init(&ctx, key, sizeof(key) - 1);
rc4_generate_keystream(&ctx, keystream, data_len);
xor_cipher(data, keystream, data_len);
printf("decrypted: %s\n", data);
free(keystream);
return 0;
}

AES 암호화
블록 암호희 일종으로 항상 128비트(16바이트)의 입력을 필요로 한다
따라서 패딩을 넣기도 한다
(암호학 둘러보기 6 참고)
#include <windows.h>
#include <bcrypt.h>
#include <stdio.h>
#pragma comment(lib, "bcrypt.lib")
// NT_SUCCESS 매크로 (성공 여부 확인용)
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
BOOL Aes256Decrypt(
PBYTE key, DWORD keyLen,
PBYTE iv, DWORD ivLen,
PBYTE data, DWORD dataLen
) {
BCRYPT_ALG_HANDLE hAlg = NULL;
BCRYPT_KEY_HANDLE hKey = NULL;
DWORD cbKeyObject = 0, cbResult = 0;
PBYTE pbKeyObject = NULL;
BOOL success = FALSE;
// 1. 알고리즘 핸들 열기 (AES)
if (!NT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, NULL, 0))) goto Cleanup;
// 2. 블록 체이닝 모드 설정 (CBC)
if (!NT_SUCCESS(BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0))) goto Cleanup;
// 3. 키 객체를 위한 메모리 할당
if (!NT_SUCCESS(BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&cbKeyObject, sizeof(DWORD), &cbResult, 0))) goto Cleanup;
pbKeyObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbKeyObject);
// 4. 실제 키 생성
if (!NT_SUCCESS(BCryptGenerateSymmetricKey(hAlg, &hKey, pbKeyObject, cbKeyObject, key, keyLen, 0))) goto Cleanup;
// 5. 복호화 수행 (In-place: data 배열의 내용이 직접 바뀜)
if (!NT_SUCCESS(BCryptDecrypt(hKey, data, dataLen, NULL, iv, ivLen, data, dataLen, &cbResult, 0))) goto Cleanup;
success = TRUE;
Cleanup:
if (hKey) BCryptDestroyKey(hKey);
if (hAlg) BCryptCloseAlgorithmProvider(hAlg, 0);
if (pbKeyObject) HeapFree(GetProcessHeap(), 0, pbKeyObject);
return success;
}
int main() {
// AES-256 키 (32바이트)
unsigned char key[32] = "mysecretkey12345678901234567890";
// 초기화 벡터 (16바이트)
unsigned char iv[16] = "initialvector123";
// 데이터 (16바이트 배수여야 함)
unsigned char data[16] = {
0x5c, 0x3d, 0x4e, 0x...
};
printf("복호화 시도 중...\n");
if (Aes256Decrypt(key, sizeof(key), iv, sizeof(iv), data, sizeof(data))) {
printf("복호화 결과: %.*s\n", 16, data);
} else {
printf("복호화 실패!\n");
}
return 0;
}
tiny-AES 기반
소스코드만으로 의존성 없이 AES를 구현
단, 패딩 기능이 없고
백신들은 이미 tiny-AES를 사용한 것을 눈치챌 수 있다
#include <stdio.h>
#include <string.h>
#include "aes.h" // tiny-aes 헤더 포함
void TinyAesDecrypt(
unsigned char* key, // 32바이트 (AES-256 기준)
unsigned char* iv, // 16바이트
unsigned char* data, // 복호화할 데이터 (16의 배수여야 함)
int data_len
) {
struct AES_ctx ctx;
// 1. AES 컨텍스트 초기화 (키와 IV 설정)
AES_init_ctx_iv(&ctx, key, iv);
// 2. CBC 모드로 복호화 수행 (In-place 연산)
// 데이터 버퍼의 내용이 직접 복호화된 내용으로 바뀝니다.
AES_CBC_decrypt_buffer(&ctx, data, data_len);
}
int main() {
// AES-256 키 (32바이트)
unsigned char key[32] = {
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
};
// 초기화 벡터 (16바이트)
unsigned char iv[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
// 암호화된 데이터 (16바이트 배수)
// "you are cooked!!" (15자) + NULL(1자) = 16바이트 예시
unsigned char data[16] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
};
printf("복호화 전 (깨진 데이터)\n");
// 복호화 실행
TinyAesDecrypt(key, iv, data, 16);
// 가변 길이 출력 활용
printf("복호화 완료: %.*s\n", 16, data);
return 0;
}'악성코드와 백신 > 악성코드 개발일지' 카테고리의 다른 글
| [악성코드 개발](11)[obfuscated-shellcode-excution] (0) | 2026.03.16 |
|---|---|
| [악성코드 개발](10)[local-payload-injection] (0) | 2026.03.12 |
| [악성코드 개발](8)[페이로드 난독화] (0) | 2026.03.10 |
| [악성코드 개발](7)[metasploit] (0) | 2026.03.09 |
| [악성코드 개발](6)[페이로드 저장] (0) | 2026.03.06 |