<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-32803974</id><updated>2008-07-17T00:41:01.344-03:00</updated><title type='text'>DriverEntry</title><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default?start-index=26&amp;max-results=25'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>52</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-32803974.post-7634273384502241046</id><published>2008-07-12T07:34:00.008-03:00</published><updated>2008-07-14T11:47:43.950-03:00</updated><title type='text'>Agora que virei Home</title><content type='html'>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Ballet.jpg" border="0" /&gt;&lt;p&gt;De repente meu gerente me liga e pergunta - &lt;em&gt;"Você é Home, certo?"&lt;/em&gt;. Fiquei meio constrangido com aquela situação, mas o que eu podia fazer? Eu tinha que responder àquela pergunta. &lt;em&gt;"Olha, ainda não sou completamente, mas estou interessado em virar"&lt;/em&gt;, respondi. &lt;em&gt;"Então preencha os formulários que eu aprovo"&lt;/em&gt;. Foi basicamente assim que virei &lt;strong&gt;Home Office&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;Como funcionário regular, eu ainda tinha o direito de fazer Home Office 2 dias por semana. No início vem um sentimento de culpa. Não dá pra aceitar, assim de cara, que não terei que levantar às cinco e meia da manhã e enfrentar quase duas horas de trânsito para sair de Santo André, pegar a Avenida dos Estados e finalmente chegar ao prédio onde trabalho. Confesso que não sair de casa tão cedo e me sentar em frente ao notebook da empresa usando um moletom causa um certo desconforto no início. Dá a impressão de que não estamos trabalhando, de que estamos fazendo alguma coisa errada.&lt;/p&gt;&lt;p&gt;Depois do sentimento de culpa vem o relaxo. Até hoje eu continuo levantando cedo para poder levar minha esposa até a estação de trem, que fica à exatos 7 minutos de minha casa. Nas primeiras semanas, eu voltava para a cama e dava mais uma dormida até às nove da manhã. Já consegui chegar no trabalho com 30 minutos atraso mesmo estando a vinte metros dele. Tomar café da manhã em frente ao notebook virou rotina. Mas isso passou. Acho que conforme o tempo foi passando, o corpo e a mente vão ficando mais descançados, até uma hora que não dá mais vontade de voltar para a cama. Além do mais, eu ainda tenho uma lista razoável de livros para ler. O tempo que antes eu gastava no trânsito, agora eu gasto lendo e isso está dando muito certo.&lt;/p&gt;&lt;p&gt;Meus vizinhos agora devem pensar que fiquei desempregado e que sou sustentado pela minha esposa. Tem uma pessoa que além limpar minha casa, prepara meu almoço. Assim, eu consigo almoçar em 15 minutos e não vou voltar correndo para o notebook depois disso. Eu preciso fazer minha &lt;a href="http://gl.wikipedia.org/wiki/Fotos%C3%ADntese"&gt;fotosíntese&lt;/a&gt;. Para isso vou tomar um café expresso em uma padaria que fica láááááááá em cima e aproveito para tomar sol caminhando até a padaria. Ter horário para tomar sol até parece programa de presidiário. Às vezes eu levo meus cães para passear durante meu horário de almoço. Eu não culpo meus vizinhos. O que você pensaria se visse uma pessoa de uns trinta anos de idade, com a barba de dias na cara, passeando com cachorros ao meio dia? &lt;em&gt;"Esse aí leva cães para passear para poder conseguir o dinheiro da pinga"&lt;/em&gt;.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/TrabalheEmCasa.PNG" border="0" /&gt;&lt;p&gt;Bom, eu estou mesmo babando e andando para os vizinhos. Complicado é o que seus parentes pensam de você. Para tomar café, eu passo em frente à casa de uma tia. Sabe aquela tia que tudo o que ela conseguiu entender sobre o que você faz é que você trabalha com computadores, ela só não tem certeza se é limpando ou vendendo. Numa dessas idas e vindas do café, acabei me encontrando com ela. &lt;em&gt;"E aí Fernando, está de folga?"&lt;/em&gt; - ela pergunta. &lt;em&gt;"Não tia, agora eu trabalho em casa"&lt;/em&gt; - eu respondo todo satisfeito, mas olhando sua expressão, dá pra imaginar as engrenagens na cabeça dela - &lt;em&gt;"Vixe tadinho, deve estar fazendo aqueles bicos do tipo &lt;strong&gt;Trabalhe em casa, pergunte-me como&lt;/strong&gt;"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Mais difícil que se convencer de que você pode trabalhar em casa, é convencer os outros de que você está trabalhando em casa. Na época em que eu fazia Home Office apenas 2 vezes por semana, minha esposa sempre me perguntava - &lt;em&gt;"Você vai trabalhar amanhã?"&lt;/em&gt;, como se eu não trabalhasse em casa. Meu irmão uma vez me disse, &lt;em&gt;" ...mas você não vai ficar em casa amanhã? Então, eu passo lá e a gente vai ver aquela peça do carro"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;Trabalhar em casa exige disciplina, embora eu possa fazer meu próprio horário, eu prefiro ter horário fixo para almoçar, começar e parar de trabalhar. Não estou falando apenas de trabalhar menos. Tabalhar demais é um risco maior. Você já está alí com um pepino pra resolver, falta só mais aquele último teste, que é o último há uns 5 testes atrás, você não tem que pegar trânsito, já está no conforto do seu lar e quando você vê já são nove da noite.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Não muda nada para a empresa?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/homeoffice.JPG" border="0" /&gt;&lt;p&gt;Eu trabalho em um time global. Uns aqui, outros nos Estados Unidos e India. O gerente do time de desenvolvimento está em outro hemisfério. Você acha mesmo que isso vai fazer alguma diferença pra ele? Mesmo quando eu estava trabalhando no prédio, tudo que eu precisava era de um ponto de rede. Apesar de eu ter um notebook, este é utilizado para abrir uma sessão remota em uma das máquinas que servem os laboratórios externos (Brasil e India). As máquinas de teste são virtuais em sua maioria, as reais também são acessadas remotamente. Todas as reuniões são feitas via &lt;em&gt;Web Conference&lt;/em&gt;. Seu ramal é desviado para uma ferramenta VOIP no notebook. Você disca um ramal pra falar com alguém do outro lado do planeta. Tudo através de um ponto de rede.&lt;/p&gt;&lt;p&gt;Quando você vira Home Office, a empresa te dá um limite de reembolso para comprar móveis, aparelho e conta telefônica, Internet banda larga, impressora e material de escritório. Obviamente tudo tem que ser devolvido quando você deixar de ser Home Office. Felizmente minha casa já tinha todos os requisitos necessários, e assim, não vou ficar usando uma mesa ou cadeira que não são minhas mesmo. O bom disso é que eu posso trocá-los quando eu quiser.&lt;/p&gt;&lt;p&gt;O que principalmente vai mudar para a empresa, é que agora eles terão mais uma baia livre. Uma empresa deste tamanho não cabe nos prédios onde estamos (isso mesmo, no plural). A empresa incentiva o Home Office. Uma das cláusulas da transição para Home Office é a de que você deve permanecer no mínimo um ano nesta condição. Fiquei preocupado quando li essa parte, então fui perguntar às pessoas que já eram Home Office o que eu não estava conseguindo ler nas entrelinhas. O que acontece é que na empresa que trabalho, a grande maioria dos funcionários são vendedores. Vendedor é o tipo de profissional extrovertido, que fala bastante e preza muito o contato com as pessoas. Para esse tipo de pessoa, ficar enclausurado dentro de casa é a morte. Tanto que muitas pessoas que são Home Office deixam suas casas para disputar baias vagas aos tapas lá no prédio. Minha esposa mesmo, se ficasse em casa, morreria louca mordendo o braço do sofá em menos de um mês.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/LivroIntrovertido.jpg" border="0" /&gt;&lt;p&gt;Pois é, existem pessoas e pessoas. Como você já deve ter percebido, sou desenvolvedor de software. Sou do tipo de pessoa que prefere ter seu tempo para fazer as coisas, gosto de ter meu espaço. Não estou dizendo que tenho alergia a pessoas e que ando com um saco de papel enfiado na cabeça. Só estou dizendo que não me importo de ficar trabalhando sozinho em casa, onvindo meu som preferido enquanto faço tudo pela internet. Aliás, isso me dá um gancho pra falar de um livro que li e que imaginei nunca ter a oportunidade de comentar a respeito neste blog técnico. &lt;a href="http://publifolha.folha.com.br/catalogo/livros/145101/"&gt;Este livro&lt;/a&gt; fala justamente das diferenças entre pessoas extrovertidas e introvertidas. Explica que apesar de 75% das pessoas serem extrovertidas, não há nada errado em ser introvertido. O livro explica que pessoas introvertidas tem lá suas vantagens. Elas se concentram com maior facilidade. Conhecem menos assuntos, mas com mais profundidade. Tem menos amigos, mas são amizades mais intensas. Achei o livro interessante, com suas estatísticas e fundamentos para explicar o por quê do comportamento das pessoas.&lt;/p&gt;&lt;p&gt;Ainda não falei tudo que poderia falar sobre minha transição para Home Office, mas este post já está ficando longo demais para um &lt;em&gt;Off Topic&lt;/em&gt;.&lt;br&gt;
Até mais...&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/07/agora-que-virei-home.html' title='Agora que virei Home'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=7634273384502241046' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/7634273384502241046'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/7634273384502241046'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-3056670469352328464</id><published>2008-07-02T14:54:00.005-03:00</published><updated>2008-07-02T16:40:15.833-03:00</updated><title type='text'>Step into Kernel (Firewire)</title><content type='html'>&lt;p&gt;Já sei! Seu computador de teste não tem porta serial e você precisa fazer debug de Kernel nele. Creio que depois da porta serial, a maneira mais utilizada para depurar o Kernel do Windows seja utilizando uma interface &lt;a href="http://pt.wikipedia.org/wiki/FireWire"&gt;firewire&lt;/a&gt;. Ainda existe a opção de se fazer o debug de Kernel utilizando USB 2.0, mas isso ainda é para poucos, já que além de apenas ser suportado pelo Windows Vista, ainda é necessário ter um cabo especial. Os detalhes sobre debug de Kernel pela porta USB podem ser encontrados &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;neste post&lt;/a&gt;. Hoje a história é outra.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Mas eu não tenho porta firewire&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Larga a mão de ser chorão. O que importa aqui não é o fato de você ter ou não uma porta firewire em seu micro de desenvolvimento, mas sim o fato da máquina do cliente ter uma porta firewire. Você sabe muito bem que pela &lt;a href="http://pt.wikipedia.org/wiki/Lei_de_Murphy"&gt;Lei de Murphy&lt;/a&gt;, aquele problema que você nem sabia que existia só acontece naquela máquina que não tem portas seriais. Então melhor você estar preparado para encontrar esse tipo de coisa. Elas realmente acontecem. Sua reclamação poderia ainda ser diferente: &lt;em&gt;"Mas eu não tenho porta serial"&lt;/em&gt;. Alguns notebooks que não possuem portas seriais oferecem portas firewire, mas idependente disso, existem placas tanto &lt;a href="http://pt.wikipedia.org/wiki/Peripheral_Component_Interconnect"&gt;PCI&lt;/a&gt; quanto &lt;a href="http://pt.wikipedia.org/wiki/Pcmcia"&gt;PCMCIA&lt;/a&gt; capazes que disponibilizar a interface &lt;a href="http://pt.wikipedia.org/wiki/FireWire"&gt;IEEE 1394&lt;/a&gt;. Desta forma, seja sua máquina um desktop ou um notebook, existem meios delas ganharem portas firewire.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o lado TARGET&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Se você ainda não sabe o que significa lado HOST/TARGET e está completamente perdido sobre o assunto, leia &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;este post introdutório&lt;/a&gt; antes de continuar. Configurar o lado TARGET não é muito diferente do que já vimos em outros posts desta série. Podemos editar o aquivo boot.ini, como já vimos &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;neste post&lt;/a&gt; para adicionar as seguintes configurações de debug.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;[boot loader]
timeout=10
default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
[operating systems]
multi(0)disk(0)&amp;lt;&amp;lt;...&gt;&gt;/fastdetect &lt;font color="#ff0000"&gt;/debugport=1394 /channel=44&lt;/font&gt;
multi(0)disk(0)&amp;lt;&amp;lt;...&gt;&gt;/fastdetect&lt;/pre&gt;&lt;p&gt;O número do canal a ser utilizado pode ser qualquer um, mas o valor deve ser o mesmo em ambos os lados TARGET e HOST. Caso você esteja querendo depurar um Windows Vista, o método de configurar as mesmas coisas mudaram um pouco como vimos &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;neste outro post&lt;/a&gt;. A figura a seguir mostra os passos para setar o modo de configuração do sistema para interface IEEE 1394.&lt;/p&gt;&lt;p&gt;Lembre-se que aqui estamos apenas configurando a maneira com a qual o sistema seria depurado caso exista uma entrada de debug na lista de boot da máquina. &lt;a href="http://www.driverentry.com.br/blog/2008/03/step-into-kernel-vista-usb2.html"&gt;Este post&lt;/a&gt; mostra os detalhes de como criar uma entrada adicional nesta lista e configurá-la para debug.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Vista1394Cmd.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Só isso? Nem doeu!&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Disable1394.png" border="0" /&gt;&lt;p&gt;Para se fazer Kernel Debug utilizando um cabo firewire, ambos os micros devem estar rodando Windows XP ou superior, não necessariamente a mesma versão em ambos os lados. Existe uma particularidade quanto utilizar sistemas anteriores ao Windows XP SP2 ou Windows 2003 Server sem service pack no lado TARGET da história, conforme informa &lt;a href="http://msdn.microsoft.com/en-us/library/cc501183.aspx"&gt;esta página&lt;/a&gt;. Para estes sistemas, deve-se desabilitar a controladora do barramento 1394. Isso é necessário porque o Windows, que está sendo depurado inconscientemente, pode querer tentar conversar com a interface Firewire durante o debug, e isso pode fazer com que a conexão com o depurador caia. Para desabilitar essa interface nos sistemas acima citados, você deve simplesmente selecionar o ítem &lt;em&gt;"Disable"&lt;/em&gt; no menu de contexto que aparece quando você clica com o botão direito do mouse sobre a controladora firewire.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Posso desabilitar a controladora Firewire independente da versão do Windows?&lt;/strong&gt; Não, se você desabilitar a controladora em sistemas posteriores aos acima citados, você poderá não conseguir depurar o sistema quando ele mudar entre os estados de energia do sistema. &lt;strong&gt;Estados de energia? Você está falando da &lt;a href="http://pt.wikipedia.org/wiki/Aura"&gt;aura&lt;/a&gt; do computador?&lt;/strong&gt; Supondo que você está querendo depurar seu driver durante as transições energia do sistema que são gerenciadas pelo Power Manager. O Power Manager determina qual barramento pode ser desligado para economizar energia. Assim, o sistema pode decidir desligar a interface firewire durante seu debug de um driver qualquer, e dessa forma, você não vai conseguir acompanhar as &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;IRPs&lt;/a&gt; de gerenciamento de energia chegando ao seu driver.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o lado HOST
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Normalmente, para fazer iniciar uma sessão de debug do lado HOST, basta abrir o WinDbg, selecionar o ítem &lt;em&gt;"Kernel Debug..."&lt;/em&gt; do menu &lt;em&gt;File&lt;/em&gt;, clicar na aba 1394, preencher o número do canal que se deseja utilizar, clicar OK como mostra a figura abaixo e correr para o abraço.&lt;/p&gt;
&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/KrnDbg1394.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Tudo isso que acabei de descrever continua valendo, mas para que seja possível utilizar firewire do lado HOST, o WinDbg precisa instalar os drivers virtuais de acesso ao barramento IEEE 1394, como mostra &lt;a href="http://msdn.microsoft.com/en-us/library/cc266341.aspx"&gt;nesta página&lt;/a&gt;. O WinDbg faz isso automagicamente quando você seleciona as opções acima, mas os drivers só poderão ser instalados se você estiver logado como administrador do sistema. Caso contrário você receberá a seguinte mensagem.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/AdminNeeded.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;&lt;strong&gt;Pô Fernando, sou desenvolvedor de driver! Você acha mesmo que não sou administrador da minha máquina?&lt;/strong&gt; Tudo bem, você pode até ser, mas mesmo sendo um administrador no Windows Vista você precisará executar o WinDbg clicando com o botão direito do mouse sobre o ícone do WinDbg e selecionar o ítem &lt;em&gt;"Run as Administrator"&lt;/em&gt;. Aí sim, você repete o procedimento descrito acima para que os drivers virtuais sejam instalados. Esse procedimento é necessário somente na primeira vêz que você utiliza a porta firewire para debug, nas próximas vezes, os drivers já estarão instalados. Para quem estiver utilizando um sistema anterior ao Vista e já estiver utilizando uma conta admininstrativa, é como meu amigo &lt;a href="http://codebehind.wordpress.com/"&gt;Thiago&lt;/a&gt; diz: &lt;em&gt;"Sai na urina"&lt;/em&gt;. Ou seja, o driver virtual será instalado e você terá a saída como mostra a figura abaixo, que demonstra as mesmas operações realizadas no Windows Vista rodando o WinDbg como Administrador.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/Install1394OK.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Daí em diante é só debug mesmo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Dump Racing&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Aproveitando que estamos todos aqui reunidos, vamos fazer um teste e verificar se a velocidade do firewire ajuda mesmo com relacão à fazer Kernel Debug. Vamos imaginar a situação onde você esteja em visita a um cliente onde obviamente seu driver não está funcionando adequadamente. Lembra daquele bug que você nem sabia que existia? Pois bem, se trata de um &lt;a href="http://pt.wikipedia.org/wiki/Deadlock"&gt;deadlock&lt;/a&gt;. Deadlock são especialmente queridos na hora de fazer debug, porque você está lá quando o problema acontece, mas &lt;a href="http://www.driverentry.com.br/blog/2007/09/como-assim-eu-no-gosto-de-tela-azul.html"&gt;tela azul que é bom&lt;/a&gt; nada. Nessa situação, você pode gerar um arquivo de dump da máquina e deixar para analizar o problema em casa, afinal de contas, roupa suja se lava em casa, e assim poder liberar a máquina do cliente para uso, já que normalmente nessas situações, ficam umas três pessoas em cima de você perguntando &lt;em&gt;"E aí? Descobriu o problema?"&lt;/em&gt; a cada 3 minutos. Configurei meu desktop aqui de casa para fazer debug do Windows Vista por uma porta serial. Esta máquina tem 2GB de memória RAM. Um arquivo de dump full é uma cópia de tudo que esta na memória do computador naquele instante, por isso, nada mais justo que este arquivo tenha aproximadamente 2GB de tamanho. Este arquivo pode ser gerado na máquina HOST durante uma sessão de debug utilizando o comando &lt;a href="http://msdn.microsoft.com/en-us/library/cc266761.aspx"&gt;.dump&lt;/a&gt;. Segue abaixo a saída deste comando quando utilizado sobre uma conexão serial.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;0: kd&gt; .dump /f c:\Temp\SERIAL.DMP
Creating a full kernel dump over the COM port is a &lt;font color="#ff0000"&gt;VERY VERY slow&lt;/font&gt; operation.
This command may take &lt;font color="#ff0000"&gt;many HOURS&lt;/font&gt; to complete.  Ctrl-C if you want to terminate the command.
Creating c:\Temp\SERIAL.DMP - Full kernel dump
Percent written 0
Percent written 1
Percent written 2
        :
        :&lt;/pre&gt;&lt;p&gt;Vocês repararam na mensagem ameaçadora que nos foi exibida? Particularmente penso que estes engenheiros de software são todos uns desesperados, provavelmente por causa quantidade de café que eles consomem por dia. Já posso até imaginar quantos nem esperam o dump começar para já pressionar CTRL+C e interromper o processo. São uns covardes mesmo. Bom, já que isso vai me custar algum tempo, vou aproveitar para dar uma mijada.&lt;/p&gt;&lt;p&gt;Muito, mas muito tempo mesmo depois...&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;        :
        :
