├── requirements.txt ├── demo.gif ├── LICENSE ├── src └── sinesp_bot.py └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | Appium-Python-Client==0.46 2 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victor-torres/sinesp-bot/HEAD/demo.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Victor Torres 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/sinesp_bot.py: -------------------------------------------------------------------------------- 1 | import re 2 | import sys 3 | import time 4 | 5 | from appium import webdriver 6 | from appium.webdriver.common.touch_action import TouchAction 7 | from selenium.common.exceptions import NoSuchElementException 8 | 9 | 10 | def get_driver(): 11 | opts = { 12 | 'platformName': 'Android', 13 | 'platformVersion': '8.0', 14 | 'appPackage': 'br.gov.sinesp.cidadao.android', 15 | 'appActivity': 'MainActivity', 16 | 'deviceName': 'emulator-5554', 17 | } 18 | return webdriver.Remote('http://localhost:4723/wd/hub', opts) 19 | 20 | def search_plate(plate): 21 | wd = get_driver() 22 | 23 | # Touch vehicle search image button 24 | botoes = wd.find_element_by_id('botoes') 25 | TouchAction(wd).tap(x=400, y=500).perform() 26 | 27 | # Fill form and search for vehicle by plate 28 | wd.find_element_by_id('txPlacaLetra').send_keys(plate[:3]) 29 | wd.find_element_by_id('txPlacaNumero').send_keys(plate[3:]) 30 | wd.find_element_by_id('imgBtnConsultar').click() 31 | 32 | try: 33 | message = wd.find_element_by_id('message') 34 | except NoSuchElementException: 35 | message = None 36 | 37 | message = message and message.text 38 | if message and message.lower() == 'Veículo não encontrado': 39 | data = { 40 | 'city': None, 41 | 'state': None, 42 | 'brand': None, 43 | 'model': None, 44 | 'year': None, 45 | 'color': None, 46 | 'municipio_uf': None, 47 | 'chassis': None, 48 | 'date': None, 49 | 'return_code': 1, 50 | 'return_message': message, 51 | 'status_code': None, 52 | 'status_message': None, 53 | } 54 | return data 55 | 56 | consulta = None 57 | start_time = int(time.time()) 58 | while not consulta: 59 | end_time = int(time.time()) 60 | if (end_time - start_time) > 30: 61 | print('Timeout') 62 | exit(1) 63 | 64 | try: 65 | consulta = wd.find_element_by_id('txDataHoraConsulta') 66 | except NoSuchElementException: 67 | pass 68 | 69 | try: 70 | situacao = wd.find_element_by_id('imgBtnSituacaoLegal') 71 | except NoSuchElementException: 72 | situacao = None 73 | 74 | marca_modelo = wd.find_element_by_id('txMarcaModelo').text 75 | munipicio_uf = wd.find_element_by_id('txMunicipioUF').text 76 | chassi = wd.find_element_by_id('txChassi').text 77 | data_hora = wd.find_element_by_id('txDataHoraConsulta').text 78 | 79 | city, state = munipicio_uf.split('/') 80 | brand_model, year, color = [x.strip() for x in marca_modelo.split('-')] 81 | brand = brand_model.split()[0] 82 | model = ' '.join(brand_model.split()[1:]) 83 | chassis = '************' + chassi.split()[-1] 84 | date = data_hora.split()[-3] + ' ' + data_hora.split()[-1] 85 | status_code = 0 if situacao else 1 86 | status_message = 'Sem restrição' if situacao else 'Com restrição' 87 | 88 | data = { 89 | 'city': city, 90 | 'state': state, 91 | 'brand': brand, 92 | 'model': model, 93 | 'year': year, 94 | 'color': color, 95 | 'municipio_uf': munipicio_uf, 96 | 'chassis': chassis, 97 | 'date': data_hora, 98 | 'return_code': 0, 99 | 'return_message': 'Sem erros.', 100 | 'status_code': status_code, 101 | 'status_message': status_message, 102 | } 103 | 104 | return data 105 | 106 | if __name__ == '__main__': 107 | try: 108 | plate = ''.join(sys.argv[1].upper().split()) 109 | except IndexError: 110 | print('Usage: sinesp_bot.py ') 111 | exit(1) 112 | 113 | results = search_plate(plate) 114 | 115 | import json 116 | print(json.dumps(results)) 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sinesp-bot 2 | 3 | O sinesp-bot torna possível a consulta automatizada à base de dados do SINESP Cidadão sem a necessidade do preenchimento de captchas ou algum outro tipo de autenticação. 4 | 5 | Esta é uma prova de conceito e oferece uma alternativa a bibliotecas como o [sinesp-client](https://github.com/victor-torres/sinesp-client/), que utilizam uma outra abordagem para obter acesso às APIs. 6 | 7 | ![](demo.gif) 8 | 9 | 10 | ## O que é o SINESP 11 | 12 | SINESP Cidadão é uma base de dados pública de veículos brasileiros. É muito útil para identificar carros ou motos roubados ou suspeitos. 13 | 14 | 15 | ## Informações disponíveis 16 | 17 | Se um veículo com a placa especificada for encontrado, o servidor irá retornar as seguintes informações que serão repassadas através de um dicionário: 18 | 19 | - return_code (código de retorno) 20 | - return_message (mensagem de retorno) 21 | - status_code (código do status) 22 | - status_message (mensagem do status) 23 | - chassis (chassi do veículo) 24 | - model (modelo/versão) 25 | - brand (marca/fabricante) 26 | - color (cor/pintura) 27 | - year (ano de fabricação) 28 | - model_year (ano do modelo) 29 | - plate (placa) 30 | - date (data e hora da consulta) 31 | - city (cidade) 32 | - state (estado ou unidade federativa) 33 | 34 | 35 | ## Por que fazer um cliente do SINESP? 36 | 37 | Não sabemos o porquê, mas o governo não mantém uma API pública para este serviço. A única maneira de acessar os dados é acessando o site do SINESP e respondendo a perguntas de verificação (captchas) para cada uma das requisições ou utilizando os aplicativos oficiais. 38 | 39 | Permitir a automatização dessa tarefa possibilita que desenvolvedores de todo o país elaborem aplicações de retorno social importantíssimo, em sua maioria voltada para a segurança pública de ruas, condomínios, estabelecimentos comerciais, estacionamentos, escolas etc. 40 | 41 | 42 | ## O que nós fizemos 43 | 44 | Felizmente as aplicações para Android e iOS permitem que a busca seja feita sem que seja preciso responder a nenhum teste captcha. Inicialmente, foi feita uma engenharia reversa no aplicativo por diversos membros da comunidade para que pudéssemos ter acesso a essas informações públicas sem que fosse preciso responder aos testes de captcha. 45 | 46 | No entanto, a equipe do SINESP constantemente lança atualizações do aplicativo dificultando o acesso automatizado das informações e obfuscando a forma com a qual o acesso é feito. 47 | 48 | Este repositório demonstra a utilização de uma técnica não invasiva utilizada principalmente em testes de QA. Usamos o SDK do Android e suas ferramentas agregadas e o software [Appium](https://appium.io/) para automatizar a consulta simulando o comportamento de um usuário comum, seja utilizando um dispositivo físico ou o emulador da plataforma. 49 | 50 | Assim, o aplicativo funciona como uma caixa preta, não sendo necessário obter detalhes sobre sua implementação, dependendo apenas dos identificadores únicos utilizados na plataforma Android e que ficam disponíveis por questões de usabilidade da Interface de Usuário do sistema operacional. 51 | 52 | 53 | # Pré-requisitos 54 | 55 | ## Android Studio 56 | 57 | A primeira coisa que precisamos fazer é baixar e instalar o Android Studio, que contém o SDK de desenvolvimento do Android e algumas ferramentas agregadas que serão muito interessantes pra gente nesse caso. O download pode ser feito a partir do site oficial do Android, [neste link](https://developer.android.com/studio). 58 | 59 | 60 | ## Configurando um dispositivo 61 | 62 | Pra gente conseguir executar o aplicativo utilizando o debugger do Android, precisamos configurar um dispositivo, seja ele físico ou virtual. Você pode utilizar um dispositivo Android conectando-o a uma porta USB do seu computador e permitindo sua utilização ativando o modo desenvolvedor. 63 | 64 | Outra opção é criar um dispositivo virtual que será emulado, simulando um dispositivo real. 65 | 66 | Não irei entrar em detalhes, pois existe amplo conteúdo cobrindo essa etapa de configuração disponível na internet. 67 | 68 | Recomendo que seja utilizada a versão 8.0 do sistema operacional Android, que provê a API de número 26. 69 | 70 | 71 | ## Instalando o aplicativo do SINESP 72 | 73 | Vamos precisar do aplicativo do SINESP instalado no dispositivo, seja ele real ou virtual. Para isso você pode utilizar a própria loja de aplicativos da Google ou utilizar o comando `adb install ` para instalar um APK armazenado em seu computador. Essa etapa é importante, pois não iremos instalar o APK pelo Appium, pois não queremos modificar sua assinatura digital. O aplicativo permanecerá intacto. 74 | 75 | 76 | ## Instalando o Node 77 | 78 | Vamos precisar do Node pra poder instalar e executar a próxima dependência do projeto. Você pode encontrar mais informações no [site oficial](https://nodejs.org/en/). 79 | 80 | 81 | ## Instalando o Appium 82 | 83 | O Appium é o software utilizado para fazer a ponte entre o nosso script de automação (aqui o exemplo é feito em Python) e a API de debugging do Android (adb). Vamos instalar a versão 1.8.1, que foi a que eu consegui fazer funcionar com essa combinação de Android (API 26 e uiautomator2). 84 | 85 | Podemos instalar o Appium com o comando `npm install -g appium@1.8.1` e para executar, se o Node foi instalado corretamente, basta rodar `appium` na sua linha de comando. Se você não utilizou a opção `-g` para instalar o módulo, pode ser que consiga executar com `node ./node_modules/appium/build/lib/main.js`. 86 | 87 | 88 | ## Instalando dependências do Python 89 | 90 | Instale as dependências do Python com `pip install -r requirements.txt`. 91 | 92 | 93 | # Executando o script 94 | 95 | Você pode utilizar a função `search_plate` dentro do arquivo `sinesp_bot.py` ou simplesmente executar via linha de comando com `python sinesp_bot.py `. Se você rodar a função, vai receber um dicionário com o retorno. Se rodar na linha de comando, vai receber um JSON na saída padrão. Dá pra usar essa abordagem pra integrar com outras linguagens e sistemas. 96 | 97 | 98 | # Resolução de problemas 99 | 100 | - O script está configurado para utilizar o emulador padrão que ganha o nome de `emulator-5554`. Você pode executar `adb devices` para obter uma lista com todos os dispoitivos. Basta trocar no dicionário `opts` no script Python. 101 | - Verifique se o emulador está ativo e o aplicativo do SINESP está instalado. 102 | - Se você precisar utilizar um proxy, tente configurar diretamente no emulador do Android, no seu próprio dispositivo ou no sistema operacional hospedeiro. 103 | - Abra uma issue descrevendo seu problema caso alguma coisa tenha dado errado. 104 | 105 | 106 | # Contribuindo 107 | 108 | Como disse, esse repositório é uma prova de conceito e apesar se funcionar, muita coisa pode ser melhorada. Possível melhorias: 109 | 110 | - criar um container docker com todas as dependências para execução stand-alone 111 | - melhorar a documentação 112 | - melhorar o código fonte 113 | - criar e distribuir um pacote python 114 | --------------------------------------------------------------------------------