2016-09-12 2 views
-1

우리 모두는 C에서 메모리를 읽고 쓰는 방법을 알고 있습니다. 우리는 포인터를 가지고 있으며, 메모리를 읽거나 쓸 때이를 참조합니다. 하위 레벨에서 제어 버스는 제어 버스에서 IO/PORT 라인을 설정하지 않아 메모리에서 읽기 요청을 처리하므로 메모리 컨트롤러가 메모리를 읽거나 쓸 수 있습니다. 그러나 포트 IO에서 포트 주소가 사용되고 읽기/쓰기는 제어 버스가 IO/PORT 행을 설정한다는 점을 제외하고는 메모리 읽기/쓰기와 동일하므로 메모리 컨트롤러는 현재 버스를 무시하고 대신 IO 컨트롤러가주의를 기울입니다. x86 ASM에는 IN과 OUT 명령어가 있습니다. 그러나 C에서이 작업을 수행 할 수있는 방법이 있습니까? Windows 7/10 (win32) 환경에 있습니다. 특정 헤더에서 _inp 또는 inp와 같은 함수가있을 수 있지만 (Windows에서 찾기가 어려움) 기본적으로 asm의 IN 및 OUT 명령어와 동일한 작업을 수행 할 수있는 방법이 있습니까? (EDIT : IN과 OUT은 링 0 명령어이기 때문에 인라인 어셈블리를 사용할 수 없음)C에서 제어 버스에 IO/PORT 행을 설정하는 방법

+0

어 ... 인라인 asm? – stackptr

+0

@stackptr IN 및 OUT은 링 0 관련 명령입니다. 그들이 사용할 수있는 유일한 곳은 Windows 커널입니다. 일반적인 Windows 응용 프로그램은 링 3과 4에서 실행됩니다. –

+0

저는 현대 OS에서이 트로프 자신의 장치 드라이버에 액세스 할 수 있다고 생각합니다 (일반적으로 생각하면 버그 공격을 계산하지 않음). 사용자 토지는 대개 해당 토큰에서 제외됩니다. (드라이버가 링 0도 아니기 때문에 미니언 같은 OS가 어떻게 해결되는지는 잘 모르겠지만, Linux OS 또는 MS 윈도우에 머물러있는 한 드라이버는 대부분 0 IIRC 링입니다.) – Ped7g

답변

3

C는 IO를 수행하는 방법을 포함하여 모든 플랫폼에서 완전히 추상적입니다.

Windows에는 READ_PORT_XXX이 있습니다.

  1. CPL (현재 권한 레벨)이 또한 링 0로 알려져 0으로 동일 : 위해

    그 기능을 사용하면 하나의 필요성을 사용할 수 있습니다. 값

  2. IOPL (IO 특권 레벨)를 IO 맵에 설정된 3
  3. 적절한 비트.

마지막 두 개는 내가 기억하는 한 Windows에서 지원되지 않습니다.
첫 번째는 장치 드라이버에서만 가능하며 현대 OS는 사용자 모드 프로그램에 커널 권한 (IO 액세스 포함)을 제공하지 않습니다. 당신은 읽기/쓰기 변환하는 매우 간단한 드라이버를 쓸 수


는 IO 작성 오프셋 읽기 /에 해당하는 포트에 액세스 쓰기 읽기 위해 64KiB Control Device Object에 액세스합니다.

필요한 것은 WDK/DDK입니다.

운좋게도 나는 그런 드라이버 소스를 가지고 있습니다.
나는 이것을 썼을 때조차 기억이 나지 않는다. 그래서 소금 한 알씩 먹는다.

#include <ntddk.h> 
#include <wdf.h> 
#include <Wdmsec.h> 


VOID IomemIoEvtIoRead (WDFQUEUE Queue, WDFREQUEST Request, size_t Length); 
VOID IomemIoEvtIoWrite (WDFQUEUE Queue, WDFREQUEST Request, size_t Length); 
NTSTATUS IoMemIoReadWrite(WDFREQUEST Request, size_t Length, ULONG_PTR* copied);   


NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) 
{ 
    WDF_DRIVER_CONFIG config; 
    NTSTATUS status = STATUS_UNSUCCESSFUL; 
    WDFDRIVER driver = NULL; 

    PWDFDEVICE_INIT deviceInit = NULL; 
    WDFDEVICE device = NULL; 

    WDF_IO_QUEUE_CONFIG queueConfig; 
    WDFQUEUE queue = NULL; 

    UNICODE_STRING acl, deviceName; 

    WDF_DRIVER_CONFIG_INIT(&config, NULL); 
    status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config,&driver); 
    if (!NT_SUCCESS(status)) 
     return STATUS_UNSUCCESSFUL; 

    /////////////////////////////////////// 


    RtlInitUnicodeString(&acl, L"D:P(A;;GA;;;WD)"); 
    RtlInitUnicodeString(&deviceName, L"\\Device\\iomem_io"); 

    status = STATUS_UNSUCCESSFUL; 
    deviceInit = WdfControlDeviceInitAllocate(driver, (PCUNICODE_STRING)&acl); 

    if (deviceInit) 
     status = WdfDeviceInitAssignName(deviceInit, (PCUNICODE_STRING)&deviceName); 

    if (NT_SUCCESS(status)) 
     status = WdfDeviceCreate(&deviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); 

    if (!NT_SUCCESS(status)) 
    { 
     WdfDeviceInitFree(deviceInit); 
     return status; 
    } 

    WdfControlFinishInitializing(device); 

    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchSequential); 

    queueConfig.EvtIoRead = IomemIoEvtIoRead; 
    queueConfig.EvtIoWrite = IomemIoEvtIoWrite; 



    return WdfIoQueueCreate(device, &queueConfig, WDF_NO_OBJECT_ATTRIBUTES, &queue); 
} 




VOID IomemIoEvtIoRead (WDFQUEUE Queue, WDFREQUEST Request, size_t Length) 
{ 
    ULONG_PTR bytesCopied = 0; 
    NTSTATUS status = IoMemIoReadWrite(Request, Length, &bytesCopied); 

    WdfRequestCompleteWithInformation(Request, status, bytesCopied); 
} 


VOID IomemIoEvtIoWrite (WDFQUEUE Queue, WDFREQUEST Request, size_t Length) 
{ 
    ULONG_PTR bytesCopied = 0; 
    NTSTATUS status = IoMemIoReadWrite(Request, Length, &bytesCopied); 

    WdfRequestCompleteWithInformation(Request, status, bytesCopied);  
} 


NTSTATUS IoMemIoReadWrite(WDFREQUEST Request, size_t Length, ULONG_PTR* copied) 
{ 
    NTSTATUS    status = STATUS_UNSUCCESSFUL; 
    WDF_REQUEST_PARAMETERS params; 
    size_t     io_length; 
    LONGLONG    port; 
    LONG     data; 
    PVOID     buffer; 
    WDFMEMORY    memory; 

    WDF_REQUEST_PARAMETERS_INIT(&params); 
    WdfRequestGetParameters(Request, &params); 
    if (params.Type == WdfRequestTypeRead) 
    { 
     io_length = params.Parameters.Read.Length; 
     port = (LONGLONG)params.Parameters.Read.DeviceOffset; 
    } 
    else if (params.Type == WdfRequestTypeWrite) 
    { 
     io_length = params.Parameters.Write.Length; 
     port = (LONGLONG)params.Parameters.Write.DeviceOffset; 
    } 
    else 
     return status; 

    if (io_length > 4 || io_length == 3 || io_length > Length) 
     return status; 

    status = WdfRequestRetrieveOutputMemory(Request, &memory); 
    if (!NT_SUCCESS(status)) 
     return status; 

    buffer = WdfMemoryGetBuffer(memory, NULL); 

    if (params.Type == WdfRequestTypeRead) 
    { 
     switch (io_length) 
     { 
      case 1: data = READ_PORT_UCHAR((PUCHAR)port); break; 
      case 2: data = READ_PORT_USHORT((PUSHORT)port); break; 
      case 4: data = READ_PORT_ULONG((PULONG)port); break;  
      default: return STATUS_UNSUCCESSFUL; 
     } 

     RtlCopyMemory(&data, buffer, io_length); 
    } 
    else 
    { 
     RtlCopyMemory(buffer, &data, io_length); 

     switch (io_length) 
     { 
      case 1: WRITE_PORT_UCHAR((PUCHAR)port, (UCHAR)data); break; 
      case 2: WRITE_PORT_USHORT((PUSHORT)port, (USHORT)data); break; 
      case 4: WRITE_PORT_ULONG((PULONG)port, data); break;  
      default: return STATUS_UNSUCCESSFUL; 
     }  

    } 

    if (copied) 
     *copied = io_length; 

    return STATUS_SUCCESS; 
} 