Percent written 97
Percent written 98
Percent written 99
Dump successfully written
0: kd&gt;&lt;/pre&gt;&lt;p&gt;OK, tudo bem até aqui. Agora vamos repetir o processo em uma sessão de debug utilizando o cabo firewire. A mesma máquina com a mesma quantidade de memória e até o mesmo comando.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;1: kd&gt; .dump /f c:\Temp\FIREWIRE.DMP
Creating c:\Temp\FIREWIRE.DMP - Full kernel dump
Percent written 0
Percent written 5
Percent written 10
        :
        :
Percent written 90
Percent written 95
Dump successfully written
1: kd&gt;&lt;/pre&gt;&lt;p&gt;Tá, tudo bem, a contagem vai de cinco em cinco ao invés de um em um como foi com o cabo serial, grande coisa. Não é a toa que demore mais pela porta serial, eles gastam processamento com tudo. Agora podemos comparar as datas de criação e modificação nos atributos de cada arquivo para poder determinar quanto tempo levou para poder gerá-los.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DumpRacing.PNG" /&gt;&lt;/div&gt;&lt;br&gt;
&lt;p&gt;Lembrando que o mês de junho termina no dia 30, podemos concluir que o dump pela porta serial levou aproximadamente 2 dias, 5 horas e 12 minutos. É uma pena essa janela não mostrar os segundos para termos mais precisão aqui. De qualquer forma, lembrando também que cada minuto tem 60 segundos, o mesmo dump gerado pela porta firewire levou aproximadamente 7 minutos. Nossa, foi quase! Se não fosse por essa pequena vantagem de 3187 minutos.&lt;/p&gt;&lt;p&gt;Até mais...&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/07/step-into-kernel-firewire.html' title='Step into Kernel (Firewire)'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=3056670469352328464' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/3056670469352328464'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/3056670469352328464'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-4147677066616088458</id><published>2008-06-24T16:23:00.005-03:00</published><updated>2008-06-25T14:24:53.354-03:00</updated><title type='text'>A matéria mais difícil</title><content type='html'>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/suicide-teddy.jpg" border="0" /&gt;&lt;p&gt;Como alguns de vocês já estão cansados de saber, estou cursando o quinto ano de Engenharia da Computação. No começo do curso, naturalmente pastei para poder aprender Cálculo, Álgebra, Física e aquelas outras matérias que tiram fumacinha da calculadora.&lt;/p&gt;&lt;p&gt;Como não pude sair do meu colegial técnico e ir direto para uma universidade, trabalhei um pouco na área até eu conseguir pagar a mensalidade de uma faculdade. Estagiário ganha pouco mesmo. Esse tempo, além de me trazer uma graninha extra por mês, também me trouxe um cadim de experiência. Não foi muita, mas o suficiente para identificar possíveis deslises de professores durante as aulas de programação. O curso de Engenharia da Computação tem duração de seis anos, e durante esse tempo eu ainda tinha que trabalhar, mesmo porque eu tinha que pagar a faculdade. Durante esse tempo acabei lendo um livro ou dois, ou três, quatro, cinco, enfim, alguns.&lt;/p&gt;&lt;p&gt;É natural os professores tenderem a não explicar exatamente como as coisas são ou funcionam, e assim, ganhar um pouco de tempo para poder abordar mais assuntos com menos detalhes. Afinal, você não aprende tudo o que precisa na universidade, mas você já consegue ter uma boa noção de onde você está se enfiando quando for trabalhar na área e por onde começar a buscar mais detalhes sobre determinado assunto. Eu mesmo, nos cursos de drivers que já dei, tento explicar o essencial, o que o aluno vai mesmo utilizar, mas sempre tento deixar claro que existem detalhes a serem vistos.&lt;/p&gt;&lt;p&gt;Durante o curso, eu mal podia esperar para ver as matérias técnicas, que me abririam os olhos para uma nova maneira de ver os dispositivos eletrônicos, circuitos integrados e os computadores. Depois de engolir algumas atravessadas de professores de Linguagem C e de Engenharia se Software, finalmente comecei a estudar algo realmente interessante. A matéria de Sistemas Operacionais é mesmo surpreendente. Nela estudamos diferentes sistemas operacionais que rodam sobre diferentes arquiteturas de computadores. Ufa! Pelo menos uma arquitetura e um sistema operacional eu conheço razoavelmente bem.&lt;/p&gt;&lt;p&gt;Tudo bem o professor dizer que &lt;span style="font-style:italic;"&gt;Thread&lt;/span&gt; e &lt;span style="font-style:italic;"&gt;Processo&lt;/span&gt; é a mesma coisa, explicar &lt;a href="http://en.wikipedia.org/wiki/Inter-process_communication"&gt;Inter Process Communication&lt;/a&gt; sem nem tocar no assunto &lt;a href="http://en.wikipedia.org/wiki/Virtual_memory"&gt;Memória Virtual&lt;/a&gt; e &lt;a href="http://en.wikipedia.org/wiki/Marshalling_%28computer_science%29"&gt;Marshaling&lt;/a&gt;, mas foi show tentar engolir a explicação sobre Sincronismo e Gerenciamento de Interrupções dos sistemas operacionais. Tudo bem, eu só posso dizer que o professor escorregou em Windows, eu não conheço todos os outros Sistemas Operacionais. Acho que ele só escolheu o sistema operacional errado na hora de dar um exemplo infeliz. Não me lembro das palavras exatas, mas meu professor nos ensinou que o Windows XP ainda não é um sistema operacional preemptivo, e que as chamadas ao sistema operacional não podiam acontecer concorrentemente. Primeiro uma, depois a outra e assim por diante. Particularmente acho que ele deve ter confundido XP com 9X. Afinal os dois possuem a letra X. Nessa hora eu tive que parar de ler o &lt;a href="http://www.amazon.com/Developing-Drivers-Windows-Foundation-Developer/dp/0735623740/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214337511&amp;sr=8-1"&gt;livro que eu estava lendo&lt;/a&gt; e pedir para ele repetir aquilo. Eu poderia ter ouvido errado, mas infelizmente não foi isso que aconteceu. Ele disse aquilo mesmo. Mais tarde na mesma aula ele nos disse que o Windows XP ainda não consegue gerenciar interrupções adequadamente, que se uma interrupção ocorre enquanto um processo está rodando, o sistema normalmente espera o processo inteiro terminar para que só depois ele possa atender à uma interrupção.&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/madagascar-penguins.JPG" border="0" /&gt;&lt;p&gt;Como em toda sala de aula universitária, sempre existe um grupinho de alunos que odeiam o Windows e defendem uma outra opção de Sistema Operacional. Nada contra eles, nem contra a opção deles, mas de vêz em quando me aparece cada figura que é um caso sério. Enfim, estes logo disseram ironicamente aos risos: &lt;span style="font-style:italic;"&gt;"É lógico! Porque é o mais inteligente a se fazer"&lt;/span&gt;. Assim todos na sala começaram a rir desse péssimo exemplo de gerenciamento de interrupções do Windows. Eu nunca falo nada, sou aquele cara que chega uns 30 minutos mais cedo na sala pra poder ler meu livro em paz. Não saio fazendo campanha a favor do Windows nem contra outros sistemas. Não fico explicando como completar uma &lt;a href="http://www.driverentry.com.br/blog/2007/02/legal-mas-o-que-uma-irp.html"&gt;IRP&lt;/a&gt; para cada pessoa que encontro no café, mas essa eu não podia deixar passar em branco. Perguntei ao professor: &lt;span style="font-style:italic;"&gt;"Nossa, que interessante isso! Onde foi mesmo que o senhor leu isso?"&lt;/span&gt;. &lt;span style="font-style:italic;"&gt;"Em qualquer livro de Sistemas Operacionais"&lt;/span&gt; ele me respondeu. Vejam só que inocência da minha parte, eu imaginava que os livros clássicos de Sistemas Operacionais tinham sido escritos quando o Windows XP ainda nem existia. Então eu disse: &lt;span style="font-style:italic;"&gt;"Não é por nada, é que não é isso que tenho visto nos livros que tenho lido. Talvêz eu esteja lendo os livros errados."&lt;/span&gt;&lt;/p&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Programming-Microsoft-Windows-Driver-Second/dp/0735618038/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412567&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/WDMBook.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows Driver Model (1999)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"The operating system can preempt any subroutine at any moment for an arbitrarily long period of time, so we cannot be sure of completing critical tasks without interfere or delay. Even if we take steps to prevent preemption, code executing simultaneously on other CPU in same computer can interfere with our code."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Windows-Device-Development-Classic-Reprints/dp/0976717522/ref=pd_bbs_sr_2?ie=UTF8&amp;s=books&amp;qid=1214412624&amp;sr=1-2"&gt;&lt;img src="http://www.driverentry.com.br/images/devicedriversml.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows NT Device Driver Development (1999)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"As was mentioned in Chapter 1, Windows NT is a pre-emptive, multithreaded, and multitasking, operating system. It employs a traditional operating system technique to provide this multitasking capability by associating a quantum with each thread when it starts running."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.temporeal.com.br/produtos.php?id=153786"&gt;&lt;img src="http://www.driverentry.com.br/images/Desvendando.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Desvendando o Windows NT (1998)&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Como o Windows NT implementa um escalador preemptivo, se outro thread, com uma prioridade mais alta se aprontar para execução, o thread sendo executado atualmente é solicitado antes de terminar sua fatia de tempo."&lt;/blockquote&gt;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412699&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/Winternals.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Windows Internals (2005):&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Windows implements a priority-driven, preemptive scheduling system the highest priority runnable (ready) thread always runs, with the caveat that thread chosen to run might be limited by the processors on wich the thread is allowed to run, a phenomenom called Processor Afinitty."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;a href="http://www.amazon.com/Developing-Drivers-Windows-Foundation-Developer/dp/0735623740/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1214412741&amp;sr=1-1"&gt;&lt;img src="http://www.driverentry.com.br/images/WDFBook.jpg" border="0"/&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;p&gt;&lt;span style="font-weight:bold;"&gt;Developing Drivers with Windows Driver Foundation (2007):&lt;/span&gt;&lt;br&gt;&lt;blockquote&gt;&lt;span style="font-style:italic;"&gt;"Windows is a preemptive multitasking operating system in wich multiple thread can try to aceess shared data or resources concurrently and multiple drivers functions can run concurrently."&lt;/span&gt;&lt;/blockquote&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Enfim, é óbvio que não vou discutir com meu professor e tornar minha vida acadêmica um inferno. Mais uma vez vou respirar fundo e fazer dizer aquele sonoro &lt;span style="font-style:italic;"&gt;"Ahhhh tá..."&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;É nestes momentos que eu fico preocupado sobre um outro aspecto. Felizmente posso ouvir e filtrar o que professores de programação em Linguagem C e Sistemas Operacionais dizem, mas nada posso fazer quanto aos professores de Eletrônica, Inteligência Artificial, Microcontroladores e outras tantas matérias que estamos aprendendo. Será que estamos ouvindo as mesmas barbaridades e não sabemos?&lt;/p&gt;&lt;p&gt;De qualquer forma, a matéria que pensei que seria a mais fácil, agora é uma das que me atormentam.&lt;br&gt;Leia sempre...&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/06/matria-mais-difcil.html' title='A matéria mais difícil'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=4147677066616088458' title='6 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/4147677066616088458'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/4147677066616088458'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-8613671958810854749</id><published>2008-06-23T16:38:00.008-03:00</published><updated>2008-06-24T11:39:51.958-03:00</updated><title type='text'>Utilizando o Registry (Parte 2)</title><content type='html'>&lt;p&gt;Pois é, como eu estava dizendo na &lt;a href="http://www.driverentry.com.br/blog/2008/06/utilizando-o-registry-parte-1.html"&gt;primeira parte deste post&lt;/a&gt;. Isso é muito código. Todo esse negócio parece trabalhoso, não? Montar uma &lt;a href="http://msdn.microsoft.com/en-us/library/aa492030.aspx"&gt;UNICODE_STRING&lt;/a&gt; para o caminho da chave que se deseja acessar, montar um &lt;a href="http://msdn.microsoft.com/en-us/library/aa491657.aspx"&gt;OBJECT_ATTRIBUTES&lt;/a&gt; com ela, abrir handle, determinar o tamanho dos valores a serem lidos, alocar um buffer grande suficiente, ler o valor que se deseja, desalocar qualquer buffer temporário utilizado no processo, e por fim, fechar o handle para a chave do registro. Você pode conferir quanto código utilizamos em nosso &lt;a href="http://www.driverentry.com.br/samples/KernelReg.zip"&gt;exemplo do post anterior&lt;/a&gt; para se ler dois valores no registro. Hoje veremos um grupo de rotinas de manipulação de registro que facilita esse processo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Rotinas RTL de Registro&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;São apenas cinco as rotinas que compõem esse grupinho. &lt;a href="http://msdn.microsoft.com/en-us/library/aa489620.aspx"&gt;Na referência&lt;/a&gt; você encontra uma descrição de cada uma delas. Esse grupo de rotinas tem como característica comum o fato de não termos que utilizar handles para as chaves. Os handles são abertos e fechados para cada operação. Outra característica comum é que você não precisa compor o nome completo da chave na qual estão os valores que você quer ler ou escrever. Essas rotinas trabalham com um esquema de nome de chave relativo a algum lugar pré-definido. Confuso? Vamos dar uma olhada numas das rotinas para que tenhamos um exemplo mais prático. Tentando seguir a mesma idéia do exemplo dado no post anterior, precisamos inicialmente verificar se a chave, onde os valores estão armazenados, existe de fato. Tem uma &lt;a href="http://msdn.microsoft.com/en-us/library/ms804317.aspx"&gt;funçãozinha&lt;/a&gt; justamente para isso.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;NTSTATUS 
  RtlCheckRegistryKey(
    IN ULONG  RelativeTo,
    IN PWSTR  Path
    );&lt;/pre&gt;&lt;p&gt;Notem que o primeiro parâmetro dessa rotina é uma constante que indica qual será a referência a ser aplicada ao segundo parâmetro. Observe a tabela abaixo que foi retirada da referência.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegRefTable.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Um exemplo seria o seguinte: Se usarmos RTL_REGISTRY_ABSOLUTE como primeiro parâmetro, o segundo deve ser o caminho completo da chave de registro. Afinal, temos que passar o caminho absoluto da chave. Por outro lado, se utilizarmos RTL_REGISTRY_SERVICES, o segundo parâmetro deveria ser apenas o complemento do caminho referente a chave &lt;em&gt;Services&lt;/em&gt;. Foi esta a opção que utilizei no exemplo deste post. Observe como ficou nossa rotina de criação dos parâmetros no registro.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     CreateParams
