x32Dbg를 활용한 Amadey Bot 악성코드 심층 분석 - Part1_[ PE Section 언패킹 및 payload 추출 ]
주요 내용런타임 메모리 언패킹Amadey의 .text 섹션은 압축·암호화된 채 유포된다. 실행 시 Stub이 VirtualAlloc으로 새 영역을 확보하고 WriteProcessMemory로 복호화된 코드를 올린 뒤, 다시 VirtualProtect로
aoi-thehackers-den.tistory.com
개요
Amadey Bot은 정보 수집 및 페이로드 다운로드 기능을 수행하는 경량형 트로이목마로, 다양한 사이버 범죄 캠페인에서 드롭퍼 혹은 로더로 활용되고 있다. 본 분석은 해당 악성코드의 기능을 역공학 관점에서 심층적으로 분석하기 위해, x32Dbg를 주요 도구로 채택하여 실행 흐름, 난독화 해제 과정, 내부 구조를 실시간 디버깅 기반으로 추적하였다.
본 시리즈의 Part 1에서는 Amadey Bot의 초기 실행 과정, 주요 기능 호출 구조, 그리고 난독화된 문자열의 복호화 루틴에 대해 기술하며, 특히 복호화 방식과 C2 통신 정보의 은닉 방식에 주목하였다.
주요 내용
Amadey Bot은 실행 직후 사용자 환경에 대한 정보 수집을 수행하고, 이후 명령 제어(C2) 서버로의 통신을 통해 외부 명령을 수신하는 구조를 가진다. 주요 행위는 다음과 같다.
- 운영체제, 사용자 계정, 권한 수준 등 환경 정보 수집
- 감염 호스트를 식별하기 위한 고유 식별자(UUID) 생성
- 하드코딩된 주소 기반의 C2 서버와의 초기 연결 시도
이러한 기능들은 대부분 난독화되어 있으며, 주요 API 호출 및 데이터 흐름은 x32Dbg를 통해 레지스터 상태 및 스택 프레임 변화를 관찰함으로써 식별하였다. 해당 분석은 정적 분석과 동적 분석을 병행하여 기능 단위로 분리하고, 주요 함수의 목적 및 호출 시점을 구조적으로 정리하였다.
주요 내용 2 (문자열 난독화 방식 및 복호화 알고리즘 분석)
Amadey Bot은 주요 문자열(C2 주소, 명령 키워드 등)을 식별하기 어렵도록 Base64 인코딩 및 XOR 기반의 커스텀 복호화 방식을 병행하여 적용하고 있다. 본 분석에서는 다음과 같은 절차로 해당 난독화 기법을 해제하였다.
- 디코딩 루틴의 호출 흐름을 추적하여, Base64 디코더 함수와 XOR 연산 키 상수를 식별함
- 레지스터 및 메모리 내용을 추적함으로써 입력값과 출력값 간의 상관관계를 정립
- 복호화 대상 문자열을 획득한 후, 원문 데이터를 추출하고 해당 문자열이 사용되는 API 또는 C2 연결 시점을 분석함
예를 들어, "Du2wPXynS9hliry="와 같은 문자열은 Amadey만의 특유한 복호화 과정을 거쳐 C2 주소로 활용되며, 해당 문자열은 초기 통신 설정 시점에서 동적으로 사용되는 것이 확인되었다.
분석 flow
- x32Dbg의 Byte Patch를 통해 파일경로검증, Mutex 검증 등 분석 방지 기능 무력화
- x32Dbg를 사용하여 문자열 복호화 기능 분석
- 복호화된 문자열을 사용하여 악성 행위 식별
분석 내용
x32Dbg를 사용하여 문자열 복호화 기능 분석
이전 분석 블로그 글에서는 packing이 해제된 unpacked payload를 추출했었다. 아래 첨부된 사진은 unpacked payload와 원본 분석 대상 샘플의 문자열만 DIE( Detect-It-Easy )의 strings viewer 기능을 통해 확인한 결과이다. 좌측 압축 해제된 payload (unpacked.bin)의 문자열에서는 base64로 보이는 문자열들이 많이 존재하며 그 양도 많다. 하지만 우측의 원본 분석 대상 파일 (si684017.exe) 파일에서는 base64와 유사한 형태의 문자열이 보이지 않으며, 추출된 문자열의 양도 적다.
packing이 해제된 파일에서 추출한 base64로 인코딩된 문자열들을 cyberchef에서 복호화를 시도했다. 하지만 복호화에 실패하였고, 몇몇 문자열의 경우 base64의 특징인 '==' 이 아닌 '='만 존재하거나, 중간에 존재하는 경우도 있었다. 아마도 다른 추가적인 암호화 알고리즘을 결합하여 인코딩된 문자열이라고 판단되었다.
약간의 자료 조사 후, Amadey는 자체적인 decoding 함수를 내포하고 있다는 정보를 찾았다. 따라서 분석 방법의 방향을 수정했다.
- Amadey 전용 복호화 프로그램을 개발하여 평문 문자열 획득
- x32Dbg, Ghidra를 동기화 한 뒤, Amadey의 복호화 함수의 결과값만 취득하여 평문 문자열 획득
위의 2가지 분석 계획을 수립하고, 하나씩 도전해보았다.
먼저 1번의 트라이 경우, 관련 사전 조사 과정에서 2023년 6월 13일에 Sonic Wall에서 작성한 분석 보고서를 찾았다.해당 보고서는 IoC를 공유하는 내용을 중심으로 작성되어 있었으며, 그 과정에 Amadey의 복호화 함수를 외부 스크립트로 활용할 수 있도록 pseudocode(의사코드)를 공유했다.
따라서, 해당 의사코드를 분석하여 Python 코드로 작성해본 뒤, 이를 여러번 검증하며 시도했다. 하지만 아래 첨부된 사진과 같이 그 결과는 실패였다. 숫자로만 이루어진 값은 제대로 작동했지만, 정작 제일 중요한 문자열이 포함된(숫자와 문자열이 동시에 포함된 것도 해당) 값들은 기대하지 못한 결과값을 도출해냈다.
따라서 2번째 계획에 도입했다.
분명히, 이 악성코드도 실행되고 나서 저렇게 이상하게 하드코딩된 문자열들을 분명히 복호화 하여 사용할 것이다. 따라서, x32Dbg로 샘플을 로드하고, Amadey 가 decoding 함수를 호출하고, 반환값을 리턴하는 순간에 breakpoint를 설정한다면 손쉽게 복호화된 평문 문자열을 구할 수 있을 것 이라고 생각되었다.
따라서 Ghidra에서 복호화를 담당하는 함수를 찾아보기로 했다. Ghidra에서 string 값을 추출하고, 해당 문자열을 참조하는 address offset을 찾아 추적할 수 있다. 본 분석 과정에서는 필터에 "=="값을 적용하여 임의의 문자열을 대상으로 진행함
Ghidra의 String viewer 테이블에서 임의의 base64와 유사한 모양을 가진 라인을 더블클릭하여 해당 문자열이 정의된 offset 으로 이동했다. Ghidra의 어셈블러 탭에서 해당 문자열을 정의한 변수의 우클릭 -> References -> Show References To Address 를 클릭하여 해당 변수를 참조하는 함수의 offset 주소값을 얻을 수 있다. 이 주소 값을 더블 클릭하면 해당 함수의 디스어셈블된 코드라인을 확인할 수 있다. 본 과정에서의 offset 주소는 0041362이다.
문자열을 정의한 변수가 참조한 함수는 정의되지 않은 함수로 출력되었다. 함수의 첫번째 주소를 클릭하고 F를 누르면 현재 수소에서 함수를 정의하여 디컴파일러의 출력이 훨씬 더 깔끔하게 보이게 된다. 본 과정에서 정의된 함수명은 FUN_00401362로, offset 주소값은 00401362이다.
FUN_00401362 함수를 분석하는 과정에서 추가적인 FUN_00414550 함수를 발견했다. 이 함수의 offset 주소 값은 00414550으로, 디컴파일러에 출력된 앞뒤 코드라인을 기반으로 앞에서 추출된 base64와 유사한 형태로 난독화된 문자열들을 처리하는 복호화 함수로 추정되어, x32Dbg에서 이를 breakpoint 설정하여 확인하려고 했다.
FUN_00414550 함수에서 교차 참조 수를 확인해보았다. 교차 참조 수가 많다는 것은 이 함수가 인코딩된 문자열 이상으로 디코딩 역활을 담당하고 있다는 것을 의미한다. 따라서 모든 문자열 관련 복호화 함수에 동일한 이 FUN_00414550 함수를 사용하는 경우 디버거와 중단점을 활용하여 접근하는 것이 더 좋은 접근 방식일 수 있다고 생각했다.
x32Dbg를 사용하여 이 문자열 복호화 기능을 내포한 것으로 의심되는 함수를 분석해보기로 결정했다. 이를 위해서는 문자열 해독을 담당하는 것으로 의심되는 함수의 offset 주소 값을 알아내고, 중단점을 설정해야 한다.
Ghidra를 통해 해당 함수의 존재를 확인했으며, Ghidra에서 발견한 내용의 주소를 x32Dbg에서 사용하려고 하면 offset 주소 값이 일치하지 않을 수 있다. 따라서, x32Dbg에서 동일한 기능을 수행하는 주소값을 찾아내기 위해서는 x32Dbg와 Ghidra의 주소값을 동기화 할 필요가 있다.
x32Dbg의 Image Base address를 찾아, 이를 Ghidra에서도 동일하게 적용하여 동기화 해야한다. x32Dbg에서 Base Image address는 Memory Map에서 찾을 수 있다. 본 분석 과정에서는 002C0000이지만, 디버거를 재시작하는 경우 이 주소값은 달라질 수 있다.
Ghidra에서는 상단의 PCB 모양 버튼을 누른 뒤, 팝업된 Memory Map 탭에서 우측 상단의 Home 버튼을 눌러 Ghidra의 Base Image address를 수정할 수 있다. x32Dbg에서 구한 값을 Ghidra에서도 동일하게 입력하여 OK 버튼을 눌러 적용
다시 x32Dbg로 돌아와 직전에 의심스러운 함수 FUN_00414550 의 offset에 BP를 설정한다.
BP를 설정한 함수 FUN_00414550에서 반환된 값은 아래 그림과 같이 EAX 버퍼에 저장된다. 확인 결과, 'jhl#A' 와 같이 알 수 없는 내용이 반환되었으며, 그다지 흥미로운 정보는 아니었다.
이 후에 반환되는 값을 확인해가면서 결국 이 함수가 최종 문자열을 decode 하는데 사용되지 않는다는 것을 깨달았다. 오히려 이 함수는 이전에 발견한 난독화된 base64 문자열의 복사본을 얻기 위한 것임을 알았다.이 시점에서 의심스러운 함수를 분석하는 실험을 했지만, 이 과정을 통해 신뢰성 있게 C2 정보를 추적하거나 얻을 수 없었다.
다만, Ghidra의 Entry 즉 main() 함수를 조사하던 중, Amadey 주요 악성 행위에 대한 2가지 흥미로운 함수를 발견했다. 이 함수는 main() 함수의 002d602a에 위치하며, Ghidra의 자동 디컴파일러에서 '_scrt_common_main_seh()'로 명명되어 있다. 이 함수 내부에는 FUN_ 002d4040 함수가 호출되며, 주소는 002d4040 이다.
아래는 Ghidra 디컴파일러에서 확인한 FUN_ 002d4040 함수의 내부이다. 해당 함수 내 FUN_002c9870, FUN_002c7b70 2개의 함수는 각 문자열 복호화 및 초기 설치 기능, 그리고 Mutex 생성 및 확인 기능을 가지고 있다.
FUN_002c9870 함수는 002c9870에 위치해 있다. 아래 첨부된 그림은 FUN_002c9870에 정의된 특정 라인에 초점을 맞추고 있다. 해당 offset 주소값은 002c98b2이며, 위에서 언급한 FUN_00414550 함수와 관련이 있는 것으로 추측된다.
FUN_00414550 함수는 .rsrc 섹션에 저장된 난독화된 base64 문자열을 얻는데 사용된다. FUN_00414550함수는 직접 복호화를 수행하지는 않지만, FUN_002c9870를 통해 반환된 문자열(난독화된 base64 문자열)은 FUN_002c98b2 함수를 통과할 때 일반 평문 데이터로 복호화되는 것으로 확인되었다.
따라서 FUN_002c98b2 함수의 반환값을 제대로 확인하기 위해서 x32Dbg에 002c98b2 주소값에 BP를 설정했다.
FUN_002c98b2가 호출되었고, 중단점이 x32Dbg에 고정되었으며, 난독화된 base64 문자열이 EAX 레지스터 버퍼에 입력된 것이 확인되었다.
Step Over (F8)을 눌러 진행하니 복호화된 값이 EAX 레지스터 버퍼에 저장되어 있는 것을 확인할 수 있었다.
Execute until Return(Ctrl+F9)을 실행하여 모든 악성 작업을 실행할 수 있도록 한다. 샘플은 다음 경로에 파일을 하나 드롭했다. "C:\\Users\\{USER_NAME}\\AppData\\Loca\\Temp\59f021478\\oneetx.exe"
분석 대상 샘플이 새로운 파일을 드롭했기 때문에, 새 파일을 x32Dbg에 로드 한 후, Ghidra의 Base Image address를 x32Dbg와 동기화 했다.
그리고, 분석을 진행하던 중, 새로운 사실을 알아냈다. Amadey가 드롭한 oneetx.exe 파일의 경우, 복호화 함수까지 문제없이 진입할 수 있었다. (기존의 샘플은 특정 시점에서 int29 호출하여 프로그램이 빠르게 종료되었음) 확인 결과, 이는 unpacked된 샘플 파일이 로드될 때마다 Mutext가 아닌 확인되지 않은 어떠한 기능에 의해 프로그램이 종료되는 것을 확인했다.
Function Call tree를 확인하던 중, 아래와 같은 내용을 찾을 수 있었다. FUN_006b9870 함수에서 'GetTempPathA', 'GetModuleFileNameA', 'CreateDirectoryA'와 같은 WinAPI를 호출하는 것으로 나타났는데, 이는 Amadey가 하드코딩된 난독화 문자열 중 일부를 디코딩하여 일종의 분석 방지 기능으로 사용하고 있다는 것으로 추측해볼 수 있기에 충분했다.
아마도, Amadey는 실행 후 자기 자신의 실행 경로와 함께 미리 정의된 적절한 실행 경로를 비교하여 이에 따라 동작을 분기하는 것 같다. (이 내용을 받힘하는 내용은 아래에서)
복호화 함수를 호출하는 offset 주소값에 다시금 BP를 설정하고 나서 분석을 진행하던 중, 아래와 같은 결과가 나왔다. EAX 레지스터 버퍼에 저장된 값은 드롭된 파일이 설치된 경로의 폴더명과 동일했다. 따라서 분석을 진행하면 설치 경로를 복호화 하는 과정을 확인해볼 수 있을 것이라고 생각되었다.
이 FUN_002c98b2 함수를 분석하던 중, EAX 레지스터에 저장된 '76XoROUVDkN1QQ=='이라는 흥미로운 값을발견했다.
EAX 값에 저장된 '76XoROUVDkN1QQ=='는 FUN_002c98b2 함수를 통과하여 평문 문자열 'oneetx.exe'와 함께 반환된다. 교차 확인 결과, 초기 분석된 샘플 'si684017.exe'와 현재 x32Dbg에서 분석 중인 드롭된 샘플 'oneetx.exe'가 그림 51과 동일한 과정을 거치는 것으로 확인되었다.
초기 분석 샘플 'si684017.exe'는 이 프로세스가 완료되면 해당 경로와 파일이 존재하는지 확인한다. 드롭된 샘플 'oneetx.exe'는 실행 중인 경로가 EAX, EDX에 저장된 결합 경로와 동일한지, 이 프로세스 이후인지 확인합니다. 실행 경로와 결합된 경로가 일치하지 않으면 초기 설치 프로세스(시동 등록, 스케줄러 등록)만 수행되고 추가적인 악성 동작은 수행되지 않는다.
이 과정에서 해당 복호화 함수가 호출된 시점은 페이로드를 압축 해제하여 시스템에 드롭하거나, 드롭된 파일의 분석 방지 기술로 적용하기 위한 것으로 판단된다.
아래 첨부된 사진은 '76XoROUVDkN1QQ=='을 복호화 하는 과정에서 확인되었다. EAX값으로는 드롭파일 이름인 'oneetx.exe'가, EDX값으로는 드롭 파일의 설치 경로가 담겨있다. 이 내용을 기반으로 드롭 파일이 설치되어 있어야할 정확한 경로 정보를 복호화하여 조합하는 과정이 아닐까.. 하고 추측해볼 수 있었다.
Step Over(F8)로 분석을 진행하던중, 아래 사진과 같이 EBP값으로 "http://77.91.124.207/plays/chapter/Plugins/cred64.dll" 이라는 값이 저장되어 있는 상황을 확인했다.
Amadey가 추가 악성 플러그인을 설치하는 과정에서 필요한 C2주소의 조합 과정과 함께 식별된 C2 주소는 77.91.124.207이며, 하단 라인에 CreateRemoteThreadEx를 호출하는 것을 보아 Cred64.dll 이라는 명칭의 플러그인의 다운로드를 요청하는 과정이라고 추측된다.
중단점에서 멈추고, 다시 실행하고 이런 과정은 너무 번거롭다. 따라서 문자열 복호화를 자동화하는 작업을 계획했다. 이 과정을 위해 선행적으로 Mutex의 값을 B6으로 수동 패치해야한다. 원래 FUN_002c98b2에서 중단점을 활성화하고, Mutex의 값을 비교검증하는 라인을 거쳐 샘플에 하드코딩된 난독화된 base64 문자열을 가져와 복호화하는 과정에 BP를 설정하고 이 값을 x32Dbg에서 즉석으로 확인했었다.
offset 주소 00407b94에 Mutex값이 0xB7인지 확인하는 라인에서 값을 0xB6으로 변경해준다.
복호화된 평문 문자열을 중단점에 도착할 때마다 자동으로 log로 찍어낼 수 있도록 복호화 함수의 종료 시점에 설정된 PB를 조건BP로 수정하고, 아래 사진과 같이 설정하였다.
로그 내용: Decoded {s:eax}
로그 조건 : eax != 0
명령 텍스트 : ;run
문자열 복호화를 자동화하려고 시도했지만, 어떤 이유에서인지 파일 경로 검사 우회와 Mutex 우회를 수행한 이후, Amadey는 복호화 함수에 도달하지 못하고 자꾸 Sleep 루프 상태에 빠져버렸다. 어제 오전 10시부터 오전 3시까지 장장 17시간 동안 이 문제를 해결해보려고 노력해보았지만.. 아직까지 뚜렷한 방법을 찾지 못했다.
만약 이 문제가 해결되었다면, Amadey에 하드코딩된 모든 난독화된 문자열을 평문으로 구할 수 있고, 이를 기반으로 Amadey가 숨기고 있는 악성 행위 및 은닉 행위에 대해 디버거로 더 심도있게 분석해볼 수 있을 것이다... 그리고 IoC 도출은 물론, 이를 기반으로 yara 룰도 작성할 수 있다... 분석 과정에서 거의 끝말까지 진행해두고, 마치 나보다 큰 벽을 마주쳐 버린 것 같았다. 이만큼 노력했는데도, 뚜렷한 해결방법이 보이지 않는 것을 보니 분명 내가 아직 해보지 않았거나 모르는 기술을 사용해야하는 것일 수도 있다. 즉, 아직 악성코드 분석에 관해서는 더 열심히 공부해야한다는 뜻~~
당장에는 취준으로 분석에 큰 시간을 할당하지 못하고 있으니.. 시간적으로 여유가 날 때 반드시 모든 분석 방지 기술들을 완벽하게 무력화시키고 기능들을 낮낮히 분석해 끝장을 내줄 것이다.
'Security > 악성코드분석보고서' 카테고리의 다른 글
x32Dbg를 활용한 Amadey Bot 악성코드 심층 분석 - Part1_[ PE Section 언패킹 및 payload 추출 ] (0) | 2025.06.13 |
---|---|
BPFDoor 악성코드 분석 보고서 (0) | 2025.06.05 |
최근 SKT 해킹사고에 사용된 BPFDoor 악성코드 상세 분석 (0) | 2025.06.02 |
악성 JPEG 파일에서 추출된 njRAT 악성코드 분석 보고서 (0) | 2025.06.01 |
FridayBotRaid Client를 내포한 JPEG 폴리글롯 악성코드 분석 보고서 (0) | 2025.06.01 |