2020. 4. 30. 20:28ㆍ0x02 Reverse Engineer/0x03. Etc
1부에서는 큰 틀을 잡았으며
2부에서는 큰 틀을 잡으면서 놓칠만한 것들을 꼼꼼히 살펴봤다.
이번 3부에서는 1부 2부를 총 정리하면서, 암호화 로직을 그대로 코딩해보자.
Enumeration
Dummy Value |
Case 0 ~ A : Default Hardcode Data [Unicode] |
Case 음절 닐 |
|
Case 어절 늴 |
|
Logic |
2바이트 (한글)을 입력받는다 프로그램 속 로직을 거친다. 라운드는 3라운드로 구성되어 있다.
암호화 로직은 이해하려고 노력하지 않았다. 개발자 마음이다.
1라운드 : 입력되어진 2바이트를 이용하여 암호화를 진행 한다. 종종 하드 코딩된 값을 연산에 사용하기도 한다. 모든 음절을 처리할 때까지 반복 수행한다.
2라운드 : 3라운드로 진입하기 위한 임시 변수를 만든다. 해당 임시 변수로 인해 케이스 별 출력문이 동작된다.
3라운드 : 2라운드에서 생성된 변수 값에 의해 데이터를 출력한다. |
한글 한 글자만 입력하여 원본과 일치성을 대조해보았다.
컴파일 환경은 Windows 64bit Debugger 모드이며, 사용된 IDE는 Visual Studio 2017 Community이다.
코드 형태는 대략적으로 현 바이너리에 존재하는 OPCODE를 그대로 본 땄고, 일부 최적화하였다.
// rev_lyl.cpp: 콘솔 응용 프로그램의 진입점을 정의합니다.
//
#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#define ERROR (0)
#define STDIN (0)
#define STDOUT (1)
#define STDERR (2)
#define _64BIT_INST (0xFFFFFFFF00000000)
#define INTRO(x) { printf("The current data is %d bytes\n",x); }
#define INFO(x) { printf("There are (%d) bytes of temporary data available for encryption\n",x);}
#define PEDUMP(x) { printf("Dump => %s\n",argv[0]); }
#define S system("pause")
typedef unsigned long long ull;
const wchar_t *dummy0 = L"릴"; // B9B4
const wchar_t *dummy1 = L"릘"; // B998
const wchar_t *dummy2 = L"륄"; // B944
const wchar_t *dummy3 = L"릴릘"; // B9B4 B998
const wchar_t *dummy4 = L"릘릘"; // B998 B998 릘릘
const wchar_t *dummy5 = L"릻륄"; // B9BB B944
const wchar_t *dummy6 = L"릴륄"; // B9B4 B944
const wchar_t *dummy7 = L"릘륄"; // B998 B944
const wchar_t *dummy8 = L"릘릴륄"; // %uB998%uB9B4%uB944
const wchar_t *dummy9 = L"릻릘륄"; // %uB998%uB998%uB944
const wchar_t *dummyA = L"릴릘륄";
const wchar_t *data = L"연"; // 휴
int main(int argc, char *argv[])
{
ull tmp = 0x5400; // 0x5400은 최적화 결과
//===========================
ull RAX = 0x24924925; // 반복 시작 하는 구문
ull RBX = 0x00;
ull RCX = 0x00;
ull RDI = 0x00;
ull RSI = 0x00;
ull RDX = 0x00;
ull RBP = 0x00;
//===========================
ull R8 = 0x00;
ull R9 = 0x00;
ull R12 = 0x00;
ull R14 = 0x19;
ull R15 = 0x00;
ull FLAG = 0x00;
// 이 값이 유동적임 추후 수정 필요
const char *dump =
"\x07\x00\x00\x00\x17\x00\x00\x00\x0A\x00\x00\x00\x0A\x00\x00\x00"
"\x01\x00\x00\x00\x8D\x02\x00\x00\x00\x00\xB8\x68\x8D\x02\x00\x00"
"\xE0\x38\xB9\x68\x8D\x02\x00\x00\x6A\x00\x00\x40\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x54\x00\x00\x00\x00\x00\x00\xF0\x19\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\xF4\xED\x90\xFE\xC8\x00\x00\x00"
"\x01\x00\x00\x00\x00\x00\x00\x00\x33\x18\xB5\x26\xF7\x7F\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x50\xE9\x90\xFE\xC8\x00\x00\x00"
"\xC2\xF5\x90\xFE\xC8\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00"
"\x02\x00\x00\x00\x03\x00\x00\x00\x05\x00\x00\x00\x07\x00\x00\x00"
"\x0B\x00\x00\x00\x0D\x00\x00\x00\x11\x00\x00\x00\x13\x00\x00\x00"
"\x17\x00\x00\x00\x1D\x00\x00\x00\x1F\x00\x00\x00\x25\x00\x00\x00"
"\x29\x00\x00\x00\x2B\x00\x00\x00\x2F\x00\x00\x00\x35\x00\x00\x00"
"\x3B\x00\x00\x00\x3D\x00\x00\x00\x43\x00\x00\x00\x47\x00\x00\x00"
"\x49\x00\x00\x00\x4F\x00\x00\x00\x53\x00\x00\x00\x59\x00\x00\x00"
"\x61\x00\x00\x00\x65\x00\x00\x00\x67\x00\x00\x00\x6B\x00\x00\x00"
"\x6D\x00\x00\x00\x71\x00\x00\x00\x7F\x00\x00\x00\x83\x00\x00\x00"
"\x89\x00\x00\x00\x8B\x00\x00\x00\x95\x00\x00\x00\x97\x00\x00\x00"
"\x9D\x00\x00\x00\xA3\x00\x00\x00\xA7\x00\x00\x00\xAD\x00\x00\x00"
"\xB3\x00\x00\x00\xB5\x00\x00\x00\xBF\x00\x00\x00\xC1\x00\x00\x00"
"\xC5\x00\x00\x00\xC7\x00\x00\x00\xD3\x00\x00\x00\xDF\x00\x00\x00"
"\xE3\x00\x00\x00\xE5\x00\x00\x00\xE9\x00\x00\x00\xEF\x00\x00\x00"
"\xF1\x00\x00\x00\xFB\x00\x00\x00\x01\x01\x00\x00\x07\x01\x00\x00"
"\x0D\x01\x00\x00\x0F\x01\x00\x00\x15\x01\x00\x00\x19\x01\x00\x00"
"\x1B\x01\x00\x00\x25\x01\x00\x00\x33\x01\x00\x00\x37\x01\x00\x00"
"\x39\x01\x00\x00\x3D\x01\x00\x00\x4B\x01\x00\x00\x51\x01\x00\x00"
"\x5B\x01\x00\x00\x5D\x01\x00\x00\x61\x01\x00\x00\x67\x01\x00\x00"
"\x6F\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
// 연 일 때 첫 출력 case : 1
// 휴 일 때 첫 출력 case : 3
//wprintf(L"%d\n", wcslen(data));
R15 = wcslen(data);
INTRO(R15);
wprintf(L"%s\n", data);
S;
/*
wprintf(L"%X\n", data[0]);
wprintf(L"%X\n", data[1]);
*/
//wprintf(L"%s\n", dummy0);
wprintf(L"[+] REAL UNICODE VALUE %X\n", data[0]);
if (!(wchar_t(data[0]) >= 0x21 && wchar_t(data[0]) <= 0x7E)) // 아스키 거부 의사
{
// 최적화
/*
00007FF | 0F B7 | movzx eax,word ptr ds:[rsi] |
00007FF | 66 83 | cmp ax,20 | 20:' '
00007FF | 75 07 | jne lyl language!.7FF7DE0716A5 |
00007FF | 33 C9 | xor ecx,ecx |
00007FF | E9 6B | jmp <lyl language!.round_1_escape> |
00007FF | 66 0F | movdqa xmm0,xmmword ptr ds:[7FF7DE075760 | 2
00007FF | 48 8D | lea rcx,qword ptr ss:[rbp+44] |
00007FF | 66 0F | movdqa xmm1,xmmword ptr ds:[7FF7DE075860 | B
00007FF | 66 41 | add ax,r13w | init
*/
__Routine_R1:
tmp = (tmp + data[0]) & 0xFFFF;
printf("[+] After optimization RAX =%lX\n", tmp); // 검산완료
printf("AX = %llX\n", tmp & 0xFF);
if ((tmp) == 0)
{
}
//////////////////////////////////////////////////////////////
// ELSE
R8 = 0x454;
RAX = 0x24924925;
R9 = RDI;
RDI = tmp;
RDX = RAX * tmp;
printf("RDX = %llX\n", RDX); // 검산 완료
//S;
RAX = RDX & 0xFFFFFFFF;
RDX = (RDX & _64BIT_INST) >> 32;
// 00007FF | F7 E7 | mul edi |
printf("RAX = %lX\n", RAX); //92492FB0
printf("RDX = %llX\n", RDX); // 3B4
printf("RDI = %llX\n", RDI); // 19F0
R9 = RDI;
printf("R9 = %llX\n", R9); // 19F0
//S;
R9 -= RDX;
printf("R9 = %llX\n", R9); //
R9 = (((tmp - RDX) >> 1) + RDX) >> 4;
printf("R9 = %llX\n", R9); // ED
RAX = R9 * 0x1C;
printf("%RAX = %llX\n", RAX);
R8 = tmp - RAX;
printf("R8 = %llX\n", R8); // 0x4
// RAX Update
RAX = 0xDEE95C4D;
printf("RAX = %llX\n", RAX); //
RDI = tmp;
RDX = (RAX * tmp & _64BIT_INST) >> 32;
RAX = (RAX * tmp) & 0xFFFFFFFF;
printf("RAX = %llX\n", RAX); // 0xC4CA0D30
printf("RDX = %llX\n", RDX); // 0x1695
printf("RDI = %llX\n", RDI); // 0x19F0
// RAX Update
RAX = 0x30C30C31;
RDX = RDX >> 9;
printf("RDX = %llX\n", RDX); // 0xB
RCX = RDX;
RAX *= R9; // imul r9d
RDX = (RAX & _64BIT_INST) >> 32;
RAX &= 0xFFFFFFFF;
printf("RAX = %llX\n", RAX); // 0x2492495D
printf("RDX = %llX\n", RDX); // 0x2D
// RCX 0x25로 체인지
// 00007FF | 8B 4C | mov ecx,dword ptr ss:[rsp+rcx*4+20] | 0x25
RCX = 0x25;
RDX >>= 2; // sar
printf("RCX = %llX\n", RCX); // 0x25
printf("RDX = %llX\n", RDX); // 0xB
RAX <<= 0x1F; // shr eax,0x1F 편의상 0X00
//RAX = 0x00;
RDX += RAX;
//00007FF | 6B C2 | imul eax, edx, 15 |
RAX = RDX * 0x15;
RAX &= 0xFF;
printf("RAX = %llX\n", RAX); // 0xE7
R9 -= RAX;
R9 &= 0xFF;
printf("R9 =%llX\n", R9); // 0x
RAX = R9;
//00007FF | 0F AF | imul ecx,dword ptr ss:[rsp+rax*4+6C] | 0x65
RCX *= 0x65;
printf("RCX = %llX\n", RCX); // 0xE99
RAX = R8;
//00007FF | 0F AF | imul ecx,dword ptr ss:[rbp+rax*4-40] | 0xC5
RCX *= 0xC5;
printf("%llX\n", RCX); // 0xB3BBD
printf("%llX\n", RAX); // 0x4
R14 = RCX;
printf("RCX = %llX\n", RCX); // 0xB3BBD
R14 = RCX;
//RSI += 2; // 흠 고민
R15 -= 1;
INFO(R15);
if (R15 != 0) // 연산 한번 더
{
puts("**LOOP**");
goto __Routine_R1;
}
// Round 2
puts("ROUND 2");
RDX = RBX;
printf("RAX = %llX\n", RAX);
printf("RDX = %llX\n", RDX);
//00007FF | 48 8D | lea rcx,qword ptr ss:[rbp+4A0] |
// RCX 값 변하지 않음
puts("Processing ROUND 2");
RDX &= RDX;
if (RDX == ERROR)
{
}
R12 = RDX;
R15 = RCX;
R14 ^= R14;
PEDUMP(argv[0]);
__Routine_R3:
RCX = R15;
R15 &= R15; // using loop
printf("RCX = %llX\n", RCX); // B3BBD
S;
if (RCX == 0)
{
puts("늴"); // 0x7FF726B555D8 (%uB2B4)
}
RDI ^= RDI;
R8 ^= R8;
RBP ^= RBP;
__Routine_R2:
RAX = 0x4BDA12F7;
printf("RAX = %llX\n", RAX);
R8++; // lea r8, qword ptr ds:[r8+1]
printf("R8 = %llX\n", R8);
printf("R9 = %llX\n", R9);
RDX = ((RAX * RCX)& _64BIT_INST) >> 32;
RAX = (RAX * RCX) & 0xFFFFFFFF;
printf("RDX = %llX\n", RDX); // 0x35412
printf("RAX = %llX\n", RAX); // 0x12FBED5B
RDI++;
RDX >>= 3;
RAX >>= 0x1F; // (shr eax, 0x1F)
printf("RDX = %llX\n", RDX); // 0x6A82
// 이 부분 없어도 됨 noisy 코드
printf("RAX = %llX\n", RAX); // 0x354100000000(origin)
RAX += RDX;
RAX = RDX * 0x1B;
printf("RAX = %llX\n", RAX); // 0xB3BB6
RCX -= RAX;
printf("RCX = %llX\n", RCX); // 0x7
RCX = RDX;
RDX &= RDX; // tet edx, edx
printf("** RDX = %llX\n", RDX);
if (RDX != 0)
{
printf("LOOP\n");
goto __Routine_R2;
}
// escape loop
// mov dword ptr ds:[r15], edx
R15 = RDX; // 추가
RDI &= RDI;
if (RDI == 0)
{
if (R14 == R12)
{
R14++;
R15 += 4;
}
else
{
puts("닐");
R14++;
R15 += 4;
if (R14 == R12)
{
goto __Routine_R3;
}
// need to check
/*else
{
}*/
}
}
else
{
// lea rsi, qword ptr ss :[rsp+20]
RSI = dump[R8 * 4] & 0xFF;
RAX = RBP;
RSI -= 4;
printf("##############dump RSI == %llx\n", RSI);
printf("##############dump R8 == %llx\n", R8);
}
printf("Loop Escape RCX = %d\n", RCX);
printf("RDI = %llX\n", RDI);
R15 ^= R15;
RBP++;
RDI--; // 한단어당 쓰이는 루프 카운터
RBX = RDI;
RAX &= RAX;
if (RAX == 0)
{
goto _escape_char;
}
else
{
// lea rdx, [0x00007FF726B555DC]
puts("ㄹ"); // %u3139 ('ㄹ')
}
printf("RBP = %llX\n", RBP);
printf("LOOP COUNT RBX = %llX\n", RBX);
// 한번 더
_escape_char:
// EBX == RSI
// 좀있다 적어야함
printf("****RSI = %llX\n", RSI);
RAX = 0x2AAAAAAB;
RAX *= RBX;
printf("RAX = %llX\n", RAX);
RDX >>= 1;
printf("RDX = %llX\n", RDX);
RAX = RDX;
RBX -= RAX;
printf("RBX = %llX\n", RBX);
// 여기 조금 수정이 필요함
wprintf(L"Before = %s\n", data[0]);
puts("Converting....");
// 흠 과연
if (RBX < 0xB)
{
//RAX = RCX;
switch (RBX)
{
case 0:
wprintf(L"%s\n", dummy0);
break;
case 1:
wprintf(L"%s\n", dummy1);
break;
case 2:
wprintf(L"%s\n", dummy2);
break;
case 3:
wprintf(L"%s\n", dummy3);
break;
case 4:
wprintf(L"%s\n", dummy4);
break;
case 5:
wprintf(L"%s\n", dummy5);
break;
case 6:
wprintf(L"%s\n", dummy6);
break;
case 7:
wprintf(L"%s\n", dummy7);
break;
case 8:
wprintf(L"%s\n", dummy8);
break;
case 9:
wprintf(L"%s\n", dummy9);
break;
case 0xA:
wprintf(L"%s\n", dummyA);
break;
}
}
}
system("pause");
}
4부 에서는 Dynamic 한 환경에서 적용될 수 있게 커스터마이징을 해보자.
'0x02 Reverse Engineer > 0x03. Etc' 카테고리의 다른 글
Credential leak from Chrome browser [MacOS Version] (0) | 2021.06.27 |
---|---|
[HTB] Bombs Landed (0) | 2021.03.24 |
lyl -2부 (4) | 2020.04.30 |
lyl -1부 (9) | 2020.04.30 |
Rootme Prob . ELF x86 - KeygenMe (0) | 2020.02.06 |