**
**      Esta rotina é chamada quando não for detectada a chave
**      que contém os parâmetros. Ela cria a chave e os parâmetros
**      com valores pré-definidos.
*/&lt;/span&gt;
NTSTATUS
CreateParams(VOID)
{
    NTSTATUS        nts = STATUS_SUCCESS;
    WCHAR           wzParam[] = L&lt;span class="literal"&gt;"DriverEntry.com.br"&lt;/span&gt;;
    ULONG           ulParam = 0x12345678;
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Cria a chave onde os parâmetros serão armazenados.
&lt;/span&gt;        &lt;span class="comment"&gt;//      Simples assim.
&lt;/span&gt;        nts = RtlCreateRegistryKey(RTL_REGISTRY_SERVICES,
                                   L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;);
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Aqui escrevemos o valor numérico
&lt;/span&gt;        nts = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
                                    L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                    L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;,
                                    REG_DWORD,
                                    &amp;amp;ulParam,
                                    &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(ulParam));
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; E aqui a String. Notem que não foi necessário
&lt;/span&gt;        &lt;span class="comment"&gt;//      criar um UNICODE_STRING para isso.
&lt;/span&gt;        nts = RtlWriteRegistryValue(RTL_REGISTRY_SERVICES,
                                    L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                    L&lt;span class="literal"&gt;"String"&lt;/span&gt;,
                                    REG_SZ,
                                    wzParam,
                                    &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(wzParam));
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Mais uma vêz, o código demonstrado aqui está disponível para download ao final deste post.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O fácil que complica&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Pelo que pudemos ver até aqui, esse grupo de rotinas já facilitou um pouco nossa vida. Mas é na rotina de leitura que podemos ver a economia de código acontecer de verdade. O custo disso fica por conta de entender como a &lt;a href="http://msdn.microsoft.com/en-us/library/ms803008.aspx"&gt;rotina de leitura&lt;/a&gt; funciona.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;NTSTATUS 
  RtlQueryRegistryValues(
    IN ULONG  RelativeTo,
    IN PCWSTR  Path,
    IN PRTL_QUERY_REGISTRY_TABLE  QueryTable,
    IN PVOID  Context,
    IN PVOID  Environment  OPTIONAL
    );&lt;/pre&gt;&lt;p&gt;Olhando assim nem parece que ela morde, mas se você der uma olhada na referência, verá que ela pode ser bem flexivel. Flexibilidade as vezes é traduzido por &lt;em&gt;"Difícil de acertar a maneira certa de utilizar"&lt;/em&gt;. Os dois primeiros parâmetros são os já conhecidos de antes. A coisa começa a mudar com o terceiro parâmetro, que é uma tabela que contém os detalhes dos valores a serem lidos do registro. Não vou entrar nos detalhes de cada parâmetro desta rotina neste post. Vou apenas fazer o código equivalente ao exemplo do post anterior.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     LoadParams
