리버싱

ld.exe 분석

0xff 2024. 4. 25. 04:32

ld.exe는 ldplayer에서 내부적으로 실행중인 LDPlayer의 VitrualBox console에 접속하기 위해 사용된다.

 

Default usage:

.\ld.exe -s <index>

 

session 값을 주지 않을 경우 모든 실행중인 에뮬레이터 중 하나에 연결된다.

 

ld.exe는 어떻게 창에 attach할까.

 

IDA로 분석해봤다.

 

CPP로 작성되었고, start 함수에서 CRTStartup 함수로 이동한다.

 

CRTStartup -> wmain

 

wmain 함수는 두개의 int pointer를 인자로 받는데, 이 값은 pre_cpp_init에서 초기화된다.

 

dword_406414에 *argc,

dword_406418에 ***argv 가 담긴다.

 

__tmainCRTStartup에서는 wmain에 env까지 인자로 넘겨주지만, 실제로 받는 값은 argc, argv만 받는다.

 

wmain 함수에는 사실상 모든 로직이 다 담겨있었다.

 

-s 옵션을 주면 특정 ldplayer session에 연결할 수 있게끔 처리가 되어있다.

session id값은 v30에 들어간다.

 

추가 인자도 처리를 하는거로 보이지만, 쓰이는 곳은 찾을수 없었다.

 

console handle을 가져오는 부분을 살펴보자.

FindWindowExW를 사용해서 핸들을 가져오는 모습을 볼 수 있다.

 

window classname: leidian4_process_msg_wnd_class

lpszWindow: Buffer

 

wchar_t Buffer[128];

 

Buffer에는 snwprintf_s 함수를 활용해 "leidian4_process_msg_wnd_%u" 포맷으로 write한다.

 

%u에는 v30 | (v9 << 24) 가 들어가는데,

v30 = session id

 

xmmword_404210 = 380036003000300030003000390030h

 

v9 = xmmword_404210을 long long integer로 변환한 값 OR 0 이 된다.

 

 

테스트

 

ID가 300인 에뮬레이터의 콘솔을 가져와보겠다.

 

#include <immintrin.h>
#include <Windows.h>
#include <iostream>
#include <format>

int main()
{
	unsigned int sign = 0x050000;
	unsigned int session = 300;
	wchar_t windowName[0x80];

	memset(windowName, 0, sizeof(windowName));

	_snwprintf_s(windowName, 0x80u, 0xFFFFFFFF, L"leidian4_process_msg_wnd_%u", session | (sign << 8));
    HWND hwnd = FindWindowExW(NULL, NULL, L"leidian4_process_msg_wnd_class", windowName);

    if (hwnd == NULL)
    {
		std::cout << "Not found" << std::endl;
	}
	else
	{
		std::cout << "Found" << std::endl;
		std::cout << "HWND: " << hwnd << std::endl;
	}
	std::cout << "windowName: " << windowName << std::endl;

    return 0;
}

HWND를 잘 받아오는 모습을 볼 수 있다.

 

명령어를 보내는 코드는 아래와 같다.

SendMessageW(hwnd, 0x4Au, (WPARAM)hwnd, (LPARAM)(L"poweroff"));

 

데이터 수신은 아직 구현하지 않았다.