Vou mostrar aqui um método para gerar um código Hardware Key baseado no MAC Address, isto é, na informação da placa de rede instalada na máquina. Essa é uma boa abordagem pois é teoricamente garantido que não haverão duas placas de rede com o mesmo MAC Address, mesmo que sejam de fabricantes diferentes. A ideia é capturar o endereço - que é uma informação binária - e gerar uma representação textual (string) dele. Para obter o endereço, vou usar uma função da API do Windows chamada GetAdaptersInfo. Ela aceita 2 parâmetros: um ponteiro para a estrutura IP_ADAPTER_INFO e o outro o tamanho da estrutura alocada:
function GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD;stdcall;
Parece que essa função não está mapeada no Delphi mas localizei no site do projeto JEDI fonte com o mapeamento das funções da biblioteca Iphlpapi.dll do Windows - incluindo a GetAdaptersInfo e as estruturas necessárias. Também tenho cópia desses fontes, caso não encontrem lá.
Esta é uma daquelas funções malucas do Windows que é preciso chamar 2 vezes: a primeira para obter a quantidade de memória a ser alocada e a segunda para efetivamente recuperar os dados desejados.
dwBufLen := 0;
AdapterInfo := Nil;
GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwBufLen > 0) then begin
GetMem (AdapterInfo, dwBufLen);
dwStatus := GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
...
AdapterInfo := Nil;
GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwBufLen > 0) then begin
GetMem (AdapterInfo, dwBufLen);
dwStatus := GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
...
O endereço que precisamos é um array de bytes, declarado como segue:
type TWMacId = array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
Listo abaixo o código para a função que recupera o MAC Address. Neste exemplo, estou recuperando apenas a primeira placa de rede encontrada; se houverem outras, o algorítmo simplesmente as ignora:
function GetMACaddress : TWMacId;
var i : integer;
AdapterInfo : Pointer;
pAdapterInfo : PIP_ADAPTER_INFO;
dwBufLen, dwStatus : LongWord;
begin
dwBufLen := 0;
AdapterInfo := Nil;{ Inicialmente, limpa o retorno }
for i := 0 to MAX_ADAPTER_ADDRESS_LENGTH - 1 do
Result [i] := 0;
GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwBufLen > 0) then begin
GetMem (AdapterInfo, dwBufLen);
dwStatus := GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwStatus = ERROR_SUCCESS) then begin
pAdapterInfo := PIP_ADAPTER_INFO (AdapterInfo);
{ Copia o endereço p/ nossa variável de retorno }
for i := 0 to MAX_ADAPTER_ADDRESS_LENGTH - 1 do
Result[i] := pAdapterInfo.Address[i];
FreeMem (AdapterInfo);
end;
end;
end;
var i : integer;
AdapterInfo : Pointer;
pAdapterInfo : PIP_ADAPTER_INFO;
dwBufLen, dwStatus : LongWord;
begin
dwBufLen := 0;
AdapterInfo := Nil;{ Inicialmente, limpa o retorno }
for i := 0 to MAX_ADAPTER_ADDRESS_LENGTH - 1 do
Result [i] := 0;
GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwBufLen > 0) then begin
GetMem (AdapterInfo, dwBufLen);
dwStatus := GetAdaptersInfo(PIP_ADAPTER_INFO (AdapterInfo), dwBufLen);
if (dwStatus = ERROR_SUCCESS) then begin
pAdapterInfo := PIP_ADAPTER_INFO (AdapterInfo);
{ Copia o endereço p/ nossa variável de retorno }
for i := 0 to MAX_ADAPTER_ADDRESS_LENGTH - 1 do
Result[i] := pAdapterInfo.Address[i];
FreeMem (AdapterInfo);
end;
end;
end;
Agora, falta formatar esse resultado como um texto. Vou transformar os bytes retornados usando a representação hexadecimal de cada um deles, numa ordem aleatória. Isto é, peguei aleatoriamente os bytes de número 4, 2, 1, 5 e 3. Segue a função completa:
function GetHardwareKey : String;
var id : TWMacId;
begin
id := getMacAddress;
Result := Format ( '%.2X', [id [4]]) +
Format ( '%.2X', [id [2]]) +
Format ( '%.2X', [id [1]]) +
Format ( '%.2X', [id [5]]) +
Format ( '%.2X', [id [3]]);
end;
var id : TWMacId;
begin
id := getMacAddress;
Result := Format ( '%.2X', [id [4]]) +
Format ( '%.2X', [id [2]]) +
Format ( '%.2X', [id [1]]) +
Format ( '%.2X', [id [5]]) +
Format ( '%.2X', [id [3]]);
end;
Se achar que usar somente o MAC Address não é suficiente para gerar um código seguro para sua aplicação, use essa informação como base e mescle-a com outros dados do computador. Por exemplo, inclua dados da CPU, do sistema operacional, etc.
16 comentários :
Até onde esta identificação é confiável? Será que há alguma forma de alterá-la, ou fazer o Windows identificar de forma "diferente"? Será que muda dependendo da versão do Windows? Obrigado
Fábio
O MAC Address pode ser modificado - veja por exemplo o link http://www.nthelp.com/NT6/change_mac_w2k.htm.
Mas, se quer gerar uma identificação única, monte seu próprio algorítmo que misture o Mac Address com outras informações do computador ou do Windows. Dessa forma, fica mais difícil alguém mal intencionado descobrir como falsificar seu código.
O programa neste post retorna um código independente da versão do Windows. Caso você mescle o MAC Address com versão do SO para gerar um código, aí sim deve levar em conta alterações na versão do SO.
Luís Gustavo, gostei muito do projeto de liberação de uso de sistema, mas te confesso que do jeito que esta explicado não consigo desenvolve-lo pois sou meio inesperiênte ainda, será que teria como me enviar um passo a passo. Desde já te agradeço.
E'mail: geraldo.tekmicro@gmail.com
Geraldo
O foco do post era outro e não me preocupei em detalhar um algorítmo de licenciamento. Segue uma visão geral do processo :
1) sua aplicação é instalada num computador
2) como no primeiro acesso a licença ainda não existe, gera o hardware key para o usuário enviar para vc.
3) Remotamente, vc criptografa o hardwarekey junto com informações de licença (recursos liberados no programa, qtde de usuários simulâneos ou outra característica que deseje).
4) A informação criptografada é devolvida para o usuário, que deve instalá-la localmente de forma que o seu programa consiga acessá-la e validar.
5) Toda vez que o usuário entrar no programa, a informação criptografada é validada pra saber se o computador em uso é o mesmo para o qual a licença foi atribuída.
Os passos podem variar dependendo da arquitetura que sua aplicação tem.
ola, Luís, onde consigo essa função GetAdaptersInfo, ond ela está na jedi? obrigado
Andrews
A função GetAdaptersInfo está na unit IpHlpApi. Há uma versão dela publicada no endereço http://www.koders.com/delphi/fidE47BDA7E986603E83E5266F72CC1ADDDAADF3EC9.aspx?s=pos. As outras units necessárias pra compilar podem ser obtidas a partir do mesmo endereço.
[]s
hummmmmm blz obrigado já baixei as unit's obrigado, agora vo tentar modificar aqui seus exemplos para ver se consigo apenas listar o nome dos adaptadores de redes
boa tarde Luíz, utilizando esta unit IpHlpApi, é possivel obter o nome dos adaptadores de rede? ex: Rele Local, Rele Local 1... ?
pois estou mexendo com configuração de ip e precissava do nome do adaptador para mostrar para o usuario
Andrews
A unit apenas mapeia as funções e estruturas do Windows para uso no Delphi. Isso quer dizer que é possível fazer as mesmas coisas descritas na documentação da API da Microsoft.
Se der uma olhada na documentação da estrutura IP_ADAPTER_INFO - que é retornada pela função GetAdaptersInfo - , verá que um dos campos é justamente o AdapterName.
[]s
Luiz, gostaria de saber porque o resultado exibido, depois de montar o exemplo acima, é zerado: 0000000000
Thiago
Isso acontecerá se a função GetMACAddress falhar, situação em que todos os bytes do MAC Address estarão preenchidos com zeros. Você depurou essa função para ver o que o GetAdaptersInfo está retornando ? Ela está conseguindo localizar alguma placa de rede ?
[]s
Se fiz certo: Inaccessible value
Estou utilizando o vmware.
O engraçado é que outras 2 funções que seguem para o resultado de informações sobre o adaptador, o resultado é normal, dentro do esperado.
Thiago
O "Inaccessible value" pode ser causado porque o AdapterInfo é um Pointer e não foi possível determinar seu conteúdo.
Ao depurar no passo-a-passo, o "for" até MAX_ADAPTER_ADDRESS_LENGTH é executado ? Que valores ele encontra para pAdapterInfo.Address[i] ?
Obs: Softwares de virtualização (como o vmware) costumam mascarar os adaptadores reais da máquina; é comum que vc tem que habilitá-los ou configurá-los manualmente. Consultando a configuração do VMWare, aparece alguma placa de rede habilitada ?
[]s
Qual seria outra "informação" que eu poderia pegar junto com a Mac ? que tornaria este número realmente mais seguro e talvez bem mais difícil de coincidir com outro ou mesmo ser descoberto.
Vou tentar fazer o exemplo, mais já gostaria de implementar uma outra coisa para torna-la a função mais segura.
Grato desde já pela resposta
Ewerton
Como digo no final do texto, você pode usar informações sobre a CPU, o sistema operacional (versão) ou outro hardware (serial do HD, por exemplo).
Há 2 posts no blog a respeito de como obter mais informações sobre a CPU : Obtendo informações da CPU em Delphi e Levantando os recursos existentes na CPU.
Informações sobre versão do Windows estão disponíveis via API, através da função GetVersionEx.
[]s
Segue abaixo outra função que já testei com o Windows XP, 7 e 8 (32 e 64 bit):
Function MacAddress: String;
var
Lib: Cardinal;
GUID1, GUID2: TGUID;
Func: Function(GUID: PGUID): Longint; stdcall;
Begin
Result := '';
Lib := LoadLibrary('rpcrt4.dll');
If not (Lib = 0) Then
Try
@Func := GetProcAddress(Lib, 'UuidCreateSequential');
If Assigned(Func) Then
Begin
If (Func(@GUID1) = 0) and
(Func(@GUID2) = 0) and
(GUID1.D4[2] = GUID2.D4[2]) and
(GUID1.D4[3] = GUID2.D4[3]) and
(GUID1.D4[4] = GUID2.D4[4]) and
(GUID1.D4[5] = GUID2.D4[5]) and
(GUID1.D4[6] = GUID2.D4[6]) and
(GUID1.D4[7] = GUID2.D4[7]) Then
Begin
Result :=
IntToHex(GUID1.D4[2], 2) + '-' +
IntToHex(GUID1.D4[3], 2) + '-' +
IntToHex(GUID1.D4[4], 2) + '-' +
IntToHex(GUID1.D4[5], 2) + '-' +
IntToHex(GUID1.D4[6], 2) + '-' +
IntToHex(GUID1.D4[7], 2);
end;
end;
Finally
FreeLibrary(Lib);
end;
end;
Espero ter ajudado!
Postar um comentário
OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.
Observação: somente um membro deste blog pode postar um comentário.