**
**      Esta rotina carrega as variaveis globais com os
**      valores recuperados do registro.
*/&lt;/span&gt;
NTSTATUS
LoadParams(VOID)
{
    NTSTATUS                    nts = STATUS_SUCCESS;
    RTL_QUERY_REGISTRY_TABLE    QueryTable[] =
    {
        { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED,
          L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;, &amp;amp;g_ulParam, REG_NONE, NULL, 0 },
        { NULL, RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED,
          L&lt;span class="literal"&gt;"String"&lt;/span&gt;, &amp;amp;g_usParam, REG_NONE, NULL, 0 },
        { NULL, 0, NULL, NULL, REG_NONE, 0, NULL }
    };
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="comment"&gt;//-f--&gt; Caso esta rotina seja chamada mais de uma vez, vamos liberar
&lt;/span&gt;        &lt;span class="comment"&gt;//      o buffer deste parâmetro.
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (g_usParam.Buffer)
            RtlFreeUnicodeString(&amp;amp;g_usParam);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Aqui todos os valores da tabela são lidos
&lt;/span&gt;        nts = RtlQueryRegistryValues(RTL_REGISTRY_SERVICES,
                                     L&lt;span class="literal"&gt;"KernelReg2\\Parameters"&lt;/span&gt;,
                                     QueryTable,
                                     NULL,
                                     NULL);
        &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            ExRaiseStatus(nts);
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Não, não está faltando nada. É só isso mesmo. Vamos olhar mais de perto o que fizemos aqui. A tabela que preenchemos tem os dados das leituras a serem realizadas e é definida como mostra abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;struct&lt;/span&gt; _RTL_QUERY_REGISTRY_TABLE {
    PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
    ULONG Flags;
    PWSTR Name;
    PVOID EntryContext;
    ULONG DefaultType;
    PVOID DefaultData;
    ULONG DefaultLength;
} RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;&lt;/pre&gt;&lt;p&gt;Olhando cada membro dela temos:&lt;/p&gt;&lt;p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;QueryRoutine:&lt;/strong&gt; Aqui você tem um ponteiro de função que será chamado quando o este ítem da tabela for lido no registro. Não usamos este método em nosso exemplo. Existem algumas coisas interessantes sobre essa rotina de callback. Ela é chamada para cada ítem desta tabela, mas se o valor a ser lido for do tipo REG_MULTI_SZ, a rotina de callback é chamada uma vez para cada string contida neste valor. Consulte a referência para uma completa descrição destes detalhes.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Flags:&lt;/strong&gt; O flag que utilizamos aqui foi o RTL_REGISTRY_DIRECT que sinaliza que não usaremos o método do callback. Outro flag é o RTL_QUERY_REGISTRY_REQUIRED que indica que o valor descrito nesta linha da tabela é obrigatório. Caso o valor não seja encontrado, RtlQueryRegistryValues retorna erro e os demais valores não serão lidos.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Name:&lt;/strong&gt; Aqui vem o nome do valor a ser lido. Note que não é um UNICODE_STRING, mas um array de WCHAR terminado em zero.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;EntryContext:&lt;/strong&gt; Em nosso exemplo, este parâmetro é o buffer onde o valor lido será armazenado, mas nos casos onde recebemos a chama pela a rotina de callback, este valor é passado como um dos parâmetros.&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;&lt;p&gt;Os parâmetros &lt;strong&gt;DefaultType&lt;/strong&gt;, &lt;strong&gt;DefaultData&lt;/strong&gt; e &lt;strong&gt;DefaultLengh&lt;/strong&gt; seriam utilizados caso o valor não existisse e não fosse um valor obrigatório.&lt;/p&gt;&lt;p&gt;Cada valor a ser lido é representado por uma linha nesta tabela. A rotina identifica o fim da tabela quando encontrar os membros &lt;em&gt;QueryRotuine&lt;/em&gt; e &lt;em&gt;Name&lt;/em&gt; forem NULL.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Calma aí Fernando! Muito bem, nada nessa manga, nada nessa e os valores são lidos? Espertinho você hein? Pensou que ia enganar todo mundo com essa conversinha? Muito bem! Onde é que eu indico o tamanho do buffer oferecido para a leitura?&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Muito boa a sua pergunta! Acho até que eu mesmo não teria feito uma pergunta tão boa. O tamanho o buffer é indicado pelo próprio buffer. No caso da string, passamos um ponteiro para um UNICODE_STRING, o tamanho do buffer da string já é descrito por essa estrutura, mas se o buffer desta estrutura for NULL, a rotina aloca o buffer do tamanho necessário. Temos que desalocar esse buffer quando não for mais utilizá-lo. Para leituras onde o buffer resultante é menor ou igual ao sizeof(ULONG) então o valor resultante é armazenado diretamente no lugar apontado por EntryContext. Para demais detalhes consulte a referência.&lt;/p&gt;&lt;p&gt;Esta função é bem flexível e não vou conseguir colocar tudo que essa rotina oferece em um único post, mas sintam-se a vontade para mandar suas dúvidas em relação a ela.&lt;/p&gt;&lt;p&gt;Have fun!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/KernelReg2.zip"&gt;KernelReg2.zip&lt;/a&gt;&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/06/utilizando-o-registry-parte-2.html' title='Utilizando o Registry (Parte 2)'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=8613671958810854749' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/8613671958810854749'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/8613671958810854749'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-2278751614243422935</id><published>2008-06-16T23:22:00.014-03:00</published><updated>2008-06-19T10:29:43.488-03:00</updated><title type='text'>Utilizando o Registry (Parte 1)</title><content type='html'>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Registry.PNG" border="0" /&gt;&lt;p&gt;No último post, em resposta à uma dúvida de um leitor, comentei um pouco sobre &lt;a href="http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html"&gt;como criar e usar novas IOCTLs&lt;/a&gt;. Afinal de contas, o lema desse blog é &lt;em&gt;"Servir bem para servir sempre"&lt;/em&gt;. Dúvidas de leitores são ótimas fontes de sugestões para novos posts. Creio que como em qualquer outra especialidade, o desenvolvimento de drivers é um tópico que pode ser desmembrado em muitas e muitas partes. Não é a toa que o &lt;a href="http://www.amazon.com/Windows-2000-Device-Driver-Book/dp/0130204315/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1213670808&amp;sr=1-1"&gt;menor livro que eu conheço sobre desenvolvimento de drivers&lt;/a&gt; não tenha menos de quatrocentas páginas. Por isso é bom saber quais são as dificuldades dos leitores para saber sobre qual assunto postar aqui. Sintam-se à vontade para mandar novas sugestões ou dúvidas. Já há algum tempo, recebi um e-mail de Fábio Dias (Fortaleza, CE), que sugeriu um post sobre como acessar o &lt;a href="http://en.wikipedia.org/wiki/Windows_Registry"&gt;Registry&lt;/a&gt; a partir de um driver. Muito bem, vamos lá.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;HKEY_LOCAL_MACHINE é coisa se User Mode&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Antes de sair dizendo quais APIs você deve usar para acessar o registro, vamos antes dar uma olhada em como o registro está organizado. Creio que o primeiro passo aqui seja falar sobre como abrir uma chave do registro. Assim como em &lt;em&gt;User Mode&lt;/em&gt;, temos que ter uma string que informe o caminho da chave a qual queremos abrir, e assim obter um handle para ela. &lt;strong&gt;Opa! Eu disse handle?&lt;/strong&gt; Sendo assim, vale comentar que as chaves do registro também são recursos gerenciados pelo &lt;strong&gt;Object Manager&lt;/strong&gt;. As chaves do registro estão todas armazenadas sob o namespace &lt;strong&gt;"\Registry"&lt;/strong&gt;. Desta forma, enquanto usamos HKEY_LOCAL_MACHINE em &lt;em&gt;User Mode&lt;/em&gt; como nome de uma das divisões básicas do registro, em &lt;em&gt;Kernel Mode&lt;/em&gt; usamos &lt;strong&gt;"\Registry\Machine"&lt;/strong&gt;. E de maneira análoga à HKEY_USERS temos &lt;strong&gt;"\Registry\Users"&lt;/strong&gt;. Mais detalhes &lt;a href="http://msdn.microsoft.com/en-us/library/aa489583.aspx"&gt;aqui&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Qual é o CurrentControlSet?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;A rotina &lt;em&gt;DriverEntry&lt;/em&gt; de qualquer driver para Windows NT recebe dois parâmetros de entrada, sendo um deles um ponteiro para a estrutura &lt;a href="http://msdn.microsoft.com/en-us/library/ms805988.aspx"&gt;DRIVER_OBJECT&lt;/a&gt; que representa a instância do nosso driver, e o outro parâmetro é um &lt;a href="http://msdn.microsoft.com/en-us/library/aa492030.aspx"&gt;UNICODE_STRING&lt;/a&gt; contendo o caminho do registro que indica onde nosso driver está cadastrado. &lt;strong&gt;Mas como assim? Nosso driver pode não saber como ele foi cadastrado no registro?&lt;/strong&gt; Aqui na referência é simples:&lt;/p&gt;&lt;div align=left&gt;&lt;em&gt;&lt;strong&gt;&lt;blockquote&gt;"The registry path string pointed to by RegistryPath is of the form \Registry\Machine\System\CurrentControlSet\Services\DriverName"&lt;/blockquote&gt;&lt;/strong&gt;&lt;/em&gt;&lt;/div&gt;&lt;p&gt;Tudo bem, vamos pegar carona em um driver qualquer e dar uma olhada nisso utilizando o &lt;a href="http://msdn.microsoft.com/en-us/library/cc266321.aspx"&gt;WinDbg&lt;/a&gt;. Vamos abrir um parênteses aqui para informar aos mais novos neste blog que estou utilizando uma máquina virtual para fazer os testes com o driver de exemplo, como explica &lt;a href="http://www.driverentry.com.br/blog/2007/05/step-into-kernel-vmwarewindbg.html"&gt;neste post&lt;/a&gt;. Isso permite que eu possa fazer Debug de Kernel sem necessariamente ter que utilizar duas máquinas. Para facilitar a substituição do driver a cada modificação que faço, estou utilizando o mapeamento de drivers do WinDbg, como explica &lt;a href="http://www.driverentry.com.br/blog/2007/07/bug-em-meu-driver-de-boot-j-posso.html"&gt;neste outro post&lt;/a&gt;. Este recurso permite que o WinDbg sempre carregue uma nova versão driver na máquina TARGET sem que eu tenha que necessariamente substituir o driver nela. Fecha parênteses.&lt;/p&gt;&lt;p&gt;Antes mesmo do driver ser carregado, coloquei um breakpoint na rotina &lt;em&gt;DriverEntry&lt;/em&gt;. &lt;strong&gt;Opa! Como você pode colocar um breakpoint em um driver que ainda não foi carregado?&lt;/strong&gt; Ah tá... Você pode fazer isso utilizando o comando &lt;strong&gt;bu&lt;/strong&gt; como mostra abaixo. Os breakpoints são setados quando o driver for carregado. Na linha seguinte, eu apenas listo os breakpoints, &lt;strong&gt;só pra...&lt;/strong&gt; Quando o driver é carregado, a execução pára em meu breakpoint e uso a extensão &lt;strong&gt;!ustr&lt;/strong&gt;, que mostra UNICODE_STRINGs, para ver o valor desse meu segundo parâmetro da &lt;em&gt;DriverEntry&lt;/em&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;font color="#ff0000"&gt;kd&gt; bu KernelReg!DriverEntry
kd&gt; bl&lt;/font&gt;
 0 eu             0001 (0001) (KernelReg!DriverEntry)
&amp;nbsp;
kd&gt; g
KD: Accessing 'Z:\Sources\DriverEntry\KernelReg\objchk_w2k_x86\i386\KernelReg.sys'
    (\??\C:\WINDOWS\system32\drivers\KernelReg.sys)
  File size 2K.
MmLoadSystemImage: Pulled \??\C:\WINDOWS\system32\drivers\KernelReg.sys from kd
Breakpoint 0 hit
KernelReg!DriverEntry:
f8d394a0 8bff            mov     edi,edi
&lt;font color="#ff0000"&gt;kd&gt; !ustr poi(pusRegistryPath)&lt;/font&gt;
String(114,114) at 82302000: \REGISTRY\MACHINE\SYSTEM\&lt;font color="#ff0000"&gt;ControlSet001&lt;/font&gt;\Services\KernelReg&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Mas não era pra ser CurrentControlSet?&lt;/strong&gt; Na verdade o &lt;em&gt;CurrentControlSet&lt;/em&gt; é um link para um outro &lt;em&gt;ControlSet&lt;/em&gt;. No caso observado acima, o &lt;em&gt;CurrentControlSet&lt;/em&gt; está refletindo o &lt;em&gt;ControlSet001&lt;/em&gt;, ou seja, as alterações feitas no &lt;em&gt;ControlSet001&lt;/em&gt; são vistas no &lt;em&gt;CurrentControlSet&lt;/em&gt; e vice-versa. O &lt;em&gt;CurrentControlSet&lt;/em&gt; pode refletir qualquer outro &lt;em&gt;ControlSet&lt;/em&gt;. Algumas regras determinam quando eles mudam, como em casos de mudanças de configurações de sistema ou instalações de drivers. Para saber para onde o &lt;em&gt;CurrentControlSet&lt;/em&gt; está apontando, dê uma olhada no valor &lt;strong&gt;Current&lt;/strong&gt; que está na chave &lt;em&gt;"\Registry\Machine\System\Select"&lt;/em&gt; como mostra a figura abaixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegCurrent.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Mas isso é apenas para efeito de curiosidade. Quando você utiliza &lt;em&gt;CurrentControlSet&lt;/em&gt; como parte do caminho da chave que você deseja acessar, o &lt;em&gt;Object Manager&lt;/em&gt; faz a traducão pra você e todos vivem felizes para sempre.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Cadê \Registry\Machine\Software?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Já pode ter acontecido com alguns de vocês. Vocês escrevem um driver que deve ler uma certa chave dentro de &lt;em&gt;"\Registry\Machine\Software"&lt;/em&gt; quando o driver é carregado. Enquanto você faz testes com o driver, que neste momento tem seu &lt;em&gt;Start&lt;/em&gt; configurado para &lt;em&gt;Manual(3)&lt;/em&gt;, tudo funciona que é uma maravilha, mas na hora de mudar o &lt;em&gt;Start&lt;/em&gt; do driver para &lt;em&gt;Boot(0)&lt;/em&gt; ou &lt;em&gt;System(1)&lt;/em&gt;, não conseguimos abrir a chave recebendo &lt;strong&gt;STATUS_OBJECT_NAME_NOT_FOUND&lt;/strong&gt; (0xC00000034).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Como assim nome não encontrado? Estava aqui agora mesmo!&lt;/strong&gt; O que acontece é que a chave SOFTWARE é montada mais tarde. Assim, durante o boot do sistema ela ainda não existe. Para fazer acesso a essa chave, você terá que esperar um &lt;em&gt;cadim&lt;/em&gt;, talvez utilizando a tática &lt;a href="http://www.driverentry.com.br/blog/2007/06/comear-de-novo.html"&gt;deste post&lt;/a&gt;. Aí tudo funciona que é uma &lt;em&gt;beless&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Algo a dizer sobre HKEY_CURRENT_USER?&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Então, veja bem. Quem é o &lt;em&gt;Current User&lt;/em&gt;? Usuário que está fazendo a chamada, certo? Assim, conforme a tradição, você começa pensando que é fácil e estando logado como Paulo, você inicia seu driver manualmente. O driver, durante a chamada à &lt;em&gt;DriverEntry&lt;/em&gt;, lê configurações referentes ao usuário logado, que de fato estão armazenadas dentro de HKEY_CURRENT_USER do Paulo. Bom, acho que está certo até aqui. Já ouviram aquela expressão &lt;em&gt;"Quem muito acha acaba se perdendo"&lt;/em&gt;? Pois é, tá tudo errado. Para Paulo, as configurações estão lá mesmo, mas a rotina &lt;em&gt;DriverEntry&lt;/em&gt; é chamada em contexto de sistema, que por sinal não é Paulo.&lt;/p&gt;&lt;p&gt;Esse é um problema enfrentado mesmo em &lt;em&gt;User Mode&lt;/em&gt;, quando serviços, que são executados em conta de sistema, tentam abrir a chave HKEY_CURRENT_USER. Uma coisa que temos que ter em mente é que "O usuário logado" não é uma informação única do sistema. Tentem imaginar um &lt;a href="http://en.wikipedia.org/wiki/Terminal_service"&gt;Terminal Service&lt;/a&gt;, podem haver vários usuários logados ao mesmo tempo. Mesmo quando há somente um usuário logado no sistema, threads podem ser executadas em contexto de outros usuários ou mesmo em contexto de sistema. Drivers não tem contexto próprio. Algumas partes do driver são executadas em contexto de sistema, outras em contexto arbitrário. Dependendo do tipo de driver e sua posiçao dentro da pilha de dispositivos, ele pode receber as chamadas em contexto do usuário. Que é o caso dos drivers de &lt;a href="http://en.wikipedia.org/wiki/File_System"&gt;File System&lt;/a&gt; por exemplo.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Ah tá! Aí sim HKEY_CURRENT_USER vai funcionar!&lt;/strong&gt; Er... Como posso dizer isso? Não, não vai funcionar. Segundo a &lt;a href="http://msdn.microsoft.com/en-us/library/aa489583.aspx"&gt;referência da Microsoft&lt;/a&gt;, não existe uma tradução simples para HKEY_CURRENT_USER, mas existem rotinas que oferecem simplificações. Depois de toda essa conversinha sobre contexto e tals, você ainda terá que acessar a chave HKEY_USERS, ou melhor dizendo, &lt;em&gt;"\Registry\User"&lt;/em&gt;, no formato já conhecido dos programadores &lt;em&gt;User Mode&lt;/em&gt; utilizado com HKEY_USERS. Um exemplo é: &lt;em&gt;"\Registry\User\S-1-5-21-73586283-1897051121-839522115-500"&lt;/em&gt;. O equivalente para uma conta de sistema é a chave &lt;em&gt;"\Registry\User\.DEFAULT"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Tá tá tá! Exemplo agora&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Mais uma vez, estou assumindo que você já sabe compilar e instalar um driver como explicado &lt;a href="http://www.driverentry.com.br/blog/2006/09/getting-started.html"&gt;neste post&lt;/a&gt;. O código exemplo disponível para download ao final deste post também pode ser compilado pelo &lt;a href="http://en.wikipedia.org/wiki/Visual_Studio"&gt;Visual Studio&lt;/a&gt; utilizando o &lt;a href="http://www.osronline.com/article.cfm?article=43"&gt;DDKBUILD&lt;/a&gt;, como explica &lt;a href="http://www.driverentry.com.br/blog/2006/11/kernel-visual-studio-2005.html"&gt;este post&lt;/a&gt;. Neste exemplo vou mostrar a leitura de dois valores do registro que estão em uma chave como mostra a figura abaixo. Para deixar a brincadeira mais divertida, quando o driver for executado pela primeira vez, este criará a chave e os valores nela contidos quando perceber que estes ainda não existem.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/RegParams.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Os valores estarão contidos em uma sub-chave da chave que recebemos como parâmetro na rotina &lt;em&gt;DriverEntry&lt;/em&gt;. Então, nosso trabalho básico na codificação da &lt;em&gt;DriverEntry&lt;/em&gt; é justamente criar o caminho completo da chave do registro que conterá estes valores. Com esta UNICODE_STRING em mãos, a passaremos para as rotinas de leitura e escrita no registro. Segue abaixo a codificação da rotina &lt;em&gt;DriverEntry&lt;/em&gt;. Ela não mostra nada de especial com relação ao registro. Como sempre, vale a pena dar uma lida nos comentários.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     DriverEntry
**
**      Ponto de entrada do nosso driver.
**      Faca nos dentes e sangue nos olhos.
*/&lt;/span&gt;
&lt;span class="keyword"&gt;extern&lt;/span&gt; &lt;span class="literal"&gt;"C"&lt;/span&gt;
NTSTATUS
DriverEntry(__in PDRIVER_OBJECT  pDriverObject,
            __in PUNICODE_STRING pusRegistryPath)
{
    UNICODE_STRING  usParameters = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"\\Parameters"&lt;/span&gt;);
    UNICODE_STRING  usFullRegPath = {0, 0, NULL};
    PWCHAR          pwzBuffer = NULL;
    NTSTATUS        nts = STATUS_SUCCESS;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui recebemos como parâmetro o caminho do registro até
&lt;/span&gt;    &lt;span class="comment"&gt;//      a chave do nosso driver. Nossos parâmetros estão em uma
&lt;/span&gt;    &lt;span class="comment"&gt;//      sub-chave chamada "Parameters". Vamos montar o caminho
&lt;/span&gt;    &lt;span class="comment"&gt;//      completo apendando o nome da sub-chave ao caminho do
&lt;/span&gt;    &lt;span class="comment"&gt;//      registro que recebemos como parâmetro.
&lt;/span&gt;    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="keyword"&gt;__try&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Seta a rotina de callback de descarga do driver.
&lt;/span&gt;            pDriverObject-&gt;DriverUnload = OnDriverUnload;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Para fazer esse append, precisaremos de um buffer
&lt;/span&gt;            &lt;span class="comment"&gt;//      que seja grande o bastante para armazenar a string
&lt;/span&gt;            &lt;span class="comment"&gt;//      original mais o tamanho da sub-chave. Aqui alocamos
&lt;/span&gt;            &lt;span class="comment"&gt;//      este buffer.
&lt;/span&gt;            pwzBuffer = (PWCHAR)ExAllocatePoolWithTag(PagedPool,
                                                      pusRegistryPath-&gt;Length +
                                                      usParameters.Length,
                                                      _KRN_REG_TAG);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (pwzBuffer == NULL)
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Agora que temos o buffer, vamos inicializar a estrutura
&lt;/span&gt;            &lt;span class="comment"&gt;//      UNICODE_STRING referente a string resultante deste append.
&lt;/span&gt;            RtlInitEmptyUnicodeString(&amp;amp;usFullRegPath,
                                      pwzBuffer,
                                      pusRegistryPath-&gt;Length +
                                      usParameters.Length);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Copia a string recebida como parâmetro
&lt;/span&gt;            RtlCopyUnicodeString(&amp;amp;usFullRegPath,
                                 pusRegistryPath);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Concatena a string "\Parameters"
&lt;/span&gt;            RtlAppendUnicodeStringToString(&amp;amp;usFullRegPath,
                                           &amp;amp;usParameters);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Utilizando o caminho completo, tenta carregar os parâmetros
&lt;/span&gt;            nts = LoadParams(&amp;amp;usFullRegPath);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
            {
                &lt;span class="comment"&gt;//-f--&gt; Em caso de falha, verifica se a causa foi a falta da
&lt;/span&gt;                &lt;span class="comment"&gt;//      sub-chave no registro. Isso deve acontecer quando o
&lt;/span&gt;                &lt;span class="comment"&gt;//      driver for executado pela primeira vez. Mas se a falha
&lt;/span&gt;                &lt;span class="comment"&gt;//      for outra, então cada um com seus pobrema.
&lt;/span&gt;                &lt;span class="keyword"&gt;if&lt;/span&gt; (nts != STATUS_OBJECT_NAME_NOT_FOUND)
                    ExRaiseStatus(nts);
&amp;nbsp;
                &lt;span class="comment"&gt;//-f--&gt; Vamos criar a sub-chave e gravar os valores
&lt;/span&gt;                &lt;span class="comment"&gt;//      pré-definidos.
&lt;/span&gt;                nts = CreateParams(&amp;amp;usFullRegPath);
                &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                    ExRaiseStatus(nts);
&amp;nbsp;
                &lt;span class="comment"&gt;//-f--&gt; Tenta ler os parâmetros novamente.
&lt;/span&gt;                &lt;span class="comment"&gt;//      Tá, eu sei que isso é burrice. Afinal, se eu
&lt;/span&gt;                &lt;span class="comment"&gt;//      acabei de criar os parâmetros, por quê eu iria
&lt;/span&gt;                &lt;span class="comment"&gt;//      lê-los novamente? Bom, pra ilustrar.
&lt;/span&gt;                nts = LoadParams(&amp;amp;usFullRegPath);
                &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                    ExRaiseStatus(nts);
            }
        }
        &lt;span class="keyword"&gt;__finally&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Limpando a bagunça.
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (pwzBuffer)
                ExFreePool(pwzBuffer);
        }
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Aqui é mais brincadeira de UNICODE_STRING mesmo. Vamos dar uma olhada na rotina que grava os valores no registro. Esta rotina também não oferece nada de muito diferente do que já estamos habituados a ver em &lt;em&gt;User Mode&lt;/em&gt;. Mas é como se diz por aí: &lt;em&gt;"Um exemplo vale mais que mil artigos"&lt;/em&gt;. Nossa, essa foi terrível! Eu preciso parar com isso. Vocês devem estar pensando agora: &lt;em&gt;"Caraca! Cada coisa que a gente precisa ler pra poder ter um exemplo de driver"&lt;/em&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     CreateParams
**
**      Esta rotina é chamada quando não for detectada a chave
**      que contém os parâmetros. Ela cria a chave e os parâmetros
**      com valores pré-definidos.
*/&lt;/span&gt;
NTSTATUS
CreateParams(__in    PUNICODE_STRING pusRegistryPath)
{
    HANDLE              hKey = NULL;
    NTSTATUS            nts = STATUS_SUCCESS;
    OBJECT_ATTRIBUTES   ObjAttributes;
    UNICODE_STRING      usStringParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"String"&lt;/span&gt;);
    UNICODE_STRING      usDWordParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;);
    ULONG               ulParam = 0x12345678;
    WCHAR               wzParam[] = L&lt;span class="literal"&gt;"DriverEntry.com.br"&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="keyword"&gt;__try&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Aqui não tem segredo. É tudo pá pum.
