A Função Principal: SHDeviceLockAndPrompt()
O Windows Mobile, a partir da versão 6, disponibiliza uma chamada de função que permite bloquear o dispositivo via código, a SHDeviceLockAndPrompt. Ela está presente também em algumas versões anteriores, caso o fabricante tenha optado por incluir tal funcionalidade na imagem do sistema. No entanto, como a maioria das operações mais específicas e pouco utilizadas, essa função está disponível apenas nas bibliotecas nativas do Windows (C/C++).
Para podermos utilizar tal funcionalidade no projeto C#, precisamos primeiro declarar um método com uma assinatura que possa ser traduzida para a função original. Esse novo método será o responsável por fazer a comunicação com o código nativo diretamente. O mecanismo acima é conhecido por “Platform Invoke”, ou apenas P/Invoke. Não será necessário conhecer profundamente P/Invoke para um entendimento completo desse artigo, mas se você quiser saber mais, veja um tutorial explicativo no site MSDN.
Na documentação da Microsoft sobre a função mencionada anteriormente, podemos ver que a biblioteca a ser referenciada, para podermos chamar o método via .NETCF, é a “Aygshell.dll”:
A declaração do método no nosso código C# poderá ser feita de 2 formas, a primeira com o mapeamento mais comum:
[DllImport("aygshell")]
private static extern int SHDeviceLockAndPrompt();
E a segunda, com um recurso de tradução de resultados para exceções automaticamente:
[DllImport("aygshell", PreserveSig = false)]
private static extern void SHDeviceLockAndPrompt();
Essa segunda forma pode ser preferível se o contexto que a usa já estiver tratando exceções e esse for o meio comum de tratamento de erros usado no seu sistema.
Estamos Prontos para Bloquear o Dispositivo?
Para bloquearmos o dispositivo, usaremos a função definida acima. No entanto, isso não é suficiente. Não comentamos nada a respeito de como modificar a senha de desbloqueio! Usando a API do Android, é possível alterar diretamente todas as restrições sobre a senha e atribuir uma nova senha, antes do bloqueio. Infelizmente, esse procedimento não pode ser mapeado diretamente para o WM. Apesar de existirem métodos específicos para verificar (CheckPassword()) e modificar (SetPassword()) a senha atual, é impossível fazer alterações sem saber qual a senha corrente no dispositivo (note que a função “SetPassword” recebe a senha antiga e falha caso ela esteja incorreta).
A única maneira que temos para assegurar o bloqueio completo do dispositivo (com as garantias que o sistema nos oferece), e poder atribuir uma senha qualquer para o desbloqueio, é reimplementar o sistema de autenticação do Windows Mobile.
Sobre essa constatação, temos um ponto positivo e um (ou diversos…) negativos.
Primeiro o positivo:
O LASS (Local Authentication SubSystem), responsável por controlar as verificações de usuário e o bloqueio do dispositivo, nos permite “trocar” o aplicativo que interage com o usuário. Assim, poderemos criar nossa própria aplicação e tratar da maneira que for mais conveniente o problema da senha e da interface! Basta armazenar a senha em um local seguro e a ler durante a verificação de desbloqueio. Dessa forma, é possível um software externo saber como e onde armazenar essa senha e, após alterá-la, chamar diretamente o método SHLockAndPrompt. Seguindo esses passos, o sistema será bloqueado e o nosso tratador terá o controle sobre o desbloqueio.
E, sobre o ponto negativo principal:
Esse novo programa, que substituirá o original, necessariamente precisa ser feito em código nativo do dispositivo.
Essa restrição existe pois os módulos necessários para carregar com sucesso uma aplicação .NET não estarão ainda em memória, dependendo do contexto em que o nosso autenticador for chamado. Por exemplo, se bloquearmos o dispositivo e em seguida fizermos um soft reset, o sistema se encarregará de manter a informação de que todo o sistema está bloqueado, e a primeira aplicação a ser carregada no novo boot será a nossa. Além desse problema, existe ainda uma segunda restrição para dlls feitas em C#, que é a impossibilidade de exportação de métodos como são feitas as dlls em C.
Sabendo disso, passaremos agora para a próxima sessão, que trata da criação desse “plugin” de autenticação.
Criando um LAP
“LAP”, sigla de Local Authentication Plugin, é o nome criado pela Microsoft para esse software que gerencia as autenticações de usuário. O conceito de um plugin customizado foi criado para que empresas diversas pudessem manter seus próprios mecanismos de autenticação, sem serem limitadas pelo sistema padrão. É possível, por exemplo, criar suporte para identificação biométrica, autenticação via smartcards, etc. Nosso intuito aqui é muito mais singelo: usaremos apenas parte do potencial de um LAP para bloquearmos o dispositivo.
Como descrito na documentação referenciada no parágrafo anterior, o LAP é uma dll (e não um código executável), que deve implementar algumas funções específicas. O detalhe é que essas funções nunca serão diretamente chamadas pelo nosso código. Elas são acessíveis apenas pelo LASS, e qualquer ação que quisermos tomar sobre o LAP deve ser direcionada ao LASS. A função que usaremos, SHDeviceLockAndPrompt, faz justamente isso: pede ao LASS para interagir com o LAP, bloqueando o dispositivo.
Como o LAP acaba sendo muito mais complicado do que parece (diversos cuidados devem ser tomados para que não ocorram problemas que travem o sistema operacional ou, pior, permitam que o usuário saia do bloqueio indevidamente), tomaremos como base a aplicação demo da própria Microsoft, que acompanha o SDK do Windows Mobile 6. Os exemplos de LAPs podem ser encontrados nas subpastas Samples\PocketPC\CPP\win32\LAP e Samples\Smartphone\CPP\win32\LAP
Para começar a implementação, precisamos criar um projeto dll em C. Utilize sempre como referência as configurações da solução do LAP exemplo para fazer alterações. Algumas opções do projeto C são muito mais complexas que as dos projetos C#, e podem ter efeitos indesejados no código ou gerar problemas difíceis de rastrear.
Nosso arquivo .c principal conterá as funções necessárias para o funcionamento básico do LAP, e terá o seguinte esqueleto:
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
BOOL InitLAP(InitLap* il)
{
return TRUE;
}
void DeinitLAP() {}
BOOL VerifyUser(const GUID* AEKey, LPCWSTR pwszAEDisplayText, HWND hwndParent, DWORD dwOptions, PVOID Extended)
{
return TRUE;
}
void VerifyUserStart(const GUID *AEKey, LPCWSTR pwszAEDisplayText, HWND hwndParent, DWORD dwOptions, PVOID pExtended) {}
void VerifyUserStop() {}
void VerifyUserToTop() {}
BOOL LAPCreateEnrollmentConfigDialog(HWND hwndParent,DWORD dwOptions)
{
return TRUE;
}
Além desse arquivo de código principal, é preciso criar um segundo arquivo, .def, que contém quais métodos da dll gerada serão efetivamente exportados e poderão ser acessados por outros módulos (no caso do LAP, apenas o LASS). Arquivos .def são uma das diversas maneiras de exportar funções de uma dll em C. Para saber mais consulte a referência na MSDN. Nosso arquivo .def tera a seguinte forma:
EXPORTS
VerifyUser
LAPCreateEnrollmentConfigDialog
InitLAP
DeinitLAP
VerifyUserStart
VerifyUserStop
VerifyUserToTop
Cada uma dessas funções tem uma finalidade bem específica. Em resumo, o que se espera de cada uma é:
- InitLAP: inicializações de variáveis globais e estados do LAP devem ser feitas aqui, pois essa função roda apenas uma vez no carregamento da dll.
- DeinitLAP: similarmente, essa função é chamada apenas uma vez quando a dll do LAP é descarregada do sistema, e deve ser usada para limpezas finais.
- LAPCreateEnrollmentConfigDialog: chamada quando existe uma requisição para a troca das configurações do LAP pelo usuário. Um exemplo simples é o que acontece ao se entrar no menu de configurações do sistema e selecionar Senha. Uma chamada a essa função deve apresentar uma janela com as opções de senha e armazenar as definições em um local acessível pelo restante do LAP. Essa funcionalidade não é necessária no escopo do lock, já que o usuário não deve poder configurar o LAP diretamente.
- VerifyUserStart: chamada uma vez antes de uma ou mais chamadas sequenciais de VerifyUser. É um bom ponto para criar a janela de verificação e inicializar seus campos.
- VerifyUser: chamada quando existe uma verificação de senha a ser feita. Pode ser feita pelo sistema (comumente depois de um certo período de inatividade) ou por código via chamada ao LASS da função de mesmo nome, VerifyUser. Como dito anteriormente, não esqueça que é proibido chamadas diretas ao LAP, e essa nova função é uma redireção do LASS. A documentação dessa função se encontra aqui. É importante levar em conta que esse método do LASS não bloqueia o aparelho, mas sim requisita uma autenticação, que pode ser usada pelo software chamador para prosseguir numa ação ou não, dependendo do retorno.
- VerifyUserStop: da mesma forma que VerifyUserStart, essa função é executada ao término de uma ou mais tentativas consecutivas de verificação. Usada principalmente para limpar as estruturas criadas em VerifyUserStart.
- VerifyUserToTop: função auxiliar do LAP, chamada automaticamente para forçar a exibição da tela de verificaçao. É importante que essa funcionalidade seja implementada corretamente para que a interface do usuário não fique bloqueada por uma autenticação pendente em segundo plano. Isso pode ocorrer em algumas situações especiais, como autenticações múltiplas sendo requisitadas ao LAP em pequenos intervalos de tempo.
A criação da interface e o código necessário para se chegar nas funcionalidades acima não fazem parte do escopo desse artigo, e envolvem uma série de conceitos relativamente complexos, como a API de mensagens do Windows usada para fazer as interações entre os controles (um bom ponto de partida é esse artigo na MSDN) e a CryptoAPI, usada para armazenar informações sensíveis como senhas no registro (referência). Lembre-se de sempre se basear no código de demonstração quando estiver utilizando essas bibliotecas, pois ele utiliza tanto a API de mensagens quanto a CryptoAPI em diversos pontos do código.
Um detalhe que pode comprometer a segurança do LAP e provocar falhas de usabilidade é o estilo das janelas criadas, por exemplo, que deve conter a flag que indica execução acima do startup. O arquivo de recurso (.rc) da aplicação demo mostra isso claramente:

