├── CMakeLists.txt ├── Makefile ├── README.md ├── cfg ├── any.pem ├── master.properties ├── rootcert.pem └── slave.properties ├── cmake └── FindPoco.cmake └── src ├── CMakeLists.txt ├── InvalidCertHandler.h ├── SecureSocketConnector.h ├── disthc.cbp ├── disthc.h ├── disthcc.cpp ├── disthcm.cpp ├── disthcm.h ├── disthcs.cpp ├── djob.cpp ├── djob.h ├── dtalk.cpp ├── dtalk.h ├── engines ├── engine.h ├── hashcat.cpp └── hashcat.h ├── tinycon.cpp └── tinycon.h /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | cmake_policy (VERSION 2.6) 3 | 4 | project(disthc) 5 | 6 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) 7 | 8 | set (DATADIR "${CMAKE_INSTALL_PREFIX}/share") 9 | set (PKGDATADIR "${DATADIR}/disthc") 10 | set (GETTEXT_PACKAGE "disthc") 11 | set (RELEASE_NAME "Alpha") 12 | set (VERSION "0.1") 13 | set (VERSION_INFO "Release") 14 | set (CMAKE_C_FLAGS "-ggdb") 15 | set (PREFIX ${CMAKE_INSTALL_PREFIX}) 16 | set (DOLLAR "$") 17 | set(CMAKE_MAN_DIR "man" CACHE STRING 18 | "Install location for man pages (relative to prefix).") 19 | 20 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 21 | 22 | IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 23 | # Mac OS X specific code 24 | SET(OperatingSystem "Mac OS X") 25 | ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 26 | 27 | add_subdirectory (src) 28 | 29 | # Copy the cfg files 30 | file(COPY cfg/master.properties DESTINATION ${CMAKE_BINARY_DIR}/bin/) 31 | file(COPY cfg/slave.properties DESTINATION ${CMAKE_BINARY_DIR}/bin/) 32 | 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | CCFLAGS=-g 3 | LDIR=/usr/local/lib 4 | IDIR=/usr/local/include 5 | EXECS=master slave console 6 | 7 | .PHONY: master slave console 8 | 9 | all: $(EXECS) 10 | 11 | master: bin/disthcm 12 | slave: bin/disthcs 13 | console: bin/disthcc 14 | 15 | bin/disthcm: src/djob.cpp src/dtalk.cpp src/disthcm.cpp 16 | @echo "Compiling Application (master server)..." 17 | @echo "#########################################" 18 | $(CC) -o $@ $(CCFLAGS) $^ -lPocoNet -lPocoNetSSL -lPocoUtil -lPocoFoundation -lPocoDataSQLite -lPocoData -lPocoCrypto 19 | @echo "done." 20 | @echo 21 | 22 | bin/disthcs: src/engines/hashcat.cpp src/djob.cpp src/dtalk.cpp src/disthcs.cpp 23 | @echo "Compiling Application (slave)..." 24 | @echo "#################################" 25 | $(CC) -o $@ $(CCFLAGS) $^ -lPocoNet -lPocoNetSSL -lPocoUtil -lPocoFoundation 26 | @echo "done." 27 | @echo 28 | 29 | bin/disthcc: src/dtalk.cpp src/tinycon.cpp src/disthcc.cpp 30 | @echo "Compiling Application (console)..." 31 | @echo "###################################" 32 | $(CC) -o $@ $(CCFLAGS) $^ -lPocoNet -lPocoNetSSL -lPocoUtil -lPocoFoundation 33 | @echo "done." 34 | @echo 35 | 36 | clean: 37 | @echo "Cleaning..." 38 | @echo "############" 39 | rm -rf *.o $(EXECS) bin/* 40 | @echo "done." 41 | @echo 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Disthc 2 | 3 | The distributed hashcat framework is a piece of software I started writing for DefCon 2012. The original codebase was a bit of a mess and depended largely on various hacks to get it to run efficiently on different pieces of hardware. 4 | 5 | I finally got aorund to writing a new codebase for it, which removes the necessity for all of the previous code hacks. It is faster, and a LOT more stable than the old code. But as before, it's far from perfect. There are a ton of things I would still like to implement; but that being said, it does work. 6 | 7 | This software is released under the BSD license. So if you feel like contributing to the code, it's completely welcome! 8 | 9 | To use this software, compile all 3 components, then: 10 | * Run the server 11 | * Run and connect your slaves 12 | * Run and connect the console to control everything 13 | 14 | ## Prequisites 15 | 16 | You must have PocoLib Complete Edition >= 1.4.4 in order to build these apps. Basic Edition will fail to compile. I believe Ubuntu ships with 1.3 in it's repos, so Ubuntu users are going to 17 | need to build PocoLib from source before compiling this. (Other distros may need to do the same). 18 | 19 | *PLEASE NOTE* Version 1.4.3 is no longer able to build disthc due to the use of the PocoCrypto module. 20 | 21 | Also, you must have a copy of hashcat and/or hashcat-ocl in order to use this software. http://www.hashcat.net/ 22 | 23 | Finally, before you can run the software, you will probably need to modify the server.properties and slave.properties files (found in the cfg directory) in order to get certain features to work properly (or at all). One example is, you must include the path to your hashcat install. 24 | 25 | ## SSL Support 26 | 27 | Disthc now uses SSL for secure communications out-of-the-box. Two pem files are needed on the master for this: 28 | * Your CA certificate file (rootcert.pem by default) 29 | * Your server's public and private keys stored in a single pem (any.pem by default) 30 | 31 | You can, of course, change these via the master.properties file to be any certs of your choosing, but the existing files will work for rapid deployment during testing. 32 | 33 | ## Building 34 | 35 | Simply use make as expected to build disthc. Run make by itself, or specify one of the three build targets: master, slave, console. 36 | Example building all modules: 37 | 38 | ``` 39 | $ make 40 | ``` 41 | 42 | Binaries are stored in bin/ once they are built. 43 | 44 | ## Running your binaries 45 | 46 | By default, each binary will look in the current working directory for its configuration file. You can specify an alternate location for a config, however, using the -c flag. 47 | Example running the master server with the default config file: 48 | 49 | ``` 50 | $ bin/disthcm -c cfg/master.properties 51 | ``` 52 | 53 | ## Notes for Windows 54 | 55 | I was able to compile this code under Windows, with a little extra massaging. I successfully built with Visual C++ 2010 Express, but I had to adjust the disthc header file to do so. (Without modification, it was unable to locate PocoLib. I am still not sure if this was due to a faulty PocoLib install or not.) At the moment, this makefile does not support Windows, so you will need to create a Visual C++ Project manually. 56 | -------------------------------------------------------------------------------- /cfg/any.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEFjCCAv6gAwIBAgIBAjALBgkqhkiG9w0BAQUwgdMxEzARBgNVBAMMCmFwcGlu 3 | Zi5jb20xNjA0BgNVBAoMLUFwcGxpZWQgSW5mb3JtYXRpY3MgU29mdHdhcmUgRW5n 4 | aW5lZXJpbmcgR21iSDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNVBAgMCUNh 5 | cmludGhpYTELMAkGA1UEBgwCQVQxHjAcBgNVBAcMFVN0LiBKYWtvYiBpbSBSb3Nl 6 | bnRhbDEtMCsGCSqGSIb3DQEJAQweZ3VlbnRlci5vYmlsdHNjaG5pZ0BhcHBpbmYu 7 | Y29tMB4XDTA5MDIyMzEzNDIwMloXDTExMTEyMDEzNDIwMlowgcoxCjAIBgNVBAMM 8 | ASoxNjA0BgNVBAoMLUFwcGxpZWQgSW5mb3JtYXRpY3MgU29mdHdhcmUgRW5naW5l 9 | ZXJpbmcgR21iSDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNVBAgMCUNhcmlu 10 | dGhpYTELMAkGA1UEBgwCQVQxHjAcBgNVBAcMFVN0LiBKYWtvYiBpbSBSb3NlbnRh 11 | bDEtMCsGCSqGSIb3DQEJAQweZ3VlbnRlci5vYmlsdHNjaG5pZ0BhcHBpbmYuY29t 12 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxjGFE96wa83Kdiv0m10O 13 | XmBmZ5xuclalVTCisLzUmAekbItMjkmI6dVw9r5gd0W5zDWrgPYUmYgtvqnxSHRK 14 | PRAN410Yq9vqWYvQscpnXGlqUag8t+OBXJhiFnnea/btA0zGVZk6RE/7cWK8AtKH 15 | Q/Xds3AUJ1L/1uV/e/5azyUDyptsmHbCMUwWhGBrj/KZEviHmRMN/xJLrbIBPkla 16 | 4HRB61rI8in0jziCwThJ7KiQumzWRu2IJjS+VoNWvG52dYLDvfxppuY1rlF0SG/h 17 | JuSJQqJjZZ11V4TePHscFkGU2tnHqF4UhSjLFJWsGuxnAmZTeIRmavmIIMm3/G6C 18 | WwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQAc+mn/ZEaK59B/UAgx8cMlGM9UigJv 19 | L9O46pno3YirBq9SrMzf5b6rrbJm8tkQNfldqaVNA5oVbfxnAHhCUDkX8m0x/De8 20 | teo9nFei8kETQ25ykV+WLapOdrYxakHPtNVgDTGWNb2GY/hH3nMvtdgFvaS80ncD 21 | tOa13tE4jopFQFY56VKq+sv4Hm5JDvr+dD/g77Cio02sUzSH96FrFIG5/kw1NihB 22 | IJKZ4n7atQizDe4TiR/NRonmZNbsB+18yTKT8traCS30JGKQqYxXuVKPyQd7FARv 23 | ajZxRPbcpAtvWBKXpRHXo4xIBJaPktVOG2hGovjRixXYb83hQ87t1Ozy 24 | -----END CERTIFICATE----- 25 | -----BEGIN RSA PRIVATE KEY----- 26 | Proc-Type: 4,ENCRYPTED 27 | DEK-Info: DES-EDE3-CBC,0950752701CB74AF 28 | 29 | OmaLdMcP3JDy8JMX41wNH/WvMp6gLGwmqQRob633n95YxVdii0oR8fk4GgmJeYF0 30 | FrNm5g32vnVyqDZylX4as3GT822HhCA+f7mYpGZltQ47TG15tGTMUNuwIhHlouZs 31 | ZGNclelBB3FHEZAD0Fns2hZ4jZhMDj67wD0YyGcp8so/A+fxedGdwNbJSC1Auuy4 32 | 7uRsY0ZJC1LjMHXkyBEXaL1QA4CGjBlm22Dbo8eoGXcaohsZpDsM7OU7MaQnldM0 33 | AK8jONZ45127JoDJD41Wgfm0m7tIErsD5CbhHPaddOjQ0OerprEkplhOR+V+ano1 34 | Pv4adRZOBjr00NDB43WK9x+ZHVQ5tIXxUbqYWZAfvA1PpiYego5XLUCxfy7D6Lms 35 | hV6CAt/fYeBrQOvwLREboKLBOe9A8quP2wi7zkR3KQHty9Tm9efF3PfQSxJTlKg9 36 | YJ2n/6omX1aXCjQghbnfEcl4tCmj6z2rHCSiJgEOcwDYhGRbQveYieZUH5iKMzYY 37 | YytHkHPfZfzhlJ0WG0AKdA6UlrjEjF09txaZR3Nj4Zf4kZAu727N81HnlFCRvDqV 38 | ZjHUrbE7fJuc3diffUfIHuQZuWcoYDejIbASjcJMHZOpbbPR2ZCYQqUmvg/IgAD6 39 | M2GDbvfvLnu1BaCrNMdOxM4j+sLNhm8qqAMxZ/wkZA9Sqhi2EifZwf5jWKNU3Vtx 40 | C/w621efHawDME3WTMunDtjn7Sgm3NP508cz8OgcEcZLwENu8JH5pWR0Y0+qvlPM 41 | DYpCu2Zh6TBLU6Cfuxl2GigHHBhm8Eza/vE6dVbpyvEozejtVKi+RYskqz8ynYtl 42 | r9NpDkEFcqGFLX/X7fajR4JxzxYx0Ms+CHHBlBLw44eMl1Izb9OBgfUK3a7wJ0Z1 43 | vEmzcVtXZMqKDvqY3wddCcbtpVZhRnAUFgT3/b5ISxQ6xxFg67YQaJ0knuRwOZCI 44 | xSvNsxXb6s5xt8gRx8MY8W1CVW0QSH4gUpKdJFiF/6nYq7h8F1A5QYr34uJn5pa2 45 | bsagCMhCUHKn/hrtTJ/4bC7n7utulXyEZJDGS38nNe5TBmAxeA+MkOAO7AEb8aDo 46 | RylaKT77tmeZXWBtlQGHj0bt2fPOEW3e0WUeNwk4qnKqSGdwbXGFK+yWxgGOxFDT 47 | 4NqUjDV7lhj1r3mKEufLIqP6GxAlewpH1uLA+ty2eNfG793pytlyhNikzmkliXex 48 | WnBUYQM6ZBclW0nALHxxOJWZlnBCESgo9lSHMeB7adJXuwaUmqHx4u+yNzaFS6pr 49 | LemBEUCHfLeGFM9E9YbgNe51q5+vXZYN5MZtqyex4AqPdGEGpwXBk43RK79mP84G 50 | QQRAAcs6KMj1/Sl7pmg9acrxskLWljtsnvdCJ8a+VXjLDyp2wks1z2Gnw7cguZdD 51 | Ah4hjH8LDTsEJxOr2DNJu/V9JDPKd0uGyaW0AOanwAn7tszivGddb/WrzImCIMBa 52 | Lb/cqujvS9YsIK6xrq4LMxR5wE6Hol0qs6xO89Y9OpuuRxAYfRUl4nDTg0WjS5Ga 53 | 0aoSXB0kOFkEwb3WGq+b26606RBYDKu7RsJoyWoXq42JZ1jkEYKCNeNS8hWh8GKd 54 | -----END RSA PRIVATE KEY----- 55 | -------------------------------------------------------------------------------- /cfg/master.properties: -------------------------------------------------------------------------------- 1 | cfg.debug = true 2 | #cfg.debug.dcode = show 3 | #cfg.server.auth.token = * 4 | #cfg.server.port = 4000 5 | #cfg.server.db.file = disthc.db 6 | #cfg.rainbow.check = true 7 | #cfg.job.autostart = true 8 | cfg.job.attack = 3 9 | #cfg.job.mode = 100 10 | cfg.job.mask = ?l?l?l?l?l?l?l?l?l?l 11 | #cfg.job.hashes.file = disthc.hashes 12 | #cfg.job.dict.file = disthc.dict 13 | #cfg.job.rules.file = 14 | cfg.results.show = true 15 | cfg.server.db.enable = true 16 | application.logger = debug 17 | 18 | 19 | openSSL.server.privateKeyFile = ${application.configDir}../cfg/any.pem 20 | openSSL.server.caConfig = ${application.configDir}../cfg/rootcert.pem 21 | openSSL.server.verificationMode = relaxed 22 | openSSL.server.verificationDepth = 9 23 | openSSL.server.loadDefaultCAFile = true 24 | openSSL.server.cipherList = ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH 25 | openSSL.server.privateKeyPassphraseHandler.name = KeyFileHandler 26 | openSSL.server.privateKeyPassphraseHandler.options.password = secret 27 | openSSL.server.invalidCertificateHandler.name = AcceptCertificateHandler 28 | openSSL.server.extendedVerification = false 29 | openSSL.server.cacheSessions = true 30 | openSSL.server.sessionIdContext = ${application.name} 31 | openSSL.server.sessionCacheSize = 100 32 | openSSL.server.requireTLSv1 = false 33 | -------------------------------------------------------------------------------- /cfg/rootcert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIEQTCCAymgAwIBAgIBATALBgkqhkiG9w0BAQUwgdMxEzARBgNVBAMMCmFwcGlu 3 | Zi5jb20xNjA0BgNVBAoMLUFwcGxpZWQgSW5mb3JtYXRpY3MgU29mdHdhcmUgRW5n 4 | aW5lZXJpbmcgR21iSDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNVBAgMCUNh 5 | cmludGhpYTELMAkGA1UEBgwCQVQxHjAcBgNVBAcMFVN0LiBKYWtvYiBpbSBSb3Nl 6 | bnRhbDEtMCsGCSqGSIb3DQEJAQweZ3VlbnRlci5vYmlsdHNjaG5pZ0BhcHBpbmYu 7 | Y29tMB4XDTA5MDIyMzEzNDAzNVoXDTExMTEyMDEzNDAzNVowgdMxEzARBgNVBAMM 8 | CmFwcGluZi5jb20xNjA0BgNVBAoMLUFwcGxpZWQgSW5mb3JtYXRpY3MgU29mdHdh 9 | cmUgRW5naW5lZXJpbmcgR21iSDEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEjAQBgNV 10 | BAgMCUNhcmludGhpYTELMAkGA1UEBgwCQVQxHjAcBgNVBAcMFVN0LiBKYWtvYiBp 11 | bSBSb3NlbnRhbDEtMCsGCSqGSIb3DQEJAQweZ3VlbnRlci5vYmlsdHNjaG5pZ0Bh 12 | cHBpbmYuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx8mI/DXh 13 | dwvwt/qMD5Mm+1ataiRJzau0ct07kyFv0hTYGJR7sl4ocC+lLqLOgOxeG0IaIKvP 14 | Apqq7KQ1ZJBtpZAAwj8NwMXg8fGYM0JtpkGYmkRGmFolwFWA0FLVmGxAuw2iKN6k 15 | ajl8U4pz5qzYFEKNL5IJMI8rS2Fsek1pgfpZ5dYFChgVW7urxFhAJCXMrHAOLfed 16 | SjXtZpvepXRvBtuvOosXSIjmY9x/3/1QeRvZpza2atWRv4pnKBldtI9BZDUQqS3F 17 | fi/ydXlZVeY3aHW6Cw9DfbnnLv2RegUzjUZCx07IrPulZmqS6rabIIwY/bhX+n7y 18 | YcP4ADkXDKct6QIDAQABoyAwHjAPBgNVHRMBAf8EBTADAQH/MAsGA1UdDwQEAwIC 19 | hDANBgkqhkiG9w0BAQUFAAOCAQEAAfoSQow7qv3C9SiOZ+h3s9reQBJnZUcSQ5AS 20 | 90jGmtmcJ1eyWQyGxES0iKYPcF1GbSBkO/kqMzdM/E2GQnbHVrgiPj+9tTxjG/hK 21 | 42K2AEACDYtuQWTui4K1BmrWRJvdoiGRrt87DhmOG6UY5wtUAZdgVjhwBGEWJhYp 22 | IgeNe5OEdrBkSBjYb2VnJOJFQA7bB7G4snTjNMY+n5+odISHU3debZLdXvX3gKVG 23 | nVwp91/LnCSCvvZ+nH9xLzNdTbXqhI96bJh/iXLNcwcHOWPYWKuANAs5FR7OSZ+l 24 | 57gz8/BTaM4pTyJzER5p0+cTIzLGF7aYLuKKqXiKykjruO3U7A== 25 | -----END CERTIFICATE----- 26 | -------------------------------------------------------------------------------- /cfg/slave.properties: -------------------------------------------------------------------------------- 1 | cfg.debug = true 2 | cfg.server.auth.token = * 3 | #cfg.reconnect.interval = 2 4 | cfg.server.address = localhost 5 | cfg.server.port = 4000 6 | cfg.server.sync = true 7 | cfg.hashcat.path = /Users/c/hashcat/hashcat-0.41/hashcat-osx.app 8 | cfg.sync.remote = false 9 | cfg.pot.path = disthc.pot 10 | cfg.engine = hashcat 11 | #cfg.engine = oclhashcat 12 | cfg.ghost.allow = false 13 | cfg.hashcat.gputemp = true 14 | cfg.data.path = slave_data/ 15 | cfg.chunk.size = 170000000 16 | -------------------------------------------------------------------------------- /cmake/FindPoco.cmake: -------------------------------------------------------------------------------- 1 | # - Find the Poco includes and libraries. 2 | # The following variables are set if Poco is found. If Poco is not 3 | # found, Poco_FOUND is set to false. 4 | # Poco_FOUND - True when the Poco include directory is found. 5 | # Poco_INCLUDE_DIRS - the path to where the poco include files are. 6 | # Poco_LIBRARY_DIRS - The path to where the poco library files are. 7 | # Poco_BINARY_DIRS - The path to where the poco dlls are. 8 | # Poco_LIBRARIES - All libraries 9 | 10 | # ---------------------------------------------------------------------------- 11 | # If you have installed Poco in a non-standard location. 12 | # Then you have three options. 13 | # In the following comments, it is assumed that 14 | # points to the root directory of the include directory of Poco. e.g 15 | # If you have put poco in C:\development\Poco then is 16 | # "C:/development/Poco" and in this directory there will be two 17 | # directories called "include" and "lib". 18 | # 1) After CMake runs, set Poco_INCLUDE_DIR to /poco<-version> 19 | # 2) Use CMAKE_INCLUDE_PATH to set a path to /poco<-version>. This will allow FIND_PATH() 20 | # to locate Poco_INCLUDE_DIR by utilizing the PATH_SUFFIXES option. e.g. 21 | # SET(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} "/include") 22 | # 3) Set an environment variable called ${POCO_ROOT} that points to the root of where you have 23 | # installed Poco, e.g. . It is assumed that there is at least a subdirectory called 24 | # Foundation/include/Poco in this path. 25 | # 26 | # Note: 27 | # 1) If you are just using the poco headers, then you do not need to use 28 | # Poco_LIBRARY_DIRS in your CMakeLists.txt file. 29 | # 2) If Poco has not been installed, then when setting Poco_LIBRARY_DIRS 30 | # the script will look for /lib first and, if this fails, then for /stage/lib. 31 | # 32 | # Usage: 33 | # In your CMakeLists.txt file do something like this: 34 | # ... 35 | # # Poco 36 | # FIND_PACKAGE(Poco) 37 | # ... 38 | # INCLUDE_DIRECTORIES(${Poco_INCLUDE_DIRS}) 39 | # LINK_DIRECTORIES(${Poco_LIBRARY_DIRS}) 40 | # 41 | # In Windows, we make the assumption that, if the Poco files are installed, the default directory 42 | # will be C:\poco or C:\Program Files\Poco. 43 | 44 | SET(POCO_INCLUDE_PATH_DESCRIPTION "top-level directory containing the poco include directories. E.g /usr/local/include/poco-1.2.1 or c:\\poco\\include\\poco-1.2.1") 45 | SET(POCO_INCLUDE_DIR_MESSAGE "Set the Poco_INCLUDE_DIR cmake cache entry to the ${POCO_INCLUDE_PATH_DESCRIPTION}") 46 | SET(POCO_LIBRARY_PATH_DESCRIPTION "top-level directory containing the poco libraries.") 47 | SET(POCO_LIBRARY_DIR_MESSAGE "Set the Poco_LIBRARY_DIR cmake cache entry to the ${POCO_LIBRARY_PATH_DESCRIPTION}") 48 | 49 | 50 | SET(POCO_DIR_SEARCH $ENV{POCO_ROOT}) 51 | IF(POCO_DIR_SEARCH) 52 | FILE(TO_CMAKE_PATH ${POCO_DIR_SEARCH} POCO_DIR_SEARCH) 53 | ELSE(POCO_DIR_SEARCH) 54 | SET(POCO_DIR_SEARCH ${POCO_ROOT}) 55 | IF(POCO_DIR_SEARCH) 56 | FILE(TO_CMAKE_PATH ${POCO_DIR_SEARCH} POCO_DIR_SEARCH) 57 | ENDIF(POCO_DIR_SEARCH) 58 | ENDIF(POCO_DIR_SEARCH) 59 | 60 | 61 | IF(WIN32) 62 | SET(POCO_DIR_SEARCH 63 | ${POCO_DIR_SEARCH} 64 | C:/poco 65 | D:/poco 66 | "C:Program Files/poco" 67 | "D:Program Files/poco" 68 | ) 69 | ENDIF(WIN32) 70 | 71 | # Add in some path suffixes. These will have to be updated whenever a new Poco version comes out. 72 | SET(SUFFIX_FOR_INCLUDE_PATH 73 | poco-1.2.4 74 | poco-1.2.3 75 | poco-1.2.1 76 | ) 77 | 78 | SET(SUFFIX_FOR_LIBRARY_PATH 79 | poco-1.2.4/lib 80 | poco-1.2.4/lib/Linux/i686 81 | poco-1.2.3/lib 82 | poco-1.2.3/lib/Linux/i686 83 | poco-1.2.1/lib 84 | poco-1.2.1/lib/Linux/i686 85 | lib 86 | lib/Linux/i686 87 | ) 88 | 89 | # 90 | # Look for an installation. 91 | # 92 | FIND_PATH(Poco_INCLUDE_DIR NAMES Poco/AbstractCache.h Foundation/include/Poco/AbstractCache.h PATH_SUFFIXES ${SUFFIX_FOR_INCLUDE_PATH} PATHS 93 | 94 | # Look in other places. 95 | ${POCO_DIR_SEARCH} 96 | 97 | # Help the user find it if we cannot. 98 | DOC "The ${POCO_INCLUDE_DIR_MESSAGE}" 99 | ) 100 | 101 | # Assume we didn't find it. 102 | SET(Poco_FOUND 0) 103 | 104 | # Now try to get the include and library path. 105 | IF(Poco_INCLUDE_DIR) 106 | 107 | IF(EXISTS "${Poco_INCLUDE_DIR}") 108 | SET(Poco_INCLUDE_DIRS 109 | ${Poco_INCLUDE_DIR}/CppUnit/include 110 | ${Poco_INCLUDE_DIR}/Foundation/include 111 | ${Poco_INCLUDE_DIR}/Net/include 112 | ${Poco_INCLUDE_DIR}/Util/include 113 | ${Poco_INCLUDE_DIR}/XML/include 114 | ${Poco_INCLUDE_DIR}/Zip/include 115 | ) 116 | SET(Poco_FOUND 1) 117 | ENDIF(EXISTS "${Poco_INCLUDE_DIR}") 118 | 119 | set( Poco_LIBRARIES "") #reset 120 | IF ( NOT Poco_LIBRARIES ) 121 | FIND_LIBRARY(Poco_LIBRARY_Zip NAMES PocoZip PocoZipd PATH_SUFFIXES ${SUFFIX_FOR_LIBRARY_PATH} PATHS 122 | # Look in other places. 123 | ${Poco_INCLUDE_DIR} 124 | ${POCO_DIR_SEARCH} 125 | # Help the user find it if we cannot. 126 | DOC "The ${POCO_LIBRARY_PATH_DESCRIPTION}" 127 | ) 128 | FIND_LIBRARY(Poco_LIBRARY_Found NAMES PocoFoundation PocoFoundationd PATH_SUFFIXES ${SUFFIX_FOR_LIBRARY_PATH} PATHS 129 | # Look in other places. 130 | ${Poco_INCLUDE_DIR} 131 | ${POCO_DIR_SEARCH} 132 | # Help the user find it if we cannot. 133 | DOC "The ${POCO_LIBRARY_PATH_DESCRIPTION}" 134 | ) 135 | set( Poco_LIBRARIES "${Poco_LIBRARY_Found};${Poco_LIBRARY_Zip}" CACHE FILEPATH "Poco libraries names" FORCE ) 136 | 137 | endif() 138 | 139 | IF ( NOT Poco_LIBRARY_DIR ) 140 | FIND_LIBRARY(Poco_LIBRARY_DIR_TMP NAMES PocoFoundation PocoFoundationd PocoZip PocoZipd PATH_SUFFIXES ${SUFFIX_FOR_LIBRARY_PATH} PATHS 141 | 142 | # Look in other places. 143 | ${Poco_INCLUDE_DIR} 144 | ${POCO_DIR_SEARCH} 145 | 146 | # Help the user find it if we cannot. 147 | DOC "The ${POCO_LIBRARY_PATH_DESCRIPTION}" 148 | ) 149 | GET_FILENAME_COMPONENT(Poco_LIBRARY_DIR_PATH ${Poco_LIBRARY_DIR_TMP} PATH) 150 | SET( Poco_LIBRARY_DIR ${Poco_LIBRARY_DIR_PATH} CACHE PATH "Poco library dir path" ) 151 | endif() 152 | 153 | IF(Poco_INCLUDE_DIR) 154 | 155 | 156 | # Look for the poco binary path. 157 | SET(Poco_BINARY_DIR ${Poco_INCLUDE_DIR}) 158 | IF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}") 159 | 160 | SET(Poco_BINARY_DIRS ${Poco_BINARY_DIR}/bin) 161 | SET(Poco_LIBRARY_DIRS "${Poco_BINARY_DIR}/bin" CACHE PATH "All Poco Library Dirs") 162 | 163 | ENDIF(Poco_BINARY_DIR AND EXISTS "${Poco_BINARY_DIR}") 164 | 165 | ENDIF(Poco_INCLUDE_DIR) 166 | 167 | ENDIF(Poco_INCLUDE_DIR) 168 | 169 | IF(NOT Poco_FOUND) 170 | IF(NOT Poco_FIND_QUIETLY) 171 | MESSAGE(STATUS "Poco was not found. ${POCO_DIR_MESSAGE}") 172 | ELSE(NOT Poco_FIND_QUIETLY) 173 | IF(Poco_FIND_REQUIRED) 174 | MESSAGE(FATAL_ERROR "Poco was not found. ${POCO_DIR_MESSAGE}") 175 | ENDIF(Poco_FIND_REQUIRED) 176 | ENDIF(NOT Poco_FIND_QUIETLY) 177 | ENDIF(NOT Poco_FOUND) 178 | 179 | IF(NOT Poco_FIND_QUIETLY) 180 | message( ${Poco_INCLUDE_DIR} + " and lib dir " + ${Poco_LIBRARY_DIR} + " poco found " + ${Poco_FOUND}) 181 | ENDIF(NOT Poco_FIND_QUIETLY) 182 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # First we check that libraries are installed 2 | 3 | find_package(Poco) 4 | 5 | set(COMMON_LIB PocoNet PocoUtil PocoFoundation PocoNetSSL) 6 | 7 | # Sources for each target 8 | set(MASTER_SRC djob.cpp dtalk.cpp disthcm.cpp) 9 | set(CONSOLE_SRC dtalk.cpp tinycon.cpp disthcc.cpp) 10 | set(SLAVE_SRC engines/hashcat.cpp djob.cpp dtalk.cpp disthcs.cpp) 11 | 12 | 13 | # The master executable 14 | add_executable(disthcm ${MASTER_SRC}) 15 | target_link_libraries(disthcm ${COMMON_LIB} PocoDataSQLite PocoData PocoCrypto) 16 | 17 | # The console executable 18 | add_executable(disthcc ${CONSOLE_SRC}) 19 | target_link_libraries(disthcc ${COMMON_LIB}) 20 | 21 | 22 | # The slave executable 23 | add_executable(disthcs ${SLAVE_SRC}) 24 | target_link_libraries(disthcs ${COMMON_LIB}) 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/InvalidCertHandler.h: -------------------------------------------------------------------------------- 1 | #ifndef NetSSL_myCertificateHandler_INCLUDED 2 | #define NetSSL_myCertificateHandler_INCLUDED 3 | 4 | 5 | #include "Poco/Net/NetSSL.h" 6 | #include "Poco/Net/InvalidCertificateHandler.h" 7 | 8 | 9 | //namespace Poco { 10 | //namespace Net { 11 | 12 | 13 | class NetSSL_API myCertificateHandler: public Poco::Net::InvalidCertificateHandler 14 | /// A myCertificateHandler is invoked whenever an error occurs verifying the certificate. 15 | /// 16 | /// The certificate is printed to stdout and the user is asked via console if he wants to accept it. 17 | { 18 | public: 19 | myCertificateHandler(); 20 | 21 | myCertificateHandler(bool handleErrorsOnServerSide); 22 | /// Creates the myCertificateHandler. 23 | 24 | virtual ~myCertificateHandler(); 25 | /// Destroys the myCertificateHandler. 26 | 27 | void onInvalidCertificate(const void* pSender, Poco::Net::VerificationErrorArgs& errorCert); 28 | /// Prints the certificate to stdout and waits for user input on the console 29 | /// to decide if a certificate should be accepted/rejected. 30 | }; 31 | 32 | 33 | myCertificateHandler::myCertificateHandler(): InvalidCertificateHandler(false) 34 | { 35 | 36 | } 37 | 38 | 39 | myCertificateHandler::myCertificateHandler(bool server): InvalidCertificateHandler(server) 40 | { 41 | } 42 | 43 | 44 | myCertificateHandler::~myCertificateHandler() 45 | { 46 | } 47 | 48 | 49 | void myCertificateHandler::onInvalidCertificate(const void*, Poco::Net::VerificationErrorArgs& errorCert) 50 | { 51 | errorCert.setIgnoreError(true); 52 | return; 53 | 54 | const Poco::Net::X509Certificate& aCert = errorCert.certificate(); 55 | std::cout << "\n"; 56 | std::cout << "WARNING: Certificate verification failed\n"; 57 | std::cout << "----------------------------------------\n"; 58 | std::cout << "Issuer Name: " << aCert.issuerName() << "\n"; 59 | std::cout << "Subject Name: " << aCert.subjectName() << "\n\n"; 60 | std::cout << "The certificate yielded the error: " << errorCert.errorMessage() << "\n\n"; 61 | std::cout << "The error occurred in the certificate chain at position " << errorCert.errorDepth() << "\n"; 62 | std::cout << "Accept the certificate (y,n)? "; 63 | char c; 64 | std::cin >> c; 65 | if (c == 'y' || c == 'Y') 66 | errorCert.setIgnoreError(true); 67 | else 68 | errorCert.setIgnoreError(false); 69 | } 70 | 71 | //} } // namespace Poco::Net 72 | 73 | 74 | #endif // NetSSL_myCertificateHandler_INCLUDED 75 | -------------------------------------------------------------------------------- /src/SecureSocketConnector.h: -------------------------------------------------------------------------------- 1 | // 2 | // SecureSocketConnector.h 3 | // 4 | // Definition of the SocketConnector class using a SecureStreamSocket. 5 | // 6 | // Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. 7 | // and Contributors. 8 | // 9 | // Permission is hereby granted, free of charge, to any person or organization 10 | // obtaining a copy of the software and accompanying documentation covered by 11 | // this license (the "Software") to use, reproduce, display, distribute, 12 | // execute, and transmit the Software, and to prepare derivative works of the 13 | // Software, and to permit third-parties to whom the Software is furnished to 14 | // do so, all subject to the following: 15 | // 16 | // The copyright notices in the Software and this entire statement, including 17 | // the above license grant, this restriction and the following disclaimer, 18 | // must be included in all copies of the Software, in whole or in part, and 19 | // all derivative works of the Software, unless such copies or derivative 20 | // works are solely in the form of machine-executable object code generated by 21 | // a source language processor. 22 | // 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 26 | // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 27 | // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 28 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | // DEALINGS IN THE SOFTWARE. 30 | // 31 | 32 | 33 | #ifndef Net_SecureSocketConnector_INCLUDED 34 | #define Net_SecureSocketConnector_INCLUDED 35 | 36 | 37 | #include "Poco/Net/Net.h" 38 | #include "Poco/Net/SocketNotification.h" 39 | #include "Poco/Net/SocketAddress.h" 40 | #include "Poco/Net/StreamSocket.h" 41 | #include "Poco/Observer.h" 42 | 43 | 44 | namespace dNet { 45 | 46 | using namespace Poco::Net; 47 | 48 | template 49 | class SocketConnector 50 | /// This class implements the Connector part of the 51 | /// Acceptor-Connector design pattern. 52 | /// 53 | /// The Acceptor-Connector pattern has been described in the book 54 | /// "Pattern Languages of Program Design 3", edited by Robert Martin, 55 | /// Frank Buschmann and Dirk Riehle (Addison Wesley, 1997). 56 | /// 57 | /// The Acceptor-Connector design pattern decouples connection 58 | /// establishment and service initialization in a distributed system 59 | /// from the processing performed once a service is initialized. 60 | /// This decoupling is achieved with three components: Acceptors, 61 | /// Connectors and Service Handlers. 62 | /// The Connector actively establishes a connection with a remote 63 | /// server socket (usually managed by an Acceptor) and initializes 64 | /// a Service Handler to manage the connection. 65 | /// 66 | /// The SocketConnector sets up a StreamSocket, initiates a non-blocking 67 | /// connect operation and registers itself for ReadableNotification, WritableNotification 68 | /// and ErrorNotification. ReadableNotification or WritableNotification denote the successful 69 | /// establishment of the connection. 70 | /// 71 | /// When the StreamSocket becomes readable or writeable, the SocketConnector 72 | /// creates a ServiceHandler to service the connection and unregisters 73 | /// itself. 74 | /// 75 | /// In case of an error (ErrorNotification), the SocketConnector unregisters itself 76 | /// and calls the onError() method, which can be overridden by subclasses 77 | /// to perform custom error handling. 78 | /// 79 | /// The ServiceHandler class must provide a constructor that 80 | /// takes a StreamSocket and a SocketReactor as arguments, 81 | /// e.g.: 82 | /// MyServiceHandler(const StreamSocket& socket, ServiceReactor& reactor) 83 | /// 84 | /// When the ServiceHandler is done, it must destroy itself. 85 | /// 86 | /// Subclasses can override the createServiceHandler() factory method 87 | /// if special steps are necessary to create a ServiceHandler object. 88 | { 89 | public: 90 | explicit SocketConnector(SocketAddress& address): 91 | _pReactor(0) 92 | /// Creates a SocketConnector, using the given Socket. 93 | { 94 | _socket.connectNB(address); 95 | } 96 | 97 | SocketConnector(SocketAddress& address, SocketReactor& reactor): 98 | _pReactor(0) 99 | /// Creates an acceptor, using the given ServerSocket. 100 | /// The SocketConnector registers itself with the given SocketReactor. 101 | { 102 | _socket.connectNB(address); 103 | registerConnector(reactor); 104 | } 105 | 106 | virtual ~SocketConnector() 107 | /// Destroys the SocketConnector. 108 | { 109 | unregisterConnector(); 110 | } 111 | 112 | virtual void registerConnector(SocketReactor& reactor) 113 | /// Registers the SocketConnector with a SocketReactor. 114 | /// 115 | /// A subclass can override this and, for example, also register 116 | /// an event handler for a timeout event. 117 | /// 118 | /// The overriding method must call the baseclass implementation first. 119 | { 120 | _pReactor = &reactor; 121 | _pReactor->addEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onReadable)); 122 | _pReactor->addEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onWritable)); 123 | _pReactor->addEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onError)); 124 | } 125 | 126 | virtual void unregisterConnector() 127 | /// Unregisters the SocketConnector. 128 | /// 129 | /// A subclass can override this and, for example, also unregister 130 | /// its event handler for a timeout event. 131 | /// 132 | /// The overriding method must call the baseclass implementation first. 133 | { 134 | if (_pReactor) 135 | { 136 | _pReactor->removeEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onReadable)); 137 | _pReactor->removeEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onWritable)); 138 | _pReactor->removeEventHandler(_socket, Poco::Observer(*this, &SocketConnector::onError)); 139 | } 140 | } 141 | 142 | void onReadable(ReadableNotification* pNotification) 143 | { 144 | pNotification->release(); 145 | int err = _socket.impl()->socketError(); 146 | if (err) 147 | { 148 | onError(err); 149 | unregisterConnector(); 150 | } 151 | else 152 | { 153 | onConnect(); 154 | } 155 | } 156 | 157 | void onWritable(WritableNotification* pNotification) 158 | { 159 | pNotification->release(); 160 | onConnect(); 161 | } 162 | 163 | void onConnect() 164 | { 165 | _socket.setBlocking(true); 166 | createServiceHandler(); 167 | unregisterConnector(); 168 | } 169 | 170 | void onError(ErrorNotification* pNotification) 171 | { 172 | pNotification->release(); 173 | onError(_socket.impl()->socketError()); 174 | unregisterConnector(); 175 | } 176 | 177 | protected: 178 | virtual ServiceHandler* createServiceHandler() 179 | /// Create and initialize a new ServiceHandler instance. 180 | /// 181 | /// Subclasses can override this method. 182 | { 183 | return new ServiceHandler(_socket, *_pReactor); 184 | } 185 | 186 | virtual void onError(int errorCode) 187 | /// Called when the socket cannot be connected. 188 | /// 189 | /// Subclasses can override this method. 190 | { 191 | } 192 | 193 | SocketReactor* reactor() 194 | /// Returns a pointer to the SocketReactor where 195 | /// this SocketConnector is registered. 196 | /// 197 | /// The pointer may be null. 198 | { 199 | return _pReactor; 200 | } 201 | 202 | Socket& socket() 203 | /// Returns a reference to the SocketConnector's socket. 204 | { 205 | return _socket; 206 | } 207 | 208 | private: 209 | SocketConnector(); 210 | SocketConnector(const SocketConnector&); 211 | SocketConnector& operator = (const SocketConnector&); 212 | 213 | SecureStreamSocket _socket; 214 | SocketReactor* _pReactor; 215 | }; 216 | 217 | } // namespace dNet 218 | 219 | 220 | #endif // Net_SecureSocketConnector_INCLUDED -------------------------------------------------------------------------------- /src/disthc.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 50 | 51 | -------------------------------------------------------------------------------- /src/disthc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include // SSL 8 | #include // SSL 9 | #include 10 | #include 11 | #include // Client Connections 12 | #include // Client Connections 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include // Client Connections 19 | #include // used by dTalk 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 36 | #include 37 | #else 38 | #include 39 | #endif 40 | 41 | using Poco::AutoPtr; 42 | using Poco::File; 43 | using Poco::FileInputStream; 44 | using Poco::FileOutputStream; 45 | using Poco::format; 46 | using Poco::MemoryInputStream; 47 | using Poco::Net::SecureServerSocket; 48 | using Poco::Net::SecureStreamSocket; 49 | using Poco::Net::SocketAcceptor; 50 | using Poco::Net::SocketAddress; 51 | using Poco::Net::SocketConnector; 52 | using Poco::Net::SocketReactor; 53 | using Poco::Net::ReadableNotification; 54 | using Poco::Net::ShutdownNotification; 55 | using Poco::Net::ServerSocket; 56 | using Poco::Net::StreamSocket; 57 | using Poco::NObserver; 58 | using Poco::NumberParser; 59 | using Poco::Observer; 60 | using Poco::Path; 61 | using Poco::Pipe; 62 | using Poco::Process; 63 | using Poco::StringTokenizer; 64 | using Poco::Thread; 65 | using Poco::trim; 66 | using Poco::trimInPlace; 67 | using Poco::Util::Application; 68 | using Poco::Util::HelpFormatter; 69 | using Poco::Util::Option; 70 | using Poco::Util::OptionSet; 71 | 72 | using std::deque; 73 | using std::string; 74 | using std::vector; 75 | 76 | 77 | // Application constants 78 | #define APP_VERSION 40 79 | #define APP_PROMPT string("disthc>") 80 | #define NODE_SLAVE 1 81 | #define NODE_CONIO 2 82 | #define DEFAULT_CHUNK_SIZE 1000000 83 | #define SYNC_AUTO 1 84 | #define NO_SYNC_AUTO 0 85 | 86 | // Application parameters 87 | #define PARAM_ATTACK "A" 88 | #define PARAM_MODE "T" 89 | #define PARAM_MASK "M" 90 | #define PARAM_RULES "R" 91 | #define PARAM_DICT "D" 92 | #define PARAM_HASHES "H" 93 | #define PARAM_CHUNK_SIZE "C" 94 | #define PARAM_GHOST "G" 95 | 96 | // Exit codes 97 | #define EXIT_BAD_DICT 2 98 | #define EXIT_BIND_FAILED 3 99 | #define EXIT_BAD_HASHCAT 5 100 | #define EXIT_BAD_ENGINE 6 101 | 102 | // DCODE definitions 103 | #define DCODE_HELO 1 104 | #define DCODE_READY 2 105 | #define DCODE_PRINT 3 106 | #define DCODE_SET_PARAM 4 107 | #define DCODE_START_COPY 5 108 | #define DCODE_END_COPY 6 109 | #define DCODE_GET_CHUNK 7 110 | #define DCODE_SET_CHUNK 8 111 | #define DCODE_RESULTS 9 112 | #define DCODE_HOTKEY 10 113 | #define DCODE_SYNC 11 114 | #define DCODE_ZAP 12 // removes a list of hashes 115 | #define DCODE_FILE_NAME 14 // starts transmission of a file 116 | #define DCODE_FILE_DATA 15 117 | #define DCODE_FILE_EOF 16 // stops transmission of a file 118 | #define DCODE_RPC 33 119 | #define DCODE_TOKEN 88 120 | 121 | // Readability shortcuts 122 | #define match !strcmp 123 | 124 | // Globals 125 | extern bool DEBUG; 126 | extern bool GHOST; -------------------------------------------------------------------------------- /src/disthcc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | application: disthcm 3 | description: distributed hash-cracking console node 4 | written by Unix-Ninja 5 | May 10, 2013 6 | */ 7 | 8 | #include "disthc.h" 9 | #include "dtalk.h" 10 | #include "tinycon.h" 11 | #include "SecureSocketConnector.h" 12 | #include "InvalidCertHandler.h" 13 | #include 14 | #include 15 | 16 | using std::string; 17 | using std::vector; 18 | 19 | // ************************************************************************** // 20 | // Local globals 21 | // ************************************************************************** // 22 | std::string clientString; 23 | std::string authToken; 24 | bool DEBUG; 25 | bool READY(false); 26 | Thread thread_con; 27 | dTalk *pTalk; 28 | bool wait_for_receive(false); 29 | 30 | // ************************************************************************** // 31 | // Console Class 32 | // ************************************************************************** // 33 | class tcon : public Poco::Runnable, public tinyConsole 34 | { 35 | public: 36 | // run parent's constructor 37 | tcon (std::string s): tinyConsole(s) {} 38 | 39 | int trigger (std::string s) 40 | { 41 | if (s == "exit") 42 | { 43 | // SIGINT the current process 44 | Poco::Process::requestTermination(Poco::Process::id()); 45 | // flag console for termination 46 | quit(); 47 | } 48 | else 49 | { 50 | // the string buffer should probably be sent out to the server. for now, just echo it. 51 | //std::cout << " " << s << std::endl; 52 | if(!s.empty()) { 53 | pTalk->rpc(DCODE_RPC, s); 54 | wait_for_receive = true; 55 | } 56 | } 57 | 58 | while(wait_for_receive) { 59 | Poco::Thread::sleep(100); 60 | } 61 | return 0; 62 | } 63 | 64 | int hotkeys(char c) 65 | { 66 | if(c == TAB) 67 | { 68 | pTalk->rpc(DCODE_HOTKEY, "\t" + getBuffer()); 69 | } 70 | return 0; 71 | } 72 | 73 | void run() 74 | { 75 | tinyConsole::run(); 76 | } 77 | } console(APP_PROMPT); 78 | 79 | // ************************************************************************** // 80 | // Service handle for main app 81 | // ************************************************************************** // 82 | class DistClientHandler 83 | { 84 | private: 85 | static const int BUFFER_SIZE = 1024; 86 | 87 | StreamSocket _socket; 88 | SocketReactor& _reactor; 89 | dTalk _talk; 90 | char* _pBuffer; 91 | 92 | public: 93 | DistClientHandler(StreamSocket& socket, SocketReactor& reactor): 94 | _socket(socket), 95 | _reactor(reactor), 96 | _talk(_socket), 97 | _pBuffer(new char[BUFFER_SIZE]) 98 | { 99 | Application& app = Application::instance(); 100 | app.logger().information("Connected to " + socket.peerAddress().toString()); 101 | 102 | _reactor.addEventHandler( 103 | _socket, 104 | NObserver(*this, &DistClientHandler::onReadable) 106 | ); 107 | 108 | _reactor.addEventHandler( 109 | _socket, 110 | NObserver(*this, &DistClientHandler::onShutdown) 112 | ); 113 | // initialize global pointer 114 | pTalk = &_talk; 115 | 116 | //greet server 117 | _talk.rpc(DCODE_HELO, format("-dhc:conio:%d", APP_VERSION)); 118 | //authorize to server 119 | _talk.rpc(DCODE_TOKEN, authToken); 120 | // identify to server 121 | _talk.rpc(DCODE_SET_PARAM, clientString); 122 | } 123 | 124 | ~DistClientHandler() 125 | { 126 | Application& app = Application::instance(); 127 | try 128 | { 129 | _reactor.stop(); 130 | app.logger().information("Disconnected from " + _socket.peerAddress().toString()); 131 | // send quit to console 132 | console.quit(); 133 | // SIGINT the current process 134 | Poco::Process::requestTermination(Poco::Process::id()); 135 | //exit(1); 136 | } 137 | catch (...) 138 | { 139 | } 140 | 141 | _reactor.removeEventHandler( 142 | _socket, 143 | NObserver(*this, &DistClientHandler::onReadable) 145 | ); 146 | 147 | _reactor.removeEventHandler( 148 | _socket, 149 | NObserver(*this, &DistClientHandler::onShutdown) 151 | ); 152 | delete [] _pBuffer; 153 | } 154 | 155 | void onReadable(const AutoPtr& pNf) 156 | { 157 | //receive data or close on end 158 | //_talk.receive() || delete this; 159 | if ( !_talk.receive() ){ delete this; } 160 | 161 | if(_talk.dcode() == DCODE_READY) 162 | { 163 | READY = true; 164 | } 165 | else if(_talk.dcode() == DCODE_HOTKEY) 166 | { 167 | //std::cout << "[" << _talk.data().size() << "] " << std::flush; 168 | if (_talk.data().substr(0,1) == "\t") 169 | { 170 | console.setBuffer(console.getBuffer() + _talk.data().substr(1)); 171 | std::cout << _talk.data().substr(1) << std::flush; 172 | } 173 | else if (_talk.data().size()) 174 | { 175 | if(_talk.data() == format("%c", 4)) 176 | { 177 | // end of output, so show prompt again 178 | console.showPrompt(); 179 | std::cout << console.getBuffer() << std::flush; 180 | } else std::cout << _talk.data() << std::endl; 181 | } 182 | } 183 | if(_talk.dcode() == DCODE_PRINT) 184 | { 185 | std::cout << _talk.data() << std::endl; 186 | std::cout << std::flush; // force output to screen 187 | } 188 | wait_for_receive = false; 189 | } 190 | 191 | void onShutdown(const AutoPtr& pNf) 192 | { 193 | delete this; 194 | } 195 | 196 | }; 197 | 198 | class mySocketConnector : public dNet::SocketConnector 199 | { 200 | private: 201 | bool _failed; 202 | bool _shutdown; 203 | 204 | public: 205 | 206 | mySocketConnector(SocketAddress& address, SocketReactor& reactor) : 207 | SocketConnector(address, reactor), 208 | _failed(false), 209 | _shutdown(false) 210 | { 211 | reactor.addEventHandler(socket(), Observer (*this, &mySocketConnector::onShutdown)); 212 | //gsocket = socket(); 213 | } 214 | 215 | void onShutdown(ShutdownNotification* pNf) 216 | { 217 | pNf->release(); 218 | _shutdown = true; 219 | } 220 | 221 | void onError(int error) 222 | { 223 | Application& app = Application::instance(); 224 | app.logger().information("Error: Unable to connect to disthc server!"); 225 | _failed = true; 226 | reactor()->stop(); 227 | exit(2); // Unable to connect to server 228 | } 229 | 230 | bool failed() const 231 | { 232 | return _failed; 233 | } 234 | 235 | bool shutdown() const 236 | { 237 | return _shutdown; 238 | } 239 | 240 | }; 241 | 242 | // ************************************************************************** // 243 | // The main application class 244 | // ************************************************************************** // 245 | class DistClient : public Poco::Util::ServerApplication 246 | { 247 | public: 248 | 249 | DistClient() : _helpRequested(false), 250 | _cfg("console.properties") 251 | { 252 | } 253 | 254 | ~DistClient() 255 | { 256 | } 257 | 258 | void shutdown() 259 | { 260 | terminate(); 261 | } 262 | private: 263 | 264 | bool _helpRequested; 265 | string _cfg; 266 | 267 | protected: 268 | 269 | void initialize(Application& self) 270 | { 271 | File f(_cfg); 272 | if(f.exists()) 273 | { 274 | loadConfiguration(_cfg); // load default configuration files, if present 275 | } 276 | ServerApplication::initialize(self); 277 | self.logger().information("----------------------------------------"); 278 | self.logger().information(format("Disthc Console Build [%d]", APP_VERSION)); 279 | } 280 | 281 | void uninitialize() 282 | { 283 | ServerApplication::uninitialize(); 284 | } 285 | 286 | void defineOptions(OptionSet& options) 287 | { 288 | ServerApplication::defineOptions(options); 289 | 290 | options.addOption( 291 | Option("help", "h", "display help information on command line arguments") 292 | .required(false) 293 | .repeatable(false)); 294 | } 295 | 296 | void handleOption(const std::string& name, const std::string& value) 297 | { 298 | ServerApplication::handleOption(name, value); 299 | 300 | if (name == "help") 301 | { 302 | _helpRequested = true; 303 | } 304 | else if (name == "config") 305 | { 306 | _cfg = value; 307 | } 308 | } 309 | 310 | void displayHelp() 311 | { 312 | HelpFormatter helpFormatter(options()); 313 | helpFormatter.setCommand(commandName()); 314 | helpFormatter.setUsage("OPTIONS"); 315 | helpFormatter.setHeader("A distributed hash-cracking server."); 316 | helpFormatter.format(std::cout); 317 | } 318 | 319 | int main(const std::vector& args) 320 | { 321 | if (_helpRequested) 322 | { 323 | displayHelp(); 324 | return Application::EXIT_OK; 325 | } 326 | 327 | Application& app = Application::instance(); 328 | 329 | // create client string 330 | clientString = format("%s %s %s %u %s %s", 331 | Poco::Environment::nodeName(), 332 | Poco::Environment::osName(), 333 | Poco::Environment::osVersion(), 334 | Poco::Environment::processorCount(), 335 | Poco::Environment::nodeId(), 336 | Poco::Environment::osArchitecture() 337 | ); 338 | 339 | // get parameters from configuration file 340 | string host = (string) config().getString("cfg.server.address","localhost"); 341 | unsigned short port = (unsigned short) config().getInt("cfg.server.port", 4000); 342 | authToken = (string) config().getString("cfg.server.auth.token", "*"); 343 | DEBUG = config().getBool("cfg.server.debug", false); 344 | 345 | // set-up a stream socket 346 | SocketAddress sa(host, port); 347 | // set-up a SocketReactor 348 | SocketReactor reactor; 349 | 350 | // Let's setup rules for SSL (I don't want to be prompted about certs) 351 | Poco::SharedPtr pConsoleHandler = new Poco::Net::KeyConsoleHandler(false); 352 | Poco::SharedPtr pInvalidCertHandler = new myCertificateHandler; 353 | Poco::Net::Context::Ptr pContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_RELAXED, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); 354 | Poco::Net::SSLManager::instance().initializeClient(pConsoleHandler, pInvalidCertHandler, pContext); 355 | 356 | // Connect to the server 357 | mySocketConnector connector(sa, reactor); 358 | 359 | // run the reactor in its own thread so that we can wait for a termination request 360 | Thread thread; 361 | thread.start(reactor); 362 | 363 | app.logger().information("----------------------------------------"); 364 | 365 | while(!READY); // loop until ready 366 | // start console 367 | thread_con.start(console); 368 | 369 | waitForTerminationRequest(); 370 | // Stop the SocketReactor 371 | reactor.stop(); 372 | thread.join(); 373 | thread_con.join(); 374 | 375 | return Application::EXIT_OK; 376 | } 377 | }; 378 | 379 | int main(int argc, char** argv) 380 | { 381 | DistClient app; 382 | return app.run(argc, argv); 383 | } 384 | -------------------------------------------------------------------------------- /src/disthcm.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | application: disthcm 3 | description: distributed hash-cracking master server 4 | written by Unix-Ninja 5 | May 10, 2013 6 | */ 7 | //#define DROLE_MASTER 8 | #include "disthc.h" 9 | #include "djob.h" 10 | #include "dtalk.h" 11 | 12 | #include "disthcm.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using Poco::Data::into; 23 | using Poco::Data::now; 24 | using Poco::Data::range; 25 | using Poco::Data::use; 26 | using Poco::Crypto::DigestEngine; 27 | 28 | // ************************************************************************** // 29 | // Local globals 30 | // ************************************************************************** // 31 | bool DEBUG; 32 | std::string authToken; 33 | bool showResults; 34 | bool showDcode; 35 | bool dbRainbowOn; 36 | Poco::Data::Session* db; 37 | 38 | 39 | // ************************************************************************** // 40 | // function to load hashes into db 41 | // ************************************************************************** // 42 | 43 | bool sendHashes() 44 | { 45 | // sends to all slaves 46 | Application& app = Application::instance(); 47 | DJob *job = DJob::Instance(); 48 | string hash; 49 | string salt; 50 | string dump; 51 | Poco::Data::Statement select(*db); 52 | 53 | app.logger().information("Sending hashes..."); 54 | select << "SELECT hash, salt FROM job_queue", into(hash), into(salt), range(0,1); 55 | while(!select.done()) 56 | { 57 | select.execute(); 58 | if(!salt.empty()) 59 | { 60 | dump += string(hash + ":" + salt + "\n"); 61 | } 62 | else 63 | { 64 | dump += string(hash + "\n"); 65 | } 66 | } 67 | pool.sendFile(job->getHashFile(), &dump); 68 | return true; 69 | } 70 | 71 | bool sendHashes(StreamSocket socket) 72 | { 73 | // sends to a particular socket 74 | Application& app = Application::instance(); 75 | DJob *job = DJob::Instance(); 76 | string hash; 77 | string salt; 78 | string dump; 79 | Poco::Data::Statement select(*db); 80 | 81 | app.logger().information("Sending hashes..."); 82 | select << "SELECT hash, salt FROM job_queue", into(hash), into(salt), range(0,1); 83 | while(!select.done()) 84 | { 85 | select.execute(); 86 | if(!salt.empty()) 87 | { 88 | dump += string(hash + ":" + salt + "\n"); 89 | } 90 | else 91 | { 92 | dump += string(hash + "\n"); 93 | } 94 | } 95 | dTalk *talk = new dTalk(socket); 96 | talk->send_text_as_file(job->getHashFile(), dump); 97 | return true; 98 | } 99 | 100 | bool loadHashes() 101 | { 102 | Application& app = Application::instance(); 103 | DJob *job = DJob::Instance(); 104 | int count; 105 | int total(0); 106 | string line; 107 | string hash; 108 | string salt; 109 | 110 | //check for hash file 111 | File f(job->getHashFile()); 112 | if(!f.exists()) return false; 113 | 114 | // open hash file 115 | FileInputStream fis(job->getHashFile()); 116 | 117 | // clear job_queue 118 | app.logger().information("Cleaning job queue from db..."); 119 | *db << "DELETE FROM job_queue", now; 120 | 121 | app.logger().information("Loading hashes into db..."); 122 | //loop through hash file and add relevant hashes to job queue 123 | while(fis >> line) { 124 | // TODO parse for different hash types and salts 125 | StringTokenizer t(line,":"); 126 | hash = t[0]; 127 | salt = ""; 128 | *db << "SELECT COUNT(hash) FROM rainbow WHERE hash=? AND salt=?", use(hash), use(salt), into(count), now; 129 | if(!count) 130 | { 131 | *db << "INSERT INTO job_queue (hash, salt) VALUES (?, ?)", use(hash), use(salt), now; 132 | total++; 133 | } 134 | } 135 | app.logger().information(format(" Added %d hashes.", total)); 136 | job->setHashCount((unsigned int) total); 137 | return true; 138 | } 139 | 140 | string lookupHash(string hash, string salt) 141 | { 142 | string plain; 143 | std::cout << "PP--PP " << salt << std::endl; 144 | *db << "SELECT plain FROM rainbow WHERE hash=? AND salt=?", use(hash), use(salt), into(plain), now; 145 | return plain; 146 | } 147 | 148 | 149 | // ************************************************************************** // 150 | // Service handle for main app 151 | // ************************************************************************** // 152 | class DistServiceHandler 153 | { 154 | private: 155 | static const int BUFFER_SIZE = 1024; 156 | 157 | StreamSocket _socket; 158 | SocketReactor& _reactor; 159 | char* _pBuffer; 160 | string _authToken; 161 | string _clientType; 162 | dTalk _talk; 163 | long int _chunkSize; 164 | 165 | public: 166 | DistServiceHandler(StreamSocket& socket, SocketReactor& reactor): 167 | _socket(socket), 168 | _reactor(reactor), 169 | _talk(_socket), 170 | _pBuffer(new char[BUFFER_SIZE]) 171 | { 172 | Application& app = Application::instance(); 173 | // Log a connection 174 | app.logger().information("+Node " + socket.peerAddress().toString()); 175 | 176 | _reactor.addEventHandler( 177 | _socket, 178 | NObserver(*this, &DistServiceHandler::onReadable) 180 | ); 181 | 182 | _reactor.addEventHandler( 183 | _socket, 184 | NObserver(*this, &DistServiceHandler::onShutdown) 186 | ); 187 | 188 | // Set default chunk size 189 | _chunkSize = DEFAULT_CHUNK_SIZE; 190 | 191 | // Send hello 192 | _talk.rpc(DCODE_HELO, "Hi"); 193 | 194 | } 195 | 196 | ~DistServiceHandler() 197 | { 198 | Application& app = Application::instance(); 199 | try 200 | { 201 | // Unregister this client 202 | if(_clientType == "slave") 203 | pool.unregisterClient(_socket, NODE_SLAVE); 204 | else 205 | pool.unregisterClient(_socket, NODE_CONIO); 206 | 207 | // Log a disconnection 208 | app.logger().information("-Node " + _socket.peerAddress().toString()); 209 | if(DEBUG) app.logger().information(format("%%Remaining slaves: %d", pool.count(NODE_SLAVE))); 210 | } 211 | catch (...) { } 212 | 213 | _reactor.removeEventHandler( 214 | _socket, 215 | NObserver(*this, &DistServiceHandler::onReadable) 217 | ); 218 | 219 | _reactor.removeEventHandler( 220 | _socket, 221 | NObserver(*this, &DistServiceHandler::onShutdown) 223 | ); 224 | 225 | delete _pBuffer; 226 | } 227 | 228 | void onReadable(const AutoPtr& pNf) 229 | { 230 | //receive data 231 | if (_talk.receive()) 232 | { 233 | Application& app = Application::instance(); 234 | DJob *job = DJob::Instance(); 235 | 236 | if(DEBUG && showDcode) app.logger().information(format("%%DCODE(%d)",_talk.dcode())); 237 | if(!_authToken.empty()) 238 | { 239 | if(_talk.dcode() == DCODE_RPC) 240 | { 241 | //TODO maybe client types should be type int? 242 | if(_clientType == "slave") 243 | { 244 | //slave is probably unsafe at this point. remove from pool 245 | pool.unregisterClient(_socket, NODE_SLAVE); 246 | return; 247 | } 248 | process_rpc(); 249 | } 250 | else if(_talk.dcode() == DCODE_HOTKEY) 251 | { 252 | if(DEBUG) app.logger().information("%Hotkey triggered."); 253 | if (_talk.data().substr(0,1) == "\t") 254 | { 255 | _talk.rpc(DCODE_HOTKEY, tab_complete(_talk.data().substr(1))); 256 | } 257 | } 258 | else if(_talk.dcode() == DCODE_GET_CHUNK) 259 | { 260 | if(DEBUG) app.logger().information("%Chunk requested from " + _socket.peerAddress().toString()); 261 | } 262 | else if(_talk.dcode() == DCODE_RESULTS) 263 | { 264 | if(DEBUG) app.logger().information("%Results received from " + _socket.peerAddress().toString()); 265 | 266 | string results = _talk.data(); 267 | if(!results.empty()) 268 | { 269 | if (showResults) 270 | { 271 | app.logger().information("==" + results); 272 | } 273 | StringTokenizer t(results,":"); 274 | string hash = t[0]; 275 | string salt = ""; 276 | string plain = t[1]; 277 | if(t.count()==3) 278 | { 279 | salt = t[1]; 280 | plain = t[2]; 281 | } 282 | *db << "INSERT INTO rainbow (hash, salt, plain) VALUES (?, ?, ?)", use(hash), use(salt), use(plain), now; 283 | *db << "DELETE FROM job_queue WHERE hash=? AND salt=?", use(hash), use(salt), now; 284 | job->setHashCount(job->getHashCount()-1); // decrement remaining hashes 285 | pool.zap(results); 286 | 287 | // check if all hashes cracked and (if so) stop job 288 | unsigned int count; 289 | *db << "SELECT COUNT(hash) FROM job_queue", into(count), now; 290 | if(!count) 291 | { 292 | job->stop(); 293 | job->msgConsoles("All hashes have been found! Job stopped."); 294 | } 295 | } 296 | pool.ready(_socket); 297 | } 298 | else if(_talk.dcode() == DCODE_READY) 299 | { 300 | if(DEBUG) app.logger().information("%Client " + _socket.peerAddress().toString() + " is ready."); 301 | pool.ready(_socket); 302 | } 303 | } 304 | else 305 | { 306 | if(authorize()) 307 | { 308 | // retreive client identity 309 | _talk.receive(); 310 | string clientString = _talk.data(); 311 | DigestEngine de("SHA256"); 312 | de.update(clientString); 313 | string clientToken = DigestEngine::digestToHex(de.digest()); 314 | string blacklist = "n"; 315 | 316 | // register the client 317 | int node_t = NODE_SLAVE; 318 | 319 | if(_clientType == "conio") 320 | { 321 | node_t = NODE_CONIO; 322 | } 323 | 324 | // add client to pool 325 | int id = pool.registerClient(_socket, node_t, clientString, clientToken); 326 | ClientNode* node = pool.get(id, node_t); 327 | 328 | // is client in db? 329 | *db << "SELECT id, blacklist FROM clients WHERE token=?", use(clientToken), into(id), into(blacklist), now; 330 | if(!id) 331 | { 332 | // add client to db 333 | *db << "INSERT INTO clients (token, type, name, os, os_version, arch, cpu, mac, address, last_seen) VALUES (?, 'slave', ?, ?, ?, ?, ?, ?, ?, date('now'))", use(clientToken), use(node->name), use(node->os), use(node->osVersion), use(node->arch), use(node->cpu), use(node->mac), use(node->socket.peerAddress().toString()), now; 334 | *db << "SELECT id FROM clients WHERE token=?", use(clientToken), into(id), now; 335 | } 336 | 337 | // check for blacklist 338 | if (blacklist == "y") 339 | { 340 | // remove from pool 341 | pool.unregisterClient(_socket, node_t); 342 | // don't allow connect 343 | _talk.rpc(DCODE_PRINT, "Unknown Error"); 344 | app.logger().information(format("|Client in blacklist (%s)", id)); 345 | delete this; 346 | return; 347 | } 348 | 349 | // assign id to node object 350 | node->id = id; 351 | 352 | // additional setup/sync for slaves 353 | if(_clientType == "slave") 354 | { 355 | // set all params 356 | _talk.receive(); 357 | if(_talk.data().substr(0,2) == "C:") 358 | { 359 | // set chunkSize if available 360 | unsigned int cs; 361 | if(NumberParser::tryParseUnsigned(_talk.data().substr(2), cs)) 362 | { 363 | if (cs > DEFAULT_CHUNK_SIZE) 364 | pool.setChunkSize(_socket, cs); 365 | } 366 | } 367 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_ATTACK)+":%d", job->getAttackMode())); 368 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_MODE)+":%d", job->getHashType())); 369 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_MASK)+":%s", job->getMask())); 370 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_RULES)+":%s", job->getRules())); 371 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_DICT)+":%s", job->getDictionary())); 372 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_HASHES)+":%s", job->getHashFile())); 373 | 374 | _talk.receive(); 375 | if(_talk.dcode() == DCODE_SYNC) 376 | { 377 | sync(); 378 | } 379 | sendHashes(_socket); // sync hashes with just this client 380 | } 381 | 382 | _talk.rpc(DCODE_READY); 383 | } 384 | else delete this; 385 | } 386 | } 387 | else delete this; 388 | } 389 | 390 | void onShutdown(const AutoPtr& pNf) 391 | { 392 | delete this; 393 | } 394 | 395 | void sync() 396 | { 397 | Application& app = Application::instance(); 398 | DJob *job = DJob::Instance(); 399 | 400 | if(!job->getDictionary().empty() && !_talk.send_file(job->getDictionary())) 401 | { 402 | app.logger().information(format("|Unable to transfer file: %s",job->getDictionary())); 403 | } 404 | 405 | if(!job->getRules().empty() && !_talk.send_file(job->getRules())) 406 | { 407 | app.logger().information(format("|Unable to transfer file: %s",job->getRules())); 408 | } 409 | } 410 | 411 | bool authorize() 412 | { 413 | Application& app = Application::instance(); 414 | // Make sure a helo is sent, or die 415 | if(_talk.dcode() != DCODE_HELO) 416 | { 417 | app.logger().information("|Protocol error!"); 418 | _talk.rpc(DCODE_PRINT, "Unknown error!\n"); 419 | return false; 420 | } 421 | 422 | // Make sure versions match, or die 423 | StringTokenizer t(_talk.data(),":"); //parse client introduction 424 | if(NumberParser::parse(t[2]) != APP_VERSION) 425 | { 426 | Application& app = Application::instance(); 427 | app.logger().information(format("|Invalid client version: %d", NumberParser::parse(t[2]))); 428 | _talk.receive(); // clear receive stream before sending DCODE 429 | _talk.rpc(DCODE_PRINT, "Invalid cient version!\n"); 430 | return false; 431 | } 432 | 433 | // Make sure is authorized, or die 434 | _talk.receive(); 435 | if(_talk.data() != authToken) 436 | { 437 | app.logger().information("|Invalid auth token"); 438 | _talk.rpc(DCODE_PRINT, "Invalid auth token!\n"); 439 | return false; 440 | } 441 | 442 | _authToken = _talk.data(); 443 | _clientType = t[1]; 444 | return true; 445 | } 446 | 447 | string expand_rpc(string rpc) 448 | { 449 | StringTokenizer rpct(rpc, " "); 450 | vector cmd_map; 451 | 452 | for (int i=0; i 1) return " "; 465 | if(cmd_map.size() == 1) return cmd_map[0]; 466 | return rpct[rpct.count()-1]; 467 | } 468 | 469 | string tab_complete(string needle) 470 | { 471 | bool listmode = false; 472 | vector cmd_map; 473 | string map; 474 | string prefix; 475 | 476 | // detect tab complete when no chars have been entered for param 477 | if(needle == "" || needle.substr(needle.length()-1) == " ") 478 | { 479 | listmode = true; 480 | } 481 | 482 | { // scope needle tokens and expand before offering tab complete 483 | StringTokenizer rpct(needle, " "); 484 | if(rpct.count() > 1) 485 | { 486 | for(int i=0; i 1 || (listmode && cmd_map.size() > 0)) 533 | { 534 | map = " \n"; 535 | for (int i=0; i param; 552 | string rpc; 553 | string cmd_tree = ""; 554 | 555 | // expand all params before processing 556 | for(int i=0; i< t.count(); i++) 557 | { 558 | string cmd_branch; 559 | if(cmd_tree.length()) 560 | { 561 | cmd_branch = expand_rpc(cmd_tree + " " + t[i]); 562 | } 563 | else 564 | { 565 | cmd_branch = expand_rpc(t[i]); 566 | } 567 | 568 | // expanded params will be whitespace if ambiguous 569 | if(cmd_branch == " ") 570 | { 571 | if(DEBUG) app.logger().information(format("|Ambiguous command: %s", t[i])); 572 | _talk.rpc(DCODE_PRINT, "Ambiguous command.\n"); 573 | return; 574 | } 575 | 576 | // pad tree string 577 | if(cmd_tree.length()) cmd_tree += " "; 578 | 579 | // only apply branch if a match is found 580 | if(cmd_branch.length()) 581 | { 582 | param.push_back(cmd_branch); 583 | cmd_tree += cmd_branch; 584 | } else { 585 | param.push_back(t[i]); 586 | cmd_tree += t[i]; 587 | } 588 | 589 | } 590 | 591 | // alias for rpc path 592 | rpc = param[0]; 593 | 594 | // Set pointer for DJob 595 | DJob *job = DJob::Instance(); 596 | 597 | if(rpc == "attack") 598 | { 599 | if(param.size()>1) 600 | { 601 | job->setAttackMode(NumberParser::parse(param[1].c_str())); 602 | _talk.rpc(DCODE_READY); // send to console 603 | pool.sendParam(PARAM_ATTACK, format("%d", job->getAttackMode())); // send to slaves 604 | if(DEBUG) 605 | { 606 | app.logger().information(format("%%Setting attack mode to %s", param[1])); 607 | } 608 | } else { 609 | 610 | _talk.rpc(DCODE_PRINT, format("Attack mode: %d\n", job->getAttackMode())); 611 | } 612 | } 613 | else if(rpc == "chunk") 614 | { 615 | if(param.size()>1) 616 | { 617 | //job->setChunk(NumberParser::parseUnsigned(param[1])); 618 | if(param[1] == "reset") 619 | { 620 | if(DEBUG) 621 | { 622 | app.logger().information("%Resetting chunk to 0..."); 623 | } 624 | _talk.rpc(DCODE_PRINT, "Resetting chunk to 0..."); 625 | job->setChunk(0); 626 | } 627 | } else { 628 | _talk.rpc(DCODE_PRINT, format("Chunk: %lu\n", job->showChunk())); 629 | } 630 | } 631 | else if(rpc == "clients") 632 | { 633 | if(param.size()>1) 634 | { 635 | int node_t = 0; 636 | int pi = 1; // set the param index to check 637 | 638 | if(param[1] == "blacklist" ) 639 | { 640 | if(param[2].substr(0,1) == "-") 641 | { 642 | int node_id = Poco::NumberParser::parse(param[2].substr(1)); 643 | *db << "UPDATE clients SET blacklist='n' WHERE id=?", use(node_id), now; 644 | _talk.rpc(DCODE_PRINT, format("Unblacklisted node %d", node_id)); 645 | } else { 646 | int node_id = Poco::NumberParser::parse(param[2]); 647 | pool.blacklist(node_id); 648 | *db << "UPDATE clients SET blacklist='y' WHERE id=?", use(node_id), now; 649 | _talk.rpc(DCODE_PRINT, format("Blacklisted node %d", node_id)); 650 | } 651 | return; 652 | } 653 | 654 | if(param[1] == "details" ) 655 | { 656 | pi = 2; // advance param index 657 | } 658 | if(param.size()>(pi)) 659 | { 660 | if(param[pi] == "conio" ) 661 | { 662 | node_t = NODE_CONIO; 663 | } 664 | else if (param[pi] == "slave") 665 | { 666 | node_t = NODE_SLAVE; 667 | } 668 | else 669 | { 670 | _talk.rpc(DCODE_PRINT, "Unknown option: " + param[pi]); 671 | return; 672 | } 673 | } 674 | if(node_t > 0) 675 | { 676 | _talk.rpc(DCODE_PRINT, format("Clients: %d",pool.count(node_t))); 677 | } 678 | else 679 | { 680 | _talk.rpc(DCODE_PRINT, format("Clients: %d",pool.count())); 681 | } 682 | if(param[1] == "details" ) 683 | { 684 | _talk.rpc(DCODE_PRINT, "Client details:\n"); 685 | clientDetails(node_t); 686 | } 687 | } else _talk.rpc(DCODE_PRINT, format("Clients: %d",pool.count())); 688 | } 689 | else if(rpc == "debug") 690 | { 691 | debugCmd(¶m); 692 | } 693 | else if(rpc == "dictionary") 694 | { 695 | if(param.size()>1) 696 | { 697 | job->setDictionary(param[1]); 698 | _talk.rpc(DCODE_READY); 699 | pool.sendParam(PARAM_DICT, job->getDictionary()); // send to slaves 700 | if(DEBUG) 701 | { 702 | app.logger().information(format("%%Setting dictionary to %s", param[1])); 703 | } 704 | } else { 705 | _talk.rpc(DCODE_PRINT, format("Dictionary: %s\n", job->getDictionary())); 706 | } 707 | } 708 | else if(rpc == "hashes") 709 | { 710 | if(param.size()>1) 711 | { 712 | if(job->getHashFile() != param[1]) 713 | { 714 | File f(param[1]); 715 | if(f.exists()) 716 | { 717 | _talk.rpc(DCODE_READY); 718 | if(DEBUG) 719 | { 720 | app.logger().information(format("%%Setting hash file to %s", param[1])); 721 | } 722 | job->setHashFile(param[1]); 723 | // TODO probably need to audit some of this code for tansfering files 724 | pool.sendParam(PARAM_HASHES, job->getHashFile()); // send to slaves 725 | loadHashes(); 726 | sendHashes(); 727 | } else { 728 | app.logger().information(format("|Cannot find hash file %s", param[1])); 729 | _talk.rpc(DCODE_PRINT, format("Cannot find hash file %s", param[1])); 730 | } 731 | } 732 | } else { 733 | _talk.rpc(DCODE_PRINT, format("Hash File: %s\n", job->getHashFile())); 734 | } 735 | } 736 | else if(rpc == "help") 737 | { 738 | if(param.size()>1) 739 | { 740 | moreHelp(param[1]); 741 | } else { 742 | if(DEBUG) app.logger().debug("%Sending help data..."); 743 | _talk.rpc(DCODE_PRINT, string("Supported commands (type help [command] to get more info):\n") + 744 | " attack\n" + 745 | " chunk\n" + 746 | " clients\n" + 747 | " dictionary\n" + 748 | " exit\n" + 749 | " hashes\n" + 750 | " help\n" + 751 | " mask\n" + 752 | " msg\n" + 753 | " mode\n" + 754 | " show\n" + 755 | " shutdown\n" + 756 | " start\n" + 757 | " status\n" + 758 | " stop\n"); 759 | } 760 | } 761 | else if(rpc == "mask") 762 | { 763 | if(param.size()>1) 764 | { 765 | if(param[1] == "minimum") 766 | { 767 | if(param.size() > 2) 768 | { 769 | // allow "-" to clear (zero value) 770 | int val = 0; 771 | if(param[2] != "-") 772 | { 773 | val =NumberParser::parse(param[2]); 774 | } 775 | // set value 776 | job->setMaskMin(val); 777 | _talk.rpc(DCODE_READY); 778 | } 779 | else 780 | { 781 | _talk.rpc(DCODE_PRINT, format("Mask minimum: %d\n", job->getMaskMin())); 782 | } 783 | } 784 | else if(param[1] == "maximum") 785 | { 786 | if(param.size() > 2) 787 | { 788 | // allow "-" to clear (zero value) 789 | int val = 0; 790 | if(param[2] != "-") 791 | { 792 | val =NumberParser::parse(param[2]); 793 | } 794 | // set value 795 | job->setMaskMax(val); 796 | _talk.rpc(DCODE_READY); 797 | } 798 | else 799 | { 800 | _talk.rpc(DCODE_PRINT, format("Mask maximum: %d\n", job->getMaskMax())); 801 | } 802 | } 803 | else 804 | { 805 | if(job->setMask(param[1])) 806 | { 807 | _talk.rpc(DCODE_READY); 808 | pool.sendParam(PARAM_MASK, job->getMask()); // send to slaves 809 | if(DEBUG) 810 | { 811 | app.logger().information(format("%%Setting mask to %s", param[1])); 812 | } 813 | } else { 814 | _talk.rpc(DCODE_PRINT, "Unable to set mask!"); 815 | app.logger().information(format("|Unable to set mask: %s", param[1])); 816 | } 817 | } 818 | } else { 819 | _talk.rpc(DCODE_PRINT, format("Mask: %s\n", job->getMask())); 820 | } 821 | } 822 | else if(rpc == "mode") 823 | { 824 | if(param.size()>1) 825 | { 826 | job->setHashType(atoi(param[1].c_str())); 827 | _talk.rpc(DCODE_READY); 828 | pool.sendParam(PARAM_MODE, format("%d", job->getHashType())); // send to slaves 829 | if(DEBUG) 830 | { 831 | app.logger().information(format("%%Setting hash mode to %s", param[1])); 832 | } 833 | } else { 834 | _talk.rpc(DCODE_PRINT, format("Hash mode: %d\n", job->getHashType())); 835 | } 836 | } 837 | else if(rpc == "msg") 838 | { 839 | if(param.size()>1) 840 | { 841 | // make sure to grab the leading whitespace 842 | int mbuf = _talk.data().find(param[0]); 843 | // send just the message (do not send leading whitespace, 'msg', or trailing space) 844 | pool.sendMessage(NODE_SLAVE, "*MSG* " + _talk.data().substr(param[0].length()+mbuf+1)); 845 | _talk.rpc(DCODE_PRINT, "Message sent.\n"); 846 | } else { 847 | _talk.rpc(DCODE_PRINT, "No message to send.\n"); 848 | } 849 | } 850 | else if(rpc == "rules") 851 | { 852 | if(param.size()>1) 853 | { 854 | job->setRules(param[1]); 855 | _talk.rpc(DCODE_READY); 856 | pool.sendParam(PARAM_RULES, job->getRules()); // send to slaves 857 | if(DEBUG) 858 | { 859 | app.logger().information(format("%%Setting rules to %s", param[1])); 860 | } 861 | } else { 862 | _talk.rpc(DCODE_PRINT, format("Rules: %s", job->getRules())); 863 | } 864 | } 865 | else if(rpc == "show") 866 | { 867 | if(param.size()>1) 868 | { 869 | string salt = ""; 870 | StringTokenizer p_hash(param[1], ":"); 871 | 872 | if(p_hash.count()>1) salt = p_hash[1]; 873 | if(DEBUG) app.logger().information("%Showing rainbow results..."); 874 | app.logger().information("..." + salt); 875 | _talk.rpc(DCODE_PRINT, format("Plain text: %s\n", lookupHash(p_hash[0], salt))); 876 | } else { 877 | _talk.rpc(DCODE_PRINT, "Invalid parameters to 'show'. Type 'help show' for usage.\n"); 878 | } 879 | } 880 | else if(rpc == "shutdown") 881 | { 882 | _talk.rpc(DCODE_PRINT, "Shutting down server...\n"); 883 | app.logger().information("Shutting down..."); 884 | 885 | // close any remaining SSL sockets 886 | job->closeClients(); 887 | 888 | // hard shutdown; this should probably be improved 889 | exit(0); 890 | } 891 | else if(rpc == "start") 892 | { 893 | if(job->isRunning()) 894 | { 895 | _talk.rpc(DCODE_PRINT, "A job is already running.\n"); 896 | } else if (job->getHashCount() == 0) { 897 | _talk.rpc(DCODE_PRINT, "There are no hashes to process. The job will not start.\n"); 898 | } else { 899 | _talk.rpc(DCODE_PRINT, "Job starting...\n"); 900 | app.logger().information("Job starting..."); 901 | if(!job->start()) 902 | { 903 | _talk.rpc(DCODE_PRINT, "Unable to start job.\n"); 904 | app.logger().information("|Unable to start job."); 905 | } 906 | } 907 | } 908 | else if(rpc == "status") 909 | { 910 | string msg; 911 | string mmin; 912 | string mmax; 913 | 914 | if(job->isRunning()) { 915 | msg = "A job is currently running."; 916 | } else { 917 | msg = "No jobs are running."; 918 | } 919 | 920 | // calculate mask min string 921 | if(job->getMaskMin()) 922 | { 923 | mmin = format("%d", job->getMaskMin()); 924 | } 925 | else 926 | { 927 | mmin = "none"; 928 | } 929 | 930 | // calculate mask max string 931 | if(job->getMaskMax()) 932 | { 933 | mmax = format("%d", job->getMaskMax()); 934 | } 935 | else 936 | { 937 | mmax = "none"; 938 | } 939 | 940 | // format can only take 7 args at a time. maybe we should use another method. 941 | msg = format("%s\n-- Stats --\n attack: %d\n mode: %d\n hashes: %s\n dictionary: %s\n mask: %s\n", msg, job->getAttackMode(), job->getHashType(), job->getHashFile(), job->getDictionary(), job->getMask()); 942 | msg = format ("%s mask min: %s\n mask max: %s\n remaining hashes: %u\n", msg, mmin, mmax, job->getHashCount()); 943 | 944 | // send output 945 | _talk.rpc(DCODE_PRINT, msg); 946 | 947 | } 948 | else if(rpc == "stop") 949 | { 950 | if(job->stop()) 951 | { 952 | _talk.rpc(DCODE_PRINT, "Job stopping...\n"); 953 | if(DEBUG) app.logger().information("Job stopping..."); 954 | } else { 955 | _talk.rpc(DCODE_PRINT, "No job running to stop.\n"); 956 | } 957 | } 958 | else 959 | { 960 | if(DEBUG) app.logger().information(format("|Unknown command: %s", _talk.data())); 961 | _talk.rpc(DCODE_PRINT, "Unknown command.\n"); 962 | } 963 | } 964 | 965 | void moreHelp(string cmd) 966 | { 967 | string msg; 968 | if (cmd == "attack") { 969 | msg = (string) "(attack) use this to view or manipulate hashcat attack mode settings.\n" + 970 | (string) " attack view current attack mode\n"+ 971 | (string) " attack set the attack mode to "; 972 | } else if(cmd == "chunk") { 973 | msg = (string) "(chunk) view or reset the current chunk position\n" + 974 | (string) " chunk view the current chunk offset\n" + 975 | (string) " chunk reset set chunk position to 0"; 976 | } else if(cmd == "clients") { 977 | msg = (string) "(clients) view information about disthc clients\n\n" + 978 | (string) " You can specify an optional \"node\" parameter to get more info about\n" + 979 | (string) " a node type. For example, 'clients conio' will show info for only the\n" + 980 | (string) " connected consoles\n\n" + 981 | (string) " clients view the number of connected clients\n" + 982 | (string) " clients details view client details for all connected clients\n" + 983 | (string) " clients blacklist add a client with to the blacklist (you can use\n" + 984 | (string) " the negative complement of the ro remove a client\n" + 985 | (string) " from the blacklist)\n";; 986 | } else if(cmd == "dictionary" || cmd == "dict") { 987 | msg = (string) "(dictionary) view or set the current dictionary file\n" + 988 | (string) " dictionary view the current dictionary filename\n" + 989 | (string) " dictionary set a new dictionary filename to use"; 990 | } else if (cmd == "exit") { 991 | msg = "(exit) closes the console client."; 992 | } else if(cmd == "hashes") { 993 | msg = (string) "(hashes) view or set the current hashes file\n" + 994 | (string) " hashes view the current hashes filename\n" + 995 | (string) " hashes set a new hashes filename to use"; 996 | } else if(cmd == "help") { 997 | msg = "(help) prints information on available commands.\n"; 998 | } else if(cmd == "mask") { 999 | msg = (string) "(mask) view or manipulate the hashcat mask.\n"+ 1000 | (string) " mask view the current mask\n"+ 1001 | (string) " mask set the mask to \n"+ 1002 | (string) " mask min set the pw-min flag to \n"+ 1003 | (string) " mask max set the pw-max flag to \n"+ 1004 | (string) " mask - clear the mask and do not use it in jobs"; 1005 | } else if(cmd == "msg") { 1006 | msg = (string) "(msg) send a message to all slave screens.\n"+ 1007 | (string) " msg sends to each slave to be printed on the screen"; 1008 | } else if(cmd == "mode") { 1009 | msg = (string) "(mode) use this command to view or manipulate the hash mode.\n"+ 1010 | (string) " mode view current hash mode\n"+ 1011 | (string) " mode set the hash mode to "; 1012 | } else if(cmd == "rules") { 1013 | msg = (string) "(rules) view, enable, or disable use of a rules file.\n"+ 1014 | (string) " rules view the current rules param\n"+ 1015 | (string) " rules on use rules when processing jobs\n"+ 1016 | (string) " rules - clear rules (do not use rules in jobs)"; 1017 | } else if(cmd == "show") { 1018 | msg = (string) "(show) view details on the various options.\n"+ 1019 | (string) " show [:] show the plain for (and optional ) if found"; 1020 | }else if(cmd == "shutdown" || cmd == "shut") { 1021 | msg = "(shutdown) this will shutdown the server and close all client connections made\n to the server."; 1022 | } else if(cmd == "start") { 1023 | msg = "(start) use this to start the processing of a job."; 1024 | } else if(cmd == "status") { 1025 | msg = "(status) use this to view the status of a job."; 1026 | } else if(cmd == "stop") { 1027 | msg = "(stop) use this to stop the processing of a job."; 1028 | } else { 1029 | msg = "No information is available for '" + cmd + "'"; 1030 | } 1031 | _talk.rpc(DCODE_PRINT, msg + "\n"); 1032 | } 1033 | 1034 | void debugCmd(vector *param) 1035 | { 1036 | DJob *job = DJob::Instance(); 1037 | 1038 | if(param->size() < 2) 1039 | { 1040 | _talk.rpc(DCODE_READY); 1041 | return; 1042 | } 1043 | if((*param)[1] == "womp") 1044 | { 1045 | string ts = (string) (*param)[0] + ": womp!"; 1046 | _talk.rpc(DCODE_PRINT, ts + "\n"); 1047 | } 1048 | else if((*param)[1] == "ghost") 1049 | { 1050 | if(param->size() > 2 && (*param)[2] == "on") 1051 | { 1052 | pool.sendParam(PARAM_GHOST, "on"); 1053 | _talk.rpc(DCODE_PRINT, "Ghost mode enabled."); 1054 | return; 1055 | } 1056 | else if(param->size() > 2 && (*param)[2] == "off") 1057 | { 1058 | pool.sendParam(PARAM_GHOST, "off"); 1059 | _talk.rpc(DCODE_PRINT, "Ghost mode disabled."); 1060 | return; 1061 | } 1062 | } 1063 | 1064 | // cleanup, just in-case 1065 | _talk.rpc(DCODE_READY); 1066 | return; 1067 | 1068 | } 1069 | 1070 | void clientDetails(int node_t) 1071 | { 1072 | ClientNode* cn; 1073 | string out; 1074 | 1075 | if (node_t == 0 || node_t == NODE_CONIO) 1076 | { 1077 | for(int i=0; iid, cn->name, cn->os, cn->osVersion, cn->arch, cn->socket.peerAddress().toString())); 1081 | } 1082 | } 1083 | if (node_t == 0 || node_t == NODE_SLAVE) 1084 | { 1085 | for(int i=0; iid, cn->name, cn->os, cn->osVersion, cn->arch, cn->socket.peerAddress().toString())); 1089 | } 1090 | } 1091 | _talk.rpc(DCODE_PRINT, out); 1092 | } 1093 | 1094 | }; 1095 | 1096 | // Disthc Worker class 1097 | // This will assign jobs to slaves 1098 | class DistWorker : public Poco::Runnable 1099 | { 1100 | public: 1101 | DistWorker() : _shutdown(false) { } 1102 | 1103 | virtual void run() 1104 | { 1105 | DJob *job = DJob::Instance(); 1106 | Application& app = Application::instance(); 1107 | StreamSocket *socket; 1108 | dTalk *talk; 1109 | unsigned long chunk; 1110 | 1111 | while(_shutdown == false) 1112 | { 1113 | Thread::sleep(1000); 1114 | if((bool) job->isRunning()) 1115 | { 1116 | // get next available slave 1117 | socket = pool.getSlave(); 1118 | if(socket != NULL) 1119 | { 1120 | app.logger().information(format("Sending chunk to %s...", socket->peerAddress().toString())); 1121 | // send chunk data 1122 | talk = new dTalk(*socket); 1123 | chunk = job->getChunk(pool.getChunkSize(*socket)); 1124 | talk->rpc(DCODE_SET_CHUNK, format("%lu", chunk)); 1125 | delete talk; 1126 | pool.unready(*socket); 1127 | } 1128 | if(job->getHashCount() == 0) 1129 | { 1130 | job->stop(); 1131 | app.logger().information("The job has completed."); 1132 | pool.sendMessage(NODE_CONIO, "The job has completed."); 1133 | } 1134 | } 1135 | } 1136 | } 1137 | 1138 | void shutdown() 1139 | { 1140 | _shutdown = true; 1141 | } 1142 | 1143 | private: 1144 | bool _shutdown; 1145 | }; 1146 | 1147 | // The main application class. 1148 | class DistServer : public Poco::Util::ServerApplication 1149 | { 1150 | public: 1151 | 1152 | DistServer() : _helpRequested(false), 1153 | _cfg("master.properties") 1154 | { 1155 | } 1156 | 1157 | ~DistServer() { } 1158 | 1159 | void shutdown() 1160 | { 1161 | terminate(); 1162 | } 1163 | private: 1164 | 1165 | bool _helpRequested; 1166 | string _cfg; 1167 | 1168 | protected: 1169 | 1170 | void initialize(Application& self) 1171 | { 1172 | File f(_cfg); 1173 | if(f.exists()) 1174 | { 1175 | loadConfiguration(_cfg); // load default configuration files, if present 1176 | } 1177 | ServerApplication::initialize(self); 1178 | self.logger().information("----------------------------------------"); 1179 | self.logger().information(format("Disthc Server Build [%d]", APP_VERSION)); 1180 | } 1181 | 1182 | void uninitialize() 1183 | { 1184 | ServerApplication::uninitialize(); 1185 | } 1186 | 1187 | void defineOptions(OptionSet& options) 1188 | { 1189 | ServerApplication::defineOptions(options); 1190 | 1191 | options.addOption( 1192 | Option("help", "h", "display help information on command line arguments") 1193 | .required(false) 1194 | .repeatable(false)); 1195 | 1196 | options.addOption( 1197 | Option("config", "c", "specify where the .properties config file is located") 1198 | .required(false) 1199 | .repeatable(false) 1200 | .argument("CONFIG")); 1201 | } 1202 | 1203 | void handleOption(const std::string& name, const std::string& value) 1204 | { 1205 | ServerApplication::handleOption(name, value); 1206 | 1207 | if (name == "help") 1208 | { 1209 | _helpRequested = true; 1210 | } 1211 | else if (name == "config") 1212 | { 1213 | _cfg = value; 1214 | } 1215 | } 1216 | 1217 | void displayHelp() 1218 | { 1219 | HelpFormatter helpFormatter(options()); 1220 | helpFormatter.setCommand(commandName()); 1221 | helpFormatter.setUsage("OPTIONS"); 1222 | helpFormatter.setHeader("A distributed hash-cracking server."); 1223 | helpFormatter.format(std::cout); 1224 | } 1225 | 1226 | int main(const std::vector& args) 1227 | { 1228 | if (_helpRequested) 1229 | { 1230 | displayHelp(); 1231 | return Application::EXIT_OK; 1232 | } 1233 | 1234 | // Set pointer for DJob 1235 | DJob *job = DJob::Instance(); 1236 | 1237 | // get parameters from configuration file 1238 | unsigned short port = (unsigned short) config().getInt("cfg.server.port", 4000); 1239 | authToken = (string) config().getString("cfg.server.auth.token", "*"); 1240 | showResults = config().getBool("cfg.results.show", false); 1241 | showDcode = config().getString("cfg.debug.dcode", "") == "show" ? true : false; 1242 | DEBUG = config().getBool("cfg.debug", false); 1243 | job->setAttackMode(config().getInt("cfg.job.attack", 1)); 1244 | job->setHashType(config().getInt("cfg.job.mode", 100)); 1245 | job->setDictionary(config().getString("cfg.job.dict.file", "disthc.dict")); 1246 | job->setHashFile(config().getString("cfg.job.hashes.file", "disthc.hashes")); 1247 | dbRainbowOn = config().getBool("cfg.server.db.rainbow", true); 1248 | job->setDb(config().getString("cfg.server.db.file", "disthc.db")); 1249 | // make sure db is not empty 1250 | if(job->getDb().empty()) 1251 | { 1252 | job->setDb("disthc.db"); 1253 | } 1254 | job->setRules(config().getString("cfg.job.rules.file", "")); 1255 | job->setMask(config().getString("cfg.job.mask", "")); 1256 | 1257 | Application& app = Application::instance(); 1258 | 1259 | // set-up a server socket 1260 | SecureServerSocket svs(port); 1261 | // set-up a SocketReactor 1262 | SocketReactor reactor; 1263 | // ... and a SocketAcceptor 1264 | SocketAcceptor acceptor(svs, reactor); 1265 | // run the reactor in its own thread so that we can wait for 1266 | // a termination request 1267 | Thread net_thread; 1268 | net_thread.start(reactor); 1269 | 1270 | app.logger().information(format("Listening on port %d",(int) port)); 1271 | if(DEBUG) app.logger().information("DEBUG mode enabled"); 1272 | app.logger().information("----------------------------------------"); 1273 | 1274 | // Check for dictionary file 1275 | app.logger().information("Scanning dictionary..."); 1276 | string dictionary = job->getDictionary(); 1277 | if(dictionary.empty()) 1278 | { 1279 | // dictionary string must be valid, otherwise die 1280 | app.logger().error("|Invalid dictionary file name."); 1281 | return (EXIT_BAD_DICT); 1282 | } 1283 | File f(dictionary); 1284 | if(!f.exists()) 1285 | { 1286 | app.logger().error(format("|Unable to load dictionary: %s", dictionary)); 1287 | return (EXIT_BAD_DICT); 1288 | } 1289 | 1290 | // Start db 1291 | app.logger().information("Enabling master DB..."); 1292 | // Check for SQLite db 1293 | Poco::Data::SQLite::Connector::registerConnector(); 1294 | db = new Poco::Data::Session(Poco::Data::SessionFactory::instance().create(Poco::Data::SQLite::Connector::KEY, job->getDb())); 1295 | 1296 | // Make sure tables are present 1297 | *db << "CREATE TABLE IF NOT EXISTS job_queue (job_name VARCHAR, hash VARCHAR NOT NULL, salt VARCHAR)", now; 1298 | *db << "CREATE TABLE IF NOT EXISTS rainbow (hash_type VARCHAR, hash VARCHAR NOT NULL, salt VARCHAR, plain VARCHAR NOT NULL)", now; 1299 | *db << "CREATE TABLE IF NOT EXISTS clients (id INTEGER PRIMARY KEY AUTOINCREMENT, token VARCHAR NOT NULL, type VARCHAR(8) NOT NULL, name VARCHAR(128) NOT NULL, os VARCHAR(16) NOT NULL, os_version VARCHAR(16) NOT NULL, arch VARCHAR(8) NOT NULL, cpu INT NOT NULL DEFAULT 1, mac VARCHAR(32) NOT NULL, address VARCHAR(32) NOT NULL, last_seen DATETIME NOT NULL, blacklist VARCHAR(1) DEFAULT 'n')", now; 1300 | loadHashes(); 1301 | 1302 | // Launch worker thread 1303 | if(DEBUG) 1304 | { 1305 | app.logger().information("Launching worker thread..."); 1306 | } 1307 | DistWorker dwork; 1308 | Thread worker_thread; 1309 | worker_thread.start(dwork); 1310 | 1311 | // Ready to work! 1312 | app.logger().information("Ready."); 1313 | 1314 | // if autostart is set, start now 1315 | //if(config().getBool("cfg.job.autostart", false)) { 1316 | // std::cout << "Starting job..." << std::endl; 1317 | // job->start(); 1318 | //} 1319 | 1320 | // Pause until finished 1321 | waitForTerminationRequest(); 1322 | 1323 | // Stop the SocketReactor and Worker 1324 | reactor.stop(); 1325 | dwork.shutdown(); 1326 | 1327 | // Join spawned threads 1328 | net_thread.join(); 1329 | worker_thread.join(); 1330 | 1331 | // Stop db and cleanup 1332 | db->close(); 1333 | delete db; 1334 | Poco::Data::SQLite::Connector::unregisterConnector(); 1335 | 1336 | // Exit program 1337 | app.logger().information("Bye."); 1338 | return Application::EXIT_OK; 1339 | } 1340 | }; 1341 | 1342 | int main(int argc, char** argv) 1343 | { 1344 | Poco::Crypto::OpenSSLInitializer::initialize(); // needed to use DigestEngines 1345 | DistServer app; 1346 | return app.run(argc, argv); 1347 | } 1348 | -------------------------------------------------------------------------------- /src/disthcm.h: -------------------------------------------------------------------------------- 1 | struct hash_list 2 | { 3 | std::string job_name; 4 | std::string hash; 5 | std::string salt; 6 | }; 7 | 8 | string cmap[] = { // map of commands to expand 9 | "attack", 10 | "chunk", 11 | "clients", 12 | "clients blacklist", 13 | "clients details", 14 | "clients details conio", 15 | "clients details slave", 16 | "dictionary", 17 | "exit", 18 | "hashes", 19 | "mask", 20 | "mask maximum", 21 | "mask minimum", 22 | "msg", 23 | "mode", 24 | "show", 25 | "shutdown", 26 | "start", 27 | "status", 28 | "stop" }; 29 | 30 | #ifndef DROLE_MASTER 31 | #define DROLE_MASTER 32 | #endif -------------------------------------------------------------------------------- /src/disthcs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | application: disthcs 3 | description: distributed hash-cracking slave node 4 | written by Unix-Ninja 5 | May 10, 2013 6 | */ 7 | 8 | #include 9 | //#include /* malloc, free, rand */ 10 | #include "disthc.h" 11 | #include "djob.h" 12 | #include "dtalk.h" 13 | #include "engines/hashcat.h" 14 | #include "SecureSocketConnector.h" 15 | #include "InvalidCertHandler.h" 16 | #include 17 | #include 18 | 19 | // ************************************************************************** // 20 | // Local globals 21 | // ************************************************************************** // 22 | std::string clientString; 23 | std::string authToken; 24 | std::string dataPath; 25 | bool DEBUG; 26 | bool GHOST; 27 | bool READY(false); 28 | unsigned int auto_reconnect; 29 | Engine *dengine; 30 | 31 | //Ngn_oclHashcat dengine; 32 | 33 | // ************************************************************************** // 34 | // Service handle for main app 35 | // ************************************************************************** // 36 | class DistClientHandler 37 | { 38 | private: 39 | static const int BUFFER_SIZE = 1024; 40 | 41 | StreamSocket _socket; 42 | SocketReactor& _reactor; 43 | dTalk _talk; 44 | char* _pBuffer; 45 | string _dataPath; 46 | 47 | public: 48 | DistClientHandler(StreamSocket& socket, SocketReactor& reactor): 49 | _socket(socket), 50 | _reactor(reactor), 51 | _talk(_socket), 52 | _pBuffer(new char[BUFFER_SIZE]) 53 | { 54 | Application& app = Application::instance(); 55 | DJob *job = DJob::Instance(); 56 | app.logger().information("Connected to " + socket.peerAddress().toString()); 57 | 58 | _reactor.addEventHandler( 59 | _socket, 60 | NObserver(*this, &DistClientHandler::onReadable) 62 | ); 63 | 64 | _reactor.addEventHandler( 65 | _socket, 66 | NObserver(*this, &DistClientHandler::onShutdown) 68 | ); 69 | // greet server 70 | _talk.rpc(DCODE_HELO, format("-dhc:slave:%d", APP_VERSION)); 71 | // authorize to server 72 | _talk.rpc(DCODE_TOKEN, authToken); 73 | // identify to server 74 | _talk.rpc(DCODE_SET_PARAM, clientString); 75 | // report chunk capability 76 | _talk.rpc(DCODE_SET_PARAM, format(string(PARAM_CHUNK_SIZE)+":%u", job->getChunkSize())); 77 | // sync with server 78 | if(dengine->remoteSync()) 79 | { 80 | _talk.rpc(DCODE_SYNC); 81 | app.logger().information("Syncing..."); 82 | sync(); 83 | } 84 | else 85 | { 86 | READY = true; 87 | _talk.rpc(DCODE_READY); 88 | app.logger().information("Ready."); 89 | } 90 | } 91 | 92 | ~DistClientHandler() 93 | { 94 | Application& app = Application::instance(); 95 | try 96 | { 97 | _reactor.stop(); 98 | app.logger().information("Disconnected from " + _socket.peerAddress().toString()); 99 | // SIGINT the current process 100 | Poco::Process::requestTermination(Poco::Process::id()); 101 | } 102 | catch (...) 103 | { 104 | } 105 | 106 | _reactor.removeEventHandler( 107 | _socket, 108 | NObserver(*this, &DistClientHandler::onReadable) 110 | ); 111 | 112 | _reactor.removeEventHandler( 113 | _socket, 114 | NObserver(*this, &DistClientHandler::onShutdown) 116 | ); 117 | delete [] _pBuffer; 118 | } 119 | 120 | void onReadable(const AutoPtr& pNf) 121 | { 122 | Application& app = Application::instance(); 123 | DJob *job = DJob::Instance(); 124 | 125 | //receive data or close on end 126 | if(!_talk.receive()) delete this; 127 | 128 | //app.logger().information(format("[%d|%s]", _talk.dcode(), (string) _talk.data())); //debug line 129 | 130 | if(_talk.dcode() == DCODE_READY) 131 | { 132 | READY = true; 133 | _talk.rpc(DCODE_READY); 134 | app.logger().information("Ready."); 135 | } 136 | else if(_talk.dcode() == DCODE_SET_CHUNK) 137 | { 138 | if (DEBUG) app.logger().information("%Received chunk from master."); 139 | job->setChunk(NumberParser::parseUnsigned(_talk.data())); 140 | // process chunk! 141 | if(dengine->isRunnable()) 142 | { 143 | dengine->run(); 144 | 145 | if (DEBUG) app.logger().information("%Sending results to master."); 146 | 147 | // send results back to master 148 | _talk.rpc(DCODE_RESULTS, dengine->results()); 149 | } else { 150 | _talk.rpc(DCODE_RESULTS, ""); 151 | } 152 | } 153 | else if(_talk.dcode() == DCODE_SET_PARAM) 154 | { 155 | StringTokenizer t(_talk.data(),":"); 156 | const char* key = t[0].c_str(); 157 | 158 | if (match(key, PARAM_ATTACK)) 159 | { 160 | int value; 161 | if(t.count()>1) 162 | { 163 | value = atoi(t[1].c_str()); 164 | } 165 | else 166 | { 167 | value = 0; 168 | } 169 | job->setAttackMode(value); 170 | if(DEBUG) app.logger().information(format("%%Set param: attack[%d]", value)); 171 | } 172 | else if (match(key, PARAM_GHOST)) 173 | { 174 | string value = string(t[1].c_str()); 175 | if(value == "on") 176 | { 177 | dengine->setGhost(true); 178 | app.logger().information("* Ghost mode enabled."); 179 | } 180 | else 181 | { 182 | dengine->setGhost(false); 183 | app.logger().information("* Ghost mode disabled."); 184 | } 185 | } 186 | else if (match(key, PARAM_MODE)) 187 | { 188 | int value; 189 | if(t.count()>1) 190 | { 191 | value = atoi(t[1].c_str()); 192 | } 193 | else 194 | { 195 | value = 0; 196 | } 197 | job->setHashType(value); 198 | if(DEBUG) app.logger().information(format("%%Set param: mode[%d]", value)); 199 | } 200 | else if (match(key, PARAM_MASK)) 201 | { 202 | string value; 203 | if(t.count()>1) 204 | { 205 | value = string(t[1].c_str()); 206 | } 207 | else 208 | { 209 | value = ""; 210 | } 211 | job->setMask(value); 212 | if(DEBUG) app.logger().information(format("%%Set param: mask[%s]", value)); 213 | } 214 | else if (match(key, PARAM_RULES)) 215 | { 216 | string value; 217 | if(t.count()>1) 218 | { 219 | value = string(t[1].c_str()); 220 | } 221 | else 222 | { 223 | value = ""; 224 | } 225 | job->setRules(value); 226 | if(DEBUG) app.logger().information(format("%%Set param: rules[%s]", value)); 227 | } 228 | else if (match(key, PARAM_HASHES)) 229 | { 230 | string value; 231 | if(t.count()>1) 232 | { 233 | value = string(t[1].c_str()); 234 | } 235 | else 236 | { 237 | value = ""; 238 | } 239 | // set job and engine hash values 240 | job->setHashFile(value); 241 | dengine->setHashFile(value); 242 | if(DEBUG) app.logger().information(format("%%Set param: hash file[%s]", value)); 243 | } 244 | else if (match(key, PARAM_DICT)) 245 | { 246 | string value; 247 | if(t.count()>1) 248 | { 249 | value = string(t[1].c_str()); 250 | } 251 | else 252 | { 253 | value = ""; 254 | } 255 | job->setDictionary(value); 256 | if(DEBUG) app.logger().information(format("%%Set param: dictionary[%s]", value)); 257 | } 258 | else 259 | { 260 | app.logger().information("|Invalid param to set!"); 261 | if(DEBUG) app.logger().information(format("++[%s]", (string) key)); 262 | } 263 | } 264 | else if (_talk.dcode() == DCODE_ZAP) 265 | { 266 | if(DEBUG) app.logger().information(format("Zapping: %s", _talk.data())); 267 | if(dengine->getName() == "hashcat" || dengine->getName() == "oclhashcat") 268 | { 269 | dengine->zapHashes(trim(_talk.data())); 270 | } 271 | } 272 | else if(_talk.dcode() == DCODE_FILE_NAME) 273 | { 274 | //app.logger().information("|ugh syncing..."); 275 | syncFile(); 276 | } 277 | else if(_talk.dcode() == DCODE_PRINT) 278 | { 279 | app.logger().information(_talk.data()); 280 | } 281 | } 282 | 283 | void onShutdown(const AutoPtr& pNf) 284 | { 285 | delete this; 286 | } 287 | 288 | void syncFile() 289 | { 290 | Application& app = Application::instance(); 291 | 292 | //if(!_talk.save_file(dataPath,_talk.data())) 293 | if(!_talk.save_file("./",_talk.data())) 294 | { 295 | app.logger().information(format("|Unable to save file: %s", _talk.data())); 296 | } 297 | } 298 | 299 | }; 300 | 301 | class mySocketConnector : public dNet::SocketConnector 302 | { 303 | private: 304 | bool _failed; 305 | bool _shutdown; 306 | 307 | public: 308 | 309 | mySocketConnector(SocketAddress& address, SocketReactor& reactor) : 310 | SocketConnector(address, reactor), 311 | _failed(false), 312 | _shutdown(false) 313 | { 314 | reactor.addEventHandler(socket(), Observer (*this, &mySocketConnector::onShutdown)); 315 | } 316 | 317 | void onShutdown(ShutdownNotification* pNf) 318 | { 319 | pNf->release(); 320 | _shutdown = true; 321 | } 322 | 323 | void onError(int error) 324 | { 325 | Application& app = Application::instance(); 326 | app.logger().information("Error: Unable to connect to disthc server!"); 327 | _failed = true; 328 | reactor()->stop(); 329 | Poco::Process::requestTermination(Poco::Process::id()); 330 | } 331 | 332 | bool failed() const 333 | { 334 | return _failed; 335 | } 336 | 337 | bool shutdown() const 338 | { 339 | return _shutdown; 340 | } 341 | 342 | }; 343 | 344 | // The main application class. 345 | class DistClient : public Poco::Util::ServerApplication 346 | { 347 | public: 348 | 349 | DistClient() : _helpRequested(false), 350 | _syncRequested(-1), 351 | _cfg ("slave.properties") 352 | { 353 | } 354 | 355 | ~DistClient() 356 | { 357 | } 358 | 359 | void shutdown() 360 | { 361 | terminate(); 362 | } 363 | private: 364 | 365 | bool _helpRequested; 366 | int _syncRequested; 367 | string _cfg; 368 | 369 | protected: 370 | 371 | void initialize(Application& self) 372 | { 373 | File f(_cfg); 374 | if(f.exists()) 375 | { 376 | loadConfiguration(_cfg); // load default configuration files, if present 377 | } 378 | ServerApplication::initialize(self); 379 | self.logger().information("----------------------------------------"); 380 | self.logger().information(format("Disthc Slave Build [%d]", APP_VERSION)); 381 | } 382 | 383 | void uninitialize() 384 | { 385 | ServerApplication::uninitialize(); 386 | } 387 | 388 | void defineOptions(OptionSet& options) 389 | { 390 | ServerApplication::defineOptions(options); 391 | options.addOption( 392 | Option("help", "h", "display help information on command line arguments") 393 | .required(false) 394 | .repeatable(false) 395 | ); 396 | options.addOption( 397 | Option("sync", "S", "automatuically sync data files from master (true|false)") 398 | .required(false) 399 | .repeatable(false) 400 | .argument("true|false", true) 401 | ); 402 | 403 | options.addOption( 404 | Option("config", "c", "specify where the .properties config file is located") 405 | .required(false) 406 | .repeatable(false) 407 | .argument("CONFIG")); 408 | } 409 | 410 | void handleOption(const std::string& name, const std::string& value) 411 | { 412 | ServerApplication::handleOption(name, value); 413 | 414 | if (name == "help") 415 | { 416 | _helpRequested = true; 417 | } 418 | else if (name == "sync") 419 | { 420 | if (Poco::toLower(value) == "true") 421 | { 422 | _syncRequested = SYNC_AUTO; 423 | } 424 | else 425 | { 426 | _syncRequested = NO_SYNC_AUTO; 427 | } 428 | } 429 | else if (name == "config") 430 | { 431 | _cfg = value; 432 | } 433 | } 434 | 435 | void displayHelp() 436 | { 437 | HelpFormatter helpFormatter(options()); 438 | helpFormatter.setCommand(commandName()); 439 | helpFormatter.setUsage("OPTIONS"); 440 | helpFormatter.setHeader("A distributed hash-cracking slave."); 441 | helpFormatter.format(std::cout); 442 | } 443 | 444 | int main(const std::vector& args) 445 | { 446 | if (_helpRequested) 447 | { 448 | displayHelp(); 449 | return Application::EXIT_OK; 450 | } 451 | 452 | Application& app = Application::instance(); 453 | DJob *job = DJob::Instance(); 454 | 455 | // get parameters from configuration file 456 | auto_reconnect = (unsigned int) config().getInt("cfg.reconnect.interval", 0); 457 | string host = (string) config().getString("cfg.server.address","localhost"); 458 | unsigned short port = (unsigned short) config().getInt("cfg.server.port", 4000); 459 | authToken = (string) config().getString("cfg.server.auth.token", "*"); 460 | dataPath = (string) config().getString("cfg.data.path", "."); 461 | DEBUG = config().getBool("cfg.debug", false); 462 | GHOST = config().getBool("cfg.ghost.allow", false); 463 | job->setPot((string) config().getString("cfg.pot.path","disthc.pot")); 464 | job->setChunkSize(config().getInt("cfg.chunk.size", DEFAULT_CHUNK_SIZE)); 465 | string engine_name = (string) config().getString("cfg.engine","oclhashcat"); 466 | 467 | // change to working directory 468 | chdir(dataPath.c_str()); 469 | 470 | // setup correct engine object 471 | if(engine_name == "hashcat") { 472 | dengine = new Ngn_Hashcat(); 473 | } 474 | else if (engine_name == "oclhashcat") 475 | { 476 | dengine = new Ngn_oclHashcat(); 477 | 478 | // set gpu-temp option 479 | if((string) config().getString("cfg.hashcat.gputemp","true") == "true") 480 | { 481 | dengine->setConfig("gpuTempDisable", "false"); 482 | } 483 | else 484 | { 485 | dengine->setConfig("gpuTempDisable", "true"); 486 | } 487 | } 488 | else 489 | { 490 | app.logger().information("Invalid option for cfg.engine!"); 491 | return EXIT_BAD_ENGINE; 492 | } 493 | 494 | 495 | // set engine configs 496 | dengine->setBinaryPath("hashcat", (string) config().getString("cfg.hashcat.path","")); 497 | dengine->setBinaryPath("oclhashcat", (string) config().getString("cfg.oclhashcat.path","")); 498 | dengine->setName(engine_name); 499 | dengine->setHashFile((string) config().getString("cfg.hashes.path","disthc.hashes")); 500 | 501 | // verify sync requests 502 | if(_syncRequested != -1) 503 | { 504 | if(_syncRequested == SYNC_AUTO) 505 | { 506 | dengine->remoteSync(true); 507 | } 508 | else 509 | { 510 | dengine->remoteSync(false); 511 | } 512 | } 513 | else 514 | { 515 | dengine->remoteSync((bool) config().getBool("cfg.server.sync","true")); 516 | } 517 | 518 | // set-up a stream socket 519 | SocketAddress sa(host, port); 520 | // set-up a SocketReactor 521 | SocketReactor reactor; 522 | 523 | // Let's setup rules for SSL (I don't want to be prompted about certs) 524 | Poco::SharedPtr pConsoleHandler = new Poco::Net::KeyConsoleHandler(false); 525 | Poco::SharedPtr pInvalidCertHandler = new myCertificateHandler; 526 | Poco::Net::Context::Ptr pContext = new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", Poco::Net::Context::VERIFY_RELAXED, 9, false, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); 527 | Poco::Net::SSLManager::instance().initializeClient(pConsoleHandler, pInvalidCertHandler, pContext); 528 | 529 | // Connect to the server 530 | mySocketConnector connector(sa, reactor); 531 | 532 | if(DEBUG) app.logger().information("DEBUG mode enabled"); 533 | 534 | if(dengine->remoteSync()) 535 | { 536 | app.logger().information("SYNC from remote enabled"); 537 | } 538 | 539 | app.logger().information("----------------------------------------"); 540 | 541 | // Check for ghosts here 542 | if(dengine->isGhost()) 543 | { 544 | if(DEBUG) app.logger().information("%Ghost detected!"); 545 | if(!GHOST) 546 | { 547 | app.logger().information("Ghosts are not allowed. Exiting."); 548 | return EXIT_BAD_HASHCAT; 549 | } 550 | } 551 | 552 | // run the reactor in its own thread so that we can wait for a termination request 553 | Thread thread; 554 | thread.start(reactor); 555 | 556 | waitForTerminationRequest(); 557 | // Stop the SocketReactor 558 | reactor.stop(); 559 | thread.join(); 560 | 561 | return Application::EXIT_OK; 562 | } 563 | }; 564 | 565 | int run_app(int argc, char** argv) 566 | { 567 | DistClient app; 568 | return app.run(argc, argv); 569 | } 570 | 571 | int main(int argc, char** argv) 572 | { 573 | int ec; 574 | string cwd = Poco::Path::current(); 575 | 576 | // create client string 577 | clientString = format("%s %s %s %u %s %s", 578 | Poco::Environment::nodeName(), 579 | Poco::Environment::osName(), 580 | Poco::Environment::osVersion(), 581 | Poco::Environment::processorCount(), 582 | Poco::Environment::nodeId(), 583 | Poco::Environment::osArchitecture() 584 | ); 585 | 586 | // run slave 587 | ec =run_app(argc, argv); 588 | if(ec != Application::EXIT_OK) 589 | { 590 | return ec; 591 | } 592 | 593 | // auto-reconnect code 594 | if(auto_reconnect>0) 595 | while(1) 596 | { 597 | chdir(cwd.c_str()); // reset cwd before looping so we can find configs 598 | Thread::sleep(auto_reconnect * 1000); // wait before trying to reconnect 599 | std::cout << std::endl; 600 | ec = run_app(argc, argv); 601 | if(ec != Application::EXIT_OK) 602 | { 603 | return ec; 604 | } 605 | } 606 | return 0; 607 | } 608 | -------------------------------------------------------------------------------- /src/djob.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "disthc.h" 4 | #include "djob.h" 5 | #include "dtalk.h" 6 | 7 | DJob::DJob() 8 | { 9 | _running = false; 10 | _chunk = 0; 11 | _ceiling = 0; 12 | _maskMin = 0; 13 | _maskMax = 0; 14 | } 15 | 16 | DJob* DJob::Instance() 17 | { 18 | if (!_pInstance) // Only allow one instance of class to be generated. 19 | _pInstance = new DJob; 20 | return _pInstance; 21 | } 22 | 23 | bool DJob::start() 24 | { 25 | _running = true; 26 | return true; 27 | 28 | // more checks needed? 29 | if(pool.slavesAvailable()) 30 | { 31 | 32 | return true; 33 | } 34 | return false; 35 | } 36 | 37 | bool DJob::stop() 38 | { 39 | if(isRunning()) 40 | { 41 | _running = false; 42 | return true; 43 | } 44 | return false; 45 | } 46 | 47 | bool DJob::isRunning() 48 | { 49 | return (bool) _running; 50 | } 51 | 52 | void DJob::msgConsoles(string msg) 53 | { 54 | pool.sendMessage(NODE_CONIO, msg); 55 | return; 56 | } 57 | 58 | void DJob::msgSlave(StreamSocket socket, string msg) 59 | { 60 | //dTalk talk(socket); 61 | //talk.rpc(DCODE_PRINT, msg); 62 | return; 63 | } 64 | 65 | bool DJob::slavesAvailable() 66 | { 67 | return pool.slavesAvailable() ? true : false; 68 | } 69 | 70 | unsigned long DJob::showChunk() 71 | { 72 | return _chunk; 73 | } 74 | 75 | unsigned long DJob::getChunk() 76 | { 77 | unsigned long chunk = _chunk; 78 | _chunk += _chunkSize; 79 | return chunk; 80 | } 81 | 82 | unsigned long DJob::getChunk(unsigned int chunkSize) 83 | { 84 | unsigned long chunk = _chunk; 85 | _chunk += chunkSize; 86 | return chunk; 87 | } 88 | 89 | void DJob::setChunk(unsigned int chunk) 90 | { 91 | _chunk = chunk; 92 | } 93 | 94 | unsigned int DJob::getChunkSize() 95 | { 96 | return _chunkSize; 97 | } 98 | 99 | void DJob::setChunkSize(unsigned int chunkSize) 100 | { 101 | _chunkSize = chunkSize; 102 | } 103 | 104 | int DJob::getAttackMode() 105 | { 106 | return (int) _attack; 107 | } 108 | 109 | void DJob::setAttackMode(int mode) 110 | { 111 | _attack = mode; 112 | } 113 | 114 | int DJob::getHashType() 115 | { 116 | return (int) _mode; 117 | } 118 | 119 | void DJob::setHashType(int mode) 120 | { 121 | _mode = mode; 122 | } 123 | 124 | unsigned int DJob::getHashCount() 125 | { 126 | return _count; 127 | } 128 | 129 | void DJob::setHashCount(unsigned int count) 130 | { 131 | _count = count; 132 | } 133 | 134 | std::string DJob::getHashFile() 135 | { 136 | return (std::string) _hashes; 137 | } 138 | 139 | void DJob::setHashFile(std::string file) 140 | { 141 | _hashes = file; 142 | } 143 | 144 | std::string DJob::getDictionary() 145 | { 146 | return (std::string) _dict; 147 | } 148 | 149 | void DJob::setDictionary(std::string dictionary) 150 | { 151 | _dict = dictionary; 152 | } 153 | 154 | std::string DJob::getDb() 155 | { 156 | return (std::string) _db; 157 | } 158 | 159 | void DJob::setDb(std::string db) 160 | { 161 | _db = db; 162 | } 163 | 164 | std::string DJob::getRules() 165 | { 166 | return (std::string) _rules; 167 | } 168 | 169 | void DJob::setRules(std::string rules) 170 | { 171 | _rules = rules; 172 | } 173 | 174 | std::string DJob::getMask() 175 | { 176 | return (std::string) _mask; 177 | } 178 | 179 | bool DJob::setMask(std::string mask) 180 | { 181 | if(_attack == 3) 182 | { 183 | // reset ceiling 184 | _ceiling = 1; 185 | 186 | // calculate ceiling 187 | for (int i=0; i< mask.length(); i+=2) 188 | { 189 | if(mask[i] != '?') 190 | { 191 | _ceiling = 0; 192 | return false; 193 | } 194 | switch(mask[i+1]) 195 | { 196 | case 'l': 197 | _ceiling *= 26; 198 | break; 199 | case 'u': 200 | _ceiling *= 26; 201 | break; 202 | case 'd': 203 | _ceiling *= 10; 204 | break; 205 | case 's': 206 | _ceiling *= 32; 207 | break; 208 | case 'a': 209 | _ceiling *= (26 + 26 + 10 + 32); 210 | break; 211 | default: 212 | _ceiling = 0; 213 | return false; 214 | } 215 | } 216 | } 217 | _mask = mask; 218 | return true; 219 | } 220 | 221 | int DJob::getMaskMin() 222 | { 223 | return _maskMin; 224 | } 225 | 226 | bool DJob::setMaskMin(int min) 227 | { 228 | if(min < 0) min = 0; 229 | _maskMin = min; 230 | return true; 231 | } 232 | 233 | int DJob::getMaskMax() 234 | { 235 | return _maskMax; 236 | } 237 | 238 | bool DJob::setMaskMax(int max) 239 | { 240 | if(max < 0) max = 0; 241 | _maskMax = max; 242 | return true; 243 | } 244 | 245 | std::string DJob::getPot() 246 | { 247 | return (std::string) _pot; 248 | } 249 | 250 | void DJob::setPot(std::string pot) 251 | { 252 | _pot = pot; 253 | } 254 | 255 | bool DJob::closeClients() 256 | { 257 | pool.closeClients(); 258 | return true; 259 | } 260 | 261 | DJob* DJob::_pInstance = NULL; // this MUST be set for Instance() to recognize the reference 262 | 263 | 264 | // ************************************************************************** // 265 | // Client Pool 266 | // ************************************************************************** // 267 | bool ClientPool::registerClient(StreamSocket socket, int node) 268 | { 269 | ClientNode cn; 270 | cn.socket = socket; 271 | cn.type = node; 272 | 273 | if(node == NODE_SLAVE) 274 | { 275 | _slaves.push_back(cn); 276 | _chunkMap.push_back(DEFAULT_CHUNK_SIZE); 277 | } else 278 | //_conio.push_back(socket); 279 | _conio.push_back(cn); 280 | return true; 281 | } 282 | 283 | int ClientPool::registerClient(StreamSocket socket, int node, string clientString, string clientToken) 284 | { 285 | // create client node object 286 | ClientNode cn; 287 | cn.socket = socket; 288 | cn.type = node; 289 | 290 | StringTokenizer cs(clientString," "); //parse client string 291 | // add client information to node 292 | cn.name = cs[0]; 293 | cn.os = cs[1]; 294 | cn.osVersion = cs[2]; 295 | cn.cpu = NumberParser::parseUnsigned(cs[3]); 296 | cn.mac = cs[4]; 297 | cn.arch = cs[5]; 298 | cn.token = clientToken; 299 | cn.id = 0; 300 | 301 | // add to queue 302 | if(node == NODE_SLAVE) 303 | { 304 | _slaves.push_back(cn); 305 | _chunkMap.push_back(DEFAULT_CHUNK_SIZE); 306 | return _slaves.size()-1; 307 | } else { 308 | //_conio.push_back(socket); 309 | _conio.push_back(cn); 310 | return _conio.size()-1; 311 | } 312 | } 313 | 314 | bool ClientPool::unregisterClient(StreamSocket socket, int node) 315 | { 316 | deque *q; 317 | if(node == NODE_SLAVE) 318 | q = &_slaves; 319 | else 320 | q = &_conio; 321 | 322 | for(int i=0; isize();i++) 323 | { 324 | if((*q)[i].socket == socket) { 325 | //(*q).erase((*q).begin()+i, (*q).begin()+i+1); 326 | (*q).erase((*q).begin()+i); 327 | if(node == NODE_SLAVE) 328 | { 329 | _chunkMap.erase(_chunkMap.begin()+i); 330 | } 331 | break; 332 | } 333 | } 334 | unready(socket); 335 | 336 | return true; 337 | } 338 | 339 | int ClientPool::count(int node) 340 | { 341 | if(node == NODE_SLAVE) 342 | return _slaves.size(); 343 | else 344 | return _conio.size(); 345 | } 346 | 347 | int ClientPool::count() 348 | { 349 | return _slaves.size() + _conio.size(); 350 | } 351 | 352 | bool ClientPool::slavesAvailable() 353 | { 354 | return _slaves.size() ? true : false; 355 | } 356 | 357 | void ClientPool::sendMessage(int node, string msg) 358 | { 359 | dTalk *talk; 360 | deque *q; 361 | if(node == NODE_SLAVE) 362 | q = &_slaves; 363 | else 364 | q = &_conio; 365 | 366 | for(int i=0; isize();i++) 367 | { 368 | talk = new dTalk((*q)[i].socket); 369 | talk->rpc(DCODE_PRINT, msg); 370 | delete talk; 371 | } 372 | } 373 | 374 | bool ClientPool::ready(StreamSocket socket) 375 | { 376 | // make sure socket isn't already ready 377 | for(int i=0; i<_ready.size(); i++) { 378 | if(_ready[i] == socket) return true; 379 | } 380 | // note the ready socket 381 | _ready.push_back(socket); 382 | return true; 383 | } 384 | 385 | bool ClientPool::unready(StreamSocket socket) 386 | { 387 | for(int i=0; i<_ready.size(); i++) 388 | { 389 | if(_ready[i] == socket) 390 | { 391 | _ready.erase(_ready.begin()+i); 392 | return true; 393 | } 394 | } 395 | return false; 396 | } 397 | 398 | ClientNode* ClientPool::get(unsigned int index, int node) 399 | { 400 | deque *q; 401 | if(node == NODE_SLAVE) 402 | q = &_slaves; 403 | else 404 | q = &_conio; 405 | 406 | return &((*q)[index]); 407 | } 408 | StreamSocket* ClientPool::getSlave() 409 | { 410 | if(DEBUG) { 411 | Application& app = Application::instance(); 412 | app.logger().information(format("%%Slaves ready: %d", (int) _ready.size())); 413 | } 414 | if (_ready.size()>0) 415 | { 416 | StreamSocket *s; 417 | s = &(_ready.front()); 418 | //_ready.pop_front(); 419 | return s; 420 | } 421 | return NULL; 422 | } 423 | 424 | void ClientPool::sendParam(std::string key, std::string value) 425 | { 426 | dTalk *talk; 427 | deque *q; 428 | q = &_slaves; 429 | 430 | for(int i=0; isize();i++) 431 | { 432 | talk = new dTalk((*q)[i].socket); 433 | talk->rpc(DCODE_SET_PARAM, format("%s:%s", key, value)); 434 | delete talk; 435 | } 436 | } 437 | 438 | void ClientPool::sendFile(std::string filename, std::string *content) 439 | { 440 | dTalk *talk; 441 | deque *q; 442 | q = &_slaves; 443 | 444 | for(int i=0; isize();i++) 445 | { 446 | talk = new dTalk((*q)[i].socket); 447 | talk->send_text_as_file(filename, *content); 448 | delete talk; 449 | } 450 | } 451 | 452 | void ClientPool::zap(std::string hashes) 453 | { 454 | if(hashes.empty()) return; 455 | if(DEBUG) { 456 | Application& app = Application::instance(); 457 | app.logger().information("%Zap!"); 458 | } 459 | dTalk *talk; 460 | deque *q; 461 | q = &_slaves; 462 | 463 | for(int i=0; isize();i++) 464 | { 465 | talk = new dTalk((*q)[i].socket); 466 | talk->rpc(DCODE_ZAP, hashes); 467 | delete talk; 468 | } 469 | } 470 | 471 | void ClientPool::setChunkSize(StreamSocket socket, unsigned int chunkSize) 472 | { 473 | deque *q; 474 | q = &_slaves; 475 | for(int i=0; isize();i++) 476 | { 477 | if((*q)[i].socket == socket) { 478 | _chunkMap[i] = chunkSize; 479 | break; 480 | } 481 | } 482 | } 483 | 484 | unsigned int ClientPool::getChunkSize(StreamSocket socket) 485 | { 486 | deque *q; 487 | q = &_slaves; 488 | for(int i=0; isize();i++) 489 | { 490 | if((*q)[i].socket == socket) { 491 | return _chunkMap[i]; 492 | break; 493 | } 494 | } 495 | return 0; // chunk size should NEVER be zero 496 | } 497 | 498 | void ClientPool::blacklist(int node_id) 499 | { 500 | 501 | deque *q; 502 | for(int loop=1; loop<=2; loop++) 503 | { 504 | if(loop == 1) q = &_slaves; 505 | if(loop == 2) q = &_conio; 506 | for(int i=0; isize();i++) 507 | { 508 | if((*q)[i].id == node_id) { 509 | // disconnect node 510 | (*q)[i].socket.close(); 511 | } 512 | } 513 | } 514 | } 515 | 516 | void ClientPool::closeClients() 517 | { 518 | 519 | deque *q; 520 | for(int loop=1; loop<=2; loop++) 521 | { 522 | if(loop == 1) q = &_slaves; 523 | if(loop == 2) q = &_conio; 524 | for(int i=0; isize();i++) 525 | { 526 | (*q)[i].socket.close(); 527 | } 528 | } 529 | } 530 | 531 | ClientPool pool; -------------------------------------------------------------------------------- /src/djob.h: -------------------------------------------------------------------------------- 1 | #ifndef DJOB_H 2 | #define DJOB_H 3 | 4 | #include 5 | 6 | using std::string; 7 | 8 | class DJob 9 | { 10 | public: 11 | static DJob* Instance(); 12 | unsigned int getChunkSize(); 13 | void setChunkSize(unsigned int); 14 | unsigned long showChunk(); // get chunk, but do not advance position 15 | unsigned long getChunk(); // get chunk and advance position 16 | unsigned long getChunk(unsigned int); // get chunk and advance position 17 | void setChunk(unsigned int); // set offset for chunk (on slaves) 18 | int getAttackMode(); 19 | void setAttackMode(int); 20 | int getHashType(); 21 | void setHashType(int); 22 | string getMask(); 23 | bool setMask(string); 24 | int getMaskMin(); 25 | bool setMaskMin(int); 26 | int getMaskMax(); 27 | bool setMaskMax(int); 28 | string getRules(); 29 | void setRules(string); 30 | string getDictionary(); 31 | void setDictionary(string); 32 | string getDb(); 33 | void setDb(string); 34 | string getPot(); 35 | void setPot(string); 36 | unsigned int getHashCount(); 37 | void setHashCount(unsigned int); 38 | string getHashFile(); 39 | void setHashFile(string); 40 | string getBinaryPath(); 41 | void setBinaryPath(string); 42 | bool slavesAvailable(); 43 | bool isRunning(); 44 | void zapHashes(string); 45 | void msgConsoles(string); 46 | void msgSlave(StreamSocket, string); 47 | bool start(); 48 | bool stop(); 49 | bool ready(StreamSocket); // set a slave in the ready queue 50 | bool unready(StreamSocket); // remove a slave from the ready queue 51 | bool closeClients(); 52 | 53 | protected: 54 | DJob(); 55 | ~DJob(); 56 | static DJob* _pInstance; 57 | int _attack; 58 | int _mode; 59 | unsigned int _chunkSize; 60 | unsigned long _chunk; 61 | string _dict; // dictionary file 62 | string _hashes; // hashlist to be used for attack 63 | string _rules; // rules file 64 | string _mask; // mask to use 65 | int _maskMin; // pw-min flag value 66 | int _maskMax; // pw-max flag value 67 | string _pot; // pot file 68 | string _db; // disthc master db file 69 | unsigned long long _ceiling; 70 | unsigned int _count; // number of hashes 71 | 72 | // Master-specific properties 73 | bool _running; 74 | deque _ready; // queue for slaves ready to process 75 | }; 76 | 77 | // Client node object for use in the client pool 78 | class ClientNode 79 | { 80 | public: 81 | StreamSocket socket; 82 | int type; // client node type (NODE_SLAVE|NODE_CONIO) 83 | unsigned int id; // id from database to reference node 84 | string os; 85 | string osVersion; 86 | string arch; 87 | unsigned int cpu; 88 | string mac; 89 | string name; 90 | string token; 91 | //unsigned int chunk; // TODO replace chunkMap with this param 92 | }; 93 | 94 | 95 | // Client nodes will be registered in the Client Pool. 96 | // Communications and stats to nodes will be tunneled through 97 | // this object 98 | class ClientPool 99 | { 100 | private: 101 | //deque _conio; // registered console clients 102 | deque _conio; // registered console clients 103 | deque _slaves; // registered slave clients 104 | deque _ready; // slaves available for processing 105 | deque _chunkMap; 106 | 107 | public: 108 | bool registerClient(StreamSocket, int node_type); 109 | int registerClient(StreamSocket, int node_type, string clientString, string clientToken); 110 | bool unregisterClient(StreamSocket, int node_type); 111 | int count(); 112 | int count(int type); 113 | ClientNode* get(unsigned int index, int node_type); 114 | bool slavesAvailable(); 115 | void sendMessage(int node_type, std::string msg); 116 | void sendParam(string, string); 117 | void sendFile(string, string*); 118 | void sendFile(string); 119 | bool ready(StreamSocket); // set a slave in the ready queue 120 | bool unready(StreamSocket); // remove a slave from the ready queue 121 | StreamSocket* getSlave(); // get first available slave in the ready queue 122 | void zap(string); 123 | void setChunkSize(StreamSocket, unsigned int); // sets the chunk size for a slave node 124 | unsigned int getChunkSize(StreamSocket); // get the chunk size for a slave node 125 | void blacklist(int node_id); // blacklist a node by node id (found in client details) 126 | void closeClients(); // safely close SSL sockets 127 | }; 128 | 129 | extern ClientPool pool; 130 | 131 | #endif // DJOB_H -------------------------------------------------------------------------------- /src/dtalk.cpp: -------------------------------------------------------------------------------- 1 | // dTalk - a simple network protocol for disthc 2 | #include "disthc.h" 3 | #include "dtalk.h" 4 | #include 5 | #include 6 | 7 | dTalk::dTalk(StreamSocket& socket) : 8 | _socket(socket), 9 | _pBuffer(new char[BUFFER_SIZE]) { 10 | } 11 | 12 | bool dTalk::rpc(int DCODE) { 13 | std::string buffer = format("%c%c%c", (char) DCODE, (char) 0, (char) 0); 14 | _socket.sendBytes(buffer.c_str(), buffer.size()); 15 | return true; 16 | } 17 | 18 | bool dTalk::rpc(int DCODE, std::string data) { 19 | if(data.empty()) 20 | { 21 | return rpc(DCODE); 22 | } 23 | int block_sz(10240); 24 | std::stringstream ss; 25 | char sbuffer[block_sz]; // stream buffer 26 | std::string buffer; // data buffer 27 | dnet_size size; 28 | 29 | // dump data into stream 30 | ss << data; 31 | 32 | // iterate through the stream and send the data 33 | while (ss.getline(sbuffer, block_sz)) { 34 | buffer = sbuffer; 35 | size.Int = buffer.size(); 36 | 37 | // header must be sent as a word. 38 | buffer = format("%c%c%c%s", (char) DCODE, (char) size.Index[0], (char) size.Index[1], buffer); 39 | 40 | // send payload 41 | _socket.sendBytes(buffer.c_str(), buffer.size()); 42 | } 43 | 44 | return true; 45 | } 46 | 47 | bool dTalk::rpc(int DCODE, int data) { 48 | dnet_size size; 49 | size.Int = format("%d", data).size(); 50 | 51 | // header must be sent as a word. 52 | std::string buffer = format("%c%c%d", (char) DCODE, (char) size.Index[0], (char) size.Index[1], data); 53 | _socket.sendBytes(buffer.c_str(), buffer.size()); 54 | return true; 55 | } 56 | 57 | bool dTalk::send(char* buffer, int size) { 58 | _socket.sendBytes(buffer, size); 59 | return true; 60 | } 61 | 62 | bool dTalk::send(std::string buffer) { 63 | _socket.sendBytes(buffer.c_str(), buffer.size()); 64 | return true; 65 | } 66 | 67 | void dTalk::zero_char(char* buffer) { 68 | for(int i=0; i 0) { 219 | _dcode = _pBuffer[0]; 220 | size.Int = 0; // initialize the var 221 | size.Index[0] = _pBuffer[1]; 222 | size.Index[1] = _pBuffer[2]; 223 | 224 | // check for payload 225 | if (size.Int > 0) 226 | { 227 | // get payload 228 | n = _socket.receiveBytes(_pBuffer, size.Int); 229 | 230 | // assign payload to data container 231 | _data.assign(_pBuffer, _pBuffer + n); 232 | } 233 | } else return false; 234 | return true; 235 | } 236 | 237 | bool dTalk::receive(int size) { 238 | char* _pBuffer; 239 | int n = _socket.receiveBytes(_pBuffer, size); 240 | if (n == 0) return false; 241 | return true; 242 | } 243 | 244 | int dTalk::dcode() { 245 | return _dcode; 246 | } 247 | 248 | std::string dTalk::data() { 249 | //TODO make this binary safe 250 | // should return data vector 251 | return std::string(_data.begin(), _data.end()); 252 | } 253 | 254 | int dTalk::size() { 255 | return _data.size(); 256 | } 257 | -------------------------------------------------------------------------------- /src/dtalk.h: -------------------------------------------------------------------------------- 1 | // Requires PocoLibs 2 | 3 | class dTalk { 4 | private: 5 | static const int BUFFER_SIZE = 1024; 6 | StreamSocket _socket; 7 | char* _pBuffer; 8 | int _dcode; 9 | std::vector _data; 10 | void zero_char(char* buffer); 11 | 12 | public: 13 | dTalk(StreamSocket& socket); 14 | bool rpc(int DCODE); 15 | bool rpc(int DCODE, std::string); 16 | bool rpc(int DCODE, int); 17 | bool send(char* buffer, int size); 18 | bool send(std::string buffer); 19 | bool send_file(std::string filename); 20 | bool send_text_as_file(std::string filename, std::string text); 21 | bool save_file(std::string savepath, std::string filename); 22 | 23 | bool receive(); // receive an rpc request 24 | bool receive(int size); // receive a raw request 25 | int dcode(); // DCODE from last received 26 | std::string data(); // data from last received 27 | int size(); // size of last received data 28 | }; 29 | 30 | union dnet_size { 31 | int32_t Int; 32 | //char Index[4]; // use 32 bits 33 | char Index[2]; // use 16 bits 34 | }; 35 | -------------------------------------------------------------------------------- /src/engines/engine.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Engine 4 | { 5 | public: 6 | Engine() { _ghost = false; } 7 | 8 | ~Engine() {} 9 | 10 | virtual int run() { return 0; } 11 | virtual string results() { return string(""); } 12 | virtual bool isGhost() { return _ghost; } 13 | virtual void zapHashes(string) { return; } 14 | //virtual void setConfig(string, string); 15 | 16 | string getName(); 17 | void setName(string); 18 | int getAttackMode(); 19 | void setAttackMode(int); 20 | int getHashType(); 21 | void setHashType(int); 22 | string getMask(); 23 | void setMask(string); 24 | string getRules(); 25 | void setRules(string); 26 | string getDictionary(); 27 | void setDictionary(string); 28 | string getBinaryPath(string); 29 | void setBinaryPath(string, string); 30 | string getPot(); 31 | void setPot(string); 32 | string getHashFile(); 33 | void setHashFile(string); 34 | string getFlags(); 35 | void setFlags(string); 36 | string getConfig(string); 37 | void setConfig(string, string); 38 | void setGhost(bool flag) { _ghost = flag; }; 39 | 40 | bool isRunnable(); 41 | bool remoteSync(); 42 | void remoteSync(bool); 43 | 44 | 45 | protected: 46 | std::map _map; 47 | std::map _cfg; 48 | string _engine; 49 | 50 | int _attack; 51 | int _mode; 52 | int _chunkSize; 53 | unsigned long _chunk; 54 | string _dict; // dictionary file 55 | string _hashes; // hashlist to be used for attack 56 | string _rules; // rules file 57 | string _mask; // mask to use 58 | string _pot; // pot file 59 | string _binaryPath; 60 | string _flags; // additional parameters for hashcat 61 | string _results; 62 | bool _sync; 63 | bool _ghost; 64 | }; 65 | 66 | extern Engine *dengine; -------------------------------------------------------------------------------- /src/engines/hashcat.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "../disthc.h" 3 | #include "../djob.h" 4 | #include "hashcat.h" 5 | 6 | 7 | // ************************************************************************** // 8 | // ************************************************************************** // 9 | // Engine 10 | // ************************************************************************** // 11 | // ************************************************************************** // 12 | 13 | 14 | void Engine::setName(string engine) 15 | { 16 | _engine = engine; 17 | } 18 | 19 | string Engine::getName() 20 | { 21 | return _engine; 22 | } 23 | 24 | int Engine::getAttackMode() 25 | { 26 | return _attack; 27 | } 28 | 29 | void Engine::setAttackMode(int mode) 30 | { 31 | _attack = mode; 32 | } 33 | 34 | int Engine::getHashType() 35 | { 36 | return (int) _mode; 37 | } 38 | 39 | void Engine::setHashType(int mode) 40 | { 41 | _mode = mode; 42 | } 43 | 44 | std::string Engine::getDictionary() 45 | { 46 | return (std::string) _dict; 47 | } 48 | 49 | void Engine::setDictionary(std::string dictionary) 50 | { 51 | _dict = dictionary; 52 | } 53 | 54 | std::string Engine::getRules() 55 | { 56 | return (std::string) _rules; 57 | } 58 | 59 | void Engine::setRules(std::string rules) 60 | { 61 | _rules = rules; 62 | } 63 | 64 | std::string Engine::getMask() 65 | { 66 | return (std::string) _mask; 67 | } 68 | 69 | void Engine::setMask(std::string mask) 70 | { 71 | _mask = mask; 72 | } 73 | 74 | string Engine::getBinaryPath(string key) 75 | { 76 | return _map.find(key)->second; 77 | } 78 | 79 | void Engine::setBinaryPath(string key, string value) 80 | { 81 | _map.insert(std::pair(key, value)); 82 | } 83 | 84 | string Engine::getPot() 85 | { 86 | return _pot; 87 | } 88 | 89 | void Engine::setPot(std::string pot) 90 | { 91 | _pot = pot; 92 | return; 93 | } 94 | 95 | string Engine::getHashFile() 96 | { 97 | return _hashes; 98 | } 99 | 100 | void Engine::setHashFile(std::string hashes) 101 | { 102 | _hashes = hashes; 103 | return; 104 | } 105 | 106 | string Engine::getFlags() 107 | { 108 | return string(""); 109 | return _flags; 110 | } 111 | 112 | void Engine::setFlags(std::string flags) 113 | { 114 | _flags = flags; 115 | return; 116 | } 117 | 118 | string Engine::getConfig(string key) 119 | { 120 | return _cfg.find(key)->second; 121 | } 122 | 123 | void Engine::setConfig(string key, string value) 124 | { 125 | _cfg.insert(std::pair(key, value)); 126 | } 127 | 128 | bool Engine::remoteSync() 129 | { 130 | return _sync; 131 | } 132 | 133 | void Engine::remoteSync(bool sync) 134 | { 135 | _sync = sync; 136 | } 137 | 138 | bool Engine::isRunnable() 139 | { 140 | Application& app = Application::instance(); 141 | int hashes = 0; 142 | string hFile(getHashFile()); 143 | string line; 144 | 145 | if (DEBUG) app.logger().information(format("%%Loading hash file %s...", hFile)); 146 | // make sure the hash file exists 147 | Poco::File f(hFile); 148 | if (!f.exists()) 149 | { 150 | app.logger().information("|Unable to find hash file"); 151 | return false; 152 | } 153 | 154 | // open the has file 155 | FileInputStream fis(hFile); 156 | if (!fis.good()) 157 | { 158 | app.logger().information("|Unable to load hash file"); 159 | return false; 160 | } 161 | 162 | // count entries 163 | while(fis >> line) { 164 | if(!line.empty()) hashes++; 165 | } 166 | if(hashes>0) return true; 167 | 168 | if(DEBUG) 169 | { 170 | app.logger().information("%No hashes to load"); 171 | } 172 | return false; 173 | } 174 | 175 | 176 | 177 | 178 | 179 | // ************************************************************************** // 180 | // ************************************************************************** // 181 | // Hashcat 182 | // ************************************************************************** // 183 | // ************************************************************************** // 184 | 185 | 186 | int Ngn_Hashcat::run () 187 | { 188 | Application& app = Application::instance(); 189 | DJob *job = DJob::Instance(); 190 | string cmd; 191 | string hashcat(dengine->getBinaryPath("hashcat")); 192 | int eCode(-1); 193 | 194 | // clean results 195 | _results = string(""); 196 | 197 | // grab settings from job 198 | setAttackMode(job->getAttackMode()); 199 | setHashType(job->getHashType()); 200 | setMask(job->getMask()); 201 | setRules(job->getRules()); 202 | setDictionary(job->getDictionary()); 203 | setPot("disthc.pot"); 204 | 205 | // clean pot before working 206 | File f(getPot()); 207 | if(f.exists()) { 208 | f.remove(); 209 | } 210 | 211 | // setup command prefix (format command takes 7 args max) 212 | cmd = format("%s -o %s -s %lu -l %u", 213 | hashcat, 214 | getPot(), 215 | job->getChunk(), 216 | job->getChunkSize() 217 | ); 218 | 219 | // Attack modes: 220 | // 0 = Straight 221 | // 1 = Combination 222 | // 2 = Toggle-Case 223 | // 3 = Brute-force 224 | // 4 = Permutation 225 | // 5 = Table-Lookup 226 | 227 | // if mask minimum set, apply it 228 | if(job->getMaskMin()) 229 | { 230 | cmd = format("%s --pw-min %d", 231 | cmd, 232 | job->getMaskMin() 233 | ); 234 | } 235 | 236 | // if mask maximum set, apply it 237 | if(job->getMaskMax()) 238 | { 239 | cmd = format("%s --pw-min %d", 240 | cmd, 241 | job->getMaskMax() 242 | ); 243 | } 244 | 245 | // discover attack mode and create command to execute 246 | switch(getAttackMode()) 247 | { 248 | case 3: 249 | cmd = format("%s -a3 -m %d %s %s %s", 250 | cmd, 251 | getHashType(), 252 | getFlags(), 253 | getHashFile(), 254 | getMask() 255 | ); 256 | break; 257 | default: 258 | // default command uses attack mode 0 259 | cmd = format("%s -m %d %s %s %s %s", 260 | cmd, 261 | getHashType(), 262 | getFlags(), 263 | getHashFile(), 264 | getDictionary(), 265 | getRules() 266 | ); 267 | } 268 | 269 | if(DEBUG) app.logger().information(format("%%Running command: %s", cmd)); 270 | 271 | // check for ghosts, and run as appropriate 272 | if(isGhost()) 273 | { 274 | app.logger().information("~~~ A ghost is loose! ~~~"); 275 | app.logger().information(" .-."); 276 | app.logger().information(" (o o) boo!"); 277 | app.logger().information(" \\| O \\/"); 278 | app.logger().information(" \\ \\ "); 279 | app.logger().information(" `~~~' "); 280 | } 281 | else 282 | { 283 | // run hashcat! :) 284 | // TODO change this over to use Poco Processes 285 | eCode = system(cmd.c_str()); 286 | 287 | // check for results 288 | if(f.exists()) { 289 | FileInputStream fis(getPot()); 290 | //std::ifstream in(pot,std::ios::in); 291 | string line; 292 | while(fis >> line) { 293 | _results.append(line + "\n"); 294 | } 295 | } 296 | 297 | // TODO might take this out? 298 | // see if it's worth it to just display hashcout output during 299 | // execution 300 | // if enabled, print pot to screen 301 | // if(false) { 302 | // app.logger().information("\n=== Recovered Hashes ==="); 303 | // if(!_results.empty()) app.logger().information(_results); 304 | // app.logger().information("========================"); 305 | // } 306 | } 307 | return 0; 308 | } 309 | 310 | std::string Ngn_Hashcat::results () 311 | { 312 | return _results; 313 | } 314 | 315 | bool Ngn_Hashcat::isGhost() 316 | { 317 | int e(0); 318 | 319 | // setup pipe to catch output 320 | Pipe outPipe; 321 | 322 | // get exit code for hashcat 323 | std::vector args; 324 | args.push_back("--help"); 325 | Poco::ProcessHandle ph = Process::launch(dengine->getBinaryPath("hashcat"), args, NULL, &outPipe, NULL); 326 | e = ph.wait(); 327 | 328 | // check to see if hashcat ran and exited cleanly 329 | if(e==0 || e==255) return false; 330 | 331 | // hashcat failed or is not present 332 | return true; 333 | } 334 | 335 | void Ngn_Hashcat::zapHashes(string data) 336 | { 337 | int i; 338 | string line; 339 | string hash; 340 | bool write, move(false); 341 | 342 | // clean data 343 | data = trim(data); 344 | if(data.empty() || isGhost()) return; 345 | 346 | Application& app = Application::instance(); 347 | 348 | if(DEBUG) { 349 | app.logger().information("%Zap!"); 350 | } 351 | 352 | // parse hashes 353 | StringTokenizer hashes(data, "\n", 354 | StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); 355 | 356 | string oldFile(getHashFile()); 357 | string newFile(string(getHashFile()) + ".new"); 358 | 359 | FileInputStream fis(oldFile); 360 | FileOutputStream fos(newFile); 361 | 362 | // remove cracked hashes 363 | while(fis >> line) { 364 | write = true; 365 | for(i=0;igetBinaryPath("oclhashcat")); 414 | string gputemp(""); 415 | int eCode(-1); 416 | 417 | // clean results 418 | _results = string(""); 419 | 420 | // grab settings from job 421 | setAttackMode(job->getAttackMode()); 422 | setHashType(job->getHashType()); 423 | setMask(job->getMask()); 424 | setRules(job->getRules()); 425 | setDictionary(job->getDictionary()); 426 | setPot("disthc.pot"); 427 | 428 | // clean pot before working 429 | File f(getPot()); 430 | if(f.exists()) { 431 | f.remove(); 432 | } 433 | 434 | if(getConfig("gpuTempDisable") == "true") 435 | { 436 | gputemp = "--gpu-temp-disable"; 437 | } 438 | // setup command prefix (format command takes 7 args max) 439 | cmd = format("%s %s -o %s -s %lu -l %u", 440 | hashcat, 441 | gputemp, 442 | getPot(), 443 | job->getChunk(), 444 | job->getChunkSize() 445 | ); 446 | 447 | // Attack modes: 448 | // 0 = Straight 449 | // 1 = Combination 450 | // 2 = Toggle-Case 451 | // 3 = Brute-force 452 | // 4 = Permutation 453 | // 5 = Table-Lookup 454 | 455 | // if mask minimum set, apply it 456 | if(job->getMaskMin()) 457 | { 458 | cmd = format("%s --pw-min %d", 459 | cmd, 460 | job->getMaskMin() 461 | ); 462 | } 463 | 464 | // if mask maximum set, apply it 465 | if(job->getMaskMax()) 466 | { 467 | cmd = format("%s --pw-min %d", 468 | cmd, 469 | job->getMaskMax() 470 | ); 471 | } 472 | 473 | // discover attack mode and create command to execute 474 | switch(getAttackMode()) 475 | { 476 | case 3: 477 | cmd = format("%s -a3 -m %d %s %s %s", 478 | cmd, 479 | getHashType(), 480 | getFlags(), 481 | getHashFile(), 482 | getMask() 483 | ); 484 | break; 485 | default: 486 | // default command uses attack mode 0 487 | cmd = format("%s -m %d %s %s %s %s", 488 | cmd, 489 | getHashType(), 490 | getFlags(), 491 | getHashFile(), 492 | getDictionary(), 493 | getRules() 494 | ); 495 | } 496 | 497 | if(DEBUG) app.logger().information(format("%%Running command: %s", cmd)); 498 | 499 | // check for ghosts, and run as appropriate 500 | if(isGhost()) 501 | { 502 | app.logger().information("~~~ A ghost is loose! ~~~"); 503 | app.logger().information(" |\\___ - boo!"); 504 | app.logger().information(" (:o ___("); 505 | app.logger().information(" |/"); 506 | } 507 | else 508 | { 509 | // run hashcat! :) 510 | // TODO change this over to use Poco Processes 511 | eCode = system(cmd.c_str()); 512 | 513 | // check for results 514 | if(f.exists()) { 515 | FileInputStream fis(getPot()); 516 | //std::ifstream in(pot,std::ios::in); 517 | string line; 518 | while(fis >> line) { 519 | _results.append(line + "\n"); 520 | } 521 | } 522 | 523 | // TODO might take this out? 524 | // see if it's worth it to just display hashcout output during 525 | // execution 526 | // if enabled, print pot to screen 527 | } 528 | return 0; 529 | } 530 | 531 | std::string Ngn_oclHashcat::results () 532 | { 533 | return _results; 534 | } 535 | 536 | bool Ngn_oclHashcat::isGhost() 537 | { 538 | int e(0); 539 | 540 | // setup pipe to catch output 541 | Pipe outPipe; 542 | 543 | // get exit code for hashcat 544 | std::vector args; 545 | args.push_back("--help"); 546 | Poco::ProcessHandle ph = Process::launch(dengine->getBinaryPath("oclhashcat"), args, NULL, &outPipe, NULL); 547 | e = ph.wait(); 548 | 549 | // check to see if hashcat ran and exited cleanly 550 | if(e==0 || e==255) return false; 551 | 552 | // hashcat failed or is not present 553 | return true; 554 | } 555 | 556 | void Ngn_oclHashcat::zapHashes(string data) 557 | { 558 | int i; 559 | string line; 560 | string hash; 561 | bool write, move(false); 562 | 563 | // clean data 564 | data = trim(data); 565 | if(data.empty() || isGhost()) return; 566 | 567 | Application& app = Application::instance(); 568 | 569 | if(DEBUG) { 570 | app.logger().information("%Zap!"); 571 | } 572 | 573 | // parse hashes 574 | StringTokenizer hashes(data, "\n", 575 | StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); 576 | 577 | string oldFile(getHashFile()); 578 | string newFile(string(getHashFile()) + ".new"); 579 | 580 | FileInputStream fis(oldFile); 581 | FileOutputStream fos(newFile); 582 | 583 | // remove cracked hashes 584 | while(fis >> line) { 585 | write = true; 586 | for(i=0;i 2 | #include "engine.h" 3 | 4 | class Ngn_Hashcat : public Engine 5 | { 6 | public: 7 | int run(); 8 | string results(); 9 | bool isGhost(); 10 | void zapHashes(string); 11 | }; 12 | 13 | class Ngn_oclHashcat : public Engine 14 | { 15 | public: 16 | Ngn_oclHashcat () { return; } 17 | ~Ngn_oclHashcat () { } 18 | int run(); 19 | string results(); 20 | bool isGhost(); 21 | void zapHashes(string); 22 | }; -------------------------------------------------------------------------------- /src/tinycon.cpp: -------------------------------------------------------------------------------- 1 | #include "tinycon.h" 2 | 3 | /* 4 | * Terminal Console 5 | * Usage: tinyConsole tc("prompt>"); 6 | * tc.run(); 7 | * 8 | * When a command is entered, it will be passed to the trigger method for 9 | * processing. This is a blocking function. 10 | * 11 | * tinyConsole::trigger can be over-ridden in a derivative class to 12 | * change its functionality. 13 | * 14 | * Additionally, a check to the 'hotkeys' method is called for each 15 | * keypress, which can also be over-ridden in a dirivative class. 16 | * 17 | */ 18 | 19 | // On Unix-like systems, add getch support without curses 20 | #if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32) 21 | int getch () 22 | { 23 | struct termios oldt, newt; 24 | int ch; 25 | tcgetattr(STDIN_FILENO, &oldt); 26 | newt = oldt; 27 | newt.c_lflag &= ~(ICANON | ECHO); 28 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 29 | 30 | ch = getchar(); 31 | if(ch == ESC) 32 | { 33 | ch = getchar(); 34 | if(ch == 91) 35 | { 36 | ch = KEY_CTRL1; 37 | } 38 | } 39 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 40 | return ch; 41 | } 42 | #endif 43 | 44 | tinyConsole::tinyConsole () 45 | { 46 | _max_history = MAX_HISTORY; 47 | _quit = false; 48 | pos = -1; 49 | line_pos = 0; 50 | skip_out = 0; 51 | } 52 | 53 | tinyConsole::tinyConsole (std::string s) 54 | { 55 | _max_history = MAX_HISTORY; 56 | _quit = false; 57 | _prompt = s; 58 | pos = -1; 59 | line_pos = 0; 60 | skip_out = 0; 61 | } 62 | 63 | std::string tinyConsole::version () 64 | { 65 | return TINYCON_VERSION; 66 | } 67 | 68 | void tinyConsole::pause () 69 | { 70 | getch(); 71 | } 72 | 73 | void tinyConsole::quit () 74 | { 75 | _quit = true; 76 | } 77 | 78 | int tinyConsole::trigger (std::string cmd) 79 | { 80 | if (cmd == "exit") { 81 | _quit = true; 82 | } else { 83 | std::cout << cmd << std::endl; 84 | } 85 | return 0; 86 | } 87 | 88 | int tinyConsole::hotkeys (char c) 89 | { 90 | return 0; 91 | } 92 | 93 | void tinyConsole::setMaxHistory (int max) 94 | { 95 | _max_history = max; 96 | } 97 | 98 | std::string tinyConsole::getLine () 99 | { 100 | return getLine(M_LINE, ""); 101 | } 102 | 103 | std::string tinyConsole::getLine (int mode) 104 | { 105 | return getLine(mode, ""); 106 | } 107 | 108 | std::string tinyConsole::getLine (int mode = M_LINE, std::string delimeter = "") 109 | { 110 | std::string line; 111 | char c; 112 | 113 | for (;;) 114 | { 115 | c = getch(); 116 | if ( c == NEWLINE) 117 | { 118 | std::cout << std::endl; 119 | return line; 120 | } else if ((int) c == BACKSPACE ) { 121 | if (line.length()) 122 | { 123 | line = line.substr(0,line.size()-1); 124 | if (mode != M_PASSWORD) 125 | { 126 | std::cout << "\b \b"; 127 | } 128 | } 129 | } else { 130 | line += c; 131 | if (mode != M_PASSWORD) 132 | { 133 | std::cout << c; 134 | } 135 | } 136 | } 137 | } 138 | 139 | std::string tinyConsole::getBuffer() 140 | { 141 | return std::string (buffer.begin(), buffer.end()); 142 | } 143 | 144 | void tinyConsole::setBuffer (std::string s) 145 | { 146 | buffer.assign(s.begin(),s.end()); 147 | line_pos = buffer.size(); 148 | } 149 | 150 | void tinyConsole::showPrompt() 151 | { 152 | std::cout << _prompt; 153 | } 154 | 155 | void tinyConsole::run () 156 | { 157 | //show prompt 158 | std::cout << _prompt; 159 | 160 | // grab input 161 | for (;;) 162 | { 163 | c = getch(); 164 | if(!hotkeys(c)) 165 | switch (c) 166 | { 167 | case ESC: 168 | // TODO escape is only detected if double-pressed. this should be fixed 169 | std::cout << "(Esc)"; 170 | break; 171 | case KEY_CTRL1: // look for arrow keys 172 | switch (c = getch()) 173 | { 174 | case UP_ARROW: 175 | if (!history.size()) break; 176 | if (pos == -1) 177 | { 178 | // store current command 179 | unused = ""; 180 | unused.assign(buffer.begin(), buffer.end()); 181 | } 182 | 183 | // clear line 184 | for (int i = 0; i < line_pos; i++) 185 | { 186 | std::cout << "\b \b"; 187 | } 188 | 189 | // clean buffer 190 | buffer.erase(buffer.begin(), buffer.end()); 191 | 192 | pos++; 193 | if (pos > (history.size() - 1)) pos = history.size() - 1; 194 | 195 | // store in buffer 196 | for (int i = 0; i < history[pos].size(); i++) 197 | { 198 | buffer.push_back(history[pos][i]); 199 | } 200 | line_pos = buffer.size(); 201 | // output to screen 202 | std::cout << history[pos]; 203 | break; 204 | case DOWN_ARROW: 205 | if (!history.size()) break; 206 | 207 | // clear line 208 | for (int i = 0; i < line_pos; i++) 209 | { 210 | std::cout << "\b \b"; 211 | } 212 | 213 | // clean buffer 214 | buffer.erase(buffer.begin(), buffer.end()); 215 | 216 | pos--; 217 | if (pos<-1) pos = -1; 218 | if (pos >= 0) { 219 | std::cout << history[pos]; 220 | // store in buffer 221 | for (int i = 0; i < history[pos].size(); i++) 222 | { 223 | buffer.push_back(history[pos][i]); 224 | } 225 | } else { 226 | if (buffer.size()) 227 | { 228 | std::cout << unused; 229 | // store in buffer 230 | for (int i = 0; i < unused.size(); i++) 231 | { 232 | buffer.push_back(unused[i]); 233 | } 234 | } 235 | } 236 | line_pos = buffer.size(); 237 | break; 238 | case LEFT_ARROW: 239 | // if there are characters to move left over, do so 240 | if (line_pos) 241 | { 242 | std::cout << "\b"; 243 | line_pos--; 244 | } 245 | break; 246 | case RIGHT_ARROW: 247 | // if there are characters to move right over, do so 248 | if (line_pos < buffer.size()) 249 | { 250 | std::cout << buffer[line_pos]; 251 | line_pos++; 252 | } 253 | break; 254 | case DEL: 255 | if (line_pos < buffer.size()) 256 | { 257 | skip_out = 1; 258 | buffer.erase(buffer.begin()+line_pos); 259 | // update screen after current position 260 | for (int i = line_pos; i < buffer.size(); i++ ) 261 | { 262 | std::cout << buffer[i]; 263 | } 264 | // erase last char 265 | std::cout << " "; 266 | for (int i = line_pos; i < buffer.size(); i++ ) 267 | { 268 | std::cout << "\b"; 269 | } 270 | // make-up for erase position 271 | std::cout << "\b"; 272 | //std::cout << "(DEL)"; 273 | } 274 | break; 275 | default: 276 | skip_out = 1; 277 | //std::cout << "(" << (int) c << ")" << std::endl; 278 | } 279 | break; 280 | case BACKSPACE: 281 | if (line_pos == 0) break; 282 | // move cursor back, blank char, and move cursor back again 283 | std::cout << "\b \b"; 284 | // don't forget to clean the buffer and update line position 285 | if (line_pos == buffer.size()) { 286 | buffer.pop_back(); 287 | line_pos--; 288 | } else { 289 | line_pos--; 290 | buffer.erase(buffer.begin()+line_pos); 291 | // update screen after current position 292 | for (int i = line_pos; i < buffer.size(); i++ ) { 293 | std::cout << buffer[i]; 294 | } 295 | // erase last char 296 | std::cout << " "; 297 | for (int i = line_pos+1; i < buffer.size(); i++ ) { 298 | std::cout << "\b"; 299 | } 300 | // make-up for erase position and go to new position 301 | std::cout << "\b\b"; 302 | } 303 | break; 304 | case TAB: 305 | break; 306 | // print history 307 | for (int i = 0; i < history.size(); i++) { 308 | std::cout << history[i] << std::endl; 309 | } 310 | break; 311 | case NEWLINE: 312 | // store in string 313 | s.assign(buffer.begin(), buffer.end()); 314 | 315 | 316 | 317 | // save command to history 318 | // trimming of command should be done in callback function 319 | if(s.length()) 320 | history.push_front(s); 321 | 322 | // run command 323 | //(*callbackFunc)(s.c_str()); 324 | std::cout << std::endl; 325 | trigger(s); 326 | 327 | // check for exit command 328 | if(_quit == true) { 329 | return; 330 | } 331 | 332 | if (history.size() > _max_history) history.pop_back(); 333 | 334 | // clean buffer 335 | buffer.erase(buffer.begin(), buffer.end()); 336 | 337 | // print prompt. new line should be added from callback function 338 | std::cout << _prompt; 339 | 340 | // reset position 341 | pos = -1; 342 | line_pos = 0; 343 | break; 344 | default: 345 | if (skip_out) { 346 | skip_out = 0; 347 | break; 348 | } 349 | std::cout << c; 350 | if(line_pos == buffer.size()) { 351 | // line position is at the end of the buffer, just append 352 | buffer.push_back(c); 353 | } else { 354 | // line position is not at end. Insert new char 355 | buffer.insert(buffer.begin()+line_pos, c); 356 | // update screen after current position 357 | for (int i = line_pos+1; i < buffer.size(); i++ ) { 358 | std::cout << buffer[i]; 359 | } 360 | for (int i = line_pos+1; i < buffer.size(); i++ ) { 361 | std::cout << "\b"; 362 | } 363 | } 364 | line_pos++; 365 | break; 366 | } // end switch 367 | } 368 | 369 | } 370 | -------------------------------------------------------------------------------- /src/tinycon.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 7 | #include 8 | #include 9 | #else 10 | #include 11 | #include 12 | #endif 13 | 14 | // Keyboard Scan Codes 15 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 16 | #define KEY_CTRL1 -32 17 | #define BACKSPACE 8 18 | #define UP_ARROW 72 19 | #define DOWN_ARROW 80 20 | #define RIGHT_ARROW 77 21 | #define LEFT_ARROW 75 22 | #else 23 | #define KEY_CTRL1 17 24 | #define BACKSPACE 127 25 | #define UP_ARROW 65 26 | #define DOWN_ARROW 66 27 | #define RIGHT_ARROW 67 28 | #define LEFT_ARROW 68 29 | #endif 30 | #define TAB 9 31 | #define ESC 27 32 | #define DEL 51 33 | 34 | // Some basic configs 35 | #define TINYCON_VERSION "0.8" 36 | #define MAX_HISTORY 500 37 | 38 | #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) 39 | const char NEWLINE = '\r'; 40 | #else 41 | const char NEWLINE = '\n'; 42 | int getch(); 43 | #endif 44 | 45 | // getLine modes 46 | #define M_LINE 0 47 | #define M_PASSWORD 1 48 | 49 | //void tinyConsole(std::string prompt, int (*callbackFunc)(const char* cmd)); 50 | 51 | //int tinyConsoleTrigger(const char* cmd); 52 | 53 | std::string tinyGetLine(); 54 | 55 | std::string tinyGetPassword(); 56 | 57 | 58 | class tinyConsole { 59 | protected: 60 | bool _quit; 61 | int _max_history; 62 | std::string _prompt; 63 | 64 | int pos; 65 | int line_pos; 66 | int skip_out; 67 | char c; 68 | std::string s, unused; 69 | std::deque buffer; 70 | std::deque history; 71 | public: 72 | tinyConsole(); 73 | tinyConsole(std::string); 74 | void run(); 75 | void setPrompt(std::string); 76 | virtual int trigger(std::string); 77 | virtual int hotkeys(char); 78 | void pause(); 79 | void quit(); 80 | std::string getLine(); 81 | std::string getLine(int); 82 | std::string getLine(int, std::string); 83 | std::string version(); 84 | void setMaxHistory(int); 85 | std::string getBuffer(); 86 | void setBuffer(std::string); 87 | void showPrompt(); 88 | }; 89 | --------------------------------------------------------------------------------