&lt;/span&gt;            InitializeObjectAttributes(&amp;amp;ObjAttributes,
                                       pusRegistryPath,
                                       OBJ_CASE_INSENSITIVE,
                                       NULL,
                                       NULL);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Cria a nova chave e já solicitamos
&lt;/span&gt;            &lt;span class="comment"&gt;//      acesso para setar valores dentro dela.
&lt;/span&gt;            nts = ZwCreateKey(&amp;amp;hKey,
                              KEY_SET_VALUE,
                              &amp;amp;ObjAttributes,
                              0,
                              NULL,
                              REG_OPTION_NON_VOLATILE,
                              NULL);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Seta o valor numérico
&lt;/span&gt;            nts = ZwSetValueKey(hKey,
                                &amp;amp;usDWordParam,
                                0,
                                REG_DWORD,
                                &amp;amp;ulParam,
                                &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(ulParam));
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Seta o valor string
&lt;/span&gt;            nts = ZwSetValueKey(hKey,
                                &amp;amp;usStringParam,
                                0,
                                REG_SZ,
                                &amp;amp;wzParam,
                                &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(wzParam));
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
        }
        &lt;span class="keyword"&gt;__finally&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Fecha o handle da nova chave criada
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (hKey)
                ZwClose(hKey);
        }
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops!
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Por fim a parte mais divertida desta história. Creio que a maior parte da diversão está concentrada na rotina &lt;a href="http://msdn.microsoft.com/en-us/library/ms804371.aspx"&gt;ZwQueryValueKey&lt;/a&gt;, que realiza a leitura de valores no registro.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;NTSTATUS 
  ZwQueryValueKey(
    IN HANDLE  KeyHandle,
    IN PUNICODE_STRING  ValueName,
    IN KEY_VALUE_INFORMATION_CLASS  KeyValueInformationClass,
    OUT PVOID  KeyValueInformation,
    IN ULONG  Length,
    OUT PULONG  ResultLength
    );&lt;/pre&gt;&lt;p&gt;O ponto que difere da API usada em &lt;em&gt;User Mode&lt;/em&gt; para fazer a mesma tarefa é o terceiro parâmetro &lt;a href="http://msdn.microsoft.com/en-us/library/aa492636.aspx"&gt;KEY_VALUE_INFORMATION_CLASS&lt;/a&gt;. Um &lt;em&gt;enum&lt;/em&gt; que informa quais as informações gostaríamos de obter sobre determinado valor no registro. Um deles solicita apenas o nome enquanto outro solicita todas as informações e o último informações parciais referentes ao valor.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;enum&lt;/span&gt; _KEY_VALUE_INFORMATION_CLASS {
  KeyValueBasicInformation,
  KeyValueFullInformation,
  KeyValuePartialInformation
} KEY_VALUE_INFORMATION_CLASS;&lt;/pre&gt;&lt;p&gt;Para cada valor do &lt;em&gt;enum&lt;/em&gt;, uma estrutura diferente é retornada. Sempre em um único bloco, a estrutura pode trazer várias informações utilizando &lt;em&gt;Offsets&lt;/em&gt; de onde encontrar o dado dentro daquela única alocação de memória.&lt;/p&gt;&lt;p&gt;No exemplo, utilizei o &lt;a href="http://msdn.microsoft.com/en-us/library/ms805931.aspx"&gt;&lt;em&gt;KeyValuePartialInformation&lt;/em&gt;&lt;/a&gt;, que imagino ser o valor mais utilizado. Mais uma vez fica minha dica para que leiam os comentários trecho abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     LoadParams
**
**      Esta rotina carrega as variáveis globais com os
**      valores recuperados do registro.
*/&lt;/span&gt;
NTSTATUS
LoadParams(__in PUNICODE_STRING pusRegistryPath)
{
    HANDLE              hKey = NULL;
    NTSTATUS            nts = STATUS_SUCCESS;
    OBJECT_ATTRIBUTES   ObjAttributes;
    UNICODE_STRING      usStringParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"String"&lt;/span&gt;);
    UNICODE_STRING      usDWordParam = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"DoubleWord"&lt;/span&gt;);
    ULONG               ulBytes = 0;
    PWCHAR              pwzBuffer = NULL;
    PKEY_VALUE_PARTIAL_INFORMATION  pValueInfo = NULL;
&amp;nbsp;
    &lt;span class="keyword"&gt;__try&lt;/span&gt;
    {
        &lt;span class="keyword"&gt;__try&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Monta o ObjectAttribute
&lt;/span&gt;            InitializeObjectAttributes(&amp;amp;ObjAttributes,
                                       pusRegistryPath,
                                       OBJ_CASE_INSENSITIVE,
                                       NULL,
                                       NULL);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Tenta abrir a chave do registro
&lt;/span&gt;            nts = ZwOpenKey(&amp;amp;hKey,
                            GENERIC_READ,
                            &amp;amp;ObjAttributes);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Como o valor tem tamanho fixo, podemos determinar
&lt;/span&gt;            &lt;span class="comment"&gt;//      o tamanho do buffer necessário para ler o valor do
&lt;/span&gt;            &lt;span class="comment"&gt;//      registro.
&lt;/span&gt;            ulBytes = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KEY_VALUE_PARTIAL_INFORMATION) + 
                      &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(ULONG) - &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(UCHAR);
&amp;nbsp;
            &lt;span class="comment"&gt;//-F--&gt; Aloca o buffer para a leitura.
&lt;/span&gt;            pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
                ExAllocatePoolWithTag(PagedPool,
                                      ulBytes,
                                      _KRN_REG_TAG);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!pValueInfo)
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui fazemos a leitura.
&lt;/span&gt;            nts = ZwQueryValueKey(hKey,
                                  &amp;amp;usDWordParam,
                                  KeyValuePartialInformation,
                                  pValueInfo,
                                  ulBytes,
                                  &amp;amp;ulBytes);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Nos certificamos de que lemos um DWORD
&lt;/span&gt;            ASSERT(pValueInfo-&gt;Type == REG_DWORD);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui nós carregamos nossa variável global
&lt;/span&gt;            &lt;span class="comment"&gt;//      com o valor lido do registro.
&lt;/span&gt;            g_ulParam = *(PULONG)pValueInfo-&gt;Data;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Libera buffer da leitura e zera ponteiro.
&lt;/span&gt;            &lt;span class="comment"&gt;//      Você pode decidir alocar o buffer de leituras
&lt;/span&gt;            &lt;span class="comment"&gt;//      baseado no maior valor que você precisa ler,
&lt;/span&gt;            &lt;span class="comment"&gt;//      e assim, utilizar o mesmo buffer para ler todos
&lt;/span&gt;            &lt;span class="comment"&gt;//      os valores menores. Aqui, mais uma vez estou
&lt;/span&gt;            &lt;span class="comment"&gt;//      refazendo tudo para demonstrar.
&lt;/span&gt;            ExFreePool(pValueInfo);
            pValueInfo = NULL;
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Como a string pode ter qualquer tamanho no registro,
&lt;/span&gt;            &lt;span class="comment"&gt;//      vamos oferecer zero bytes de buffer de leitura para
&lt;/span&gt;            &lt;span class="comment"&gt;//      que a API nos diga quanto ela precisa para todo o buffer.
&lt;/span&gt;            nts = ZwQueryValueKey(hKey,
                                  &amp;amp;usStringParam,
                                  KeyValuePartialInformation,
                                  NULL,
                                  0,
                                  &amp;amp;ulBytes);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Temos que receber um destes erros.
&lt;/span&gt;            ASSERT(nts == STATUS_BUFFER_OVERFLOW ||
                   nts == STATUS_BUFFER_TOO_SMALL);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui alocamos o um buffer do tamanho
&lt;/span&gt;            &lt;span class="comment"&gt;//      solicitado pela API.
&lt;/span&gt;            pValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)
                ExAllocatePoolWithTag(PagedPool,
                                      ulBytes,
                                      _KRN_REG_TAG);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!pValueInfo)
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Agora faremos a leitura novamente, mas agora
&lt;/span&gt;            &lt;span class="comment"&gt;//      oferencendo um buffer de tamanho descente.
&lt;/span&gt;            nts = ZwQueryValueKey(hKey,
                                  &amp;amp;usStringParam,
                                  KeyValuePartialInformation,
                                  pValueInfo,
                                  ulBytes,
                                  &amp;amp;ulBytes);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
                ExRaiseStatus(nts);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Temos que ter lido uma string.
&lt;/span&gt;            ASSERT(pValueInfo-&gt;Type == REG_SZ);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Agora vamos alocar o buffer que será utilizado para manter
&lt;/span&gt;            &lt;span class="comment"&gt;//     a string lida do registro. Assim podemos descartar o buffer
&lt;/span&gt;            &lt;span class="comment"&gt;//     utilizado pela leitura. O campo DataLength traz o tamanho da
&lt;/span&gt;            &lt;span class="comment"&gt;//     string unicode com o terminador zero. Repare que estra estrutura
&lt;/span&gt;            &lt;span class="comment"&gt;//     não traz uma UNICODE_STRING, e sim um array de WCHAR.
&lt;/span&gt;            pwzBuffer = (PWCHAR)ExAllocatePoolWithTag(PagedPool,
                                                      pValueInfo-&gt;DataLength,
                                                      _KRN_REG_TAG);
            &lt;span class="keyword"&gt;if&lt;/span&gt; (!pwzBuffer)
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Depois de algum tempo programando, você fica ligêro com algumas
&lt;/span&gt;            &lt;span class="comment"&gt;//      coisas. Aposto que alguém, e isso inclui a mim mesmo, um dia vai
&lt;/span&gt;            &lt;span class="comment"&gt;//      dar um Copy-Paste desta função para usar em outro lugar. Aqui,
&lt;/span&gt;            &lt;span class="comment"&gt;//      prevendo que a leitura do registro pode acontecer várias vezes,
&lt;/span&gt;            &lt;span class="comment"&gt;//      e assim, se houver a alocação de uma leitura anterior, vamos
&lt;/span&gt;            &lt;span class="comment"&gt;//      desalocá-la.
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (g_usParam.Length)
                RtlFreeUnicodeString(&amp;amp;g_usParam);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Inicializa string global com o buffer que acabamos de alocar.