IDD_VERIFY DIALOG 0, 24, 130, 120
STYLE DS_MODALFRAME | DS_SETFOREGROUND | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_BORDER
EXSTYLE 0x60000000L
BEGIN
LTEXT "Enter your password and tap OK.",IDC_VERIFY,8,6,124,33
CONTROL "",IDC_PASSWORD,"sbedit",WS_BORDER | WS_TABSTOP | 0xa0,8,42,124,13
CONTROL "",IDC_EMERGENCY,"sbedit",NOT WS_VISIBLE | WS_BORDER | WS_TABSTOP | 0x2080,8,42,124,13
LTEXT "",IDC_INFO,8,60,124,100,SS_NOPREFIX
END
Com esse pequeno subconjunto de funções do LAP (existem ainda diversas outras funções opcionais a serem implementadas que não foram mencionadas aqui, consulte aqui), já é possível comerçar os testes! Basta compilarmos o projeto, e substituirmos a dll do LAP original pela nossa. Mas como fazemos isso?
Substituindo o LAP Padrão
O LASS, além de intermediar toda a comunicação com o LAP, controla qual dos LAPs está ativo em um dado momento no sistema. Sim, é possível ter diversos LAPs simultaneamente no sistema de arquivos e carregá-los independentemente um do outro. Para fazer isso, é necessário mais um passo de configuração, que é a edição do registro do Windows. Assume-se que um desenvolvedor em busca de uma solução avançada como o lock no Windows Mobile saiba que edições no registro podem corromper todo o sistema, mesmo assim vale o alerta: modifique apenas as configurações presentes nesse artigo, sob risco de danificar o sistema operacional!
Para funcionar, cada LAP deve ter uma entrada própria (uma chave) no registro, em HKEY_LOCAL_MACHINE\Comm\Security\LASSD\LAP. Para os testes, podemos criar uma chave com o nome “LAPDemo”, na chave LAP.
Com a chave criada, precisamos de um valor string, que apontará para nossa dll. Crie um valor com o tipo string (REG_SZ), com nome “Dll” e coloque como dado o caminho completo de onde deseja colocar a dll. Note que se ela estiver na pasta windows, será necessário apenas incluir o nome da dll (o lap padrão, por exemplo, fica na pasta windows).
A estrutura nesse ponto deve ser a seguinte:

