-
Notifications
You must be signed in to change notification settings - Fork 0
/
2008-07.html
914 lines (717 loc) · 54.7 KB
/
2008-07.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
<!DOCTYPE html>
<html lang="en-us" dir="ltr" itemscope itemtype="http://schema.org/Article">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Blogue do Caloni</title>
<meta name="author" content="Caloni" />
<meta name="generator" content="txt2blog 0.0.1">
<meta property="og:title" content="Blogue do Caloni"/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="http://www.caloni.com.br"/>
<meta property="og:image" content="/img/about-brand.png"/>
<meta property="og:description" content="Write for computers, people and food."/>
<link href="/index.xml" rel="feed" type="application/rss+xml" title="Blogue do Caloni"/>
<link rel="stylesheet" type="text/css" href="/css/custom.css"/>
<link rel="stylesheet" type="text/css" href="/css/jquery-ui.css"/>
<script src="/js/jquery-1.12.4.js"></script>
<script src="/js/jquery-ui.js"></script>
<script src="/js/copy_clipboard.js"></script>
<script>
var quick_search_posts = [
];
</script>
<script src="/js/quick_search.js"></script>
<script src="/js/list.js"></script>
<link rel="icon" href="/img/favicon.ico"/>
</head>
<body style="min-height:100vh;display:flex;flex-direction:column">
<nav class="navbar has-shadow is-white"
role="navigation" aria-label="main navigation">
<div class="container">
<div class="navbar-brand">
<a class="navbar-item" href="months.html">
<div class="is-4"><b>caloni::2008-07</b></div>
</a>
</div>
</div>
</nav>
<div class="container">
<div class="column">
<div style="min-height:56vh">
<div style="padding-bottom: 1em;"></div>
<ul style="list-style: none;">
<li><small><a href="2008-07.html#pesquisas_sobre_a_gina">Pesquisas sobre a GINA</a></small></li>
<li><small><a href="2008-07.html#virtualbox">VirtualBox</a></small></li>
<li><small><a href="2008-07.html#projeto_modelo">Projeto-modelo</a></small></li>
<li><small><a href="2008-07.html#primeiros_passos_no_vmware_workstation">Primeiros passos no VMware Workstation</a></small></li>
<li><small><a href="2008-07.html#segunda_versao_do_houaiss2babylon">Segunda versão do Houaiss2Babylon</a></small></li>
<li><small><a href="2008-07.html#o_caso_da_funcao_de_delay_load_desaparecida">O caso da função de Delay Load desaparecida</a></small></li>
<li><small><a href="2008-07.html#o_conhecido_unresolved_external">O conhecido unresolved external</a></small></li>
<li><small><a href="2008-07.html#aprenda_a_usar_sua_api">Aprenda a usar sua API</a></small></li>
<li><small><a href="2008-07.html#entrevista_com_o_caloni_no_do_zero_ao_mestre">Entrevista com o Caloni no 'Do ZERO ao MESTRE'</a></small></li>
<li><small><a href="2008-07.html#antidebugging_using_exceptions_part_one">Antidebugging using exceptions (part one)</a></small></li>
<li><small><a href="2008-07.html#antidebugging_using_exceptions_part_two">Antidebugging using exceptions (part two)</a></small></li>
</ul>
<span id="pesquisas_sobre_a_gina" title="Pesquisas sobre a GINA"/></span>
<section id="section_pesquisas_sobre_a_gina">
<p class="title"><a href="2008-07.html#pesquisas_sobre_a_gina">#</a> Pesquisas sobre a GINA</p>
<span class="title-heading">Caloni, 2008-07-02 <a href="coding.html">coding</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_pesquisas_sobre_a_gina')"><sup>[copy]</sup></a></span>
<p>Já sabemos o que é uma GINA. Afinal, todo mundo já viu uma antes. E sabemos que hoje em dia ela está morta.</p>
<p>No entanto, algumas pequenas mudanças foram feitas nela no Windows XP que ainda almaldiçoam o código de quem tenta reproduzir a famosa GINA da Microsoft. Nem todos chegam no final e morrem tentando.</p>
<p>Eu sou um deles.</p>
<p>Uma explicação sobre como funciona o processo de logon (local e remoto) e os componentes envolvidos está no artigo "How Interactive Logon Works" da Technet. Esse artigo irá abrir os olhos para mais detalhes que você gostaria de saber sobre nossa velha e querida amiga. Os desenhos explicativos estão ótimos!</p>
<p>Após essa leitura picante, podemos voltar ao feijão com arroz e começar de novo lendo a descrição de como funciona a GINA na Wikipedia, que nos remete a vários linques interessantes, entre os quais:</p>
<ul><li>A explicação documentada do MSDN de como funciona a interação entre Winlogon e GINA.</li>
<li>Um ótimo artigo dividido em duas partes que explica como fazer sua própria customização de GINA. Foi nele que encontrei o retorno que precisava para emular a execução do Gerenciador de Tarefas baseado na digitação do Ctrl + Alt + Del. De brinde ainda vem uma GINA de exemplo para download.</li>
</ul>
<p>A partir de mais algumas buscas e execuções do Process Monitor podemos encontrar os valores no registro que habilitam o Fast User Switching e a Tela de Boas Vindas do Windows XP. O valor da Tela de Boas Vindas é que habilita e desabilita a execução do Gerenciador de Tarefas baseado em Ctrl + Alt + Del. Esses itens são essenciais para os que quiserem criar uma réplica perfeita da GINA da Microsoft no Windows XP. Isso finaliza a minha busca.</p>
<p>Sempre tem mais. Se a máquina estiver no domínio essa opção não funciona. Porém, o WinLogon verifica se existe um valor chamado ForceFriendlyUi, que descobri graças ao Process Monitor. Aliado ao LogonType, sendo igual a 1, a Tela de Boas-Vindas é habilitada, mesmo em um ambiente com servidor de domínio.</p>
<p>Por último, claro, salvo se não existir o valor GinaDll dentro da chave do WinLogon. Se esse for o caso, o ForceFriendlyUi também não funciona. E é exatamente aí que uma GINA é instalada.</p>
<p>E eis que surge uma nova GINA.</p>
</section><hr/>
<span id="virtualbox" title="VirtualBox"/></span>
<section id="section_virtualbox">
<p class="title"><a href="2008-07.html#virtualbox">#</a> VirtualBox</p>
<span class="title-heading">Caloni, 2008-07-04<a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_virtualbox')"><sup>[copy]</sup></a></span>
<p>O VirtualBox parece ser o concorrente mais próximo atualmente da VMWare. Descobrimos ele essa semana e resolvemos fazer alguns testes. O resultado foi bem animador.</p>
<p>Desenvolvido pela Sun Microsystems, as características do VirtualBox impressionam pelo cuidado que houve em torná-lo muito parecido com sua concorrente paga. Apenas para começar, ela suporta dispositivos USB, possui múltiplos snapshots e já suporta o modo do VMWare Fusion - chamado de "seamless mode" - que estará integrado na versão 7 da VMWare.</p>
<p>No entanto, entre as coisas que testamos (instalado em um Windows Vista SP1 como host), o que não funcionou já não agradou tanto. A lista de prós e contras ainda confirma a liderança da VMWare, pelo menos em qualidade:</p>
<pre>
Funcionalidade VMWare VirtualBox
Snapshots Sim Sim. Mesma velocidade.
USB Sim Sim. Não funcionou.
Seamless Mode Não Sim.
Clipboard Sim Sim. Não funcionou.
Shared Folders Sim Sim. Erros de acesso.
Ferramentas Guest Sim Sim.
Pause Momentâneo Não Sim.
</pre>
<p>Além da tabela de testes acima, é necessário notar que por mas três vezes a VM simplesmente parou de responder, sendo necessário reiniciar o programa Host.</p>
<p>Em suma, o VirtualBox tem tudo para arrasar em futuras versões. Se, é claro, conseguir competir em qualidade com a VMWare que, no momento, é a líder em soluções de virtualização. Talvez por isso sua solução não seja tão barata.</p>
</section><hr/>
<span id="projeto_modelo" title="Projeto-modelo"/></span>
<section id="section_projeto_modelo">
<p class="title"><a href="2008-07.html#projeto_modelo">#</a> Projeto-modelo</p>
<span class="title-heading">Caloni, 2008-07-08<a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_projeto_modelo')"><sup>[copy]</sup></a></span>
<p>É muito difícil construir um modelo de pastas que sirva para a maioria dos projetos que tivermos que colocar na fôrma. Ainda mais se esses projetos tiverem que futuramente fazer parte da mesma ramificação. Foi pensando em várias coisas que chegamos a uma versão beta que pode ajudar aqueles que ficam pensando durantes dias antes mesmo de colocar as mãos no código.</p>
<p>Antes de começar a pensar em como as pastas estarão alinhadas, é importante saber como funcionará o controle de código do seu projeto. Como eu disse sobre o Bazaar, a estrutura inicial permitirá a junção de dois projetos distintos se estes compartilharem do mesmo commit no começo de suas vidas.</p>
<p>Portanto, trate de iniciar a estruturação em um projeto-modelo que já contenha pelo menos um commit: o das pastas vazias já estruturadas.</p>
<pre>
bzr init _Template
cd _Template
bzr mkdir Docs
bzr mkdir Interface
bzr ...
bzr ci -m "Projeto-modelo. Herde desse projeto sua estrutura inicial"
</pre>
<p>Estruturação proposta:</p>
<ul><li>Build. Essa pasta contém tudo que é necessário para compilar e testar o projeto como um todo. Idealmente a execução da batch build.bat deve executar todo o processo. Após a compilação, é de competência dos componentes na subpasta Tests fazer os testes básicos do projeto para se certificar de que tudo está funcionando como deveria.</li>
<li>Common. Aqui devem ser colocados aqueles includes que servem para vários pontos do projeto. Está exemplificado pelo arquivo de versão (Version.h), pois todos os arquivos devem referenciar uma única versão do produto. Podem existir Outras definições básicas, como nome do produto, dos arquivos, etc. É aqui que são gravadas as interfaces que permitem dependência circular entre os componentes (e.g. Interface de componentes COM).</li>
<li>Docs. Aqui deve ser colocada toda a documentação que diz respeito ao projeto. A organização interna ainda não foi definida, pois imagina-se ser possível usar diversas fontes, como doxygen, casos de uso, bugs, arquivos de projeto e UML. Foi exemplificado com o arquivo todo.txt e changes.txt, que deve ter sempre a lista de coisas a fazer e a lista de coisas já feitas, respectivamente, tendo, portanto, que ser sempre atualizados.</li>
<li>Drivers. Essa é a parte onde ficam todos os componentes que rodam em kernel mode. Por se tratar de um domínio específico e muitas vezes compartilhar código-fonte de maneira não-heterodoxa (e.g. sem uso de LIBs), faz sentido existir uma pasta que agrupe esses elementos. Dentro da pasta existem subpastas para cada driver, exemplificados em Driver1 e Driver2.</li>
<li>Install. Todas as coisas relacionadas com instalação, desinstalação e atualização do software deve vir nessa pasta. Foi reservada uma subpasta para cada item, não sendo obrigatória sua divisão. Também existe uma pasta de DLLs, onde possivelmente existam telas personalizadas e biblioteca de uso comum pelos instaladores (o desinstalador conversa com o instalador e assim por diante).</li>
<li>Interface. Todas as telas de um programa devem ser colocadas nessa pasta. Essa é uma divisão que deve ser seguida conceitualmente. Por exemplo, se existir um gerenciador de alguma coisa no produto, as telas do gerenciador e o comportamento da interface ficam nessa pasta, mas o comportamento intrínseco do sistema (regras de negócio) devem ficar em Libraries. Para exemplificar o uso, foram criadas as Interface1 e Interface2.</li>
<li>Libraries. O ponto central do projeto, deve conter o código mais importante. Imagine a pasta Libraries como a inteligência de um projeto, de onde todos os outros componentes se utilizam para que a lógica do software seja sempre a mesma. As outras partes do projeto lidam com aspectos técnicos, enquanto o Libraries contém as regras abstratas de funcionamento. Opcionalmente ela pode ser estática ou dinâmica, caso onde foi criada a subpasta DLLs. Porém, elas devem ser divididas por função em bibliotecas estáticas, como foi exemplificado em Library1 e Library2.</li>
<li>Resources. A origem de todas as imagens, sons, cursores, etc de um projeto devem residir primeiramente na pasta Resources. A divisão interna desse item fica a critério do designer responsável, pois ele pode dividir tanto por função (Install, Interface) quanto por elementos (Images, Sounds).</li>
<li>Services. Além dos drivers e das interfaces alguns projetos necessitam de processos "invisíveis" que devem fazer algo no sistema. Isso inclui serviços do Windows, GINAs, componentes COM e coisas do gênero. Devem ser colocados nessa pasta e distribuídos como no exemplo, em Service1 e Service2.</li>
<li>Tools. Além dos componentes essenciais para o funcionamento do software também existem aqueles componentes que fornecem mais poder ao usuário, ao pessoal do suporte ou ao próprio time de desenvolvimento. Essas são as ferramentas de suporte que permitem a fácil identificação de erros no programa ou a configuração mais avançada de um item que a Interface não cobre. Adicionalmente foi colocada a subpasta Develop, que deve conter ferramentas usadas estritamente durante a fase de desenvolvimento.</li>
</ul>
<p>Todos os componentes que disponibilizarem unidades de testes devem conter uma pasta Tests dentro de si. Essa padronização permite facilmente a localização de testes internos aos componentes. Além disso, os arquivos executáveis de testes devem sempre terminar seu nome com Test, o que permite a automatização do processo de teste durante o build.</p>
<p>Acredito que este esboço esteja muito bom. É o modelo inicial que estou utilizando nos projetos da empresa e de casa.</p>
</section><hr/>
<span id="primeiros_passos_no_vmware_workstation" title="Primeiros passos no VMware Workstation"/></span>
<section id="section_primeiros_passos_no_vmware_workstation">
<p class="title"><a href="2008-07.html#primeiros_passos_no_vmware_workstation">#</a> Primeiros passos no VMware Workstation</p>
<span class="title-heading">Caloni, 2008-07-10 <a href="coding.html">coding</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_primeiros_passos_no_vmware_workstation')"><sup>[copy]</sup></a></span>
<p>Como uma ferramenta essencial que uso todos os dias da minha vida de programador, sou obrigado a falar neste blogue sobre a VMware, ferramenta que tem me salvado algumas centenas de horas de depuração, testes e alguns cabelos brancos (a mais).</p>
<p>Para os que não sabem, o VMware é um software de virtualização que permite rodar diversos sistemas operacionais secundários (chamados de convidados, ou guests) em cima do sistema operacional primário (chamado de hospedeiro, ou host). Para isso ele utiliza uma técnica muito interessante conhecida como virtualização, onde o desempenho da máquina virtual chega bem próximo da máquina nativa em que estamos rodando, ainda mais se instalados os apetrechos de otimização (vide VMware Tools) dentro dos sistemas operacionais convidados.</p>
<p>O VMware, diferente de alguns outros programas de virtualização, não é gratuito. No entanto, o tempo despendido pela equipe da VMware em tornar esta a solução a de melhor qualidade (opinião pessoal de quem já mexeu com Virtual PC e pouco de VirtualBox) está bem cotado, sendo que seu preço é acessível pelo desenvolvedor médio. Pior que o preço da VMware com certeza será o dos sistemas operacionais convidados, se estes forem da Microsoft, que obriga cada instância do Windows, seja hospedeiro ou convidado, a possuir uma licença separada. Se rodar um Windows XP como hospedeiro e um Vista e 2000 como convidados vai desembolsar pelo menos o quíntuplo da licença da VMware.</p>
<p>No entanto, não entremos em mais detalhes financeiros. Os detalhes técnicos são mais interessantes.</p>
<p>A instalação é simples e indolor, sendo constituída de cinco ou seis botões de next. O resto, e mais importante, é a instalação de um sistema operacional dentro de sua primeira máquina virtual. Outro assistente existe nessa fase para guiá-lo através de suas escolhas que irão configurar sua futura máquina.</p>
<p>Vejamos um pouco sobre redes.</p>
<ul><li>Use bridged networking. É criada uma conexão real através de uma ponte feita em cima de uma placa de rede da máquina real. É usado um IP diferente da máquina real e se comporta como uma outra máquina qualquer na rede.</li>
<li>Use NAT. As conexões são criadas usando o IP do sistema operacional hospedeiro. Para isto acontecer é usado o conhecido esquema de NAT, onde um único IP externo pode representar n IPs internos de uma rede (nesse caso, a rede virtual formada pelas máquinas virtuais de uma mesma máquina real).</li>
<li>Use host-only networking. O IP usado nessa conexão é diferente da máquina real, mas só é enxergada por ela e por outras VMs localizadas na mesma máquina hospedeira. Muito útil para isolar um teste de vírus, quando se precisa de uma rede mas não podemos usar a rede da empresa inteira.</li>
</ul>
<p>Imagine uma VM (Virtual Machine) como uma máquina de verdade, onde podemos dar boot, formatar HDs (virtuais ou reais), colocar e remover dispositivos. Tendo isso em mente, fica simples entender o que funciona por dentro de sua console, ou seja, a tela onde vemos a saída da virtualização.</p>
<p>Vejamos um pouco sobre discos virtuais.</p>
<p>Os HDs que criamos para nossas VMs são arquivos lógicos localizados em nosso HD real. A mágica em que o sistema operacional virtual acessa o disco virtual como se fosse de verdade é feita pela VMware, inclusive a doce ilusão que ele cotém 80 GB, enquanto seu arquivo-repositório ocupa meros 5 GB no disco. Nas edições novas do software, é possível mapear um HD virtual e exibi-lo na máquina real.</p>
<p>Se você dispõe do CD de instalação de um sistema operacional, por exemplo, Windows XP, basta inseri-lo no CD virtual de sua VM. Ela aceita também imagens ISO, se for o caso. Lembre-se apenas que ele terá que ser "bootável", do contrário é necessário um disquete de boot.</p>
<p>Vejamos um pouco sobre BIOS.</p>
<p>A sua VM emula todo o comportamento de uma máquina real. Ela, portanto, contém uma BIOS, feita pela VMware. Essa BIOS possui as mesmas opções interessantes de ordem de boot (primeiro o disquete, depois o HD, etc) e escolha de dispositivo de boot (tecla ESC).</p>
<p>A instalação do sistema operacional segue os mesmos passos que a instalação do sistema operacional de qualquer máquina de verdade.</p>
<p>Vejamos um pouco sobre as teclas mágicas.</p>
<ul><li>Entrar o foco na VM. Digite Ctrl + G. Todos seus movimentos de teclado e mouse só irão funcionar dentro da máquina virtual, exceto o Ctrl + Alt + Del, exclusividade do sistema de autenticação do Windows.</li>
<li>Tirar o foco da VM. Digite Ctrl + Alt. Todos seus movimentos de teclado e mouse passam a ser do SO hospedeiro.</li>
<li>Ctrl + Alt + Del dentro da VM. Use Ctrl + Alt + Insert. Ele terá o mesmo efeito que um CAD, independente em que tela estiver em sua VM.</li>
</ul>
<p>Após feita a instalação, você terá um sistema operacional rodando dentro de um sistema operacional. Isso não é legal?</p>
<p>A primeira coisa a fazer em sua VM com SO recém-instalado é criar um snapshot, ou seja, salvar o estado atual de sua máquina virtual. Ao fazer isso, se fizer alguma coisa dentro da VM que possa se arrepender depois, basta voltar para o estado que salvou anteriormente. A VMware permite criar quantos snapshots precisar (basta ter espaço em disco). Ela permite que você crie novas máquinas virtuais a partir de um estado de uma VM já criada, o que pode economizar todo o tempo de montar do zero outra VM ou copiar o disco virtual.</p>
<ul><li>Abrir os seus e-mails suspeitos. Não tenha mais medo de sujar seu computador com e-mails de conteúdo duvidoso. Crie um estado seguro em sua VM através de um snapshot (fotografia de estado da máquina virtual) e execute os anexos mais absurdos. Depois basta voltar para o estado seguro.</li>
<li>Testes que costumam alterar o estado da máquina. Driver, GINA ou serviço novo? Que tal usar uma VM para fazer os testes iniciais e parar de reformatar o Windows?</li>
</ul>
<p>As VMs possibilitam um mundo de utilidades que o mundo ainda está descobrindo. Para nós, desenvolvedores, a maior vantagem de tudo isso é termos nossos ambientes de testes mais bizarros facilmente configurados no conforto de uma caixinha de areia.</p>
</section><hr/>
<span id="segunda_versao_do_houaiss2babylon" title="Segunda versão do Houaiss2Babylon"/></span>
<section id="section_segunda_versao_do_houaiss2babylon">
<p class="title"><a href="2008-07.html#segunda_versao_do_houaiss2babylon">#</a> Segunda versão do Houaiss2Babylon</p>
<span class="title-heading">Caloni, 2008-07-14 <a href="coding.html">coding</a> <a href="projects.html">projects</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_segunda_versao_do_houaiss2babylon')"><sup>[copy]</sup></a></span>
<p>Depois de vários comentários de pessoas tendo problemas em converter seus dicionários Houaiss para o formato Babylon, resolvi criar vergonha na cara e dar uma pequena melhora na versão beta do conversor.</p>
<p>Agora a maioria dos erros que houver será descrita por uma mensagem no seguinte formato:</p>
<img src="img/segunda_versao_do_houaiss2babylon_houaiss2babylonerror.png"/>
<p>O primeiro erro acima ocorre principalmente se não houver algum Houaiss instalado que o programa possa detectar. Resolva este problema comprando um.</p>
<p>Abaixo segue a função criada para exibir essas mensagens:</p>
<pre>
void MessageError(DWORD err, PCSTR msg, ...)
{
CHAR errBuffer[100];
CHAR msgBuffer[ERR_STR_BUF_SIZE];
va_list vaList;
va_start(vaList, msg);
vsprintf(msgBuffer, msg, vaList);
va_end(vaList);
sprintf(errBuffer, " Erro de sistema número %d.", (int) err);
strcat(msgBuffer, errBuffer);
MessageBox(NULL, msgBuffer, STR_PROJECT_NAME, MB_OK | MB_ICONERROR);
}
</pre>
<p>Se você notou, a função acima pode receber um número de argumentos variáveis para formatar a string da mensagem principal do erro, além de exibir seu código. Essa mágica pode ser feita usando-se o cabeçalho padrão "stdarg.h". Através dele temos acesso ao tipo va_list, que representa uma lista de argumentos variáveis.</p>
<p>Pela convenção de chamada da linguagem C (e C++), quem desmonta a pilha é o chamador. Sendo assim, a função chamada não precisa conhecer o número de argumentos com que foi chamado.</p>
<p>A função de formatação de string é uma variante do conhecidíssimo printf, na versão que recebe um tipo va_list. Muito útil para formatação de logs.</p>
<p>A versão beta do Houaiss2Babylon está para sair. Não estarei mais atualizando o saite do projeto no LaunchPad. Aguardem por mais novidades no próprio blogue.</p>
</section><hr/>
<span id="o_caso_da_funcao_de_delay_load_desaparecida" title="O caso da função de Delay Load desaparecida"/></span>
<section id="section_o_caso_da_funcao_de_delay_load_desaparecida">
<p class="title"><a href="2008-07.html#o_caso_da_funcao_de_delay_load_desaparecida">#</a> O caso da função de Delay Load desaparecida</p>
<span class="title-heading">Caloni, 2008-07-16 <a href="coding.html">coding</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_o_caso_da_funcao_de_delay_load_desaparecida')"><sup>[copy]</sup></a></span>
<p>Todos os projetos do Visual Studio 6 estavam compilando normalmente com a nova modificação do código-fonte, uma singela chamada a uma função da DLL iphlpapi.dll. No entanto, ainda restava a compilação para Windows 95, um legado que não era permitido esquecer devido ao parque antigo de máquinas e sistemas operacionais de nossos clientes.</p>
<p>Ora, acontece que a função em questão não existe em Windows 95! O que fazer?</p>
<p>Essa é uma situação comum e controlada, que chega a ser quase um padrão de projeto: funções novas demais. A saída? Não chamar a função quando o sistema não for novo o suficiente. Isso pode ser resolvido facilmente com uma chamada a GetVersion.</p>
<p>Porém, um outro problema decorrente dessa situação é que a função chamada estaticamente cria um link de importação da DLL para o executável. Ou seja, uma dependência estática. Dependências estáticas necessitam ser resolvidas antes que o programa execute, e o carregador (loader) de programas do sistema é responsável por essa verificação.</p>
<p>Para verificar a existência de todas as DLLs e funções necessárias para nosso programa podemos utilizar o mundialmente conhecido Dependency Walker:</p>
<pre>
depends meu_executavel.exe
</pre>
<img src="img/o_caso_da_funcao_de_delay_load_desaparecida_depends_meu_executavel.png"/>
<p>Se a função ou DLL não existe no sistema, o seguinte erro costuma ocorrer (isso depende da versão do Sistema Operacional):</p>
<img src="img/o_caso_da_funcao_de_delay_load_desaparecida_loader_erro.png"/>
<p>Mas nem tudo está perdido!</p>
<p>Existe uma LIB no Visual Studio que serve para substituir a dependência estática de uma DLL pela verificação dinâmica da existência de suas funções quando, e se, for executada a função no programa.</p>
<p>Essa LIB contém algumas funções-chave que o Visual Studio utiliza ser for usado o seguinte parâmetro de compilação:</p>
<pre>
/delayload:iphlpapi.dll
</pre>
<p>A função principal se chama "__delayLoadHelper@8", ou seja, é uma função com convenção de chamada WINAPI (stdcall) que recebe dois parâmetros.</p>
<p>Isso costuma sempre funcionar, sendo que tive uma grande surpresa com os seguintes erros de compilação na versão do programa que deve ser executada em Windows 95:</p>
<pre>
--------------------Configuration: Project - Win32 Win95 Release--------------------
Linking...
iphlpapi.lib(iphlpapi.dll) : error LNK2001: unresolved external symbol ___delayLoadHelper@8
release/meu_executavel.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.
meu_executavel.exe - 3 error(s), 0 warning(s)
</pre>
<p>Isso, é claro, depois de ter checado e rechecado a existência da LIB de Delay Load na lista de LIBs a serem lincadas:</p>
<img src="img/o_caso_da_funcao_de_delay_load_desaparecida_delayimp.png"/>
<p>Acontece que eu conheço algumas ferramentas que podem sempre me ajudar em situações de compilação e linque: Process Monitor e dumpbin. O Process Monitor pode ser usado para obter exatamente a localização da LIB que estamos tentando verificar:</p>
<img src="img/o_caso_da_funcao_de_delay_load_desaparecida_delayimpprocmon.png"/>
<p>Após localizar o local, podemos listar seus símbolos, mais precisamente a função "delayLoadHelper":</p>
<pre>
C:\DDK\3790\lib\w2k\i386>dumpbin /symbols delayimp.lib | grep delayLoadHelper
108 00000000 SECT3C notype () External | ___delayLoadHelper2@8
</pre>
<p>A análise mostra que a função possui um "2" no final de seu nome, causando o erro de linque.</p>
<p>Essa função, pelo visto, tem mudado de nome desde o Visual C++ 6, o que fez com que LIBs mais novas não funcionassem com essa versão do Visual Studio.</p>
<p>Para sanar o problema, existem duas coisas que podem ser feitas:</p>
<p> 1. Usar a delayimp.lib antiga. Isso não exige nenhuma mudança no código.</p>
<p> 2. Criar uma função delayLoadHelper como wrapper. Isso exige a escrita de código. O código-fonte dessa função está disponível no diretório Include do Visual Studio, e pode ser adaptada para versões antigas.</p>
<p>Nessa sessão de depuração você aprendeu como usar o Process Monitor para rastrear arquivos usados na compilação e como listar símbolos de LIBs que são usadas para lincar o programa.</p>
</section><hr/>
<span id="o_conhecido_unresolved_external" title="O conhecido unresolved external"/></span>
<section id="section_o_conhecido_unresolved_external">
<p class="title"><a href="2008-07.html#o_conhecido_unresolved_external">#</a> O conhecido unresolved external</p>
<span class="title-heading">Caloni, 2008-07-18 <a href="coding.html">coding</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_o_conhecido_unresolved_external')"><sup>[copy]</sup></a></span>
<p>O artigo anterior mostrou que nem sempre as coisas são simples de resolver, mas que sempre existe um caminho a seguir e que, eventualmente, todos os problemas se solucionarão.</p>
<p>Porém, resolver um problema por si só não basta: é preciso rapidez. E como conseguimos rapidez para resolver problemas? Um jeito que eu, meu cérebro e o Dmitry Vostokov conhecem é montando padrões.</p>
<p>Um padrão nos ajuda a não pensar novamente em coisas que sabemos a resposta, de tantas vezes que já fizemos. Só precisamos saber o caminho para resolver determinado problema.</p>
<p>Mesmo assim, existem diversos caminhos a percorrer. Até mesmo para um singelo e batidíssimo "unresolved external".</p>
<h4>Primeiro passo: você está usando a LIB correta?</h4>
<p>O erro mais comum é usar uma LIB onde não está a função que estamos usando, ou usar uma versão diferente da mesma LIB que não contém a função, ou contém, mas com assinatura (parâmetros da função) diferentes. Isso pode ser verificado no código-fonte da LIB, se disponível, ou então pelo uso do dumpbin, como já vimos anteriormente.</p>
<p>Dica extra: às vezes você pensa que está usando uma LIB em um determinado caminho, mas o linker achou a LIB primeiro em outro lugar. Para se certificar que está verificando a mesma LIB que o linker achou, use o Process Monitor.</p>
<p>Às vezes, porém, não estamos usando a função diretamente e não conhecemos quem a usaria. Para isso que hoje em dia os compiladores mais espertos nos dizem em que parte do código foi referenciado a tal função:</p>
<pre>
test.obj : error LNK2019: unresolved external symbol func referenced in function main
</pre>
<p>É sábio primeiro inspecionar a função que referencia, para depois entender porque ela não foi encontrada. Mesmo parecendo diferente, essa operação faz parte do primeiro passo, que é identificar a origem.</p>
<h4>Segundo passo: você digitou direito?</h4>
<p>Parece estúpido, mas às vezes é esse o caso. Essa é a segunda coisa a fazer porque não é tão comum quanto a primeira, visto que hoje em dia é rotina colocarmos as funções em um header e incluirmos esse cabeçalho em nosso código-fonte (em C++, praticamente obrigatório). Se houvesse discrepância entre o nome da função chamada e o nome da função existente, provavelmente teríamos um erro de compilação ("função não encontrada") antes do erro de linking.</p>
<h4>Terceiro passo: tente incluir a função diretamente no seu código</h4>
<p>Se a LIB não está cooperando, e der pouco trabalho, experimente incluir a função inteira (ou o cpp) dentro do seu projeto, para linkar diretamente. Se funcionar, então existe alguma diferença de compilação entre os dois projetos (o seu e o da LIB) para que haja uma divergência no nome procurado. Procure nas opções de projeto.</p>
<h4>Quarto passo: comece de novo!</h4>
<p>Sempre que nos deparamos com um problema que aos poucos vai consumindo o nosso tempo, tendemos a gastar mais tempo fazendo coisas inúteis que sabemos que não irá adiantar de nada. Às vezes fazer brute force pode dar certo. Outras vezes, seria melhor recomeçar a pesquisa e tentar entender de fato o que está acontecendo na compilação. Em outras palavras: gastar o seu tempo pensando pode ser mais produtivo do que agir instintivamente.</p>
</section><hr/>
<span id="aprenda_a_usar_sua_api" title="Aprenda a usar sua API"/></span>
<section id="section_aprenda_a_usar_sua_api">
<p class="title"><a href="2008-07.html#aprenda_a_usar_sua_api">#</a> Aprenda a usar sua API</p>
<span class="title-heading">Caloni, 2008-07-22<a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_aprenda_a_usar_sua_api')"><sup>[copy]</sup></a></span>
<p>É conhecido que uma das desvantagens de se programar diretamente em Win32 API é a dificuldade de se entender os parâmetros e o retorno das funções. Concordo em parte. Constituída de boa documentação, parte da culpa dos programas mal-feitos reside na preguiça do programador em olhar a documentação por completo. A Win32 API está longe de ser perfeita, mas pelo menos está razoavelmente documentada, e é na leitura atenta da documentação que iremos encontrar as respostas que precisamos para que o programa funcione.</p>
<p>Vejamos alguns exemplos. O código abaixo parece bem razoável:</p>
<pre>
#include <windows.h>
int main()
{
HANDLE hFile = CreateFile("c:\\tests\\myfile.txt", GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if( hFile )
{
DWORD read = 0;
CHAR buffer[100];
if( ReadFile(hFile, buffer, sizeof(buffer), &read, NULL) )
{
WriteBuffer(buffer);
}
CloseHandle(hFile);
}
}
</pre>
<p>No entanto, está errado.</p>
<p>É fato que a maioria das funções que retornam handles retornam NULL para indicar o erro na tentativa de obter o recurso. Ao comparar o retorno com NULL, o programador geralmente faz uma chamada a GetLastError para saber o que aconteceu. No entanto, uma das funções mais usadas, a CreateFile, não retorna NULL, mas INVALID_HANDLE_VALUE.</p>
<p>Sendo assim, o código acima deveria ser:</p>
<pre>
if( hFile != INVALID_HANDLE_VALUE )
</pre>
<p>GetVersion também é uma função que muitos erraram. Erraram tanto que eles fizeram uma nova versão menos complicada. Como está escrito no MSDN: "The GetVersionEx function was developed because many existing applications err when examining the packed DWORD value returned by GetVersion, transposing the major and minor version numbers."</p>
<p>O motivo de tantos erro pode ter sido o fato que o valor retornado é uma estrutura de bits dentro de um DWORD, coisa que nem todos programadores C sabem lidar muito bem, e o fato de ser uma função muito utilizada por todos (pegar a versão do sistema operacional).</p>
<p>Eis a tabela de campos do retorno de GetVersion:</p>
<pre>
Platform High-order bit Next 7 bits Low-order byte
------------------------------------- -------------- ------------ --------------
Windows NT 3.51 0 Build number 3
Windows NT 4.0 0 Build number 4
Windows 2000 or Windows XP 0 Build number 5
Windows 95, Windows 98, or Windows Me 1 Reserved 4
Win32s with Windows 3.1 1 Build number 3
</pre>
<p>Mesmo que não seja tão difícil, pode ser ambíguo. Por exemplo, como saber se o Windows é 95, 98 ou ME?</p>
<p>O código abaixo, muito usado por todos que suportam ainda o Windows mais velhinhos, verifica se estamos rodando em plataforma NT ou 9x.</p>
<pre>
#include <windows.h>
#include <stdio.h>
int main()
{
DWORD winVer = GetVersion();
BOOL isPlatformNT = winVer >= 0x80000000 ? FALSE : TRUE;
if( isPlatformNT )
printf("Plataforma NT\n");
else
printf("Bem-vindo ao parque dos dinossauros!\n");
return isPlatformNT ? 1 : 0;
}
</pre>
<img src="img/aprenda_a_usar_sua_api_jurassicpark.png"/>
<p>Nem sempre o handle que obtemos é fechado com CloseHandle. As funções abaixo retornam handles que devem ser desalocados com as funções à direita:</p>
<pre>
Função que obtém recurso Função que libera recurso
------------------------ -------------------------
LoadLibrary FreeLibrary
RegOpenKey RegCloseKey
GetDC ReleaseDC
BeginPaint EndPaint
</pre>
<p>Sempre tem mais exemplos. Algumas dicas úteis para o dia-a-dia de um programador Win32 API são:</p>
<ul><li>Leia a documentação</li>
<li>Se atente aos valores de retorno em caso de sucesso e erro</li>
<li>Leia sempre a seção remarks pelo menos uma vez; ela explica como desalocar recursos</li>
<li>Releia a documentação</li>
</ul>
<p>Às vezes uma singela chamada de uma função de autenticação pode nos fazer preencher uma estrutura de 20 membros, sendo que seis deles são obtidos com mais sete chamadas de funções, todas com direito a desalocar recursos no final. O importante é sempre manter a calma, o espírito de aprendizado e aventura. Afinal, quem mandou não fazer software de telinha?</p>
</section><hr/>
<span id="entrevista_com_o_caloni_no_do_zero_ao_mestre" title="Entrevista com o Caloni no 'Do ZERO ao MESTRE'"/></span>
<section id="section_entrevista_com_o_caloni_no_do_zero_ao_mestre">
<p class="title"><a href="2008-07.html#entrevista_com_o_caloni_no_do_zero_ao_mestre">#</a> Entrevista com o Caloni no 'Do ZERO ao MESTRE'</p>
<span class="title-heading">Caloni, 2008-07-24<a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_entrevista_com_o_caloni_no_do_zero_ao_mestre')"><sup>[copy]</sup></a></span>
<p>Há muito pouco tempo atrás surgiu um blogue de um programador com o desejo de aprender C++ em seis meses. Ele entrou em contato comigo para divulgar seu trabalho, e lhe disse que na internet seu trabalho se divulga por si só. E é verdade. No entanto, não contente, ele me pediu para responder um questionário no estilo entrevista. Não sei se o resultado foi satisfatório, mas pelo menos foi curioso. Foram perguntas simples e respostas mais simples ainda.</p>
<p>Para os que quiserem ler a entrevista e/ou acompanhar as desventuras de um programador com pressa, dê uma olhada no <a href="http://dozeroaomestre.blogspot.com/2008/07/entrevista-wanderlei-caloni.html">artigo</a> em "Do ZERO ao MESTRE em 6 meses", um blogue recém-nascido.</p>
<p>Update de 2020-03-14: estava corrigindo posts antigos e ao chegar neste fui verificar o blog do Rafael Becker. Ele havia parado por uns anos e voltou a postar em 2014. Fico feliz em saber que ele seguiu carreira e hoje trabalha com o que gosta. =)</p>
</section><hr/>
<span id="antidebugging_using_exceptions_part_one" title="Antidebugging using exceptions (part one)"/></span>
<section id="section_antidebugging_using_exceptions_part_one">
<p class="title"><a href="2008-07.html#antidebugging_using_exceptions_part_one">#</a> Antidebugging using exceptions (part one)</p>
<span class="title-heading">Caloni, 2008-07-28 <a href="coding.html">coding</a> <a href="projects.html">projects</a> <a href="english.html">english</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_antidebugging_using_exceptions_part_one')"><sup>[copy]</sup></a></span>
<p>A debugger puts breakpoints to stop for a moment the debuggee execution. In order to do this it makes use of a well known instruction: int 3. This instruction throws an exception - the breakpoint exception - that is caught by the operating system and bypassed to the handling code for this exception. For debuggee processes this code is inside the debugger. For free processes this code normally doesn't exist and the application simply crashs.</p>
<p>The main idea in this protection is to take care these exceptions during the application execution. Doing this, we can make use of this fact and, in the handling code, run the protected code. The solution here looks like a script interpreter. It consists basically of two threads: The first one read an instructions sequence and tells the second thread to run it step to step. In order to do this the second thread uses a small functions set with well defined code blocks. Here's the example in pseudocode:</p>
<pre>
// the well-defined functions are functional blocks of code and have
// the same signature, allowing the creation of a pointer array to them
void WellDefinedFunction1( args );
void WellDefinedFunction2( args );
void WellDefinedFunction3( args );
//...
void WellDefinedFunctionN( args );
// this thread stays forever waiting execution commands from some
// well-defined function. the parameter that it receives is the function number
void ExecutionThread()
{
// 2. ad aeternum
while( true )
{
// 5. it runs some well-defined function by number
ExecuteWellDefinedFunction( functionNumber );
}
}
// the well-defined functions script is an integer array indicating
// the number for the next function that is going to be called
int FunctionsToBeCalled[] = { 3, 4, 1, 2, 34, 66, 982, n };
int Start()
{
// 1. we create the thread that is going to run commands
CreateThread( ExecutionThread );
// 3. for each script item (each function number)
for( int i = 0; i < sizeof(FunctionsToBeCalled); ++i )
{
// 4. tells the thread to run the function number N
TellExecutionThreadToExecuteWellDefinedFunction( FunctionToBeCalled[i] );
}
// 6. end of execution.
return 0;
}
</pre>
<p>The protection isn't there yet. But it will as intrinsic part of the execution thread. All we need to do is to add a exception handling and to throw lots of int 3. The thrown exceptions are caught by a second function that runs the instruction before to returning:</p>
<pre>
// filter exceptions that were thrown by the thread below
DWORD ExceptionFilterButExecuteWellDefinedFunction()
{
// 5. run some well-defined function by number
ExecuteWellDefinedFunction( number );
return EXCEPTION_EXECUTE_HANDLER; // goes to except code
}
// this thread stays forever waiting execution commands from a
// well-defined function. its "parameter" is the function number
void ExecutionThread()
{
// 2. ad aeternum
while( true )
{
__try
{
__asm int 3 // breakpoint exception
// it stops the debugger if we have an attached debugger in
// the process, or throws an exception if there is no one
}
__except( ExceptionFilterButExecuteWellDefinedFunction() )
{
// it does nothing. here is NOT where is the code (obvious, huh?)
}
Sleep( someTime ); // give some time
}
}
</pre>
<p>The execution thread algorithm is the same. Just the point where each instruction is executed depends to the exception throw system. Note that this exception has to be thrown in order to the next instruction run. This is fundamental, since this way nobody can just rip of the int 3 code to avoid the exception. If one does that, so no instruction will be executed at all.</p>
<p>In practice, if one tries to debug such a program one will have to deal with tons of exceptions until find out what's happening. Of course, as in every software protection, is's not definitive; it has as a purpose to make hard the reverse engineering understanding. That's not going to stop those who are <a href="http://www.codebreakers-journal.com">really good</a> doing that stuff.</p>
<p>Nothing is for free</p>
<p>The price paid for this protection stays on the source code visibility and understanding, compromised by the use of this technique. The programming is state machine based, and the functions are limited to some kind of behavior standard. So much smaller the code blocks inside the minifunctions, so much hard the code understanding will be.</p>
<p>The example bellow receives input through a command prompt and maps the first word typed to the function that must be called. The rest of the typed line is passed as arguments to the functions. The interpreter thread reads the user input and writes into a global string variable, at the same time the executor thread waits the string to be completed to starts the action. It was used the variable pool to let the code simpler, but the ideal would be some kind of synchronise, just like events, by example.</p>
<pre>
/** @brief Sample demonstrating how to implemente antidebug in a code exception based.
@date jul-2007
@author Wanderley Caloni
*/
#include <windows.h>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <stdlib.h>
using namespace std;
// show available commands
bool Help(const string&)
{
cout << "AntiDebug Test Program\n"
<< " Echo string to be printed\n"
<< " System command [params]\n"
<< " Quit\n\n";
return true;
}
// run system/shell command
bool System(const string& cmd)
{
system(cmd.c_str());
return true;
}
// print string to output
bool Echo(const string& str)
{
cout << str << endl;
return true;
}
// quit program
bool Quit(const string&)
{
exit(0);
return false;
}
// minifunctions array
bool (* (g_miniFuncs[]) )(const string&) = { Help, System, Echo, Quit };
// "minifunction -> index" mapping
map<string, int> g_miniFuncIdx;
// start minifunctions mapping
void InitializeMiniFuncIdx()
{
g_miniFuncIdx["Help"] = 0;
g_miniFuncIdx["System"] = 1;
g_miniFuncIdx["Echo"] = 2;
g_miniFuncIdx["Quit"] = 3;
}
// last line read from input
string g_currentLine;
// how much time are we going to wait for the next line?
const DWORD g_waitTime = 1000;
// run minifunctions
DWORD FilterException()
{
DWORD ret = EXCEPTION_CONTINUE_EXECUTION;
if( ! g_currentLine.empty() )
{
istringstream line(g_currentLine);
g_currentLine.clear();
string function;
string params;
line >> function;
getline(line, params);
// 5. run some well-defined function by number
if( ! g_miniFuncs[g_miniFuncIdx[function] ](params) )
ret = EXCEPTION_CONTINUE_SEARCH;
}
return ret;
}
DWORD WINAPI AntiDebugThread(PVOID)
{
InitializeMiniFuncIdx(); // start minifunction mapping
// 2. ad aeternum (or almost)
while( true )
{
//FilterException();
__try // the extern try waits for an exit command
{
__try // the intern try stays generating exceptions continuously
{
__asm int 3
}
// FilterException is the function who runs minifunctions
__except( FilterException() )
{
// we can put some fake code here
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
break; // get out from ad aeternum (to the limbo?)
}
Sleep(g_waitTime);
}
return ERROR_SUCCESS;
}
/** and God said: 'int main!'
*/
int main()
{
DWORD ret = ERROR_SUCCESS;
DWORD tid = 0;
HANDLE antiDebugThr;
// 1. we create the thread that is going to run the commands
antiDebugThr = CreateThread(NULL, 0, AntiDebugThread, NULL, 0, &tid);;
if( antiDebugThr )
{
// 3. for each item in the script (function numbers)
while( cin )
{
cout << "Type something\n";
// 4. tells the thread to run the function number N
getline(cin, g_currentLine);
if( WaitForSingleObject(antiDebugThr, g_waitTime * 2) != WAIT_TIMEOUT )
break;
}
GetExitCodeThread(antiDebugThr, &ret);
CloseHandle(antiDebugThr), antiDebugThr = NULL;
}
// 6. end of execution.
return (int) ret;
}
</pre>
<p>The strength in this protection is to confound the attacker easily in the first steps (days, months...). Its weakness is the simplicity for the solution, since the attacker eventually realize what is going on. It is so easy that I will let it as an exercise for my readers.</p>
<p>In the next part we will se an alternative to make the code clearer and easy to use in the every day by a security software developer.</p>
</section><hr/>
<span id="antidebugging_using_exceptions_part_two" title="Antidebugging using exceptions (part two)"/></span>
<section id="section_antidebugging_using_exceptions_part_two">
<p class="title"><a href="2008-07.html#antidebugging_using_exceptions_part_two">#</a> Antidebugging using exceptions (part two)</p>
<span class="title-heading">Caloni, 2008-07-30 <a href="coding.html">coding</a> <a href="projects.html">projects</a> <a href="english.html">english</a><a href="2008-07.html"> <sup>[up]</sup></a> <a href="javascript:;" onclick="copy_clipboard('section#section_antidebugging_using_exceptions_part_two')"><sup>[copy]</sup></a></span>
<p>In the first article we saw how it's possible to spoof the debugger through exceptions and let the attacker lose some considerable time trying to unbind the program from the fake breakpoints. However, we saw also that this is a difficult solution to keep in the source code, besides its main weakness to be easily bypassed if discovered. Now it's time to put things easier to support and at the same time to guarantee tough times even if the attacker discover what is going on.</p>
<p>The upgrade showed here still uses the exception throwing intrinsically, but now it doesn't depends on the code division in minifunctions and minicalls. Instead, we just need to get code traces and put them inside a miraculous macro that will do everything we want. This, of course, after some "hammer work" that will be explained here.</p>
<pre>
// Go back to place pre-defined by the restoration point.
void LongJmp(restorePoint)
{
// Here we will generate an exception to make things difficult.
// @todo Make a breakpoint exception and catch it.
// 3. We return to the if without using the stack, but from the restoration point.
GoBackToTheStartFunction(restorePoint);
}
// Here everything begins.
int Start()
{
// Obs.: follow the agreement flow according to the numbers.
// 1. First pass: we define a restoration point to the return of LongJmp.
// 4. Second pass: we go back from the LongJmp function, but this time we get into the else.
if( RestorePointDefined() == Defined )
{
// 2. We call the function that will return to the if.
LongJmp( if );
}
else
{
// 5. Call the real function, our true target.
CallTheUsefulFunction();
}
// 6. End of execution.
return 0;
}
</pre>
<p>The solution above is explained in pseudocode to make things clearer. Notice that exist some kind of invisible return, not stack based. To handle it, however, we can use the good for all C ANSI standard, using the setjmp (step one) and longjmp (step 3). To understand the implementation for theses functions running on the 8086 platform we need to get the basic vision of the function calls in a stack based environment (the C and Pascal way).</p>
<h4>Registers, stack frame and call/ret</h4>
<p>Registers are reserved variables in the processor that can be used by the assembly code. Stack frame is the function calling hierarchy, the "who called who" in a given execution state. Call and ret are assembly instructions to call and return from a function, respectively. Both change the stack frame.</p>
<p>Imagine you have a function, CallFunc, and another function, Func, and one calls the other. In order to analyse just the function call, and just that, let's consider Func doesn't receive any argument and doesn't return any value. The C code, would be like bellow:</p>
<pre>
void Func()
{
return;
}
void CallFunc()
{
Func();
}
</pre>
<p>Simple, huh? Being simple, the generated assembly will be simple as well. In CallFunc it should have the function call, and inside Func the return from the call. The rest of the code is related with Debug version stuff.</p>
<pre>
Func:
00411F73 prev_instruction ; ESP = 0012FD38 (four bytes stacked up)
00411F74 ret ; *ESP = 00411FA3 (return address)
CallFunc:
00411F9C prev_instruction
00411F9E call Func (411424h) ; ESP = 0012FD3C
00411FA3 next_instruction
</pre>
<p>From the assembly above we can conclude two things: 1. The stack grows down, since its value decremented four bytes (0012FD3C minus 0012FD38 equal four) and 2. The return value from the calling is the address of the very next instruction after the call instruction, in the case 00411FA3.</p>
<p>Well, in the same way we can follow this simple execution, the attacker will do as well. That's why in the middle of this call we will throw an exception and, in the return, we will not do the return in the conventional way, but using another technique that, instead using the ret instruction, sets manually the esp value (stack state) and jumps to the next instruction in CallFunc.</p>
<pre>
Func:
00411F60 throw_exception
00411F61 ...
00411F73 catch_exception
00411F74 mov ESP, 0012FD3C ; ESP = 0012FD3C, just like CallFunc
00411F75 jmp 00411FA3 ; jumps to CallFunc::next_instruction
</pre>
<h4>Back to the Middle Earth</h4>
<p>All this assembly stuff doesn't need to be written in assembly level. It was just a way I found to illustrate the differences between the stack return and the jump return. As it was said, to the luck and well being for all, this same technique can be implemented using ANSI C functions:</p>
<pre>
jmp_buf env; // Contains the next instruction (stack state).
void Func()
{
// 3. Return using the "nonconventional" way
longjmp(env, 1);
}
void CallFunc()
{
// 1. If we're setting, returns 0.
// 2. If we're returning, returns a value different from 0.
if( setjmp(env) == 0 )
Func();
int x = 10; // 4. Next instruction.
}
</pre>
<p>That was the new trick for the trowing of exceptions. The final code is clearer, now:</p>
<pre>
/** The only purpose of this function is to generate an exception.
*/
DWORD LongJmp(jmp_buf* env)
{
__try
{
__asm int 3
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
longjmp(*env, 1);
}
return ERROR_SUCCESS;
}
/** And God said: 'int main!'
*/
int main()
{
DWORD ret = ERROR_SUCCESS;
while( cin )
{
string line;
cout << "Type something\n";
getline(cin, line);
jmp_buf env;
if( setjmp(env) == 0 )
{
LongJmp(&env);
}
else
{
cout << line << endl;
}
}
return (int) ret;
}
</pre>
<p>At first sight, it seems a waste the if being directly in the code (remember we gonna use the same conditional structure in several parts in the code). To turn things clearer, resume the protected call and allows the protection to be disabled in debug version code, let's create a macro:</p>
<pre>
/** Use this macro instead LongJmp
*/
#define ANTIDEBUG(code)
{
jmp_buf env;
if( setjmp(env) == 0 )
{
LongJmp(&env);
}
else
{
code;
}
}
/** And God said: 'int main!'
*/
int main()
{
DWORD ret = ERROR_SUCCESS;
while( cin )
{
string line;
cout << "Type something\n";
getline(cin, line);
ANTIDEBUG(( cout << line << endl ));
}
return (int) ret;
}
</pre>
<p>Now we allow the antidebugging selection by call, what turns things much easier than to choose the protected points inside the code.</p>
</section><hr/>
<span style="float: left;">
<a href="2008-06.html">[2008-06]</a>
<a href="2008-08.html">[2008-08]</a>
</span>
</div>
</div>
</section>
<footer class="footer">
<div class="container">
</div>
</footer>
</body>
</html>