&lt;/span&gt;            &lt;span class="comment"&gt;//      Apesar de setarmos um buffer para esta UNICODE_STRING, este
&lt;/span&gt;            &lt;span class="comment"&gt;//      ainda está vazio (Length=0)
&lt;/span&gt;            RtlInitEmptyUnicodeString(&amp;amp;g_usParam,
                                      pwzBuffer,
                                      (USHORT)pValueInfo-&gt;DataLength);
&amp;nbsp;
            &lt;span class="comment"&gt;//-f--&gt; Aqui apendamos o WCSTR (array de WCHAR terminado em zero), lido
&lt;/span&gt;            &lt;span class="comment"&gt;//      do registro em nosso buffer vazio. Isso é similar a uma cópia,
&lt;/span&gt;            &lt;span class="comment"&gt;//      mas com uma incrivel vantagem. A de não utilizar wcslen().
&lt;/span&gt;            &lt;span class="comment"&gt;//      Liga não, é paranóia de purista. Assim deixamos por conta da API
&lt;/span&gt;            &lt;span class="comment"&gt;//      determinar o Length e MaximumLegth da UNICODE_STRING. Lembre-se
&lt;/span&gt;            &lt;span class="comment"&gt;//      de que o terminador zero não é contado como parte da UNICODE_STRING.
&lt;/span&gt;            RtlAppendUnicodeToString(&amp;amp;g_usParam,
                                     (PCWSTR)pValueInfo-&gt;Data);
        }
        &lt;span class="keyword"&gt;__finally&lt;/span&gt;
        {
            &lt;span class="comment"&gt;//-f--&gt; Faxina geral
&lt;/span&gt;            &lt;span class="keyword"&gt;if&lt;/span&gt; (pValueInfo)
                ExFreePool(pValueInfo);
&amp;nbsp;
            &lt;span class="keyword"&gt;if&lt;/span&gt; (hKey)
                ZwClose(hKey);
        }
    }
    &lt;span class="keyword"&gt;__except&lt;/span&gt;(EXCEPTION_EXECUTE_HANDLER)
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops! I did it again.
&lt;/span&gt;        nts = GetExceptionCode();
        ASSERT(FALSE);
    }
&amp;nbsp;
    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;Ufa! Quanto código! Nesta primeira parte do assunto busquei utilizar as APIs que mais se parecem com as APIs de &lt;em&gt;User Mode&lt;/em&gt;. No próximo post trarei uma maneira diferente de fazer a mesma coisa que fizemos aqui.&lt;/p&gt;&lt;p&gt;Espero ter ajudado,&lt;br&gt;Até mais...&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/KernelReg.zip"&gt;KernelReg.zip&lt;/a&gt;&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/06/utilizando-o-registry-parte-1.html' title='Utilizando o Registry (Parte 1)'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=2278751614243422935' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/2278751614243422935'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/2278751614243422935'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-5755225765149179928</id><published>2008-06-07T02:00:00.011-03:00</published><updated>2008-06-09T13:43:06.961-03:00</updated><title type='text'>Criando e usando IOCTLs</title><content type='html'>&lt;p&gt;Essa semana recebi a seguinte pergunta de um leitor:&lt;/p&gt;&lt;blockquote&gt;&lt;strong&gt;&lt;em&gt;"É possível meu aplicativo passar um IOCTL customizável (feito por mim) para o driver, e este reconhecer sem problemas?"&lt;/em&gt;&lt;/strong&gt;&lt;/blockquote&gt;&lt;p&gt;A resposta à curto prazo é: "Sim, e boa sorte!", mas que graça tem ter um blog se não podemos falar um pouco mais a respeito e até dar um exemplo assim de lambuja? Este post assume que você já sabe alguns conceitos básicos, tais como compilar, instalar e testar drivers. Mas se você ainda não sabe fazer isso, não se preocupe, a vida ainda vale a pena. Basta ler &lt;a href="http://www.driverentry.com.br/blog/2007/06/nos-queremos-exemplos.html"&gt;este post&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;O driver que calculava&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Vamos criar um driver de exemplo que usa a mesma idéia que meu amigo Heldai já usava para ilustrar o uso de IOCTLs quando eu ainda estava aprendendo a fazer telas azuis. No exemplo de hoje vamos fazer um driver que some dois números contidos em uma estrutura que será recebida via um IOCTL.&lt;/p&gt;&lt;p&gt;Sem mais blablablas acho que podemos começar pela definição da estrutura. Como a tia do prezinho já nos ensinou, vamos criar apenas um arquivo de header que seja incluído tanto pelo projeto da aplicação como pelo projeto do driver. Este arquivo de header não deve incluir nenhum outro arquivo de header específico de &lt;em&gt;User Mode&lt;/em&gt; e nem de &lt;em&gt;Kernel Mode&lt;/em&gt;, ou seja, nada de &lt;em&gt;Windows.h&lt;/em&gt; nem de &lt;em&gt;ntddk.h&lt;/em&gt;. Tudo isso já está prontinho, testadinho, e como meu amigo &lt;a href="http://www.sk5.com.br/"&gt;Rafael&lt;/a&gt; costuma dizer, &lt;em&gt;"compilandinho"&lt;/em&gt; num projeto disponível para download ao final do post.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;struct&lt;/span&gt; _KERNEL_MATH_REQUEST
{
    &lt;span class="comment"&gt;//-f--&gt; Eu posso definir minha estrutura da maneira
&lt;/span&gt;    &lt;span class="comment"&gt;//      que eu achar o maior dos melhor de bão.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Estes serão os dois números a serem somados.
&lt;/span&gt;    ULONG   x;
    ULONG   y;
&amp;nbsp;
} KERNEL_MATH_REQUEST, *PKERNEL_MATH_REQUEST;
&amp;nbsp;
&amp;nbsp;
&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="keyword"&gt;struct&lt;/span&gt; _KERNEL_MATH_RESPONSE
{
    &lt;span class="comment"&gt;//-f--&gt; Aos mais mocinhos: Eu sei que dá para fazer
&lt;/span&gt;    &lt;span class="comment"&gt;//      tudo em uma só estrutura. Estou fazendo desta
&lt;/span&gt;    &lt;span class="comment"&gt;//      forma para melhor ilustrar.
&lt;/span&gt;    ULONG   r;
&amp;nbsp;
} KERNEL_MATH_RESPONSE, *PKERNEL_MATH_RESPONSE;&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Criando o IOCTL&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O IOCTL é mais do que simplesmente um número para identificar a operação desejada. Ele é composto por uma máscara de bits que são interpretados pelo Windows. Esta máscara é definida como mostra a figura abaixo. Você pode obter maiores detalhes sobre esta macro &lt;a href="http://msdn.microsoft.com/en-us/library/ms795909.aspx"&gt;neste link&lt;/a&gt;.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/IOCTL_Bits.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Para definir o IOCTL nós utilizamos a macro CTL_CODE que tem seus parâmentros como mostra abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)&lt;/pre&gt;&lt;p&gt;Vamos definir nosso IOCTL como mostra abaixo.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;//-f--&gt; Aqui definimos o IOCTL para nosso driver.
&lt;/span&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_SOMA_QUE_EU_TO_MANDANDO \
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
&amp;nbsp;
&lt;span class="comment"&gt;//-f--&gt; Apesar deste driver ter "Soma" como parte do nome, resolvi
&lt;/span&gt;&lt;span class="comment"&gt;//      colocar um IOCTL de subtração para exemplificar.
&lt;/span&gt;&lt;span class="keyword"&gt;#define&lt;/span&gt; IOCTL_SUBTRAI_QUE_EU_TO_MANDANDO \
    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)&lt;/pre&gt;&lt;p&gt;Agora vamos dar uma olhada em cada parâmetro que foi escolhido aqui e dar uma pincelada a respeito.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;FILE_DEVICE_UNKNOWN&lt;/strong&gt; - Dando uma olhada em nossa &lt;em&gt;DriverEntry&lt;/em&gt; podemos verificar que este foi o mesmo tipo utilizado na chamada à rotina &lt;a href="http://msdn.microsoft.com/en-us/library/aa490468.aspx"&gt;IoCreateDevice&lt;/a&gt;.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     DriverEntry
**
**      Ponto de entrada do nosso driver.
**      Faca nos dentes e sangue nos olhos.
*/&lt;/span&gt;
&lt;span class="keyword"&gt;extern&lt;/span&gt; &lt;span class="literal"&gt;"C"&lt;/span&gt; NTSTATUS
DriverEntry(__in PDRIVER_OBJECT     pDriverObj,
            __in PUNICODE_STRING    pusRegistryPath)
{
    UNICODE_STRING  usDeviceName = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"\\Device\\KernelSum"&lt;/span&gt;);
    UNICODE_STRING  usSymbolicLink = RTL_CONSTANT_STRING(L&lt;span class="literal"&gt;"\\DosDevices\\KernelSum"&lt;/span&gt;);
    NTSTATUS        nts;
    PDEVICE_OBJECT  pDeviceObj;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Setando a rotina de unload e permitir que o driver
&lt;/span&gt;    &lt;span class="comment"&gt;//      possa ser descarregado dinamicamente
&lt;/span&gt;    pDriverObj-&gt;DriverUnload = OnDriverUnload;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; As rotinas de Open, Cleanup e Close são idênticas.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Então, pela lei do mínimo esforço, serão uma única.
&lt;/span&gt;    &lt;span class="comment"&gt;//      Além dessa, temos que tratar a DeviceControl, que é
&lt;/span&gt;    &lt;span class="comment"&gt;//      onde os IOCTLs são recebidos
&lt;/span&gt;    pDriverObj-&gt;MajorFunction[IRP_MJ_CREATE] =
    pDriverObj-&gt;MajorFunction[IRP_MJ_CLEANUP] =
    pDriverObj-&gt;MajorFunction[IRP_MJ_CLOSE] = OnCreateCleanupClose;
    pDriverObj-&gt;MajorFunction[IRP_MJ_DEVICE_CONTROL] = OnDeviceIoControl;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Criamos o device de controle do driver. Repare que utilizamos
&lt;/span&gt;    &lt;span class="comment"&gt;//      FILE_DEVICE_UNKNOWN como device type. Este mesmo device type
&lt;/span&gt;    &lt;span class="comment"&gt;//      é utilizado na macro CTL_CODE. Veja: IoCtl.h
&lt;/span&gt;    nts = IoCreateDevice(pDriverObj,
                         0,
                         &amp;amp;usDeviceName,
                         FILE_DEVICE_UNKNOWN,
                         0,
                         FALSE,
                         &amp;amp;pDeviceObj);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Tudo bem até aqui? Então beleza...
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
    {
        ASSERT(FALSE);
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Cria symbolic link para que a aplicação consiga
&lt;/span&gt;    &lt;span class="comment"&gt;//      obter um handle para o device de controle
&lt;/span&gt;    nts = IoCreateSymbolicLink(&amp;amp;usSymbolicLink,
                               &amp;amp;usDeviceName);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Tá tudo bem?
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!NT_SUCCESS(nts))
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops... Acho que eu pisei em alguma coisa mole...
&lt;/span&gt;        IoDeleteDevice(pDeviceObj);
&amp;nbsp;
        ASSERT(FALSE);
        &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Beleza, fechou, é nóis na fita!