Note na imagem acima que existe uma segunda chave já cadastrada, “lap_pw”. Cada entrada na chave pai LAP é um LAP instalado no sistema, e lap_pw é o LAP default.
Após criar a nossa chave customizada, precisamos agora dizer ao sistema qual LAP deve ser carregado. Para fazer isso, observe o valor “ActiveLAP” na chave LAP. O dado armazenado nesse valor diz qual LAP está sendo usado especificando o nome da chave do LAP. Ao trocarmos o lap_pw por LAPDemo, estamos dizendo ao sistema para, na próxima atualização do plugin, carregar o nosso autenticador.

O Windows carrega o LAP toda vez que o sistema é reiniciado ou, alternativamente, após a chamada de uma outra função, LASSReloadConfig. A documentação dessa nova função pode ser encontrada aqui. A exemplo da nossa função de bloqueio, LASSReloadConfig também é uma função nativa sem equivalente no .NetCF, portanto precisamos novamente usar P/Invoke para usá-la:
[DllImport("coredll")]
private static extern bool LASSReloadConfig();
Sobre a assinatura do LAP
Um outro fator que compromete o funcionamento do LAP, e que ainda não foi mencionado, é a necessidade de assinatura do arquivo gerado (.dll) por um certificado presente no dispositivo. Não somente isso, mas o certificado deve estar presente no certificate store “Privileged Execution Trust Authorities”.
Uma das maneiras de instalar um certificado nesse certificate store do dispositivo é criando um xml-provisioning doc com as informações necessárias e enviar diretamente via código ou através de um arquivo .cab. Para saber mais sobre a API de configuração via arquivos xml consulte a referência na MSDN. A estrutura de um xmldoc com as configurações de um certificado é a seguinte:
<wap-provisioningdoc>
<characteristic type="CertificateStore">
<characteristic type="Privileged Execution Trust Authorities">
<characteristic type=<thumbprint_certificado>
<parm name="EncodedCertificate" value=<base64_certificado>/>
</characteristic>
</characteristic>
</characteristic>
<wap-provisioningdoc>
Para obter o valor de cada um dos 2 campos (thumbprint e valor codificado em base64) é preciso ter acesso ao certificado no sistema. Vá em executar, digite “certmgr.exe” e tecle enter. Encontre o certificado desejado e dê um duplo clique sobre ele. O thumbprint pode ser encontrado na listagem de parâmetros. Copie-o para o local indicado no xml.

