Hoje vou mostrar uma dica útil para quem quer monitorar invasões em servidores utilizando AdvPL.
Se você tem um Protheus instalado em uma rede com Windows, hoje vou mostrar uma dica interessante. Recentemente precisei criar uma função em AdvPL que ficasse rodando de tempos em tempos, verificando se algum usuário local foi criado em algum servidor. Esse processo de usuário local, geralmente é feito por invasores que querem sequestrar os dados das empresas.
Pois bem, como funciona essa rotina, basicamente através de um array (aServers), é executado um comando via PowerShell, e esse comando gera um arquivo csv com a lista de usuários locais no servidor (lembrando que o usuário que você estiver usando tem que ter privilégio para acessar esses servidores). O arquivo csv ficará dentro da pasta C:\Spool\zUsrSrv.
Ao rodar pela segunda vez, o arquivo csv será comparado com esse primeiro arquivo. Assim se houver diferenças, irá mostrar que foi criado algum usuário local novo, e você precisa ficar atento e conferir isso no seu servidor.
Para agendar, você pode agendar no Windows mesmo, pelo scheduler dele, e pode colocar para rodar a cada 4 horas +-. O comando, você pode colocar dentro de um bat, sendo basicamente apontando o exe, a conexão (nesse exemplo tcp), o ambiente (nesse exemplo ambiente_desenv) e qual é o programa (u_zUsrSrv). Abaixo o exemplo do comando completo:
C:\TOTVS\Producao\smartclient.exe -m -q -c=tcp -e=ambiente_desenv -p=u_zUsrSrv
Vale ressaltar que, confira a variável cPsw, coloque sua senha, sendo o ideal criar um parâmetro, ou alguma função para encapsular ela (como a clássica Embaralha).
Abaixo o código fonte desenvolvido:
//Bibliotecas
#Include "Totvs.ch"
Static nPosCod := 1
Static nPosIP := 2
Static nPosDNS := 3
Static nPosDesc := 4
Static nPosDif := 5
Static nPosOld := 6
Static nPosNew := 7
/*/{Protheus.doc} User Function zUsrSrv
Script para verificar usuarios locais nos servers
@type Function
@author Atilio
@since 08/04/2020
@version version
@obs Essa funcao nao pode ser agendada no Scheduler por causa do PowerShell
Ela deve ser executada via .bat (conteudo abaixo), agendada em uma maquina local (testada com Windows 10)
C:\TOTVS\Producao\smartclient.exe -m -q -c=tcp -e=ambiente_desenv -p=u_zUsrSrv
/*/
User Function zUsrSrv()
Local aArea := GetArea()
Local cPsw := "Sua Senha"
Private aServers := {}
Private cDirSpool := "C:\spool\"
//Se não estiver preparado, prepara o ambiente
If Select("SX2") == 0
RPCSetEnv("01", "01", "Admin", cPsw, "", "", {})
EndIf
//Se nao existir a pasta para o procedimento, cria ela
If ! ExistDir(cDirSpool)
MakeDir(cDirSpool)
EndIf
//Diretorio interno do spool
cDirSpool += "zUsrSrv\"
If ! ExistDir(cDirSpool)
MakeDir(cDirSpool)
EndIf
//Monta os arrays
fMontaSrv()
//Faz as execucoes
fExecCmd()
//Faz as comparacoes
fCompara()
RestArea(aArea)
Return
Static Function fMontaSrv()
//Adiciona os servidores e ips das maquinas que terao analise
aAdd(aServers, {"111", "192.168.70.111", "srv01.dominio.org", "DBAccess e License", .F., "", ""})
aAdd(aServers, {"112", "192.168.70.112", "srv02.dominio.org", "Protheus e Protheus Data", .F., "", ""})
aAdd(aServers, {"113", "192.168.70.113", "srv03.dominio.org", "Banco de Dados", .F., "", ""})
Return
Static Function fExecCmd()
Local nAtual := 1
Local cCommand := ""
Local cExecCmd := ""
Local cDirTmp := GetTempPath()
Local cArqBat := "servers_local_users.bat"
Local cNewFile := ""
Local cOldFile := ""
//Montando a base do comando
cCommand := 'powershell -Command "Get-WmiObject -ComputerName \"%zUsrSrv_DNS%\" -Class Win32_UserAccount -Filter '
cCommand += '\"LocalAccount=' + "'True'\" + '" | Select PSComputername, Name, Status, Disabled, AccountType, Lockout, PasswordRequired, PasswordChangeable, SID '
cCommand += '| Export-csv \"%zUsrSrv_FILE%\" -NoTypeInformation '// + CRLF + 'pause'
//Percorre os servidores
For nAtual := 1 To Len(aServers)
cArqBat := "servers_local_" + aServers[nAtual][nPosCod] + ".bat"
//Exclui o arquivo novo
cNewFile := cDirSpool + aServers[nAtual][nPosCod] + "_new.csv"
FErase(cNewFile)
//Cria .bat com comando
cExecCmd := cCommand
cExecCmd := StrTran(cExecCmd, "%zUsrSrv_DNS%", aServers[nAtual][nPosIP])
cExecCmd := StrTran(cExecCmd, "%zUsrSrv_FILE%", cNewFile)
MemoWrite(cDirTmp + cArqBat, cExecCmd)
//Executa o comando
ShellExecute("Open", cArqBat, "", cDirTmp, 0 )
Sleep(10000)
//Pega o conteudo dele e coloca no array
aServers[nAtual][nPosNew] := MemoRead(cNewFile)
//Se o arquivo original nao existir, cria uma copia dele conforme o arquivo novo
cOldFile := cDirSpool + aServers[nAtual][nPosCod] + "_old.csv"
If ! File(cOldFile)
MemoWrite(cOldFile, aServers[nAtual][nPosNew])
EndIf
aServers[nAtual][nPosOld] := MemoRead(cOldFile)
//Compara os dois conteudos
If aServers[nAtual][nPosNew] != aServers[nAtual][nPosOld]
aServers[nAtual][nPosDif] := .T.
EndIf
Next
Return
Static Function fCompara()
Local cCorpo := ""
Local cVazio := ""
Local nAtual := 0
Local aConteudo := {}
Local nPosUsr := 0
Local cNomeUsr := ""
Local cMensagem := ""
Local cPara := u_zRetMail("zUsrSrv")
//Percorrendo os servers
For nAtual := 1 To Len(aServers)
//Se houver diferencas
If aServers[nAtual][nPosDif]
cCorpo += "<h3>Servidor - " + aServers[nAtual][nPosIP] + " (" + aServers[nAtual][nPosDesc] + ")</h3>"
//Percorre linhas do novo arquivo
aConteudo := StrTokArr(aServers[nAtual][nPosNew], CRLF)
cCorpo += "<ul>"
For nPosUsr := 1 To Len(aConteudo)
//Se nao for a linha de cabecalho
If (aConteudo[nPosUsr] != '"PSComputerName","Name","Status","Disabled","AccountType","Lockout","PasswordRequired","PasswordChangeable","SID"')
cNomeUsr := aConteudo[nPosUsr]
cNomeUsr := SubStr(cNomeUsr, At(',', cNomeUsr) + 1, Len(cNomeUsr))
cNomeUsr := SubStr(cNomeUsr, 1, At(',', cNomeUsr) - 1)
cNomeUsr := StrTran(cNomeUsr, '"', '')
//Se o usuario estiver no antigo, saira normal, senao saira em negrito
If aConteudo[nPosUsr] $ aServers[nAtual][nPosOld]
cCorpo += "<li>" + cNomeUsr + "</li>"
Else
cCorpo += "<li><strong>" + cNomeUsr + "</strong></li>"
EndIf
EndIf
Next
cCorpo += "</ul>"
cCorpo += "<br>"
EndIf
//Se tiver vazio o conteudo
If Empty(aServers[nAtual][nPosNew])
cVazio += "<li>" + aServers[nAtual][nPosIP] + " (" + aServers[nAtual][nPosDNS] + " / " + aServers[nAtual][nPosDesc] + ")</li>"
EndIf
Next
//Se tiver corpo do e-Mail
If ! Empty(cCorpo) .Or. ! Empty(cVazio)
cMensagem := "<p>Ola.</p><br>"
If ! Empty(cCorpo)
cMensagem += "<hr>"
cMensagem += "<p>Os seguintes servidores tiveram alteracao recente nos usuarios locais, confira com urgencia!</p><br><br>"
cMensagem += cCorpo + "<br>"
EndIf
//Se tiver arquivos vazios
If ! Empty(cVazio)
cMensagem += "<hr>"
cMensagem += "<p>Nos seguintes servidores, nao foi possivel buscar os usuarios locais:</p><br>"
cMensagem += "<ul>"
cMensagem += cVazio
cMensagem += "</ul>"
EndIf
//MemoWrite("C:\Users\daniel.atilio\Desktop\arquivo_tst.html", cMensagem)
//Disparando o e-Mail
u_EnviarEmail( cPara,; //Destinatário do e-Mail
"[dominio] - Usuarios Locais nos Servidores ",; //Assunto
cMensagem,; //Corpo do e-Mail
"",; //Arquivo anexo
,; //Grava log
,; //Deu certo disparo de e-Mail?
.F.) //Novo tipo de envio?
EndIf
Return
E você, gostou da solução divulgada hoje? Deixe nos comentários.
Bom pessoal, por hoje é só.
Abraços e até a próxima.