이번 글에서는 사용자측 프로그램을 Win32로 작성합니다.
새 프로젝트 만들기(N) 를 클릭합니다.

Windows 데스크톱 애플리케이션을 선택하고 다음(N) 을 클릭합니다.

프로젝트 이름은 Samsung_HD502IJ 로 주고 만들기(C) 를 클릭합니다.

그리고 Samsung_HD502IJ.cpp 에 소스 코드를 작성합니다.

// Development environment:
//
// Windows 10 IoT Enterprise LTSC 21H2 x64
// Visual Studio 2026 v18.10.2
// Windows 11 SDK v10.0.28000.2526
// Windows Driver Kit v10.028000.2526
#include <windows.h>
#include <strsafe.h>
#define IOCTL_AHCI_EXECUTE_CMD CTL_CODE(FILE_DEVICE_UNKNOWN, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IDC_EDIT_MMIO 201
#define IDC_EDIT_PORT_IDX 202
#define IDC_BTN_START 203
#define IDC_EDIT_LOG 204
typedef struct _AHCI_CMD_PACKET {
ULONG_PTR PhysicalBaseAddress;
ULONG PortIndex;
UCHAR Feature;
UCHAR SectorCount;
UCHAR SectorNumber;
UCHAR CylinderLow;
UCHAR CylinderHigh;
UCHAR DeviceHead;
UCHAR Command;
} AHCI_CMD_PACKET, * PAHCI_CMD_PACKET;
typedef struct _THREAD_PARAM {
HWND hwndLog;
HWND hwndBtn;
ULONG_PTR mmioAddr;
ULONG portIndex;
} THREAD_PARAM, * PTHREAD_PARAM;
HWND g_hEditMmio, g_hEditPortIdx, g_hBtnStart, g_hEditLog;
void LogMessage(HWND hwndLog, LPCWSTR message) {
int length = GetWindowTextLengthW(hwndLog);
SendMessageW(hwndLog, EM_SETSEL, length, length);
SendMessageW(hwndLog, EM_REPLACESEL, FALSE, (LPARAM)message);
SendMessageW(hwndLog, EM_REPLACESEL, FALSE, (LPARAM)L"\r\n");
}
DWORD WINAPI AhciUnlockWorkerThread(LPVOID lpParam) {
PTHREAD_PARAM pParam = (PTHREAD_PARAM)lpParam;
HWND hwndLog = pParam->hwndLog;
LogMessage(hwndLog, L"[정보] AHCI 커널 드라이버 통신을 시작합니다...");
HANDLE hDriver = CreateFileW(L"\\\\.\\AhciPortControl", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
LogMessage(hwndLog, L"[오류] 드라이버 로드 실패! (관리자 권한 및 드라이버 서명 확인 필요)");
EnableWindow(pParam->hwndBtn, TRUE);
HeapFree(GetProcessHeap(), 0, pParam);
return 0;
}
// AHCI 커맨드 패킷 구성 (SATA Port 3번 타겟)
AHCI_CMD_PACKET packet = { 0 };
packet.PhysicalBaseAddress = pParam->mmioAddr;
packet.PortIndex = pParam->portIndex; // 3번 포트
packet.Feature = 0x00;
packet.SectorCount = 0x01;
packet.SectorNumber = 0x01;
packet.CylinderLow = 0xA5; // 삼성 키
packet.CylinderHigh = 0x5A; // 삼성 키
packet.DeviceHead = 0xA0;
packet.Command = 0xEE; // VSC Unlock 명령 코드
WCHAR buf[128];
StringCchPrintfW(buf, 128, L"[전송] MMIO: 0x%p / Port: %d 방향으로 명령을 주입합니다.", (void*)packet.PhysicalBaseAddress, packet.PortIndex);
LogMessage(hwndLog, buf);
DWORD bytesReturned;
BOOL result = DeviceIoControl(hDriver, IOCTL_AHCI_EXECUTE_CMD, &packet, sizeof(packet), &packet, sizeof(packet), &bytesReturned, NULL);
if (result) {
StringCchPrintfW(buf, 128, L"[완료] 명령 전송 성공. 반환된 TFD 상태 바이트: 0x%02X", packet.Command);
LogMessage(hwndLog, buf);
if (packet.Command & 0x01) {
LogMessage(hwndLog, L"[결과] 드라이브가 ERR 상태를 표시했습니다. (명령 거부됨)");
}
else {
LogMessage(hwndLog, L"[결과] AHCI 매핑 구조 기반 삼성 VSC 진입 명령 완료!");
}
}
else {
LogMessage(hwndLog, L"[오류] 드라이버 내부에서 MMIO 매핑 또는 통신에 실패했습니다.");
}
CloseHandle(hDriver);
EnableWindow(pParam->hwndBtn, TRUE);
HeapFree(GetProcessHeap(), 0, pParam);
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CREATE:
CreateWindowW(L"STATIC", L"AHCI BAR5 MMIO 주소 (Hex):", WS_CHILD | WS_VISIBLE, 20, 22, 200, 20, hwnd, NULL, NULL, NULL);
// 장치관리자에서 찾은 메모리 범위 입력란 (예: DF340000 등)
g_hEditMmio = CreateWindowW(L"EDIT", L"EE24B000", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 225, 20, 100, 20, hwnd, (HMENU)IDC_EDIT_MMIO, NULL, NULL);
CreateWindowW(L"STATIC", L"SATA 포트 번호:", WS_CHILD | WS_VISIBLE, 20, 52, 200, 20, hwnd, NULL, NULL, NULL);
// SATA 3번 포트 고정 지정
g_hEditPortIdx = CreateWindowW(L"EDIT", L"3", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_NUMBER, 225, 50, 40, 20, hwnd, (HMENU)IDC_EDIT_PORT_IDX, NULL, NULL);
g_hBtnStart = CreateWindowW(L"BUTTON", L"AHCI 명령 주입", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 340, 18, 130, 52, hwnd, (HMENU)IDC_BTN_START, NULL, NULL);
g_hEditLog = CreateWindowW(L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, 20, 90, 460, 250, hwnd, (HMENU)IDC_EDIT_LOG, NULL, NULL);
LogMessage(g_hEditLog, L"AHCI 모드 락해제 툴이 준비되었습니다.");
LogMessage(g_hEditLog, L"장치 관리자 -> 'SATA AHCI 컨트롤러' 속성 -> 자원 탭에서 '메모리 범위'의 시작 주소를 입력하세요.");
break;
case WM_COMMAND:
if (LOWORD(wParam) == IDC_BTN_START) {
WCHAR szMmio[32] = { 0 };
WCHAR szPort[8] = { 0 };
GetWindowTextW(g_hEditMmio, szMmio, 32);
GetWindowTextW(g_hEditPortIdx, szPort, 8);
ULONG_PTR mmioAddr = (ULONG_PTR)wcstoull(szMmio, NULL, 16);
ULONG portIdx = (ULONG)wcstoul(szPort, NULL, 10);
if (mmioAddr == 0) {
MessageBoxW(hwnd, L"올바른 AHCI MMIO 주소를 입력하세요.", L"입력 오류", MB_ICONWARNING);
break;
}
EnableWindow(g_hBtnStart, FALSE);
PTHREAD_PARAM pParam = (PTHREAD_PARAM)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(THREAD_PARAM));
if (pParam) {
pParam->hwndLog = g_hEditLog;
pParam->hwndBtn = g_hBtnStart;
pParam->mmioAddr = mmioAddr;
pParam->portIndex = portIdx;
HANDLE hThread = CreateThread(NULL, 0, AhciUnlockWorkerThread, pParam, 0, NULL);
if (hThread) CloseHandle(hThread);
else {
EnableWindow(g_hBtnStart, TRUE);
HeapFree(GetProcessHeap(), 0, pParam);
}
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
WNDCLASSW wc = { 0 };
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszClassName = L"AhciPortControlGUI";
if (!RegisterClassW(&wc)) return 0;
HWND hwnd = CreateWindowExW(0, wc.lpszClassName, L"AHCI SATA 3 포트 제어 유틸리티", WS_OVERLAPPEDWINDOW & ~WS_THICKFRAME & ~WS_MAXIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 515, 400, NULL, NULL, hInstance, NULL);
if (!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
MSG msg;
while (GetMessageW(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return (int)msg.wParam;
}
F7 키를 눌러 솔루션 빌드를 합니다.

1개 성공 이라고 나오면 OK 입니다.

빌드 작업 후 아래 파일들이 만들어집니다.

이 프로그램은 드라이버와 서로 통신을 하기 때문에 관리자 권한으로 실행해야 합니다.

① AHCI BAR5 MMIO 주소 (HEX) :
AHCI BAR5 MMIO 주소를 알기 위해서는 다음 과정을 거쳐야 합니다.
장치관리자의 표준 SATA AHCI 컨트롤러를 더블클릭합니다.

리소스 탭을 클릭합니다.
여기서 가장 아래쪽에 있는 메모리 범위의 시작 부분이 AHCI BAR5 MMIO 주소입니다.
여기서는 00000000EE24B000 입니다. 일단 기본값으로 입력을 만들어 놓았지만 본인 컴퓨터에서 값을 확인 후 수정하시기 바랍니다.

② SATA 포트 번호:
SATA 포트 번호는 BIOS 화면에서 확인할 수 있습니다.
Windows 가 설치되어 있는 저장 드라이브는 이 프로그램으로는 동작시킬 수 없습니다.
프로그램에서 테스트할 대상 하드디스크는 스핀포인트 F1(Spinpoint F1) 시리즈에 속하는 제품인 SAMSUNG HD502IJ 입니다.

장치 관리자의 디스크 드라이브에서 드라이브를 선택한 후 마우스 오른쪽 버튼을 클릭하여 디바이스 사용 안 함(D)으로 해줍니다. 해당 하드디스크는 윈도우에서 사라지게 됩니다.

예(Y) 를 클릭합니다.

아이콘 모양이 바뀐 것을 볼 수 있습니다.

이 작업을 해주는 이유는 Windows 의 SATA 컨트롤러 드라이버 storahci.sys 대신에 우리가 작성한 LowLevelPortDriver.sys 를 테스트하기 위해서입니다.
- 역할: 하드디스크가 연결된 메인보드의 SATA 포트(AHCI 컨트롤러)를 제어하는 드라이버입니다.
- 설명: Windows 10이 하드디스크라는 하드웨어 장치 자체와 물리적/전기적 신호로 직접 통신할 수 있도록 통로를 열어주는 가장 근본적인 파일입니다. 장치 관리자에서 "표준 SATA AHCI 컨트롤러"로 표시되는 드라이버의 실체입니다.
- 위치: C:\Windows\System32\drivers\storahci.sys
(※ 메인보드 칩셋에 따라 인텔의 iaStorAC.sys나 AMD의 전용 드라이버 파일이 이 역할을 대신하기도 합니다).
- 역할: 통신 통로가 열린 후, Windows가 해당 하드디스크를 하나의 독립된 '저장 장치(Disk Device)'로 인식하고 제어하게 해주는 드라이버입니다.
- 설명: 앞선 컨트롤러 드라이버 위에서 작동하며, Windows의 파일 시스템(NTFS 등)이 디스크에 데이터를 읽고 쓸 수 있도록 표준화된 명령을 하드디스크에 전달합니다. 장치 관리자의 "디스크 드라이브" 항목에 등록된 하드디스크 모델명(예: SAMSUNG HD502IJ)에 연결된 드라이버입니다.
- 위치: C:\Windows\System32\drivers\disk.sys
Windows 10은 storahci.sys를 통해 하드디스크가 꽂힌 포트와 하드웨어적 통신을 수행하고, 그 위에 disk.sys를 구동하여 저장 장치로서의 데이터 읽기/쓰기를 제어합니다. 둘 중 하나라도 손상되면 Windows에서 하드디스크를 인식하지 못하거나 블루스크린(BSOD)이 발생할 수 있습니다.
다음 글에서는 실제 테스트하는 과정을 이야기하겠습니다.