O valor do certificado codificado em base64 pode ser obtido exportanto o certificado para um arquivo .cer. Para fazer isso, clique em “Copy to File” na janela de propriedades do certificado e siga as instruções até alcançar a janela de formato, selecionando “Base64 encoded x.509″:

Salve o arquivo gerado em um lugar de sua escolha e abra-o com um editor de texto. O valor mostrado entre as tags “—–BEGIN CERTIFICATE—–” e “—–END CERTIFICATE—–” deve ser copiado para a posição indicada no xml.
O arquivo assim gerado pode ser usado diretamente para instalar o certificado no dispositivo.
O processo para assinar uma dll C com um certificado presente na estação de desenvolvimento é bem simples. Basta entrar nas propriedades do projeto, selecionar “Authenticode Signing”, ativar a assinatura e finalmente escolher um certificado, como mostra a figura abaixo:

Antes de copiar a dll para o dispositivo, é interessante testar a sua validade pela ferramenta “Security Configuration Manager”, que pode ser encontrada aqui. Ela permite visualizar os certificados instalados no dispositivo, e ainda verificar a compatibilidade das assinaturas de um arquivo com o dispositivo.
A imagem a seguir mostra como deve estar a situação da assinatura da dll do LAP:

É imprescindível que a dll tenha a permissão “run pre-boot”, que é o nível máximo de acesso, para que possa funcionar corretamente quando o dispositivo é reiniciado. É também importante tomar cuidado com o fato de que a verificação feita por esse aplicativo não leva em conta as datas de validade dos certificados, apenas a sua presença. O certificado de desenvolvimento contido na SDK do Windows Mobile 6, por exemplo, já está expirado, e não servirá para testes a não ser que as datas do sistema, tanto na estação de desenvolvimento que assina a dll quanto no próprio dispositivo, sejam alteradas de acordo. Para obter um certificado mais recente (e as imagens dos emuladores com as instalações corretas) baixe a última versão do DTK (6.5.3) aqui.
Conclusão
Dependendo do nível de suporte e da generalidade da API que nos é oferecida, algumas tarefas podem ser muito mais complexas ou requerer um esforço maior de desenvolvimento do que outras. Nosso caso com o lock no WM, em comparação à implementação do Android, é nitidamente um exemplo disso. Todo o problema recai em estarmos usando uma API extremamente poderosa (mais até que as das outras plataformas) para usufruir de um pequeno conjunto de suas garantias (o bloqueio do dispositivo).
No entanto, para uma empresa que concentra seus esforços no desenvolvimento de soluções móveis, é extremamente importate conhecer os diversos recursos de cada uma de suas plataformas alvo. A possibilidade de criação de um LAP mais complexo certamente abre um leque de oportunidades que podem ser exploradas num futuro próximo.
Aqui encerra nossa série de artigos sobre lock e wipe em Windows Mobile! Obrigado aos que conseguiram acompanhar esse artigo. Esperamos que ele tenha sido útil de alguma forma, face à escassez de documentação sobre LAPs na internet.
Escrito por Juliano Gonçalves