당신은 컴파일이 드라이버를로드 한 후 (작업 내가 당신에게 두는 것이)는 포트 에서 읽기/쓰기에 당신은/쓰기, 파일 \Device\iomem_io을 열 오프셋 X을 추구하고 읽을 수 있습니다 X.


답안에서 제공 한 버스 토폴로지에 대한 설명이 가장 오래되었습니다.
최신 버스 토폴로지는 더욱 복잡하며 특히 PCIe 이후에는 레거시 또는 플랫폼 특정 기능 (예 : 전원 관리) 만 IO 주소 공간을 통해 액세스됩니다.
다른 모든 것은 메모리 매핑 된 IO입니다.

3

최소 커널 드라이버가 없으면 : 불가능합니다.

C에는 IN 및 OUT 어셈블러 명령에 가까운 메커니즘이 없습니다. 예전에는 메모리 매핑 I/O를 지원하지 않는 머신에서 초기에는 불편했지만 예지력은 상당히 적었습니다. 오늘날 운영 체제의 대부분은 권한이있는 것으로 간주되기 때문에 이러한 명령을 허용하지 않습니다. 작업.

간접적으로 I/O 포트에 액세스하려면 IN 또는 OUT 요청을 전달하여 0 링을 보내는 커널 드라이버가 있어야합니다. 일부 예제는 "직접 I/O"를 검색하여 찾을 수 있습니다. 특정 타이밍 요구 사항 및 보안을 지원할 수있는 방법이 없다는 사실과 같은 많은 수의 레슨의 경우 사용자 공간에서 사용하는 것은 나쁜 습관으로 간주됩니다.

일부 Windows 컴파일러 (직접 포트 액세스를 지원하는 Windows 버전을 대상으로 함)는 사용자 프로그램에서 직접 포트 주소를 허용하는 _inp()_outp() 라이브러리 함수를 지원합니다. 일부는 커널이나 장치 드라이버 코드에서 여전히 수행합니다.

+0

글쎄, 그 "nuissance"몇 가지 심각한 배경이있다 : 그것은 주변 주소에 대한 소중한 64KiB 주소 공간을 낭비하지 허용. 또한 주소 범위를 해독하기 위해 외부 논리 (AND/NAND/등 4 개의 간단한 게이트를 가진 IC를 자주 사용하는)를 단순화했습니다. – Olaf

+0

@Olaf 별도의 I/O 및 메모리 주소 공간이 불편하다는 말은 아니 었습니다 (I/O에 대해 희소 한 주소 공간을 복제해야하는 좋은 이유가 있음). I/O 공간에 대한 직접적인 언어 지원은 불편했습니다.) – tofro

+0

글쎄요, 모든 아키텍쳐가 그랬던 것처럼 (그리고 오늘날 x86의 유산이기도하고 심지어 가장 많이 묻혀있는 것도 그렇습니다), 문제가 될 것입니다 표준에서 그들을 지원합니다. 뿐만 아니라 C가 기본적으로 그러한 저급 기능을 제공하도록 요구합니다. 주변 장치 레지스터에 액세스하는 방법에는 너무 많은 메커니즘이 너무 많습니다 (I/O 공간 대 메모리 맵핑뿐 아니라 후자의 경우 추가 측정이 필요할 수도 있음). 이러한 낮은 수준의 액세스를 잊지 말고 ** 항상 ** 드라이버 모듈에서 다뤄져야합니다. 아키텍처와 독립적입니다. – Olaf

관련 문제