&lt;/span&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; STATUS_SUCCESS;
}&lt;/pre&gt;&lt;p&gt;O segundo parâmetro, o &lt;em&gt;Function&lt;/em&gt;, recebe o número &lt;strong&gt;0x800&lt;/strong&gt;, já que os números abaixo disso são reservados à Microsoft.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;METHOD_BUFFERED&lt;/strong&gt; - Indica ao IoManager que o driver receberá uma cópia do buffer de entrada passado à função &lt;a href="http://msdn.microsoft.com/en-us/library/aa363216.aspx"&gt;DeviceIoControl&lt;/a&gt;. O IoManager aloca um buffer de sistema, ou seja, em &lt;em&gt;Kernel Space&lt;/em&gt;, suficientemente grande para que caibam tanto os dados de entrada como os dados de saída. Quando a função &lt;em&gt;DeviceIoControl&lt;/em&gt; é chamada, o IoManager aloca o buffer de sistema e copia o buffer de entrada para dentro dele. O driver recebe a &lt;a href="http://msdn.microsoft.com/en-us/library/aa491631.aspx"&gt;IRP&lt;/a&gt;, obtém os parâmetros de entrada, processa-os e escreve os dados de saída no mesmo buffer de sistema. Quando a IRP é completada, o IoManager copia os dados de saída do buffer de sistema para o buffer de saída oferecido pela aplicação.&lt;/p&gt;&lt;p&gt;Além do método &lt;strong&gt;Buffered&lt;/strong&gt;, ainda existem os métodos de &lt;strong&gt;Direct I/O&lt;/strong&gt; e &lt;strong&gt;Neither&lt;/strong&gt;. Para o nosso exemplo de hoje, o méthodo Buffered está ótimo. Fico devendo um post que fala sobre como usar cada um dos outros métodos.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;FILE_ANY_ACCESS&lt;/strong&gt; - Indica que o handle não precisa ser aberto com um tipo de acesso especial para poder executar este IOCTL.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Da aplicação para o driver&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Para enviar um IOCTL para o driver, você precisa utilizar a função DeviceIoControl como mostra o exemplo abaixo. Este é um programa bem simples que mostra este uso.&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     main
**
**      Espero que todos saibam que este é o ponto
**      de entrada de uma aplicação. Caso contrário,
**      você pode estar se preciptando em ler um blog
**      de driver para Windows.
*/&lt;/span&gt;
&lt;span class="keyword"&gt;int&lt;/span&gt; __cdecl main(&lt;span class="keyword"&gt;int&lt;/span&gt; argc, CHAR* argv[])
{
    HANDLE                  hDevice;
    DWORD                   dwError = ERROR_SUCCESS,
                            dwBytes;
    KERNEL_MATH_REQUEST     Request;
    KERNEL_MATH_RESPONSE    Response;
&amp;nbsp;
    printf(&lt;span class="literal"&gt;"Opening \\\\.\\KernelSum device...\n"&lt;/span&gt;);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Aqui abrimos um handle para o nosso device que
&lt;/span&gt;    &lt;span class="comment"&gt;//      foi criado pelo driver. Vale lembrar que nosso
&lt;/span&gt;    &lt;span class="comment"&gt;//      driver de exemplo tem que ser instalado e iniciado
&lt;/span&gt;    &lt;span class="comment"&gt;//      para que a chamada abaixo funcione corretamente.
&lt;/span&gt;    hDevice = CreateFile(&lt;span class="literal"&gt;"\\\\.\\KernelSum"&lt;/span&gt;,
                         GENERIC_ALL,
                         0,
                         NULL,
                         OPEN_EXISTING,
                         0,
                         NULL);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Verifica se o handle foi aberto.
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (hDevice == INVALID_HANDLE_VALUE)
    {
        printf(&lt;span class="literal"&gt;"Error #%d opening device...\n"&lt;/span&gt;,
               (dwError = GetLastError()));
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Fiquei com preguiça e coloquei os valores hard-coded mesmo.
&lt;/span&gt;    Request.x = 3;
    Request.y = 2;
&amp;nbsp;
    printf(&lt;span class="literal"&gt;"Calling DeviceIoControl...\n"&lt;/span&gt;);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Envia o IOCTL
&lt;/span&gt;    &lt;span class="keyword"&gt;if&lt;/span&gt; (!DeviceIoControl(hDevice,
                         IOCTL_SOMA_QUE_EU_TO_MANDANDO,
                         &amp;amp;Request,
                         &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KERNEL_MATH_REQUEST),
                         &amp;amp;Response,
                         &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KERNEL_MATH_RESPONSE),
                         &amp;amp;dwBytes,
                         NULL))
    {
        &lt;span class="comment"&gt;//-f--&gt; Ops...
&lt;/span&gt;        printf(&lt;span class="literal"&gt;"Error #%d calling DeviceIoControl...\n"&lt;/span&gt;,
               (dwError = GetLastError()));
&amp;nbsp;
        CloseHandle(hDevice);
        &lt;span class="keyword"&gt;return&lt;/span&gt; dwError;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Mostrando resultatdos
&lt;/span&gt;    printf(&lt;span class="literal"&gt;"%d + %d = %d\n"&lt;/span&gt;,
           Request.x,
           Request.y,
           Response.r);
&amp;nbsp;
    printf(&lt;span class="literal"&gt;"Closing device...\n"&lt;/span&gt;);
    &lt;span class="comment"&gt;//-f--&gt; Fim de conversa
&lt;/span&gt;    CloseHandle(hDevice);
    &lt;span class="keyword"&gt;return&lt;/span&gt; 0;
}&lt;/pre&gt;&lt;p&gt;Note que não existe nenhuma amarração forte entre o IOCTL e os bufferes de entrada ou de saída utilizados, mas é preciso ficar atento aos tamanhos dos bufferes quando o driver fizer uso deles. Ninguém vai querer corromper o heap de alocações de Kernel ou sair disparando excessões em Kernel Mode, vai?&lt;/p&gt;&lt;p&gt;Vejam como os dados são tratados pelo driver ao receber a &lt;a href="http://msdn.microsoft.com/en-us/library/ms796116.aspx"&gt;IRP_MJ_DEVICE_CONTROL&lt;/a&gt;. Leiam os comentários, eles fazem parte do texto explicativo. Nossa, até que essa frase que representa a minha preguiça ficou legal. No fundo mesma frase quer dizer: &lt;em&gt;"Ah meu! Fala sério que além de preparar esse exemplo, você ainda quer que eu duplique toda a informação dos comentários. E na bunada não vai dinha?"&lt;/em&gt;&lt;/p&gt;&lt;pre class="code" id="codemain" style="margin: 6px;"&gt;&lt;span class="comment"&gt;/****
***     OnDeviceIoControl
**
**      Esta rotina é chamada quando uma aplicação
**      envia um IOCTL via DeviceIoControl para nosso device.
*/&lt;/span&gt;
NTSTATUS
OnDeviceIoControl(__in PDEVICE_OBJECT   pDeviceObj,
                  __in PIRP             pIrp)
{
    PIO_STACK_LOCATION      pStack;
    NTSTATUS                nts;
    PKERNEL_MATH_REQUEST    pRequest;
    PKERNEL_MATH_RESPONSE   pResponse;
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Obtemos os parâmetros para nosso driver
&lt;/span&gt;    pStack = IoGetCurrentIrpStackLocation(pIrp);
&amp;nbsp;
    &lt;span class="keyword"&gt;switch&lt;/span&gt;(pStack-&gt;Parameters.DeviceIoControl.IoControlCode)
    {
    &lt;span class="keyword"&gt;case&lt;/span&gt; IOCTL_SOMA_QUE_EU_TO_MANDANDO:
    &lt;span class="keyword"&gt;case&lt;/span&gt; IOCTL_SUBTRAI_QUE_EU_TO_MANDANDO:
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Vamos fazer as verificações de tamanho dos
&lt;/span&gt;        &lt;span class="comment"&gt;//      buffers de entrada e saída.
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (pStack-&gt;Parameters.DeviceIoControl.InputBufferLength !=
            &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KERNEL_MATH_REQUEST) ||
            pStack-&gt;Parameters.DeviceIoControl.OutputBufferLength !=
            &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KERNEL_MATH_RESPONSE))
        {
            nts = STATUS_INVALID_BUFFER_SIZE;
            pIrp-&gt;IoStatus.Information = 0;
            &lt;span class="keyword"&gt;break&lt;/span&gt;;
        }
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; O seguro morreu de velho.
&lt;/span&gt;        ASSERT(pIrp-&gt;AssociatedIrp.SystemBuffer != NULL);
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Utilizando METHOD_BUFFERED, o sistema aloca um buffer único
&lt;/span&gt;        &lt;span class="comment"&gt;//      para transportar os dados de entrada e de saída. O tamanho
&lt;/span&gt;        &lt;span class="comment"&gt;//      este buffer é mesmo do maior deles. Assim, tome cuidado
&lt;/span&gt;        &lt;span class="comment"&gt;//      para não escrever nada na saída antes de ler toda a entrada.
&lt;/span&gt;        pRequest = (PKERNEL_MATH_REQUEST)pIrp-&gt;AssociatedIrp.SystemBuffer;
        pResponse = (PKERNEL_MATH_RESPONSE)pIrp-&gt;AssociatedIrp.SystemBuffer;
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Faz a operação e sinaliza sucesso
&lt;/span&gt;        &lt;span class="keyword"&gt;if&lt;/span&gt; (pStack-&gt;Parameters.DeviceIoControl.IoControlCode == IOCTL_SOMA_QUE_EU_TO_MANDANDO)
            pResponse-&gt;r = pRequest-&gt;x + pRequest-&gt;y;
        &lt;span class="keyword"&gt;else&lt;/span&gt;
            pResponse-&gt;r = pRequest-&gt;x - pRequest-&gt;y;
&amp;nbsp;
        nts = STATUS_SUCCESS;
&amp;nbsp;
        &lt;span class="comment"&gt;//-f--&gt; Informa ao IoManager quantos bytes devem ser tranferidos de
&lt;/span&gt;        &lt;span class="comment"&gt;//      volta para a aplicação
&lt;/span&gt;        pIrp-&gt;IoStatus.Information = &lt;span class="keyword"&gt;sizeof&lt;/span&gt;(KERNEL_MATH_RESPONSE);
        &lt;span class="keyword"&gt;break&lt;/span&gt;;
&amp;nbsp;
    &lt;span class="keyword"&gt;default&lt;/span&gt;:
        &lt;span class="comment"&gt;//-f--&gt; Ops... Recebemos um IOCTL não previsto.
&lt;/span&gt;        nts = STATUS_INVALID_DEVICE_REQUEST;
        pIrp-&gt;IoStatus.Information = 0;
    }
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; Copia o status final da IRP e a completa.
&lt;/span&gt;    pIrp-&gt;IoStatus.Status = nts;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
&amp;nbsp;
    &lt;span class="comment"&gt;//-f--&gt; NOTA: Lembre-se de que não podemos tocar na IRP depois de completa-la,
