Driver Verifier 와 STATUS_WAIT_n
2012/11/07 08:55
작성중인 드라이버에 딱히 원인을 알수 없는 문제가 생겨 Driver Verifier 옵션을 모두 on 시키고 디버깅 작업시도.
드라이버를 UpperFilter 에 등록하고 IRP_MJ_PNP/IRP_MN_START_DEVICE 에서 STATUS_UNSUCCESSFUL를 return 하도록 하는 부분에서 Driver Verifier 가 아래와 같이 에러를 발생시켰다.
***********************************************************************
* THIS VALIDATION BUG IS FATAL AND WILL CAUSE THE VERIFIER TO HALT    *
* WINDOWS (BUGCHECK) WHEN THE MACHINE IS NOT UNDER A KERNEL DEBUGGER! *
***********************************************************************

WDM DRIVER ERROR: [xxxxx.sys @ 0xF77F1FE0] An IRP dispatch handler (
                  F77F1FE0 ) has returned a status that is inconsistent with
                  the Irp's IoStatus.Status field. ( Irp = 8746AE90 -
                  Irp->IoStatus.Status = 00000003 - returned = 00000000 )
IRP_MJ_PNP.IRP_MN_REMOVE_DEVICE -
[ DevObj=86C043B8, FileObject=00000000, Parameters=00000000 00000000 00000000 00000000 ]
http://www.microsoft.com/hwdq/bc/default.asp?os=5.1.2600&major=0xc9&minor=0x224&lang=0x9

Break, Ignore, Zap, Remove, Disable all (bizrd)?

IRP_MJ_PNP/IRP_MN_START_DEVICE 에서 STATUS_UNSUCCESSFUL 을 return 하는건 장치를 start 할수 없는경우 당연한 리턴값이므로 전혀 문제될것이 없다.

!drvstack 을 통해 driver stack 을 살펴보니 작성중인 드라이버 아래위로 VERIFIER 드라이버 보이고
작성중인 드라이버 앞단에서 status 값이 STATUS_SUCCESS 값이 아닌 STATUS_WAIT_2 또는 STATUS_WAIT_3 이 넘어온다.
그 값을 작성중인 드라이버에서는 STATUS_UNSUCCESSFUL 로 변경시켜 다시 아래의 VERIFIER 드라이버로 넘긴다.
(STATUS_WAIT_n 에 대한 정의는 아래와 같이 되어 있는데 전혀 도움이 안된다.)
0x00000001 STATUS_WAIT_1 The caller specified WaitAny for WaitType and one of the dispatcher objects in the Object array has been set to the signaled state.
이 과정에서 Driver Verifier 는 앞뒤의 status 값이 다르기 때문에 에러는 발생시킨다.
(Driver Verifier 의 모든 옵션을 on 했지만 아마도 Enhanced I/O verification 으로 추정)

그러면 왜 STATUS_SUCCESS 를 return 하지 않고 STATUS_WAIT_n 와 같은 값을 return 하는걸까?

아래 코드를 보면 IoCallDriver() 호출이후 성공시 다음 처리를 하도록 되어 있다.

status = IoCallDriver(...);
if status == STATUS_SUCCESS

그러나 위와 같은 코드에서는 Driver Verifier 옵션을 on 시켰을 경우 원하는 대로 동작하지 않는 경우가 생긴다.
물론 Driver Verifier 를 on 하지 않는 경우에도 원하는 동작을 하지 않는 경우가 생긴다.
(아래의 NT_SUCCESS macro 정의를 보면 성공값은 0으로 정의된 STATUS_SUCCESS 만 있는 것이 아니다.)

따라서 if 문 에서 성공값을 검사할때 STATUS_SUCCESS 값을 검사하지 않고 NT_SUCCESS(...)와 같은 macro 사용하여야 한다.

NT_SUCCESS(status)
 status의 값이 성공 유형에 대한 값(0−0x3FFFFFFF) 또는 정보 유형에 대한 값(0x40000000-0x7FFFFFFF) 이면 TRUE.

NT_INFORMATION(status)
 status의 값이 정보 유형에 대한 값(0x40000000-0x7FFFFFFF) 이면 TRUE.

NT_WARNING(status)
 status의 값이 경고 유형에 대한 값(0x80000000−0xBFFFFFFF) 이면 TRUE.

NT_ERROR(status)
 status의 값이 오류 유형에 대한 값(0xC0000000 - 0xFFFFFFFF) 이면 TRUE.


NTSTATUS 의 성공값 또는 기타 에러에 대한 검사는 NT_SUCCESS() 또는 NT_ERROR()를 사용해야 할듯.
2012/11/07 08:55 2012/11/07 08:55
Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다


inf 파일로 드라이버 설치시 경고창 없이 설치하기
2011/07/26 09:12
1. 64bit 에서 경고창 없이 설치

64bit OS에서 변경된 보안기능으로 서명되지 않은 장치 드라이버는 사용할수 없기 때문에 드라이버 파일에 모든 인증을 받아 사용해왔다.
XP 64bit에서는 NDIS 5.x 를 지원하는 Passthru를 기반으로 작성했고 64bit Win7에서는 NDIS 6.x 이상을 지원하는 filter를 기반으로 작성하였기 때문에 각각 inf 파일을 이용하여 설치해야 한다.
따라서 bindview 예제를 가지고 설치 테스트를 했는데 이 설치 과정에서 문제가 발생했다.분명 singtool로 드라이버 인증을 하였음에도 불구하고 서명되지 않은 어쩌구 저쩌구 설치하겠느냐? 설치하지 않겠느냐? 라는 대화창이 뜬다.
사용자에게 선택권을 주면 사용자가 설치하지 않는 경우가 있기 때문에 보안 프로그램 특성상 경고창 없이 설치를 해야만 하기때문에 이 과정을 강제로 skip을 해야한다.
사용자 삽입 이미지
서명 어쩌구 저쩌구 애매한 문구 때문에 다소 삽질(?)을 했으나 결국 inf파일로 설치하는 드라이버는 Windows Logo 관련 WHQL 인증을 받지 않으면 위와 같은 경고창이 뜨는걸 확인.
WHQL인증을 받아볼까 알아봤다가 .... 포기하고 ...결국 bindview 코드를 분석해서 수정하고 설치하는 몇차례 삽질을 거쳐 win7 64bit에서는 아래의 과정으로 경고창 없이 설치가 가능하다.

1. bindview 예제에 있는 SetupCopyOEMInf 를 생략하고 INetCfgClassSetup class의 Install 을 통해서 설치하도록 설치 프로그램 작성
2. SetupCopyOEMInf 를 삭제했기 때문에 수동으로 inf, sys 파일을 windows\inf 폴더에 복사2. 설치프로그램을 통해 inf 파일로 드라이버 설치
3. windows\system32\drivers 폴더에 드라이버가 복사가 되지 않으니 수동으로 sys 파일을 복사이렇게 하면 Win7에서 경고창없이 설치가 되고 동작도 문제가 없다.(vista에서는 테스트 해보지 않았지만 문제없이 될거라고 생각한다.)그런데 문제는 XP 64bit의 Passthru를 설치하려고 했더니 설치가 되지 않는다.
우선 inf 파일이 2개 인데다가 Win7에서 설치한 방법으로는 드라이버 파일을 찾는 대화창이 표시된다.또 삽집을 통해 방법이 다소 다르지만 XP 64bit에서도 경고창 없이 설치가 가능하다.

1. bindview 예제에 있는 SetupCopyOEMInf 를 생략하고 INetCfgClassSetup class의 Install 을 통해서 설치하도록 설치 프로그램 작성(중요한건 2개의 inf 파일을 모두 같이 install 해야한다. 테스트해보니 미니포트용 inf (netsf_m.inf)를 설치하지 않아도 됨)
2. SetupCopyOEMInf 를 삭제했기 때문에 역시 마찬가지로 수동으로 2개의 inf, sys 파일을 windows\inf 폴더에 복사
3. inf 파일의 SourceDisksNames 섹션과 SourceDisksFiles 섹션의 항목을 주석처리하고 C:\WINDOWS\ServicePackFiles 에 sys 파일을 복사(sys 파일을 찾는 경로명을 생략하면 ServicePackFiles에서 드라이버를 찾기때문에)4. 설치프로그램을 통해 inf 파일로 드라이버 설치5. windows\system32\drivers 폴더에 드라이버가 복사가 되지 않으니 수동으로 sys 파일을 복사 Unintalll은 설치과정의 반대로만 하면된다.


