Сигнализация события при вставке флешки

Тема в разделе "WASM.WIN32", создана пользователем groomy, 29 июн 2010.

  1. groomy

    groomy New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    5
    Доброго времени суток!
    Столкнулся со странным поведением объекта "событие" при установленном хуке WH_CBT в момент вставки флешки в USB порт.
    Глюк проявляется только при компиляции в VC6+SP6 и никак не хочет проявляться в Visual Studio 2008.
    Итак , код:
    Код (Text):
    1. #pragma data_seg(".shared")  
    2.    HHOOK g_hook=NULL;
    3. #pragma data_seg()
    4.  
    5. #pragma comment(linker, "/SECTION:.shared,RWS")
    6.  
    7. HINSTANCE g_hInstance;
    8.  
    9. LRESULT CALLBACK HookFunc(int nCode, WPARAM wParam,LPARAM lParam){
    10.     return CallNextHookEx(g_hook, nCode, wParam, lParam);
    11. }
    12.  
    13. extern "C" __declspec(dllexport) void InitHook(){
    14.    g_hook=SetWindowsHookEx(WH_CBT,HookFunc,g_hInstance,0);
    15.    MSG msg;
    16.    int ret;
    17.    // ставим цикл обработки сообщений
    18.    while((ret=GetMessage(&msg,0,0,0))!=0){
    19.         if(ret==-1){
    20.        
    21.     } else {
    22.        DispatchMessage(&msg);
    23.     }
    24.    }
    25. }
    26.  
    27. unsigned WINAPI MyThread(void *p){
    28.    HANDLE hEvent=CreateEvent(NULL,false,false,"myevent");
    29.    DWORD dwErr=GetLastError();
    30.    if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то
    31.        SetEvent(hEvent);            // глюка
    32.        Sleep(3000);                 // не происходит
    33.    }
    34.  
    35.    WaitForSingleObject(hEvent,INFINITE); // ожидаем бесконечно
    36.    CloseHandle(hEvent);
    37.    ExitProcess(0);                       // завершаемся
    38.    return 0;
    39. }
    40.  
    41.  
    42.  
    43. BOOL APIENTRY DllMain( HANDLE hModule,
    44.                        DWORD  ul_reason_for_call,
    45.                        LPVOID lpReserved
    46.                      )
    47. {
    48.     if(ul_reason_for_call==DLL_PROCESS_ATTACH){
    49.        g_hInstance=(HINSTANCE)hModule;
    50.        char szFileName[MAX_PATH];
    51.        GetModuleFileName(NULL,szFileName,MAX_PATH);
    52.        strlwr(szFileName);
    53.            BOOL bOk=false;
    54.        if(strstr(szFileName,"notepad")!=NULL || strstr(szFileName,"rundll32")!=NULL) bOk=true;
    55.        
    56.        if(bOk){
    57.                    UINT uid;
    58.            HANDLE hTh=(HANDLE)_beginthreadex(NULL,0,MyThread,NULL,0,&uid);
    59.            CloseHandle(hTh);
    60.        }
    61.        
    62.        return bOk;
    63.     }
    64.     else
    65.     if(ul_reason_for_call==DLL_PROCESS_DETACH){
    66.       UnhookWindowsHookEx(g_hook);
    67.     }
    68.  
    69.     return TRUE;
    70. }
    Вкратце суть приложения:
    — это длл-ка, которая запускается через rundll32.exe и устанавливает хук WH_CBT хук;
    — чтобы процесс rundll32 не завершился, устанавливается цикл приема сообщений(GetMessage\DispatchMessage);
    — из DllMain порождается поток в котором создается некое событие, а после ставится бесконечное ожидание на нем;
    — при запуске блокнота, в процесс notepad.exe внедрится наша длл.

    Обратите внимание на эту часть кода функции потока:
    Код (Text):
    1.    DWORD dwErr=GetLastError();
    2.    if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то
    3.        SetEvent(hEvent);            // глюка
    4.        Sleep(3000);                 // не происходит
    5.    }
    Не важно, какую смысловую нагрузку несет данный код. Важно то, что по логике при запуске единственного экземпляра rundll32 (событие с именем myevent больше нигде не открывается и не создается) функция GetLastError никак не может вернуть ERROR_ALREADY_EXISTS, поэтому два вызова — SetEvent и Sleep никогда не выполнятся. Так и есть, эти два вызова не происходят, выполнение сразу переходит на WaitForSingleObject. Здесь идет бесконечное ожидание. И вот ! Как только мы вставляем флешку в USB порт, каким-то невообразимым образом, событие hEvent стает сигнальным и вызов WaitForSingleObject завершается с кодом WAIT_OBJECT_0.

    В сущности, смысл выше приведенного участка прост: если запустили еще один rundll32, прогрузив туда нашу длл, то нужно завершить предыдущий — там ведь идет ожидание события hEvent, которое стает сигнальным, если GetLastError возвращает ERROR_ALREADY_EXISTS. После, уже в новом экземпляре rundll32 будет происходить бесконечное ожидание на событии hEvent c именем myevent до того момента, как кто-то снова не запустит rundll32, прогрузив нашу длл-ку — предыдущий завершится. И так далее.

    Самое забавное, что глюка при вставке флешки не происходит, если код откомпилировать в VS2008. Попробуйте компильнуть код в VC6 (у меня sp6) и после запустить длл-ку из командной строки: rundll32.exe c:\project\some\debug\prg.dll, InitHook
    И вставьте флешку и увидите, что процесс завершится.
    Как это пояснить с позиции логики, пока не пойму. Я так понимаю, что всему виной цикл обработки сообщений GetMessage\DispatchMessage, ведь именно GetMessage принимает событие с кодом 0x219 (WM_DEVICECHANGE) и параметром 7 (DBT_DEVNODES_CHANGED) в момент вставки флешки. Но выхода-то из цикла не происходит, GetMessage как бы даже не успевает возвратиться даже, как WaitForSingleObject завершает ожидание (событие hEvent стало сигнальным). Кстати если заменить цикл обработки
    Код (Text):
    1.    while((ret=GetMessage(&msg,0,0,0))!=0){
    2.         if(ret==-1){
    3.        
    4.     } else {
    5.        DispatchMessage(&msg);
    6.     }
    7.    }
    на
    Код (Text):
    1.   Sleep(INFINITE);
    Глюк будет тот же — при вставке флешки событие стает сигнальным и все тут!

    А теперь фокус.
    Убираем эту часть кода:
    Код (Text):
    1.    DWORD dwErr=GetLastError();
    2.    if(dwErr==ERROR_ALREADY_EXISTS){ // если убрать эту ветку, то
    3.        SetEvent(hEvent);            // глюка
    4.        Sleep(3000);                 // не происходит
    5.    }
    И вуаля! Глюк исчезает. Получается, будто какой-то невидимый фантомный код запускает эту ветку постфактум (или функцию потока заново) — и, так как событие myevent уже есть, GetLastError возвратит ERROR_ALREADY_EXISTS и вызов SetEvent(hEvent) cделает свое черное дело. Кстати , если убрать этот вызов SetEvent(hEvent), то глюк исчезает. Также , если убрать хуки, то глюк исчезнет. Но как? Почему? В чем конфликт? Почему 2008 студия не подвержена этому?
    Система: Windows XP SP3
    Заранее благодарен за ответы!
     
  2. skomarov

    skomarov New Member

    Публикаций:
    0
    Регистрация:
    14 май 2008
    Сообщения:
    389
    groomy
    Может быть ошибка кодогенерации? Проявляется всегда или только в debug (или release) режиме?
    Для отладки попробовать:
    1) сбрасывать код ошибки через SetLastError();
    2) сгенерировать уникальное имя через UID.
     
  3. groomy

    groomy New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    5
    К сожалению и в debug и в release версиях проявляется одинаково. По поводу имени - разные имена пробовал, тут дело не в этом.
    Код ошибки сбрасывал и перед вызовами и после. Наличие оптимизации никак не влияет тоже.
    В кодогенерации ошибка... даже не знаю как проверить. Пробовал по разному - пока результат одинаков.
    Если кому не тяжело, компильните выше приведенный код в VC6 , он дан полностью за исключением include. И гляньте, проявляется ли у вас такая проблема. Может, у меня с системой что-то не так...
     
  4. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Не пойму в чем суть проблемы. Вы вызываете SetEvent(hEvent) и потом удивляетесь почему WaitForSingleObject(hEvent) возвращает WAIT_OBJECT_0?
     
  5. groomy

    groomy New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    5
    В том то и дело, что SetEvent находится на участке кода, который не получает управление - как этот участок может получить управление если GetLastError не возвращает ERROR_ALREADY_EXIST, потому как события с таким именем нет еще, мы его только что создали ведь. Но после вставки флешки этот участок почему-то получает управление, это абсурд просто! Гляньте на код, там все очень наглядно.
     
  6. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    Это с какой стати? Вы же запустили rundll32 с параметрами Вашей DLL. При вставке флешки как раз таки объект уже существует и ветка выполнится.
     
  7. groomy

    groomy New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    5
    простите, а с какой стати после вставки флешки управление попадает в мой поток? Есть процесс rundll32, в который прогружена моя длл. Она же , после прогрузки в этот процесс установила глобальный хук WH_CBT. Висит себе процесс, никого не трогает. Вставили флешку. SetEvent в моем потоке почему-то сработал и ожидание на hEvent завершилось, после чего процесс завершился (вызов ExitProcess после WaitForSingleObject). Выходит, после вставки флешки кто-то инициировал создание потока. Инициация создания потока возможна из DllMain при условии что процесс в который прогрузилась длл либо rundll32 либо notepad. Получается, что моя дллка снова попыталась прогрузится в rundll32 из-за установленого хука и был вызов DllMain c DLL_PROCESS_ATTACH? Но ведь она там есть уже (поток выполняется то наш, ожидание на событии висит), система же проверяет перед внедрением, нет ли в процессе уже такой. И если нет, то только тогда внедряет! Или в моих рассуждениях есть ошибка?
     
  8. google

    google New Member

    Публикаций:
    0
    Регистрация:
    10 авг 2007
    Сообщения:
    140
    При вставке флешки инициализируется диалоговое окно автозапуска, которое, на сколько я помню, запускается с помощью rundll32. Т.е. создается новый процесс rundll32, создается новый поток MyThread, вызывается CreateEvent("myevent"). Естественно CreateEvent возвращает ERROR_ALREADY_EXIST, т.к. объект существует (Вы его создали при первом запуске rundll32). В итоге оба потока завершатся после WaitForSingleObject(hEvent), т.к. событие свершилось.
     
  9. groomy

    groomy New Member

    Публикаций:
    0
    Регистрация:
    29 июн 2010
    Сообщения:
    5
    google
    Блин, 100 %. У меня просто это окно отключено, но rundll32 и правда вызывается , считываются параметры - показывать это окно или нет...
    Вам огромная благодарность за подсказку, чето не догадался сам :dntknw:
    Тему можно закрывать!
     
  10. punxer

    punxer Андрей

    Публикаций:
    0
    Регистрация:
    16 окт 2006
    Сообщения:
    1.327
    Адрес:
    Ржев
    Интересненько и поучительно!
     
  11. RET

    RET Well-Known Member

    Публикаций:
    17
    Регистрация:
    5 янв 2008
    Сообщения:
    789
    Адрес:
    Jabber: darksys@sj.ms
    Для определения вставки флэхи давно уж нотификаторы [ч\з RegisterDeviceNotification] нужно использовать (читаем MSDN).