&lt;/span&gt;    &lt;span class="comment"&gt;//      então não seja espertão modificando a linha abaixo para a obter o Status
&lt;/span&gt;    &lt;span class="comment"&gt;//      de dentro da IRP, e assim usa-lo como retorno de função.
&lt;/span&gt;    &lt;span class="keyword"&gt;return&lt;/span&gt; nts;
}&lt;/pre&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Compilando assim ou assado&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;O projeto de exemplo que está disponível para download ao final deste post pode ser compilado de duas maneiras. Uma delas é utilizando a &lt;a href="http://msdn.microsoft.com/en-us/library/ms792418.aspx"&gt;maneira padrão de compilar drivers&lt;/a&gt; oferecida pelo WDK. Resumidamente você vai em &lt;em&gt;Start-&gt;All Programs-&gt;Windows Drivers Kits-&gt;WDK 6000-&gt;Build Environments-&gt;Windows XP-&gt;Windows XP x86 Checked Build Environment&lt;/em&gt;. Isso deve abrir uma jabela de prompt como mostra abaixo. Depois disso é só ir ao diretório onde você baixa essas tranqueiras da internet e entrar no diretório raiz do projeto. Lá você chama o &lt;strong&gt;Build.exe&lt;/strong&gt; e um abraço.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BuildKrnSum.PNG" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;A outra maneira de compilar todo o projeto permite que você compile de dentro do Visual Studio 2008, que foi o ambiente que utilizei para compor este projeto. Porém, para compilar o projeto pela IDE, você precisará usar o &lt;a href="http://www.osronline.com/article.cfm?article=43"&gt;DDKBUILD&lt;/a&gt; como mostra &lt;a href="http://www.driverentry.com.br/blog/2006/11/kernel-visual-studio-2005.html"&gt;este outro post&lt;/a&gt; que fala a respeito.&lt;/p&gt;&lt;p&gt;Enquanto estive fora, recebi uma outra dúvida de um leitor que queria saber como ler/escrever no registro usando um driver. No próximo post falarei sobre isso além de outros detalhes referentes ao registro do ponto de vista de um driver.&lt;/p&gt;&lt;p&gt;Até lá!&lt;/p&gt;&lt;p&gt;Download &lt;a href="http://www.driverentry.com.br/samples/KernelSum.zip"&gt;KernelSum.zip&lt;/a&gt;&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/06/criando-e-usando-ioctls.html' title='Criando e usando IOCTLs'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=5755225765149179928' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/5755225765149179928'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/5755225765149179928'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-4068172336386781200</id><published>2008-06-05T18:06:00.010-03:00</published><updated>2008-06-06T15:44:46.448-03:00</updated><title type='text'>Tirando o atraso :-P</title><content type='html'>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/Palestra290308.JPG" border="0" /&gt;&lt;p&gt;Mais uma vez fiquei sem postar por um longo tempo. Agora você deve estar pensando: &lt;em&gt;"Lá vem ele com aquela ladainha de que não tem tempo, que tudo está difícil, que Deus não gosta dos programadores de Kernel e assim por diante"&lt;/em&gt;. Bom, vou pular essa parte de dar minhas desculpas e vou logo dizendo o que estive fazendo durante esse tempo.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Curso de Drivers para Windows&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Bom deixe-me ver onde foi mesmo que eu parei. Meu último post foi durante o período em que eu estava dando um curso de desenvolvimento de drivers para uma turma fechada. O curso foi ministrado em cinco sábados de 8 horas cada. Não preciso dizer que isso me ocupou por um tempo. Tá tá tá, sem desculpas.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Quarto Encontro de Programadores de C/C++&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;No sábado subsequente ao final do curso, fui dar uma passeada &lt;a href="http://www.cbrasil.org/wiki/index.php?title=Quarto_Encontro_de_Programadores"&gt;neste evento&lt;/a&gt;. Deixaram a porta meio aberta e consegui entrar para dar uma espiada e falar um pouco sobre Arquitetura e Desenvolvimento de drivers para Windows com linguagem C. Confesso que o convite para participar deste evento foi uma surpresa para mim. Não sou um grande programador de C++, mas utilizo a linguagem C com classes como meu amigo &lt;a href="http://www.1bit.com.br/content.1bit/weblog"&gt;Strauss&lt;/a&gt; gosta de dizer. Posso dizer que foi muito interessante poder participar desse evento de altíssimo nível técnico e ter a oportunidade de ver como a linguagem pode ser utilizada em diferentes cenários. Os slides da minha palestra estão disponíveis &lt;a href="http://www.driverentry.com.br/downloads/Palestra-290308.zip"&gt;neste link&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;De volta à Boston&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/IBM_Fernando.png" border="0" /&gt;&lt;p&gt;Até parece que as coisas só acontecem aos sábados. De qualquer forma, no sábado subsequente ao encontro, embarquei em uma viajem de um mês para Boston. Da última vez que estive por lá foi para fazer um treinamento na OSR como descrevi &lt;a href="http://www.driverentry.com.br/blog/2007/05/e-deu-boston.html"&gt;neste post&lt;/a&gt;. Desta vez foi um programa de integração da IBM. Pude conhecer pessoalmente as pessoas com quem trabalho e que antes só conhecia por Web Conferência. Mais uma vez foi bem interessante e tentei aproveitar ao máximo. Fomos em dois brasileiros em um Indiano. Senti muita saudade do nosso cafézinho brasileiro. Nessa foto abaixo estão, da esquerda para a direita, David E. Jones (Gerente), Scott D. Hankin (Documentação), William C. Oliveira (Linux), William H. Taber (Linux), Mridula Muppana (Testes), Paul Ganshirt (Windows), Niraj Kumar (AIX), Kaveesh Mishra (Windows), Fernando Roberto (Windows) e Richard Kissel (AIX e Linux). Esta é apenas uma parte de todo o time de MVFS.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/MFVS_Team.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Não sabe o que é &lt;a href="http://en.wikipedia.org/wiki/MultiVersion_File_System"&gt;MVFS&lt;/a&gt;? É um File System que faz parte de um produto chamado ClearCase. Não conhece o &lt;a href="http://en.wikipedia.org/wiki/Clearcase"&gt;ClearCase&lt;/a&gt;? Bom, além da Wikipedia, pude ter o prazer de esbarrar com uma observação sobre o ClearCase no &lt;a href="http://www.amazon.com/Windows-System-Internals-Classic-Reprints/dp/0976717514/ref=sr_1_2?ie=UTF8&amp;s=books&amp;qid=1212723974&amp;sr=8-2"&gt;livro de Rajeev Nagar&lt;/a&gt;. Este livro, como eu não me canso de dizer, ainda é a única referência respeitável sobre desenvolvimento de File Systems para Windows apesar de ter sido publicado pela primeira vez em 1997. &lt;strong&gt;Mas onde está o ClearCase nesta história?&lt;/strong&gt; Bom, se você é doente como eu e tem esse livro, dê uma olhadinha no início do capítulo nove. Na primeira página deste capítulo você encontrará as seguintes passagens.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/CC_in_Book.png" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Isso é realmente muito legal! :-)&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Volta às aulas&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Durante um mês inteiro após minha chegada de volta ao Brasil, dediquei algum tempo correndo atrás da matéria que perdi na faculdade. Pois é, ainda estou me graduando em Engenharia da Computação. Já estou trabalhando em meu TCC (Trabalho de Conclusão de Curso). Meu TCC terá obrigatóriamente que ter um driver de Windows, é o mínimo que eu poderia fazer. Finalmente vou poder mostrar o que sei fazer aos meus amiguinhos de sala. Durante o curso, alguns amigos me perguntam com o quê eu trabalho, já que freqüentemente estou lendo grandes livros da Microsoft. Depois de tentar explicar das maneiras mais simples possíveis, desenhando ou mesmo usando fantoches, eles ainda ficam com aquela cara de interrogação. Bom, na hora que eu mostrar a tela azul, eles vão acabar entendendo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/maxII_dev_kit_th.jpg" /&gt;&lt;/div&gt;&lt;br&gt;&lt;p&gt;Aproveitei o passeio nos Estados Unidos para comprar &lt;a href="http://www.altera.com/products/devkits/altera/kit-maxii-1270.html"&gt;esse kit&lt;/a&gt; de desenvolvimento da &lt;a href="http://"&gt;ALTERA&lt;/a&gt;. Esse kit vai fazer parte do meu TCC e futuramente poderei utilizá-lo em meus cursos de desenvolvimento de drivers. Hoje conto apenas com as placas de treinamento da OSR, conforme já falei a respeito &lt;a href="http://www.driverentry.com.br/blog/2007/05/curso-de-drivers-para-windows.html"&gt;neste post&lt;/a&gt;. Diferente das placas da &lt;a href="http://www.osronline.com"&gt;OSR&lt;/a&gt;, este kit pode ter seu hardware definido por uma linguagem chamada &lt;a href="http://en.wikipedia.org/wiki/Vhdl"&gt;VHDL&lt;/a&gt;, e assim, poderei fazer com que a placa tenha os mais variados comportamentos e poderei ilustrar a construção de drivers de interface para cada situação. Essa placa custa em torno de R$ 1.700,00 aqui no Brasil, enquanto que paguei apenas US$ 150,00 lá. Praticamente veio no doce. Sabe quando íamos à padaria comprar aquela casquinha de sorvete que vem recheada com maria-mole, e enfiada nela vem um brinquedo. Então, foi quase a mesma coisa. Uma outra coisa interessante nesse kit, além do preço, é que é possível fazer interface USB e interface PCI. Isso vai ser bem divertido.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Seminário de Portabilidade e Performance
&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Neste último final de semana, participei de mais esse evento organizado pelo grupo de C/C++ Brasil. É impressionante ver como estes eventos estão trazendo cada vez mais gente. Bati algumas fotos, mas vou deixar os comentários por conta do meu amigo &lt;a href="http://www.caloni.com.br/"&gt;Lesma&lt;/a&gt;, que já fez um &lt;a href="http://www.caloni.com.br/blog/archives/resultado-do-seminario-ccpp"&gt;post muito legal&lt;/a&gt; à respeito.&lt;/p&gt;&lt;p&gt;De volta ao mundo dos bloggers, vou tentar não abandoná-los por tanto tempo. Desculpinhas à parte, num tá fáci não.&lt;/p&gt;&lt;p&gt;Até a próxima.&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://www.driverentry.com.br/blog/2008/06/tirando-o-atraso-p.html' title='Tirando o atraso :-P'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=32803974&amp;postID=4068172336386781200' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://www.driverentry.com.br/blog/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/4068172336386781200'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/32803974/posts/default/4068172336386781200'/><author><name>Fernando Roberto da Silva</name><uri>http://www.blogger.com/profile/12893657520734789532</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-32803974.post-1214271067770790308</id><published>2008-03-06T11:34:00.008-03:00</published><updated>2008-03-06T14:09:04.546-03:00</updated><title type='text'>Step into Kernel (Vista + USB2)</title><content type='html'>&lt;img style="FLOAT: right; MARGIN: 0px 10px; HEIGHT: 100%" src="http://www.driverentry.com.br/images/DebugCable.png" border="0" /&gt;&lt;p&gt;Já comentei em outro &lt;a href="http://www.driverentry.com.br/blog/2006/10/serial-killers.html"&gt;post&lt;/a&gt; que é possível fazer Kernel Debugging através de uma porta USB 2.0. Neste post vou demonstrar essa tal conexão USB 2.0 funcionando. Ê laiá coisa boa. Então vamos deixar de conversinha mole e vamos logo ao que interessa. Se você não entende nada sobre Kernel Debugging, você pode ler este &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;post&lt;/a&gt;, que tráz os conceitos e passos básicos sobre o assunto, ou você pode se perguntar &lt;em&gt;"Como é que eu fui cair neste site?"&lt;/em&gt; e voltar para o &lt;a href="http://www.orkut.com"&gt;ORKUT&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;O barramento USB não permite que tenhamos conexões diretas entre dois computadores. Para que possamos utilizar uma porta USB para este fim, teremos que contar com a ajuda de um hardware adicional. Apesar de receber o nome de &lt;strong&gt;Debug Cable&lt;/strong&gt;, trata-se de um pequeno dispositivo que une os computadores através de portas USB. Não sei se já existem outros fabricantes para o Debug Cable, mas o que vou utilizar neste experimento é o &lt;a href="http://www.plxtech.com/products/NET2000/NET20DC/default.asp"&gt;NET20DC&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Utilizando o Debug Cable&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Utilizar o Debug Cable é um luxo que apenas o &lt;a href="http://en.wikipedia.org/wiki/Windows_Vista"&gt;Windows Vista&lt;/a&gt; ou sistemas posteriores podem desfrutar. Ele precisa ser conectado a uma porta USB 2.0 sem passar por nenhum HUB do lado TARGET. Mas como saber com certeza quais das minhas portas é de fato 2.0?&lt;/p&gt;&lt;p&gt;Uma maneira fácil de saber isso, principalmente para quem tem o &lt;strong&gt;WDK&lt;/strong&gt; instalado, é compilar o exemplo &lt;strong&gt;USBView&lt;/strong&gt;, que está na pasta &lt;em&gt;"C:\WinDDK\6000\src\usb\usbview"&lt;/em&gt; do WDK. Este programa enumera os controladores e os respectivos dispositivos USB conectados. Desta forma, quando conectarmos o Debug Cable à máquina TARGET, o USBView deve informar em qual porta este novo dispositivo está conectado. Repare que nesta janela podemos ver em qual controlador a porta que estamos utilizando pertence.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/USBView.PNG" /&gt;&lt;/div&gt;&lt;p&gt;O Windows solicitará um driver ao detectar a presença deste novo dispositivo USB. Do lado TARGET nenhum driver precisa ser instalado para utilizar o Debug Cable. Assim você pode selecionar &lt;em&gt;"Não exibir esta mensagem novamente para este dispositivo"&lt;/em&gt; na janela da figura abaixo. Não instalar nenhum driver faz todo o sentido do lado TARGET. Lembre-se de quem vai utilizar esta interface de debug do lado TARGET é a BIOS, e vai repassar o controle para o &lt;em&gt;core &lt;/em&gt;do sistema operacional. Vale lembrar também que existe um outro requisito para utilizar o Debug Cable é que a BIOS da máquina TARGET implementar este controle de interface de debug. A parte mais infeliz desta história é que para saber se a sua BIOS está preparada ou não, basta tentar. Ou seja, você pode ter o Debug Cable, uma porta USB 2.0 livre, os cabos e ainda assim correr o risco de nada conectar pelo simples fato da BIOS não dar suporte. Fico feliz em saber que meu notebook dá esse suporte e que não gastei essa grana a toa importando o Debug Cable. Ufa!!&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/FoundUSBCable.PNG" /&gt;&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Modificando o Boot.ini&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Hêin? Boot.ini? Isso não te pertence mais. O Windows Vista utiliza uma nova arquitetura chamada &lt;a href="http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx"&gt;Boot Configuration Data&lt;/a&gt; (BCD). Isso levou nosso amigo Boot.ini para a casa do chapéu. Para editar as configurações do BCD, utilizamos a ferramenta &lt;strong&gt;BCDEdit.exe&lt;/strong&gt;, uma aplicação console que precisa ser executada com poderes administrativos. A execução desta ferramenta sem qualquer parâmetro nos retorna a seguite saída.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BCDEdit.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Para configurar o sistema de forma a termos duas opções de boot, uma com debug habilitado e outra não, siga os passos abaixo descritos. O &lt;strong&gt;Boot Manager&lt;/strong&gt; gerencia &lt;strong&gt;Boot Loaders&lt;/strong&gt;, que podem ser outros sistemas operacionais, mesmo que anteriores ao Windows Vista, como também podem ser conjuntos de configurações do mesmo sistema. Cada um destes ítens é identificado por um GUID. Observe que o primeiro comando faz uma cópia da configuração atual para uma que contenha a string &lt;em&gt;"Windows Vista Cobaia Mode"&lt;/em&gt; como descrição. Em resposta a essa cópia, a ferramenta nos retorna o GUID resultante da cópia realizada. Observe também que no segundo comando habilitamos o debug de kernel para a configuração identificada pelo GUID que acabamos de receber. Neste momento, se dermos uma olhada nas configurações teremos o resultado como é exibido na mesma imagem abaixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BCDEdit2.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Beleza, agora temos que configurar o meio de comunicação que será utilizado pelo Kernel Debugger. creio que a maioria dos micros ainda utilizem portas seriais para este fim. Neste caso, a configuração mais comum seria a de utilizar a porta serial COM1 com um baudrate de 115200. Para setar estas configurações, utilize a seguinte linha de comando. Para ver as configurações atuais, basta executar a segunda linha de comanodo como exibida abaixo.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DbgSerial.PNG"/&gt;&lt;/div&gt;&lt;p&gt;Se você ainda não sabe como utilizar a porta serial como meio de comunicação para o debug de kernel, este &lt;a href="http://www.driverentry.com.br/blog/2006/12/step-into-kernel-serial.html"&gt;post&lt;/a&gt; fala a respeito. Entretando, neste post vou comentar sobre o debug de kernel utilizando uma porta USB 2.0 como meio de comunicação. Neste caso as configurações serão as descritas na linha de comando exibida abaixo. O &lt;strong&gt;TARGETNAME&lt;/strong&gt; pode receber qualquer nome. Utilizei o nome &lt;em&gt;"WINDBG"&lt;/em&gt; por motivos óbvios, mas você pode colocar qualquer nome.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/DbgUSB.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Como vocês podem ter imaginado, as configurações sobre o meio de comunicação de debug são globais, ou seja, não estão vinculadas a esta ou àquela configuração de boot identificada por um GUID.&lt;/p&gt;&lt;p&gt;Creio que do lado TARGET já está tudo pronto para efetuar o link. Desta forma, quando reiniciarmos o computador vítima teremos o seguinte menu exibido pelo Boot Manager.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/BootManager.png" /&gt;&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o HOST&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Do lado HOST desta brincadeira, teremos que adicionar o driver que controla o Debug Cable e permitir que o Windbg possa utilizar este dispositivo. Apesar de eu estar utilizando Windows Vista no lado HOST, nada impede de você utilizar o Windows 2000 ou superior neste lado da conversa. A brincadeira começa quando você pluga o dispositivo e clica em &lt;em&gt;"Localizar e instalar o driver (recomendado)"&lt;/em&gt; na janela de &lt;em&gt;"Novo hardware encontrado"&lt;/em&gt; já exibida neste post. Neste momento o Windows  procura pelo driver no banco de dados do Windows Update.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/AjaysWindowsUpdate.PNG" /&gt;&lt;/div&gt;&lt;p&gt;A coisa começa a ficar divertida quando o Windows me pede para inserir o disco que veio com o Debug Cable. Apenas duas palavras ficaram em minha mente neste momento: &lt;em&gt;"Que disco?"&lt;/em&gt;. Sem muitas opções eu cliquei em &lt;em&gt;"Eu não tenho essa porcaria de disco! Cê tá loco? Me mostre qualquer coisa pelo amor de Deus"&lt;/em&gt;. Alguns de vocês já devem até ter esta seqüência de janelas decoradas de tanto não encontrar drivers para todos os dispositivos que você tem.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/WinWasUnable.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Por fim quando a última janela me foi mostrada, me veio uma luz, mais uma daquelas idéias brilhantes que tenho a cada ano bisexto. Foi quando pensei comigo mesmo &lt;em&gt;"Já sei! Vou visitar o site do fabricante e procurar por uma sessão de suporte."&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;E no site do fabricante...&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/PlxWebSite.PNG" /&gt;&lt;/div&gt;&lt;p&gt;E no site da Microsoft...&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/UsbDbgMail.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Em fim, mandei o tal e-mail para obter qualquer sinal de que eu não tivesse jogado meu dinheiro no lixo. Em menos de uma hora recebo a resposta com uma lista de requisitos e passos a serem seguidos, mas o mais importante só me foi revelado no final.&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/MsAnswer.PNG" /&gt;&lt;/div&gt;&lt;p&gt;Eu sabia que podia contar com a inteligência e a agilidade da Microsoft para resolver isso. Mas fica minha dica para os que estão lendo este post. Quando o Windows solicitar o driver do novo dispositivo, aponte a pasta &lt;em&gt;"C:\Program Files\Debugging Tools for Windows\usb"&lt;/em&gt;.&lt;/p&gt;&lt;p&gt;E é com prazer que vos mostro a janela a seguir:&lt;/p&gt;&lt;div align="center" width="100%"&gt;&lt;img src="http://www.driverentry.com.br/images/USBDebugOK.PNG" /&gt;&lt;/div&gt;&lt;p&gt;&lt;br&gt;&lt;span style="font-size:130%;"&gt;&lt;strong&gt;Configurando o Windbg&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;&lt;img style="FLOAT: right; MARGIN: 0px 10px;