2. 32bit에서 경고창없이 설치


win7에서는 64bit와 같은 과정을 통하면 경고창 없이 설치가능하였음.
혹시 그래도 경고창이 뜬다면 아래 링크참고.
테스트는 해보지 않았으니 참고할것.(disable하여 설치후 다시 enable하면 크게 문제 없을듯)
http://www.killertechtips.com/2009/05/05/disable-driver-signing-windows-7/
XP에서는 64bit에서 처럼 설치하였더니 실패.
레지스트리에 HKEY_LOCAL_MACHINE\Software\Microsoft\Driver Signing 를 0으로 설정하여야 함.
하지만 쉽게 변경이 되지 않으니 아래 링크를 참고
Osrloader.exe 로 서비스 생성(모든 옵션은 그냥 디폴트로 설정)후 테스트 해볼것.

gabriel 님 리플 정리(테스트 해보지 않았으니 실제 적용시 테스트 하셔야 합니다.)

1. xp 드라이버서명 무시는 아래의 코드를 참조하시고 예제 처럼 사용하시면 됩니다.

예제)
SetRegistyDriverInstall(0); //xp 드라이버설치 무시
...드라이버설치코드...
SetRegistyDriverInstall(1);//xp 드라이버설치 경고

서명무시하는 코드
void SetRegistyDriverInstall(BYTE ignoreOn)

{
HCRYPTPROV hCryptProv;
HCRYPTHASH hHash;
BYTE data[16];
DWORD len;
DWORD seed;
HKEY hkey;
char input[4];

// HKLM\System\WPA\PnP\seed
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "System\\WPA\\PnP", 0, KEY_READ, &hkey)!=ERROR_SUCCESS)
{
return ;
}

len=sizeof(seed);
if(RegQueryValueEx(hkey, "seed", NULL, NULL, (BYTE*)&seed, &len)!=ERROR_SUCCESS)
{

}

if(hkey)
{
RegCloseKey(hkey);
}

if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0)) // flag values
{

}
else
{
if (GetLastError() == NTE_BAD_KEYSET)
{  
if(CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
}
}
} // End of else.

//--------------------------------------------------------------------
// Create a hash object.

if(!CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash))
{
return ;
}
//--------------------------------------------------------------------
// Compute the cryptographic hash on the data.

input[0]=0;
input[1]=ignoreOn; // This is the Value!
input[2]=0;
input[3]=0;

if(!CryptHashData(hHash, (BYTE*)input, sizeof(input), 0))
{
return ;
}
//--------------------------------------------------------------------

if(!CryptHashData(hHash, (BYTE*)&seed, sizeof(seed), 0))
{
return ;
}

//--------------------------------------------------------------------
len=sizeof(data);
if(!CryptGetHashParam(hHash, HP_HASHVALUE, data, &len, 0))
{
return ;
}

//--------------------------------------------------------------------
// Clean up.

// Destroy the hash object.

if(hHash)
{
if(!(CryptDestroyHash(hHash)))
{
return ;
}
}

// Release the CSP.

if(hCryptProv)
{
if(!(CryptReleaseContext(hCryptProv,0)))
{
return ;
}
}

//--------------------------------------------------------------------
// HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\PrivateHash
if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Setup", 0, KEY_WRITE, &hkey)!=ERROR_SUCCESS)
{
len=sizeof(seed);
if(RegSetValueEx(hkey, "PrivateHash", 0, REG_BINARY, data, sizeof(data))!=ERROR_SUCCESS)
{
}

if(hkey)
{
RegCloseKey(hkey);
}
}

//--------------------------------------------------------------------
// HKLM\Software\Microsoft\Driver Signing\Policy
if(!RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Driver Signing", 0, KEY_WRITE, &hkey )!=ERROR_SUCCESS)
{
len=sizeof(seed);
if(RegSetValueEx(hkey, "Policy", 0, REG_BINARY, &ignoreOn, 1)!=ERROR_SUCCESS)
{
}

if(hkey)
{
RegCloseKey(hkey);
}
}
//--------------------------------------------------------------------
// HKLM\Software\Microsoft\Driver Signing\Policy
if(!RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Driver Signing", 0, KEY_WRITE, &hkey )!=ERROR_SUCCESS)
{
len=sizeof(seed);
if(RegSetValueEx(hkey, "Policy", 0, REG_BINARY, &ignoreOn, 1 )==ERROR_SUCCESS)
{
}

if(hkey)
{
RegCloseKey(hkey);
}
}
}

2. 윈도우 7 에서의 방법
저 같은 경우 display드라이버라서 INetCfgClassSetup 함수와는 매칭 이 되지 않았습니다.
그래서 안되는가 하고 고민하다가 우연찬게 알수없는 드라이버 상태에서 다시시작을 누르게 됐는데 그 순간 바로 설치가 되더라구요.
말이 길어 졌는데 짧게 말씀드리자면
1.windows/inf로 드라이버을 복사한다
2.UPDATEDRIVERFORPLUGANDPLAYDEVICES로 설치할때 실패하도록한다(성공하도록하면 경고창에 뜨기 때문에 실패할수 있는 옵션을 넣으면 됩니다)
3.드라이버을 다시시작한다.
이렇게 하면 경고창 없이 자동으로 설치가 됩니다.
그리고 저같은 경우 디지탈인증을 서명한 상태였습니다 서명이 안되면 3번에서 경고창이 뜹니다.
재생각에는 서명한 상태에서 저와 같은 방식을 사용하면 모든 드라이버가 자동으로 설치가 되지 않을까 생각합니다. 
2011/07/26 09:12 2011/07/26 09:12
Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • sally 2011/03/11 14:43  댓글주소  수정/삭제  댓글쓰기
    죄송하지만 설치프로그램을 통해 inf 파일로 드라이버 설치라는 것이 어떻게 해야되는건지요? 혹시 인스톨쉴드에서 작업하라는 뜻인가여?
    • TWEETY 2011/03/11 17:25  댓글주소  수정/삭제
      아닐껄요? ^^
      소개된 API를 검토해 보시면 아실 수 있을 꺼에요.
    • hongyver 2011/03/15 17:50  댓글주소  수정/삭제
      bindview 예제를 보시면 bindview.exe를 실행하여 inf 파일로 드라이버를 설치하는 예제입니다.(DDK경로/src/network/config/bindview)

      설치프로그램이란 bindview와 같은 프로그램을 말합니다.
  • TWEETY 2011/03/11 17:19  댓글주소  수정/삭제  댓글쓰기
    오... 나도 이거 써볼까봐... 매번 SetupCopyOEMInf만 써왔는데.. 흡..ㅋㅋ
  • sally 2011/03/15 18:01  댓글주소  수정/삭제  댓글쓰기
    친절한 답변 감사합니다^^. 그렇게 수정하여 며칠 테스트해봤는데. 아쉽게도 창이 뜨는 경우가 있더군요. OS마다 다른듯도 하고 같은 OS라도 뜨는 경우가 있고. 비스타나 7에서도 맨 처음 설치시에는 창이 뜨더라구요. 물론 수정전 모듈은 매번 설치시마다 창이 뜨지만요.
    • hongyver 2011/03/15 21:59  댓글주소  수정/삭제
      창이 뜬다구요?
      보안 관련창은 SetupCopyOEMInf 라는 API를 사용할때 설치할때 sys 와 inf 파일을 시스템 폴더로 복사하면서 창을 띄울텐데요.

      어떤환경인지 설명해주시면 저도 한번 해보고 싶군요.
    • 비밀방문자 2011/03/16 14:36  댓글주소  수정/삭제
      관리자만 볼 수 있는 댓글입니다.
  • hongyver 2011/03/17 12:44  댓글주소  수정/삭제  댓글쓰기
    음. inf 파일도 수정을 조금 하셔야 하는데 혹시 수정하셨나요?
  • hoho 2011/04/05 17:48  댓글주소  수정/삭제  댓글쓰기
    inf 파일을 WINDOWS\INF 파일로 복사할때 oemXX.inf 같이 파일명을 바꿀 필요 없나요?
  • kuaaan 2011/04/08 10:52  댓글주소  수정/삭제  댓글쓰기
    저도 해봤는데요... 전 XP에서도 창이 계속 뜹니다.
    SetupCopyOEMInfW는 확실히 주석처리했는데... 마찬가지네요.
    혹시 테스트하신 DDK 버젼을 좀 알 수 있을까요?? ^^
  • kuaaan 2011/04/09 15:45  댓글주소  수정/삭제  댓글쓰기
    HrInstallNetComponent 에서 SetupCopyOEMInf 함수를 주석처리한 후 InstallSpecifiedComponent 를 호출하는 방식으로 설치본을 개발하여 windows/inf 폴더에 inf와 sys파일을 가져다놓고 실행시켰습니다만, "CD-ROM 드라이브에 'Microsoft Passthru Driver Disk' CD를 넣고 [확인]을 클릭하십시오." 라는 경고창이 뜨는군요. 만약, Apply가 호출되기 전에 drivers폴더에 sys파일을 복사해놓으면 로고인증을 받지 않았다는 메시지가 그대로 뜨구요. 어떻게 하신건지 궁금하네요... ^^
    OS는 WinXP 32비트입니다.
  • hongyver 2011/04/11 09:09  댓글주소  수정/삭제  댓글쓰기
    @hoho oemXX와 같이 변경되는건 os에서 바꾸어주는거니 굳이 바꾸어주실필요는 없을듯 합니다. 저도 바꾸지 않고 했구요.
    @kuaaan 제가 테스트한 DDK 버전은 7600.16385.1 입니다. 복사하실때 그냥 API를 사용하시던지 아니면 그냥 미리 복사해 넣고 테스트해보세요. 설치 API로 복사하시면 아마 경고문이 계속 뜰껍니다.
  • hk 2011/10/19 17:39  댓글주소  수정/삭제  댓글쓰기
    NDIS Sample Passthru를 win7 64bit에 설치하면 아예 create file 자체도 안되던데요. 어떻게 해줘야 할지 모르겠네요. win7 32에서는 잘 되는데요. 디지털 서명이 문제 인듯 한데...디지털 서명 어떻게 받죠?
  • trip2me 2011/12/19 17:37  댓글주소  수정/삭제  댓글쓰기
    안녕하세요~
    올리신 글 잘 보고 배우고 있습니다. : )

    windows XP SP3 32bit 에서 NDIS passthru 예제를 가지고 드라이버 설치를 하였는데
    역시나 서명 경고가 떠서 본문 아랬부분에 적어놓은 링크를 참조해 해 봤지만
    CryptCreateHash 함수에서 에러가 나면서 막히네요. 적어주신대로 단순히 해당 레지스트리 키를 바꾸는 것은 어렵더라구요.

    windows XP SP3 32bit 에서도 경고가 안 뜨도록 하려면 저런 설정을 disable하는 방법 말고는 WHQL의 cross 서명을 반드시 받아야 하는 것인지요?

    궁금해서 답변드려 봅니다. : )
    • hongyver 2011/12/20 18:37  댓글주소  수정/삭제
      32bit 에서는 드라이버 sign이 필요없지만 필터 드라이버는 whql의 서명이 반드시 필요해 보입니다.(레지스트리 disable 방법 말고는)
      whql의 인증에 관해서는 제 분야도 아니고 해 본 경험도 없어 더 드릴 말씀이 없는게 안타깝네요.
    • trip2me 2011/12/26 01:12  댓글주소  수정/삭제
      링크해주신 메일링 리스트에 글을 쓴 사람의 블로그 댓글에 보니 제가 댓글에 적었던 문제가 있던 부분 코드 수정이 필요하다고 적혀 있네요.
      답변 감사드립니다. :)
  • ljw8412 2012/01/09 14:03  댓글주소  수정/삭제  댓글쓰기
    안녕하세요 블로그를 통해서 많이 배우고 있습니다.
    다름이 아니라 Filter Driver를 설치 하는 부분에서 질문 사항이 있어서 이렇게 댓글을 남김니다.
    BindView를 이용하여 NDIS FilterDriver(.inf, .sys)설치 파일(exe)을 만든후 WinXP 32bit의 Window\\inf 폴더에 inf 파일을 수동으로 복사한후 설치파일(exe), FilterDriver(.inf, .sys)파일을 한 폴더에 넣고 설치 파일을 실행 해서 WinXP 32bit에서 설치를 성공 했습니다.
    그런데 Win7 32bit와 64bit에서는 위의 방법에 codesigning(.cat파일 포함)을 한후 설치파일(exe)를 실행 하면 sys 파일의 경로를 찾을 수가 없다는 "필요한 파일" 이라는 Window MSG Box가 뜹니다. MSG Box에서 Sys파일의 위치를 지정해주면 Filter Driver는 설치가 되는데 제가 궁금한건 Win7 32bit 같은 경우 XP와 sys를 찾는 경우가 같을텐데 못찾는 다고 나오는 이유가 궁금합니다.
    수동으로 sys 파일을 windows\\inf폴더 windows\\system32\\river폴더에 복사 한 한후 설치 파일(exe)를 실행 시켜도 마찬가지로 sys파일을 찾을 수 없다는 MSG 박스가 뜹니다.
    이 문제를 어떻게 해결 해야 할지 도와 주셨으면 합니다. 감사 합니다.
  • gabriel 2012/01/16 14:29  댓글주소  수정/삭제  댓글쓰기
    //xp 드라이버서명 무시

    void SetRegistyDriverInstall(BYTE ignoreOn)
    {
    HCRYPTPROV hCryptProv;
    HCRYPTHASH hHash;
    BYTE data[16];
    DWORD len;
    DWORD seed;
    HKEY hkey;
    char input[4];

    // HKLM\System\WPA\PnP\seed
    if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
    "System\\WPA\\PnP",
    0,
    KEY_READ,
    &hkey
    )!=ERROR_SUCCESS)
    {
    return ;
    }

    len=sizeof(seed);
    if(RegQueryValueEx(
    hkey,
    "seed",
    NULL,
    NULL,
    (BYTE*)&seed,
    &len
    )!=ERROR_SUCCESS)
    {

    }

    if(hkey)
    {
    RegCloseKey(hkey);
    }

    if(CryptAcquireContext(
    &hCryptProv,
    NULL,
    NULL,
    PROV_RSA_FULL,
    0)) // flag values
    {

    }
    else
    {
    if (GetLastError() == NTE_BAD_KEYSET)
    {
    if(CryptAcquireContext(
    &hCryptProv,
    NULL,
    NULL,
    PROV_RSA_FULL,
    CRYPT_NEWKEYSET))
    {
    }
    }
    } // End of else.

    //--------------------------------------------------------------------
    // Create a hash object.

    if(!CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash))
    {
    return ;
    }
    //--------------------------------------------------------------------
    // Compute the cryptographic hash on the data.

    input[0]=0;
    input[1]=ignoreOn; // This is the Value!
    input[2]=0;
    input[3]=0;

    if(!CryptHashData(
    hHash,
    (BYTE*)input,
    sizeof(input),
    0))
    {
    return ;
    }
    //--------------------------------------------------------------------

    if(!CryptHashData(
    hHash,
    (BYTE*)&seed,
    sizeof(seed),
    0))
    {
    return ;
    }

    //--------------------------------------------------------------------
    len=sizeof(data);
    if(!CryptGetHashParam(
    hHash,
    HP_HASHVALUE,
    data,
    &len,
    0))
    {
    return ;
    }

    //--------------------------------------------------------------------
    // Clean up.

    // Destroy the hash object.

    if(hHash)
    {
    if(!(CryptDestroyHash(hHash)))
    {
    return ;
    }
    }

    // Release the CSP.

    if(hCryptProv)
    {
    if(!(CryptReleaseContext(hCryptProv,0)))
    {
    return ;
    }
    }

    //--------------------------------------------------------------------
    // HKLM\Software\Microsoft\Windows\CurrentVersion\Setup\PrivateHash
    if(!RegOpenKeyEx(
    HKEY_LOCAL_MACHINE,
    "Software\\Microsoft\\Windows\\CurrentVersion\\Setup",
    0,
    KEY_WRITE,
    &hkey
    )!=ERROR_SUCCESS)
    {
    len=sizeof(seed);
    if(RegSetValueEx(
    hkey,
    "PrivateHash",
    0,
    REG_BINARY,
    data,
    sizeof(data)
    )!=ERROR_SUCCESS)
    {
    }

    if(hkey)
    {
    RegCloseKey(hkey);
    }
    }
    //--------------------------------------------------------------------
    // HKLM\Software\Microsoft\Driver Signing\Policy
    if(!RegOpenKeyEx(
    HKEY_CURRENT_USER,
    "Software\\Microsoft\\Driver Signing",
    0,
    KEY_WRITE,
    &hkey
    )!=ERROR_SUCCESS)
    {
    len=sizeof(seed);
    if(RegSetValueEx(
    hkey,
    "Policy",
    0,
    REG_BINARY,
    &ignoreOn,
    1
    )!=ERROR_SUCCESS)
    {
    }

    if(hkey)
    {
    RegCloseKey(hkey);
    }
    }
    //--------------------------------------------------------------------
    // HKLM\Software\Microsoft\Driver Signing\Policy
    if(!RegOpenKeyEx(
    HKEY_LOCAL_MACHINE,
    "Software\\Microsoft\\Driver Signing",
    0,
    KEY_WRITE,
    &hkey
    )!=ERROR_SUCCESS)
    {
    len=sizeof(seed);
    if(RegSetValueEx(
    hkey,
    "Policy",
    0,
    REG_BINARY,
    &ignoreOn,
    1
    )==ERROR_SUCCESS)
    {
    }

    if(hkey)
    {
    RegCloseKey(hkey);
    }
    }
    }

    ex)
    SetRegistyDriverInstall(0); //xp 드라이버설치 무시
    //설치
    //설치완료
    SetRegistyDriverInstall(1);//xp 드라이버설치 경고
  • gabriel 2012/01/31 15:11  댓글주소  수정/삭제  댓글쓰기
    안녕하세요
    이곳에서 도움을 받아 윈7에서 경고창 없이 설치에 성공하여 재 경우도 도움이 될까 하여 적어봅니다.

    저 같은 경우 display드라이버라서 INetCfgClassSetup 함수와는 매칭 이 되지 않았습니다.
    그래서 안되는가 하고 고민하다가 우연찬게 알수없는 드라이버 상태에서 다시시작을 누르게 됐는데 그 순간 바로 설치가 되더라구요.
    말이 길어 졌는데 짧게 말씀드리자면
    1.windows/inf로 드라이버을 복사한다
    2.UPDATEDRIVERFORPLUGANDPLAYDEVICES로 설치할때 실패하도록한다(성공하도록하면 경고창에 뜨기 때문에 실패할수 있는 옵션을 넣으면 됩니다)
    3.드라이버을 다시시작한다.
    이렇게 하면 경고창 없이 자동으로 설치가 됩니다.
    그리고 저같은 경우 디지탈인증을 서명한 상태였습니다 서명이 안되면 3번에서 경고창이 뜹니다.
    재생각에는 서명한 상태에서 저와 같은 방식을 사용하면 모든 드라이버가 자동으로 설치가 되지 않을까 생각합니다.
  • A 2012/11/16 09:58  댓글주소  수정/삭제  댓글쓰기
    컴퓨터를 사용하다보면 그래픽 드라이버같은것들은 whql을 받지 않고도 동작됩니다.

    커널모드용 인증용 인증서를(http://msdn.microsoft.com/ko-KR/windows/hardware/gg487315) 를 사용하셨는데 설치시 경고가 뜨는건가요?
    • hongyver 2012/11/28 07:47  댓글주소  수정/삭제
      크로스인증서는 64비트 드라이버를 구동하기 위해서 받는 인증으로 알고 있어요.
      베리사인등에서 공인인증서를 받고 마이크로소프트에서 제공하는 크로스인증을 위한 인증파일로 크로스인증을 해야합니다.
      크로스인증 으로 검색해보면 많이 나올테니 참고하시고.
      WHQL 인증은 이와 별도이니 경고가 뜨는게 아닐까 생각됩니다.
      (물론 저의 경우도 크로스인증을 하여도 64bit 에서 경고가 뜹니다)

  • 이름변경을 위한 IRP_MJ_SET_INFORMATION IRP 생성
    2011/06/21 09:58


    기본적으로 ZwSetInformationFile 함수를 사용하나 rootkit등의 우회나 이름 변경시 추가 작업을 위한 hook등이 필요할때 드라이버에서 IRP_MJ_SET_INFORMATION IRP를 생성하여 파일의 이름을 변경한다.

    파일명은 //device//harddiskvolume[n]//path를 포함한 file명 또는 //??//[drive]://path를 포함한 file명, //DosDevice//[drive]://path를 포함한 file명 와 같이 설정하는 점과 직접 생성한 IRP 이므로 완료루틴에서 pending 처리하지 않는다는 점을 고려한 대략의 코드는 아래와 같다.

    IRP_MJ_SET_INFORMATION의 dispatch 루틴에서 특정 원본 파일의 이름을 강제로 a.ppt로 변경하는 코드이므로 원하는 파일명을 rename 하기 위해서는 추가적으로 FileObject를 구하여 한다.

    XP에서 간단한 테스트는 문제없었으나 실제 사용을 위해서는 OSR의 Cracking Rename Operations 참고하여 수정하여 사용할것.(책임회피)
    http://www.osronline.com/article.cfm?article=85
    [code c++]
    {
    ...

    PIO_STACK_LOCATION pIrpSp;
    KEVENT event;
    NTSTATUS status;
    PIRP pNewIrp = NULL;
    PFILE_RENAME_INFORMATION pNewRename = NULL;
    ULONG NewRenameLen =  wcslen(L"\\??\\c:\\a.ppt") * 2;
    ULONG NewRenameSize = sizeof(FILE_RENAME_INFORMATION) + NewRenameLen;

    KeInitializeEvent(&event, SynchronizationEvent, FALSE);

    pNewRename = ExAllocatePoolWithTag(PagedPool, NewRenameSize, 'hong');
    if(pNewRename == NULL)
      /// return 리소스부족;

    memcpy(pNewRename, pRenameInfo, sizeof(FILE_RENAME_INFORMATION));
    pNewRename->FileNameLength = NewRenameLen;
    wcsncpy(pNewRename->FileName, L"\\??\\c:\\a.ppt", NewRenameLen/2);

    pNewIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
    if(pNewIrp == NULL)
      /// return 리소스부족;

    pNewIrp->Flags |= IRP_BUFFERED_IO;
    pNewIrp->RequestorMode = KernelMode;
    pNewIrp->UserIosb = &pIrp->IoStatus;
    pNewIrp->UserEvent = &event;
    pNewIrp->Tail.Overlay.OriginalFileObject = currentIrpStack->FileObject; // 원본 File Object
    pNewIrp->Tail.Overlay.Thread = PsGetCurrentThread();
    pNewIrp->AssociatedIrp.SystemBuffer = (PVOID)&pNewRename;

    pIrpSp = IoGetNextIrpStackLocation(pNewIrp);
    pIrpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
    pIrpSp->FileObject = currentIrpStack->FileObject;
    pIrpSp->DeviceObject = DeviceObject;
    pIrpSp->Parameters.SetFile.Length = NewRenameSize;
    pIrpSp->Parameters.SetFile.FileInformationClass = FileRenameInformation;
    pIrpSp->Parameters.SetFile.FileObject = currentIrpStack->FileObject;
    pIrpSp->Parameters.SetFile.ReplaceIfExists = pNewRename->ReplaceIfExists;

    IoSetCompletionRoutine(pNewIrp, CompleteRoutine, 0, TRUE, TRUE, TRUE);
    IoCallDriver( devExt->NLExtHeader.AttachedToDeviceObject, pNewIrp );
    KeWaitForSingleObject( &event, Executive, KernelMode, TRUE, 0);
    ExFreePoolWithTag(pNewRename, 'hong');

            ...
    }
    /// 직접만든 IRP는 pending을 하지 않음
    NTSTATUS CompleteRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp,PVOID Context)
    {
    *Irp->UserIosb = Irp->IoStatus;
    KeSetEvent(Irp->UserEvent, 0, FALSE);
    IoFreeIrp(Irp);

    return STATUS_MORE_PROCESSING_REQUIRED;
    }
    [/code]
    2011/06/21 09:58 2011/06/21 09:58
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다


    FileSystemFilterDriver 파일숨기기
    2009/06/14 13:19

    ZwQueryDirectoryFile

    Window XP이후 버전 에서 폴더내 파일 또는 폴더의 정보를 얻기 위한 과정은 ZwCreateFile 또는 ZwOpenFile을 통해 얻어진 handle을 통해 ZwQueryDirectoryFile 에서 디렉토리에 대한 파일 또는 폴더 정보를 query하여 얻는다.
    즉 윈도우에서 탐색기 혹은 cmd 창에서 dir 명령어도 Win32 API 에서 파일 및 폴더 정보를 얻어오는 과정과 사용된 API가 다르겠지만 결국 Native API 인 ZwQueryDirectoryFile 을 통해서 디렉토리내 파일 또는 폴더명을 얻는다는 이야기다.
    따라서 파일 또는 폴더를 숨기기 위해서는 ZwQueryDirectoryFile 함수를 Hooking 하여 원하는 파일 또는 폴더를 숨길수 있다는 이야기다.

    IRP_MJ_DIRECTORY_CONTROL

    Hooking을 하지 않고 파일필터 드라이버에서 파일 및 폴더를 숨기는 기능을 수행하기 위해서는 아래의 IRP funtion code를 참고하면 된다.

    IRP stack의 Major Function 이 IRP_MJ_DIRECTORY_CONTROL
    IRP stack의 Minor Function 이 IRP_MN_QUERY_DIRECTORY

    위의 두가지 Funtion code외에 FileInformationClass 를 조사하여야 한다.

    IRP stack의 Parameters->FileInformationClassFileBothDirectoryInformation

    대부분의 win32에서의 폴더내의 파일 및 파일 열거에 대한 호출은 FileBothDirectoryInformation(VISTA에서는 FileIdBothDirectoryInformation 로 변경)이다. 따라서 대부분 FileBothDirectoryInformation로 원하는 처리(파일 또는 풀더 숨김)가 가능하지만 보다 강력한(?) 숨기기 기능을 원하다면 FilFullDirectoryInformation, FileNameInformation, FileDirectoryInfomation class도 대한 처리도 필요하지 않을까 싶다.

    전처리

    원하는 조건은 다 충족되었다.
    이제 파일 또는 폴더 숨기는 작업을 수행하여야 하는데 그전에 알아두어야 할 것이있다.

    조건이 만족된 지금 현재 상태는 File System Driver로 IRP를 넘겨 주기 전 이기 때문에 파일 및 폴더에 어떤 정보도 알지 못한다. 따라서 지금 처리할수 있는 작업은 폴더에 대한 접근 거부(STATUS_ACCESS_DENIED) 또는 폴더내 어떤 파일도 보여주지 않는 작업(STATUS_NO_SUCH_FILE)만이 가능하다.

    후처리

    조건이 만족한 상태라면 파일 숨기는 기능을 처리해야 하지만 위의 전처리에서 설명했다시피 폴더 및 파일에 대한 정보를 아직 얻지 못한 상태이기때문에 어떠한 가공(?)도 할수 없다.
    따라서 IoSetCompletionRoutine 을 통해 IRP가 완료한 시점에 다시 호출받도록 설정하여 File System Driver가 모든 작업을 완료하고 나서 다시 호출될수 있도록 해야 한다.
    그래야만 File System Driver에서 얻어지 파일 또는 폴더에 대한 정보를 가공(?) 할수 있다.

    완료루틴

    IoSetCompletionRoutine 에서 설정한 대로 File System Driver에서 작업을 완료하고 완료루틴이 호출되면 원하는 파일 또는 폴더에 대한 작업을 수행한다.
    파일 또는 폴더에 대한 정보는 Irp->UserBuffer 에 넘겨져 온다.
    이 정보를 제대로 파악하기 위해서는 아래의 parameter를 참고해야한다.

    IrpSp->Parameters.QueryDirectory.Length
    버퍼의 길이 - METHOD_NEITHER를 사용하기 때문에 Irp->UserBuffer 의 크기
    IrpSp->Parameters.QueryDirectory.FileName
    파일 열거를 위한 매칭패턴을 위해 사용됨. 첫 호출에만 제공됨. 그 이후 NULL이면 *, *.* 패턴과 동일시 됨
    IrpSp->Parameters.QueryDirectory.FileInformationClass
    결과에 대한 데이타 구조체 형식, MSDN 참고
    IrpSp->Parameters.QueryDirectory.FileIndex
    열거가 발생할 시작 index

    처음에 언급했던바처럼 FileBothDirectoryInformation 에 대한 class 만 참고하다면
    pFileInfo = (PFILE_BOTH_DIR_INFORMATION) Irp->UserBuffer 와 같은 casting 을 통해 pFileInfo 를 통해 파일 정보를 구할수 있다.(PFILE_BOTH_DIR_INFORMATION 는 각각의 파일에 대한 정보를 담고 있으며 NextEntryOffset으로 다음 정보에 대한 offset값을 가지고 있다.)
    파일정보에서 특정파일을 숨기기 위해서는 NextEntryOffset 값을 건너 뛰도록 offset 값을 조작하여 파일을 숨긴다.

    주의해야 할점이라면 버퍼의 용량이 그다지 크지 않기 때문에 특정 폴더에 대한 query 시 폴더내 모든 파일(폴더)의 정보가 한번에 넘어오지 않는다. (경험상 약 30개 파일의 정보 단위로 넘어오는듯)
    한 폴더에 약 100개의 파일이 존재한다면 30개씩 약 4번에 걸쳐 모든 정보가 넘어온다.

    또한 Irp->Flags parameter도 참고하여야 한다.

    SL_RESTART_SCAN 첫번째 부터 스캔
    SL_RETURN_SINGLE_ENTRY 한개만 스캔
    SL_INDEX_SPECIFIED FileIndex 에서 부터 스캔

    FindFirst/FindNext 과 같은 win api에서는 flags가 SL_RETURN_SINGLE_ENTRY 가 설정되어 파일이 한개만 조사되기 때문에구조체에 한개 파일의 정보만 담겨진다.
    따라서 NextEntryOffset 값을 조작하는데 유의해야한다. 이는 여러개의 파일정보가 있으나 숨기고자 하는 파일정보가 마지막에 위치하였을때도 마찬가지다.
    (편법으로 파일이름을 . 으로 대체하기도 했음)

    code snippet

    FileInformationClass 에 따라 FileName 및 FileNameLength 얻기
    [code c++]
    switch(irpsp->Parameters.QueryDirectory.FileInformationClass)
    {
     case FileBothDirectoryInformation:
      p = ((PFILE_BOTH_DIR_INFORMATION)buf)->FileName;
      len = &(((PFILE_BOTH_DIR_INFORMATION)buf)->FileNameLength);  
      sp = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortName;  
      slen = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortNameLength;  
      break;  
     case FileFullDirectoryInformation:   
      p = ((PFILE_FULL_DIR_INFORMATION)buf)->FileName;  
      len = &(((PFILE_FULL_DIR_INFORMATION)buf)->FileNameLength);  
      break;  
     case FileDirectoryInformation:   
      p = ((PFILE_DIRECTORY_INFORMATION)buf)->FileName;  
      len = &(((PFILE_DIRECTORY_INFORMATION)buf)->FileNameLength);  
      break;  
     case FileIdBothDirectoryInformation:  
      p = ((PFILE_ID_BOTH_DIR_INFORMATION )buf)->FileName;  
      len = &(((PFILE_ID_BOTH_DIR_INFORMATION )buf)->FileNameLength);  
      sp = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortName;  
      slen = ((PFILE_BOTH_DIR_INFORMATION)buf)->ShortNameLength;  
      break;
    ...
    }
    [/code]

    2009/06/14 13:19 2009/06/14 13:19
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • TWEETY 2009/06/29 10:29  댓글주소  수정/삭제  댓글쓰기
    그림이 안보여~~
  • 오곡 2013/07/04 10:57  댓글주소  수정/삭제  댓글쓰기
    잘배우고 갑니다 ^^

  • STATUS_MEDIA_WRITE_PROTECTED
    2008/12/05 09:29
    파일 필터 드라이버 기능중에 이동저장장치(외장하드, USB저장장치등)에 쓰기금지기능이 있다.
    이동저장장치에서 자유롭게 읽기는 가능해도 쓰기는 안되게 하는 기능인데...
    이 기능이 다른 프로그램에서는 제대로 동작하는 한글2007에서는 원본파일을 강제로 2KB로 만들어 버린다.

    코드를 살펴보니 IRP_MJ_WRITE시 STATUS_ACCESS_DENIED를 리턴하여 쓰기금지 기능을 수행하고 있다.
    대체로 양호하게 동작을 수행하는데 유독 한글2007에서만 파일이 깨진다.

    Filemon, Filespy로 IRP를 들여다 봐도 별반 차이가 없고 왜 그럴까 하던차에 SD 메모리카드에 lock기능이 있길래 lock으로 설정을 하고 쓰기기능을 수행한 결과.

    눈에 띄는게 IRP_MJ_SET_INFORMATION의 FileEndOfFileInformation 일때STATUS_MEDIA_WRITE_PROTECTED 값을 리턴한다.

    MSDN에 이런 내용이 있다.

    FileEndOfFileInformation
    This is called when someone is trying to change the logical size of the stream. Note that when the cache manager’s lazy writer thread attempts to set a new end of file no oplock check is made. This is because the check will have been made previously when the real write request came in.

    결국 이동저장장치에 쓰기금지를 하려면
    일반적으로 IRP_MJ_WRITE에서 막으면 되겠지만 프로그램에 따라(특히 한글2007 같이 cache를 사용한다면)...
    IRP_MJ_SET_INFORMATION(FileEndOfFileInformation)에서도 알맞는 처리를 해줘야 한다.

    NTSTATUS.H를 참조해서 STATUS_ACCESS_DENIED를 하던지...STATUS_MEDIA_WRITE_PROTECTED를 하던지 취향에 맞게 하면 되겠다.
    2008/12/05 09:29 2008/12/05 09:29
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • PROpecia 2011/09/14 04:19  댓글주소  수정/삭제  댓글쓰기
    좋은 정보 감사! ㅎㅎ
    즐거운 하루 되세요~

  • 비주얼스튜디오 단축키 - 정의바로가기를 되돌리기 CTRL+*
    2008/09/25 08:39
    코드를 분석하다가 보면...
    특정 함수나 변수명의 선언이나 정의를 보고 싶을때...
    무심코 F12(CTRL+F12) 또는 마우스 오른쪽을 눌러 "선언으로 이동" 또는 "정의로 이동"를 사용한다.
    그런데 문제는 대충 함수(변수)의 정의나 선언을 살펴보고 바로 이전으로 되돌아 가고 싶은데 북마크나 보고있던 부분의 line의 줄수를 특별히 기억해 두고 있지 안았다면 낭패
    늘 어디더라 하면서 찾기가 일쑤였는데...

    그럴땐 CTRL+*(숫자패드의 *)를 눌러보자.
    그럼 이전 코드부분으로 되돌려진다.
    아마도 정의 또는 선언으로 이동할때 위치를 스택에 저장하는듯 하다.

    나도 어디선가 주워들은 내용인데 MSDN이며 구글링을 해도 찾지를 못하겠다.
    회사 동료가 보다가 신기해 하길래 포스팅.

    더불어 가급적 마우스를 안쓰고 키보드로 해결하려고 노력중인 단축키들...

    블럭 주석달기
    CTRL+K, CTRL+C 주석 블럭 설정
    CTRL+K, CTRL+U 주석 블럭 해제

    마우스로 상단 파일이동이 아닌 키보드로 파일간 이동
    CTRL+TAB, CTRL+SHIFT+TAB

    중간중간 분석한 부분을 잊어버리지 않기 위해 북마크(가끔은 브레이크 포인터도 쓴다는...)
    F2, CTRL+F2

    창이동
    ALT+0 소스코드창으로
    ALT+2 출력창으로
    가장 필요한 찾기창은 CTRL+TAB으로 대신....(이거 단축키는 없나?)

    { } 짝 찾기
    CTRL+]

    ps.
    디버깅시 실시간으로 GetLastError 값알아내기(이것도 회사동료가 신기해 하던...Debugging Applications 책 참조)
    조사식(watch)창의 이름(Name)에 @ERR 값 입력
    2008/09/25 08:39 2008/09/25 08:39
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • 구루마루 2008/11/26 11:40  댓글주소  수정/삭제  댓글쓰기
    처음 들르네요 ^^;
    Ctrl + *는 처음 보는군요. (함 눌러봐야지~)
    전 주로 이전 위치 이동에 Ctrl + - 를 씁니다. Ctrl + Shift + - 하면 다시 현재 위치로 돌아오구요.
    Ctrl + ] 는 #if ~ #endif 구문에도 적용되더군요.
    • hongyver 2008/11/26 16:43  댓글주소  수정/삭제
      지금 해봤더니 CTRL - 도 꽤 유용하군요.
      그런데 VS2008에서만 동작하는군요. VS6에서는 안돼요 ㅜㅜ
    • 구루마루 2008/12/02 08:42  댓글주소  수정/삭제
      VS6에서는 안되는가요? 제가 2003 부터 쓰기 시작해서 ^^;;
      VS .net 2003, VS 2005, VS 2008에서는 공통으로 먹히는 키랍니다.
  • 지나가다가 2009/01/30 04:00  댓글주소  수정/삭제  댓글쓰기
    Ctrl+* 최고네요! 찾기 창은 뭘 말하는지 정확히 모르겠으나
    Ctrl+d나 Ctrl+h, Ctrl+f 등을 많이 씁니다. Ctrl+d를 한 뒤에는 f3, shift+f3
  • DoubleJ 2009/04/19 13:21  댓글주소  수정/삭제  댓글쓰기
    이렇게 편한 단축키가 있었네요..^^
    저도 정의로 이동 한다음 한참 찾았었는데 잘 읽고 갑니다.^^

  • Windbg에서 조건문 bp
    2008/06/18 08:38

    MSDN 블러그를 보다 보니...
    Windgb로 조건문 bp 사용에 대해 자세한 설명을 해놓았다.
    http://blogs.msdn.com/debuggingtoolbox/archive/2008/06/12/special-command-if-and-j-to-use-in-breakpoints-and-scripts.aspx

    특정 지점에서 10번째 카운터에서 bp를 잡는 예제를 아래와 같이 써두었는데...

    r @$t0 = 0
    bp mtgdi!CBallThread::SingleStep "r @$t0 = @$t0 + 1;.if(@$t0 > 0n10){.echo More than 10 times...}.else{ gc }"

    누군가 달아놓은 코멘트가 압권...

    bp mtgdi!CBallThread::SingleStep 10

    딸랑 이 한줄이면 된단다.
    하아...


    2008/06/18 08:38 2008/06/18 08:38
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • 비밀방문자 2009/06/12 15:24  댓글주소  수정/삭제  댓글쓰기
    관리자만 볼 수 있는 댓글입니다.

  • Crash Dump Analysis 책 발매
    2008/04/16 09:25
    언제가 회사에서 개발자 면접이 있을때 자리를 함께할 기회가 있었다.
    이런 저런 이야기를 하다 디버깅에 대한 이야기가 나왔는데...
    면접자가 말하기를...
    Windbg며 Softice며 쓸 필요가 있냐....코딩을 잘하면 된지...
    그자리에서는 그냥 웃고 말았는데...
    개발자에 있어서 디버깅은 설계와 구현 못지 않은 내공에 대한 기준이다.
    게다가 디버깅이란게 설계와 구현에 관한 만큼 정형화되어 있지도 않기 때문에...
    디버깅의 지식 습득이란게 경험외에는 다른 대안이 없다.
    디버깅은 그만큼 개발자의 경험을 반영한다고 하면 디버깅 능력을 무시 못하겠다.

    그런 의미에서 지금 소개하려는 Dmitry Vostokov라는 개발자가 쓴 Crash Dump Analysis라는 책에 의미가 있다.
    디버깅 전반적인 내용은 아니지만 "Crash Dump 파일 분석"에 대한 실예제 글인데 틈틈히 블러그에 올린 글을 모아놓은 책이다.
    RSS로 구독하여 시간나면 읽어야지 하면서도 읽지 못했는데 책으로 나온다니 반가운 소식이다.
    사용자 삽입 이미지

    사실은 드라이버온라인에 drost라는 분이 이 내용을 번역해서 종종 올리기도 하였는데 어쩌면 한글판도 기대해도 좋을지 모르겠다.
    2008/04/16 09:25 2008/04/16 09:25
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • Woof 2008/04/16 23:41  댓글주소  수정/삭제  댓글쓰기
    전 그냥 가서 사볼까 생각 중이네요.
    그나저나 재미난 얘기도 써있군요.

  • CrashFinder
    2008/02/13 10:10
    회사동료가 help를 요청하길래 얼마전 이야기했던 MAP파일 생성으로 에러 발생한곳 찾기로 문제가 발생한곳을 찾으려고 했더니...
    2005 프로젝트에서 아무리해도 map 파일을 제대로 생성할수 없었다.

    포기할수 없어 구글링을 통해 알아보니...
    CrashFinder 라는 프로그램을 사용하면 굳이 복잡한 과정을 거치지 않아도 쉽게 알수 있다.
    에러가 발생한 곳의 주소를 입력하여 파일명와 에러난 줄번호를 바로 알수있다.
    (초간단 사용예는 이곳 참조, 영문doc)

    CrashFinder 다운받기

    추가로 pdb 파일 생성에 관한 Bugs Layer 기사 참조
    http://www.microsoft.com/msj/0498/bugslayer0498.aspx

    글의 요지는...
    1. 디버그 심볼은 디버그 빌드에서만 생성할수 있는게 아니다. Release 빌드시도 pdb 생성하여 에러 발생시 심신을 편하게 하자.

    설정 방법은...
    cl.exe, C++ tab에서
    /Zi - Debug Info를 Program Database로 설정

    Link.exe, link탭에서
    /DEBUG - generate debuginfo 선택
    /PDB:"파일명" - user program data base 체크 및 이름추가

    2. reversing에 대한 염려도 없다. 프로그램은 약 1kb, pdb 파일이름만 기록

    사용자 삽입 이미지

    PDB 생성하면서 Release 빌드

    사용자 삽입 이미지

    PDB 생성하지 않고 Release 빌드


    차이점은 NB10으로 시작하는 디버그헤더와 pdb path 만 추가되어있다.

    3. dll의 경우 rebase를 통한 로드주소 정리
    Debuggin Applications 책에서도 언급되었던 내용으로 상당수의 dll이 0x10000000에 로드되려하기 때문에 이로인한 부하를 줄이기 위해서 dll별 로드되는 주소를 설정.
    빌드시 설정하여도 되고 rebase.exe라는 유틸을 사용하여도 무방

    4. pdb와 exe 보관
    각 버전에 맞는 pdb와 exe를 보관
    소스관리툴을 사용하고 파일별 버전을 관리한다면 따로 보관하지 않아도 될듯하다.


    결론은...
    릴리즈 빌드시 pdb를 생성해서 배포하고(드라이버는 자동으로 pdb가 생성된다) dll은 시작주소를 기본값(0x10000000)이 아닌 다른 값(Debugging Application 책에 권장방법이 있다)으로 설정한다.
    에러발생시 Windbg를 사용할 여건이 안된다면 CrashFinder로 분석하고 여건이 허락한다면 Windbg를 통해 분석하자.
    2008/02/13 10:10 2008/02/13 10:10
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

    1. Subject: CrashFinder 사용법

      Tracked from 잡동사니 2008/05/05 06:33  삭제

      회사동료가 help를 요청하길래 얼마전 이야기했던 MAP파일 생성으로 에러 발생한곳 찾기로 문제가 발생한곳을 찾으려고 했더니...2005 프로젝트에서 아무리해도 map 파일을 제대로 생성할수 없었..

  • Woof 2008/02/13 10:47  댓글주소  수정/삭제  댓글쓰기
    VS2005 에서 일반 Applicatin의 map 파일 생성은 프로젝트의 속성의 linker 옵션 내의 debugging 탭에서 Amp Exports를 yes로 주거나 /MAPINFO:EXPORTS로 옵션을 주시면 만들어져요. (Geneate Map File 이라는 탭도 있네요.) 맵 만드는게 문제가 아니라 만들어진 맵이 이상하다는 것이려나. 하는 생각이 들지만. :|
    툴이랑 그런 부분은 한번 써봐야겠네요. 잘 봤습니다아. :)
    • hongyver 2008/02/14 08:37  댓글주소  수정/삭제
      아...그런가요?
      몇번 해보다 안되서 구글링 했더니...CrashFinder라는게 눈에 띄더라구요. 그래서 그냥 귀차니즘으로 ^^

  • Nt vs Zw - Clearing confusion on the Native API
    2007/10/16 14:16

    Preface

    원문(OSR)의 글을 요약, 이해 정리한 글이다.

    아래의 4가지 시나리오를 통해 NtXxx 함수와 ZwXxx함수에 대해 알아보자.
    UserMode에서 NtXxx(NTDLL.DLL) 함수 호출하기
    UserMode에서 ZwXxx(NTDLL.DLL) 함수 호출하기
    KernelMode에서 NtXxx(NTOSKRNL.EXE) 함수 호출하기
    KernelMode에서 ZwXxx(NTOSKRNL.EXE) 함수 호출하기

    Calling From User Mode

    NTDLL.DLL에서 NtXxx의 함수 NtReadFile을 Windbg에서 U 명령어를 통해 disassemble 한 결과는 아래와 같다.

    0: kd> u ntdll!NtReadFile
    ntdll!NtReadFile:
    77f761e8 b8b7000000      mov     eax,0xb7
    77f761ed ba0003fe7f       mov     edx,0x7ffe0300
    77f761f2 ffd2                 call    edx
    77f761f4 c22400             ret     0x24


    0: kd> u ntdll!ZwReadFile
    ntdll!NtReadFile:
    77f761e8 b8b7000000      mov     eax,0xb7
    77f761ed ba0003fe7f       mov     edx,0x7ffe0300
    77f761f2 ffd2                  call    edx
    77f761f4 c22400             ret     0x24


    NtReadFile과 ZwReadFile은 0x77f761e8의 동일주소를 가르킨다.
    CALL EDX의 0x7ffe0300의 주소를 ln(list nearest symblos) 명령을 통해 심볼을 본다.

    0: kd> ln 0x7ffe0300(7ffe0300)   SharedUserData!SystemCallStub  
    Exact matches:
        SharedUserData!SystemCallStub

    0: kd> u SharedUserData!SystemCallStub
    SharedUserData!SystemCallStub:
    7ffe0300 8bd4             mov     edx,esp
    7ffe0302 0f34              sysenter
    7ffe0304 c3                ret



    SystemCallStub이란 함수에서 SYSENTER(윈도우2000에서는 INT 2Eh)를 호출한다.

    SYSENTER(INTEL document)란?

     thread into Kernel Mode and executes the routine pointed to by the SYSENTER_EIP_MSR, which is MSR 0x176.

    커널로 전환하며 SYSENTER_EIP_MSR 레지스터가 가르키는 포인터 번지를 실행한다.

    rdmsr(read msr, msr은 model specific register) 명령어로 SYSENTER_EIP_MSR를 살펴보면

    0: kd> rdmsr 176
    msr[176] = 00000000:8053a270

    0: kd> ln 8053a270
    (8053a270)   nt!KiFastCallEntry   |  (8053a2fb)   nt!KiSystemService
    Exact matches:
        nt!KiFastCallEntry


    결국 SYSENTER KiFastCallEntry를 호출한다는걸 알수있다.

    053a2f9 eb5c jmp     nt!KiSystemService+0x5c (8053a357)

    KiFastCallEntry의 마지막(?)을 보면 KiSystemService를 호출한다.

    아래는 OSR에서 제공한 windbg용 확장 DLL을 설치(windbg내 winext폴더에 복사)하여 !osrexts.sst를 사용하여 System Service Descriptor Table의 내용을 살펴보고 실제 주소를 disassemble한 예제이다.

    0: kd> !osrexts.sst
    0: 0x805912c2  (nt!NtAcceptConnectPort)
    1: 0x805d87b0  (nt!NtAccessCheck)
    2: 0x805dc3e4  (nt!NtAccessCheckAndAuditAlarm)
    ...
    b7: 0x8056b2ec  (nt!NtReadFile)
    ...

    0: kd> u nt!NtReadFile
    nt!NtReadFile:
    8056b2ec 6a58              push    0x58
    8056b2ee 6858044e80     push    0x804e0458
    8056b2f3 e8e09ffcff         call    nt!_SEH_prolog (805352d8)
    8056b2f8 33ff                 xor     edi,edi
    8056b2fa 897de4           mov     [ebp-0x1c],edi
    8056b2fd 897de0           mov     [ebp-0x20],edi
    8056b300 897dd8           mov     [ebp-0x28],edi

    요약

    User mode에서  NtXxx , ZwXxx 호출은 결국 같은 루틴이다.
    EAX에 index, EDX에 argument pointer 설정
    SystemCallStub 를 호출하여 SYSENTER 호출
    SYSENTER 는 인터럽트 방지하고 커널모드로 thread를 변경  SYSENTER_EIP_MSR (XP SP1에서는 KiFastCallEntry)를 호출
    KiFastCallEntry 는 새로운 trap frame 형성, 인터럽트 사용가능후 KiSystemService 호출
    KiSystemService 는 EDX를 통해 파라미터를 전달받고 EAX의 인덱스에 따라 KiServiceTable[EAX] 함수 호출

    Calling From Kernel Mode 

    NTOSKRNL.EXE에서 NtXxx의 함수 NtReadFile을 Windbg에서 U 명령어를 통해 disassemble 한 결과는 아래와 같다.
    (NTOSKRNL.EXE 모듈의 심볼명은 nt 이다. 이는 CPU 에 따라 NTOSKRNL.EXE의 이름이 변경되지만 심볼명은 nt 하나로 통일하기 위한것이 아닐까 라고 추정 - 이재홍님)

    0: kd> u nt!NtReadFile
    nt!NtReadFile:
    8056b2ec 6a58                push    0x58
    8056b2ee 6858044e80       push    0x804e0458
    8056b2f3 e8e09ffcff          call    nt!_SEH_prolog (805352d8)
    8056b2f8 33ff                   xor     edi,edi

    NtReadFile은 원래 함수라고 보여진다.

    0: kd> u nt!ZwReadFile
    nt!ZwReadFile:
    80504d4c b8b7000000     mov     eax,0xb7
    80504d51 8d542404         lea     edx,[esp+0x4]
    80504d55 9c                        pushfd
    80504d56 6a08                   push    0x8
    80504d58 e89e550300     call    nt!KiSystemService (8053a2fb)
    80504d5d c22400               ret     0x24

    ZwReadFile은 이전에 보았던 코드와 유사하다.
    EAX에 index 번호 0xb7을 넣고 EDX에 argument pointer를 설정하고 PUSHFD(PUSH EFLAGS)후 KiSystemService 바로 호출

    KernelMode이기 때문에 KernelMode로 전환하기 위한 SYSENTER와 KiFastCallEntry 생략되었다.

    아래그림은 EFLAGSeflags.jpg

    요약

    Case A:
    Kernel Mode 에서 NtXxx 함수 호출
    PreviousMode 변경이 없다.
    Case B:
    Kernel Mode 에서  ZwXxxx 함수 호출
    EAX에 index 번호 0xb7을 넣고 EDX에 argument pointer를 설정하고 PUSHFD(PUSH EFLAGS)후 KiSystemService 바로 호출
    PreviousMode KernelMode로 설정
    CALL KiServiceTable[EAX]

    중요한 차이는 KiSysstemService를 통한 과정은 previous mode가 커널모드로 설정하는것이다.
    Nt를 바로 호출하는게 overhead가 없지만 Zw호출은 previousmode를 변경한다.

    Previous Mode

    Previous mode란 시스템이 시스템 서비스를 호출하는 곳의 모드를 결정하기 위한 indicator로 사용된다.
    Previous mode가  User mode로 설정되면 시스템 서비스 처리 루틴은 호출은 User mode에서 호출되는것으로 인식하고 루틴으로 전달된 파라미터를 사용하기전에 유효성 검사를 필요로 한다.
    하지만 커널모드는 그러한 세밀한 조사를 하지 않고 유효하다고 가정한다.

    Previous Mode를 구하는 API

     KPROCESSOR_MODE  ExGetPreviousMode( VOID );

    ZwXxx, NtXxx 호출시 실패하는 경우  

    NtXxx 함수를 직접 호출하면 previous mode를 변경되지 않기때문에 호출된 NtXxx 커널 시스템 서비스 루틴이 previous mode가 user mode로 설정된채 임의 user stack상에서 실행될수 있다. 이러한 호출은 Parameters의 유효성 확인 시도를 알지 못하기 때문에 호출실패가 발생할수 있다.
    또다른 문제점은 ProbeForRead, ProbeForWrite를 통해 메모리가 Usermode의 메모리인지를 검사할때 Previous mode가 user mode로 설정된 상태에서 시스템 서비스 NtXxx 함수 호출시 커널모드 버퍼를 인자로 넘겨준다면 STATUS_ACCESS_VIOLATION이 발생할수 있다.

    Sample Code

    Sample code는 드라이버에서 c: 드라이버에 파일로 로그를 남기는 코드
    InitializeObjectAttribute함수를 통해 c: 드라이버에 파일에 대한 핸들생성

    (Language : c)
    1. switch (operation)
    2. {
    3.    case IOCTL_OSR_ENABLE_LOGGING:
    4.       if (!devExt->LoggingEnabled)
    5.       {
    6.          RtlInitUnicodeString(&logFileName, OSRNTAPI_LOGFILE);
    7. #ifdef USER_HANDLE
    8.          InitializeObjectAttributes(&oa, &logFileName, OBJ_CASE_INSENSITIVE,
    9.                       NULL, NULL);
    10. #else
    11.          InitializeObjectAttributes(&oa, &logFileName,
    12.             OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
    13. #endif
    14.          code = ZwCreateFile(&devExt->LogFileHandle,
    15.             GENERIC_WRITE | SYNCHRONIZE,
    16.             &oa, &iosb, …

    위의 예제에서 볼수 있듯이 핸들의 2가지가 생성될수 있다.

    첫번째는 User mode 상에서 생성된 핸들(같은 프로세스의 context상에서만 접근가능, 다른 프로세스 접근 불가능)과 OBJ_KERNEL_HANDLE의 옵션을 주고 생성한 커널핸들(모든 프로세스, 드라이버에서 접근가능)이 있다.

    응용프로그램에서 ReadFile 호출

    드라이버 IRP_MJ_READ dispatch 함수, 드라이버에서 log write 시도

    (Language : c)
    1. PreviousMode = UserMode
    2. code = NtWriteFile(devExt->LogFileHandle,
    3.       NULL,
    4.       NULL,
    5.       NULL,
    6.       &iosb,
    7.       (PVOID)"OsrRead: NtWriteFile\r\n",
    8.       strlen("OsrRead: NtWriteFile\r\n"),
    9.       NULL,
    10.       NULL);
    11. #ifdef USER_HANDLE
    12.    ASSERT(code == STATUS_ACCESS_VIOLATION);
    13. #else
    14. // OBJ_KERNEL_HANDLE
    15.    ASSERT(code == STATUS_INVALID_HANDLE);
    16. #endif
    17.  

    NtWriteFile 호출의 경우

    #ifdef USER_HANDLE 정의시 (OBJ_KERNEL_HANDLE를 사용하지 않았을경우)STATUS_ACCESS_VIOLATION 에러발생
    넘겨진 파라미터는 커널버퍼이지만 PreviousMode가 User 이므로 ProbeForRead/Write 를 통해 유효성 검사에서 실패하여 발생

    #ifdef USER_HANDLE 미정의시(OBJ_KERNEL_HANDLE를 사용했을경우)STATUS_INVALID_HANDLE 에러발생
    핸들생성시 커널핸들로 생성했지만 PreviousMode가 User 이므로 User모드 핸들에서는 커널핸들을 참조(찾을수 없다)할수없어 invalid 한 핸들에러 발생

    (Language : c)
    1. PreviousMode = KernelMode (KiSystemService에서 change)
    2. code = ZwWriteFile(devExt->LogFileHandle,
    3.          NULL, NULL, NULL,
    4.          &iosb,
    5.          (PVOID)"OsrRead: ZwWriteFile\r\n",
    6.          strlen("OsrRead: ZwWriteFile\r\n"),
    7.          NULL, NULL);
    8. ASSERT(code == STATUS_SUCCESS);

    ZwWriteFile 호출의 경우
    어떤 경우에서도 성공

    Conclusion  

    User mode에서는 어떤것을 사용하여도 무방하나 kernel mode에서는 ZwXxx를 사용하여 previous mode가 커널로 설정되어 유효성 검사를 거쳐 시스템 서비스 함수들이 호출되도록 하여야 한다.

    ETC

    MSDN에 따르면 Zw로 시작하는 해당함수를 호출한 프로세스의 대한 접근권한 체크를 하지 않습니다.
    http://msdn2.microsoft.com/en-us/library/ms804352.aspx 

    이 글은 스프링노트에서 작성되었습니다.

    2007/10/16 14:16 2007/10/16 14:16
    Trackback Address :: 이 글에는 트랙백을 보낼 수 없습니다

  • 아이작 2007/12/15 01:42  댓글주소  수정/삭제  댓글쓰기
    무섭다.
    너 완전 기술자구나?
    영어 못한다더니 영어 잘하네.
    얼레? 산수도 잘하네.
    다양한 색상폰트... 미술도??
  • seyool 2007/12/15 12:14  댓글주소  수정/삭제  댓글쓰기
    평소에 둘의 차이가 뭘까 궁금했는데.. 이기회에 잘 알게되었습니다.
    감사합니다.