Noteable Posts

Tuesday, April 14, 2020

How To Start | How To Become An Ethical Hacker

Are you tired of reading endless news stories about ethical hacking and not really knowing what that means? Let's change that!
This Post is for the people that:

  • Have No Experience With Cybersecurity (Ethical Hacking)
  • Have Limited Experience.
  • Those That Just Can't Get A Break


OK, let's dive into the post and suggest some ways that you can get ahead in Cybersecurity.
I receive many messages on how to become a hacker. "I'm a beginner in hacking, how should I start?" or "I want to be able to hack my friend's Facebook account" are some of the more frequent queries. Hacking is a skill. And you must remember that if you want to learn hacking solely for the fun of hacking into your friend's Facebook account or email, things will not work out for you. You should decide to learn hacking because of your fascination for technology and your desire to be an expert in computer systems. Its time to change the color of your hat 馃榾

 I've had my good share of Hats. Black, white or sometimes a blackish shade of grey. The darker it gets, the more fun you have.

If you have no experience don't worry. We ALL had to start somewhere, and we ALL needed help to get where we are today. No one is an island and no one is born with all the necessary skills. Period.OK, so you have zero experience and limited skills…my advice in this instance is that you teach yourself some absolute fundamentals.
Let's get this party started.
  •  What is hacking?
Hacking is identifying weakness and vulnerabilities of some system and gaining access with it.
Hacker gets unauthorized access by targeting system while ethical hacker have an official permission in a lawful and legitimate manner to assess the security posture of a target system(s)

 There's some types of hackers, a bit of "terminology".
White hat — ethical hacker.
Black hat — classical hacker, get unauthorized access.
Grey hat — person who gets unauthorized access but reveals the weaknesses to the company.
Script kiddie — person with no technical skills just used pre-made tools.
Hacktivist — person who hacks for some idea and leaves some messages. For example strike against copyright.
  •  Skills required to become ethical hacker.
  1. Curosity anf exploration
  2. Operating System
  3. Fundamentals of Networking
*Note this sites





Continue reading
  1. Hack Tools
  2. Hack Website Online Tool
  3. Pentest Tools Github
  4. Easy Hack Tools
  5. Hack Tools For Mac
  6. Pentest Automation Tools
  7. Pentest Tools For Ubuntu
  8. Hacking Tools For Pc
  9. Kik Hack Tools
  10. Hacking Tools
  11. Hacking Tools Name
  12. Game Hacking
  13. Pentest Tools Framework
  14. Hack Tool Apk
  15. Termux Hacking Tools 2019
  16. Top Pentest Tools
  17. Hacker Tools Mac

Hacking Windows: Tricks Para Saltarse AppLocker

AppLocker es una funcionalidad que apareci贸 con Windows 7 (versi贸n Enterprise y Ultimate) y Windows Server 2008 R2 para sustituir a las Pol铆ticas de Restricci贸n de Software - conocidas como SRP "Software Restriction Policies" - de las versiones anteriores. Igual que las directivas de restricci贸n de software, AppLocker permite definir las aplicaciones autorizadas para ser ejecutadas por sus usuarios est谩ndar dentro de su dominio instalando sus par谩metros mediante directivas de grupo.

Figura 1: Hacking Windows: Tricks para saltarse AppLocker

La utilidad principal de esta funcionalidad es limitar la instalaci贸n de malware e impedir la instalaci贸n de software no normalizado y, por supuesto, son pieza fundamental de la Seguridad en Windows Server 2016 y de la aplicaci贸n de procesos de fortificaci贸n para conseguir la M谩xima Seguridad en Windows.

Figura 2: Windows Server 2016: Configuraci贸n, Adminisraci贸n y Seguridad
de 脕ngel N煤帽ez (Puedes contactar con 茅l en MyPublicInbox)

Con la aparici贸n de AppLocker, el n煤mero de cosas que se pueden realizar a la hora de evitar la ejecuci贸n de determinado tipo de programas son muchas. Ente la lista se encuentran:
- Definir reglas basadas en atributos de archivo que se mantengan a lo largo de las actualizaciones de la aplicaci贸n (nombre del archivo, versi贸n…) , reglas basadas en la ruta y el hash del archivo. 
- Asignar una regla a un grupo de seguridad o a un usuario individual. 
- Crear Excepciones a ciertas reglas. 
- Modo auditor铆a para implementar la directiva y ver el impacto que tendr谩 antes de aplicarla. 
- Simplificar la creaci贸n y la administraci贸n de reglas de AppLocker con PowerShell.
Las tecnolog铆as de control de acceso, como Active Directory Rights Management Services (ADRMS) y las listas de control de acceso (ACL), ayudan a controlar los usuarios a los que se permite el acceso al bien m谩s preciado de las organizaciones: la informaci贸n que posee. Al crear una lista de aplicaciones y archivos aprobados y permitidos, AppLocker tambi茅n se ayuda a impedir la ejecuci贸n de aplicaciones para determinados usuarios.

Figura 3: M谩xima Seguridad en Windows Gold Edition de
Sergio de los Santos (Puedes contactar con 茅l en MyPublicInbox)

Como AppLocker puede controlar archivos .dll, tambi茅n es 煤til para controlar qui茅n puede instalar y ejecutar controles ActiveX y es ideal para aquellas organizaciones que actualmente usan la directiva de grupo para administrar sus equipos.

Hacking Windows: Bypass de AppLocker

Antes de empezar con la prueba de concepto es necesario activar el servicio de identidad de aplicaci贸n, as铆 una vez que configuremos las reglas en AppLocker pueda aplicar las reglas que a帽adamos posteriormente. Para ello abrimos una CMD con permisos de administrador y escribimos el siguiente comando:

Figura 4: Activando el servicio de identidad de aplicacion

Una vez nos muestre que ha sido activado con 茅xito es necesario reiniciar el sistema operativo, para que se apliquen correctamente los cambios de esta activaci贸n. Despu茅s, ya podemos proceder a abrir las directivas de seguridad local, que se encuentra dentro de Panel de control\Sistema y  ah铆 dentro de Seguridad\Herramientas administrativas. Una vez abierta se nos mostrar谩 una ventana como est谩 de la imagen siguiente.

Figura 5: AppLocker

Para comenzar a configurar el servicio, debemos pulsar en el bot贸n verde que dice "Configure rule enforcement" para configurar la aplicaci贸n de reglas. En el cuadro de configuraci贸n vamos a habilitar reglas de ejecutables tal y como se puede ver a continuaci贸n.

Figura 6: La primera opci贸n es "Executable Rules"

Ahora procedemos a crear la regla para limitar la ejecuci贸n de una determinada aplicaci贸n, en nuestro caso vamos a bloquear a nuestra m谩s que conocida, y muy querida, FOCA - Fear the FOCA! Se puede configurar de diferentes maneras, ya sea por la ruta de una carpeta o fichero, editor de software (el que firma el binario) o bien por el hash de archivo.

Figura 7: Configurando regla de ejecutable por ruta

Prueba 1: Saltando la regla de ruta

Nosotros vamos a elegir la ruta del ejecutable, para ver de manera sencilla c贸mo funciona AppLocker y despu茅s verificamos que se ha creado la regla para FOCA.

Figura 8: Reglas de prohibir FOCA por ruta creada

Como somos muy fan del Pentesting con Powershell, tambi茅n se os ense帽amos c贸mo se pueden visualizar las reglas que hay creadas en el Windows en el que est谩s trabajando con un peque帽o script , tal y como pod茅is ver en la siguiente imagen.

Figura 9: Script en PowerShell (Haz clic para ver en grande)

Tras ejecutarlo, el resultado que nos arroja dicho script es lo siguiente. Es decir, la misma informaci贸n pero directamente en nuestra PowerShell.

Figura 10: Reglas creadas en AppLocker

Y ahora s铆, para ver si la configuraci贸n que hemos hecho funciona, lo que deber铆a suceder cuando  intentamos ejecutar el binario de FOCA es que se nos mostrara el siguiente mensaje, dej谩ndonos claro que no es posible hacer uso de esta aplicaci贸n.

Figura 11: AppLocker prohibe la ejecuci贸n de ese archivo

Saltar esta primera protecci贸n es bastante sencillo y conocido. Al final, como sabemos por la regla que hemos visto cuando hemos ejecutado nuestro script PowerShell, esta aplicaci贸n est谩 bloqueada por ruta, es decir, que si tenemos permisos de lectura del binario de la FOCA - o de toda la carpeta - y escritura en una carpeta del sistema, podemos hacer lo siguiente:

Figura 12: Copiamos la carpeta de la FOCA a otra ubicaci贸n

Si la fortificaci贸n del sistema no se ha hecho acompa帽谩ndolo de una estricta ACL, podremos copiar la carpeta de Origen (FocaPro_locked) a una nueva ubicaci贸n (FocaPro_unlocked), invalidando completamente la regla de AppLocker que est谩 configurada.

Figura 13: Carpetas copiadas. Una afectada por AppLocker y otra no.

Ahora ya, si intentamos ejecutar el nuevo binario de FOCA, no encontraremos ninguna regla en AppLocker que le afecte, as铆 que podemos disfrutar de

Figura 14: FOCA Final

No es nada sorprendente que esto pase de esta forma. Esta regla hace lo que dice, que es evitar que un programa que est茅 en una ruta concreta no se ejecute y si no lo acompa帽as de otras medidas de fortificaci贸n la regla sirve para lo que sirve. Por eso hay m谩s medidas en AppLocker.

Prueba 2: Saltando la regla de Hash

Ahora que ya entendemos algo mejor AppLocker, vamos a ver otro ejemplo de bypass, pero esta vez vamos a saltarnos la regla del Hash de un fichero, que al igual que la regla anterior tiene sus limitaciones. Trabajaremos en este caso con Process Explorer (procexp64.exe), la herramienta de Sysinternals para ver los procesos en Windows. Primero obtenemos el Hash del fichero :

Figura 15: Obteniendo el Hash de un fichero con PowerShell

Como hab茅is visto, esto es algo que tambi茅n podemos hacer con PowerShell. Y una vez que lo tenemos, configuramos una regla como en el caso anterior, pero esta ver seleccionando Hash File e introduciendo el Hash que acabamos de obtener.

Figura 16: Hash File Rule creada

Una vez tenemos la regla creada, AppLocker se encargar谩 de comprobar ese hash en cualquier ejecutable que se intente lanzar, y si coincide con el de la regla, bloquear谩 el binario y no permitir谩 su ejecuci贸n, tal y como se puede ver en la imagen siguiente.

Figura 17:AppLocker bloquea procexp64.exe

Por supuesto, si llevas a帽os en la industria de la detecci贸n de malware, ya sabes que hacer reglas para malware basado en Hashes siempre fue una mala idea, y haciendo un "Morphing de Superman", es decir, cambiando cualquier byte de una cadena de caracteres podemos modificar ese Hash. Para ello basta con que abramos el binario con un editor Hexadecimal y hacer una ligera modificaci贸n en una cadena de texto, para que el programa siga siendo totalmente funcional.

Figura 18: Haciendo un "Morphing de Superman"

Los caracteres en rojo, son aquellos que hemos modificado, simplemente hemos sustituido las letras que se visualizaban en la parte derecha por puntos. Guardamos como un nuevo binario llamado "procexp64_unlocked.exe" pero no porque con otro nombre lo vaya a ejecutar, si no para la prueba, pod茅is renombrarlo con el mismo nombre del binario para comprobar que realmente es efectivo. Una vez terminado el proceso del "Morphing de Superman", comprobamos que el Hash de los dos binarios es diferente :

Figura 19: Ya no tienen el mismo hash

Por supuesto, si ahora ejecutamos el nuevo binario - con diferente Hash - vemos que realmente nos hemos saltado la restricci贸n por Hash de AppLocker porque, evidentemente, esta ya no le aplica para nada al tenerlo cambiado.

Figura 20: Process Explorer se ejecuta

Como os pod茅is imaginar, conocer en detalle el funcionamiento de estas tecnolog铆as es fundamental para fortificar cualquier entorno Windows en una empresa, y entender c贸mo funcionan las reglas de ruta y de hash, y cuales son sus limitaciones es importante. Por supuesto, saber qu茅 reglas est谩n configuradas y c贸mo se puede saltar  AppLocker en un proyecto de auditor铆a que requiera tirar de t茅cnicas de Hacking Windows es muy 煤til.

Figura 21: Hacking Windows: Ataques a sistemas y redes Microsoft

Te puedes encontrar AppLocker configurado en una auditor铆a, y si te encuentras estas reglas en las restricciones ya has visto que no es muy complicado. Sin embargo AppLocker tambi茅n tiene reglas basadas en los certificados digitales con la que est谩n firmados los ejecutables, donde un administrador concienzudo puede elegir qu茅 fabricantes de software, qu茅 programas y qu茅 versiones concretas son las que se pueden utilizar o las que est谩n prohibidas. Cuando esto es as铆, encontrar la forma de saltarse la restricci贸n es m谩s complicada. Eso s铆, siempre puedes traerte tus propios programas sin firmar cuando haya listas negras...

Saludos!

Autor: V铆ctor Rodriguez Boyero, Security Researcher en el equipo de Ideas Locas de CDCO de Telef贸nica.


More information


  1. Hack Tools For Ubuntu
  2. Hacking Tools 2019
  3. Pentest Tools Download
  4. Hack Apps
  5. Pentest Tools Kali Linux
  6. Hacking Tools Windows 10
  7. Hacking Tools For Windows 7
  8. Termux Hacking Tools 2019
  9. Pentest Tools Alternative
  10. Pentest Tools
  11. Tools Used For Hacking
  12. Pentest Tools For Mac
  13. Pentest Reporting Tools
  14. Hacking Tools Hardware
  15. Pentest Tools Open Source
  16. Pentest Tools Tcp Port Scanner
  17. Hack And Tools
  18. Hacking Tools Mac
  19. Hacker Tools Github
  20. Hacking Tools Mac

Gridcoin - The Bad

In this post we will show why Gridcoin is insecure and probably will never achieve better security. Therefore, we are going to explain two critical implementation vulnerabilities and our experience with the core developer in the process of the responsible disclosure. 
    In our last blog post we described the Gridcoin architecture and the design vulnerability we found and fixed (the good). Now we come to the process of responsibly disclosing our findings and try to fix the two implementation vulnerabilities (the bad).

    Update (15.08.2017):
    After the talk at WOOT'17 serveral other developers of Gridcoin quickly reached out to us and told us that there was a change in responsibility internally in the Gridcoin-Dev team. Thus, we are going to wait for their response and then change this blog post accordingly. So stay tuned :)

    Update (16.08.2017):
    We are currently in touch with the whole dev team of Gridcoin and it seems that they are going to fix the vulnerabilities with the next release.


    TL;DR
    The whole Gridcoin currency is seriously insecure against attacks and should not be trusted anymore; unless some developers are in place, which have a profound background in protocol and application security.

    What is Gridcoin?

    Gridcoin is an altcoin, which is in active development since 2013. It claims to provide a high sustainability, as it has very low energy requirements in comparison to Bitcoin. It rewards users for contributing computation power to scientific projects, published on the BOINC project platform. Although Gridcoin is not as widespread as Bitcoin, its draft is very appealing as it attempts to  eliminate Bitcoin's core problems. It possesses a market capitalization of $13,530,738 as of August the 4th 2017 and its users contributed approximately 5% of the total scientific BOINC work done before October 2016.

    A detailed description of the Gridcoin architecture and technical terms used in this blog post are explained in our last blog post.

    The Issues

    Currently there are 2 implementation vulnerabilities in the source code, and we can mount the following attacks against Gridcoin:
    1. We can steal the block creation reward from many Gridcoin minters
    2. We can efficiently prevent many Gridcoin minters from claiming their block creation reward (DoS attack)
    So why do we not just open up an issue online explaining the problems?

    Because we already fixed a critical design issue in Gridcoin last year and tried to help them to fix the new issues. Unfortunately, they do not seem to have an interest in securing Gridcoin and thus leave us no other choice than fully disclosing the findings.

    In order to explain the vulnerabilities we will take a look at the current Gridcoin source code (version 3.5.9.8).

    WARNING: Due to the high number of source code lines in the source files, it can take a while until your browser shows the right line.

    Stealing the BOINC block reward

    The developer implemented our countermeasures in order to prevent our attack from the last blog post. Unfortunately, they did not look at their implementation from an attacker's perspective. Otherwise, they would have found out that they conduct not check, if the signature over the last block hash really is done over the last block hash. But we come to that in a minute. First lets take a look at the code flow:

    In the figure the called-by-graph can be seen for the function VerifyCPIDSignature.
    1. CheckBlock → DeserializeBoincBlock [Source]
      • Here we deserialize the BOINC data structure from the first transaction
    2. CheckBlock → IsCPIDValidv2 [Source]
      • Then we call a function to verify the CPID used in the block. Due to the massive changes over the last years, there are 3 possible verify functions. We are interested in the last one (VerifyCPIDSignature), for the reason that it is the current verification function.
    3. IsCPIDValidv2 → VerifyCPIDSignature [Source]
    4. VerifyCPIDSignature → CheckMessageSignature [Source, Source]
    In the last function the real signature verification is conducted [Source]. When we closely take a look at the function parameter, we see the message (std::string sMsg)  and the signature (std::string sSig) variables, which are checked. But where does this values come from?


    If we go backwards in the function call graph we see that in VerifyCPIDSignature the sMsg is the string sConcatMessage, which is a concatenation of the sCPID and the sBlockHash.
    We are interested where the sBlockHash value comes from, due to the fact that this one is the only changing value in the signature generation.
    When we go backwards, we see that the value originate from the deserialization of the BOINC structure (MiningCPID& mc) and is the variable mc.lastblockhash [Source, Source]. But wait a second, is this value ever checked whether it contains the real last block hash?

    No, it is not....

    So they just look if the stored values there end up in a valid signature.

    Thus, we just need to wait for one valid block from a researcher and copy the signature, the last block hash value, the CPID and adjust every other dynamic value, like the RAC. Consequently, we are able to claim the reward of other BOINC users. This simple bug allows us again to steal the reward of every Gridcoin researcher, like there was never a countermeasure.

    Lock out Gridcoin researcher
    The following vulnerability allows an attacker under specific circumstances to register a key pair for a CPID, even if the CPID was previously tied to another key pair. Thus, the attacker locks out a legit researcher and prevent him from claiming BOINC reward in his minted blocks.

    Reminder: A beacon is valid for 5 months, afterwards a new beacon must be sent with the same public key and CPID.

    Therefore, we need to take a look at the functions, which process the beacon information. Every time there is a block, which contains beacon information, it is processed the following way (click image for higher resolution):


    In the figure the called-by-graph can be seen for the function GetBeaconPublicKey.
    We now show the source code path:
    • ProcessBlock → CheckBlock [Source]
    • CheckBlock → LoadAdminMessages [Source]
    • LoadAdminMessages → MemorizeMessages [Source]
    • MemorizeMessages → GetBeaconPublicKey [Source]
    In the last function GetBeaconPublicKey there are different paths to process a beacon depending on the public key, the CPID, and the time since both were associated to each other.
    For the following explanation we assume that we have an existing association (bound) between a CPID A and a public key pubK_A for 4 months.
    1. First public key for a CPID received [Source]
      • The initial situation, when pubK_A was sent and bind to CPID  A (4 months ago)
    2. Existing public key for a CPID was sent [Source]
      • The case that pubK_A was resent for a CPID A, before the 5 months are passed by
    3. Other public key for a CPID was sent [Source]
      • The case, if a different public key pubK_B for the CPID A was sent via beacon.
    4. The existing public key for the CPID is expired
      • After 5 months a refresh for the association between A and pubK_A is required.
    When an incoming beacon is processed, a look up is made, if there already exists a public key for the CPID used in the beacon. If yes, it is compared to the public key used in the beacon (case 2 and 3).
    If no public key exists (case 1) the new public key is bound to the CPID.

    If a public key exists, but it was not refreshed directly 12.960.000 seconds (5 months [Source]) after the last beacon advertisement of the public key and CPID, it is handled as no public key would exist [Source].

    Thus, case 1 and 4 are treated identical, if the public key is expired, allowing an attacker to register his public key for an arbitrary CPID with expired public key. In practice this allows an attacker to lock out a Gridcoin user from the minting process of new blocks and further allows the attacker to claim reward for BOINC work he never did.

    There is a countermeasure, which allows a user to delete his last beacon (identified by the CPID) . Therefore, the user sends 1 GRC to a special address (SAuJGrxn724SVmpYNxb8gsi3tDgnFhTES9) from an GRC address associated to this CPID [Source]. We did not look into this mechanism in more detail, because it only can be used to remove our attack beacon, but does not prevent the attack.

    The responsible disclosure process

    As part of our work as researchers we all have had the pleasure to responsible disclose the findings to developer or companies.

    For the reasons that we wanted to give the developer some time to fix the design vulnerabilities, described in the last blog post, we did not issue a ticket at the Gridcoin Github project. Instead we contacted the developer at September the 14th 2016 via email and got a response one day later (2016/09/15). They proposed a variation of our countermeasure and dropped the signature in the advertising beacon, which would result in further security issues. We sent another email (2016/09/15) explained to them, why it is not wise to change our countermeasures and drop the signature in the advertising beacon.
    Unfortunately, we did not receive a response. We tried it again on October the 31th 2016. They again did not respond, but we saw in the source code that they made some promising changes. Due to some other projects we did not look into the code until May 2017. At this point we found the two implementation vulnerabilities. We contacted the developer twice via email (5th and 16th of May 2017) again, but never received a response. Thus, we decided to wait for the WOOT notification to pass by and then fully disclose the findings. We thus have no other choice then to say that:

    The whole Gridcoin cryptocurrency is seriously insecure against attacks and should not be trusted anymore; unless some developers are in place, which have a profound background in protocol and application security.

    Further Reading
    A more detailed description of the Gridcoin architecture, the old design issue and the fix will be presented at WOOT'17. Some days after the conference the paper will be available online.

    More information


    Sunday, April 12, 2020

    Exploring Monster Taming Mechanics In Final Fantasy XIII-2: Relational Data

    In this next installment of the miniseries of exploring the monster taming mechanics of Final Fantasy XIII-2, we'll fill out another database table that we need in order to start connecting all of the monster data together. In the last article, we built the core monster table with hundreds of attributes for each of 164 monsters. In the first article, we had identified four other tables that we would need as well, these being abilities, game areas, monster materials, and monster characteristics. The data in these four tables is all related in one way or another to the monsters in the monster table. We'll start with the abilities table, which will end up being three tables because we actually have passive, command, and role abilities. Once the passive abilities table is complete, we'll see how to connect that data in the database so that we can later make inferences on the data.

    Final Fantasy XIII-2 battle scene

    Finding Monster Abilities

    Like for the core monster data, the first thing we need to do is get the data for the monster abilities into a .csv format. We'll start with the passive abilities because this is the largest group of abilities and they have a rank attribute that the role and hidden abilities don't have. Since there are over 200 passive abilities, we're going to want to pull this data out of the FAQ with another script instead of building it up manually. We can use an FSM very similar to the one used for parsing the monster data with a few simplifications because there are not nearly so many attributes for the passive abilities. I'll just throw the script out there, and then we can discuss it:

    SECTION_TAG = "PassADe"
    ABILITY_SEPARATOR = "........................................"
    ABILITY_REGEX = /(\w\S+(?:\s[^\s\(]+)*)\s\((RL|\d)\)-*:\s(\w\S+(?:\s\S+)*)/
    ABILITY_EXT_REGEX = /^\s+(\S+(?:\s\S+)*)/

    end_abilities = lambda do |line, data|
    return end_abilities, data
    end

    new_ability = lambda do |line, data|
    props = line.scan(ABILITY_REGEX)
    if props.empty?
    if line.include? ABILITY_SEPARATOR
    return end_abilities, data
    else
    extra_line = ABILITY_EXT_REGEX.match(line)
    data.last["description"] += ' ' + extra_line[1]
    return new_ability, data
    end
    end

    if props.first[1] == "RL"
    props.first[1] = "99"
    props.first[0] += " (RL)"
    end
    data << {"name" => props.first[0], "rank" => props.first[1], "description" => props.first[2]}
    return new_ability, data
    end

    find_abilities = lambda do |line, data|
    if line.include? ABILITY_SEPARATOR
    return new_ability, data
    end
    return find_abilities, data
    end

    find_sub_section = lambda do |line, data|
    if line.include? SECTION_TAG
    return find_abilities, data
    end
    return find_sub_section, data
    end

    section_tag_found = lambda do |line, data|
    if line.include? SECTION_TAG
    return find_sub_section, data
    end
    return section_tag_found, data
    end

    start = lambda do |line, data|
    if line.include? SECTION_TAG
    return section_tag_found, data
    end
    return start, data
    end

    next_state = start
    data = []
    File.foreach("ffiii2_monster_taming_faq.txt") do |line|
    next_state, data = next_state.(line, data)
    end
    Starting from the bottom, we can see that we kickoff the FSM by looking for the SECTION_TAG, which is "PassADe" in this case. We have to find it three times instead of the two times we looked for the section tag in the monster FSM because the tag appears one extra time in the FAQ in a sub-contents under the main "Monster Infusion" section. Then, we look for the ABILITY_SEPARATOR, which is the same as the MONSTER_SEPARATOR from the previous script, and then go into a loop of matching and storing the ability data in the data list of hash tables. The ability data has a pretty clean format with the possibility of overflowing to a second line. The first couple abilities showcase the variations we have to handle:
    Uncapped Damage (RL)---------: Raises the cap on damage to 999,999.
    Enhanced Commando (RL)-------: Enhances the Commando Role Bonus by one Bonus
    Boost.
    We have a name that can be multiple words (and by looking further ahead we also see some special characters) followed by a space and a rank in parentheses, a -: separator, and the description, possibly extending to a second line. We want to capture the name, rank, and description, and the rank can be either "RL" (for Red Lock, meaning it can never be removed from the monster) or a number 1-9. From what we've learned of regexes in past posts, it should be clear that the regex

    /(\w\S+(?:\s[^\s\(]+)*)\s\((RL|\d)\)-*:\s(\w\S+(?:\s\S+)*)/

    is sufficient to match on the first line and capture those three attributes. Looking at the rest of the new_ability state code, we can see that if the line doesn't match the ABILITY_REGEX, it checks if the line is the ABILITY_SEPARATOR and moves to the last state if it is. Otherwise, we know that the line is an extra line of description, so we capture it and add it to the description of the last ability captured. If the line did match the ABILITY_REGEX, then we create a new hash with the values populated from the regex captures and continue in the same state.

    Note that if the rank was "RL", we convert that to "99" so that all ranks will be integers in the database. Why not "10?" Well, when an ability is yellow-locked, its rank increases by nine, so making the red-locked abilities have a rank of 10 would conflict. I could have made it 19 or 20, but 99 works just as well and really sets them apart. We also need to add the " (RL)" text back into the name of the ability because it's possible to have red-locked and non-red-locked abilities with the same name, and they are different abilities for all intents and purposes.

    Validating and Exporting Monter Abilities

    Next, we want to do a little validation check on this data and write it to a .csv file, so here's the code to do that:
    PROPER_NAME_REGEX = /^\w.*[\w)!%]$/
    NUMBER_REGEX = /^\d\d?$/
    FREE_TEXT_REGEX = /^\S+(?:\s\S+)*$/

    VALID_ABILITY = {
    "name" => PROPER_NAME_REGEX,
    "rank" => NUMBER_REGEX,
    "description" => FREE_TEXT_REGEX,
    }

    data.each do |ability|
    VALID_ABILITY.each do |key, regex|
    if ability.key?(key)
    unless ability[key] =~ regex
    puts "Monster ability #{ability["name"]} has invalid property #{key}: #{ability[key]}."
    end
    else
    puts "Monster ability #{ability["name"]} has missing property #{key}."
    end
    end
    end

    require 'csv'
    opts = {headers: data.first.keys, write_headers: true}
    CSV.open("monster_abilities.csv", "wb", opts) do |csv|
    data.each { |hash| csv << hash }
    end
    Since we're building up each hash table directly with name, rank, and description, it's not necessary to make sure that each hash contains all three attributes and no others. We just want to make sure that each attribute value has the right (admittedly loose) format. Then we write it out to the monster_abilities.csv file.

    Importing Monster Abilities

    Moving right along, we can now import this .csv file into the database with another simple addition to our seed script:
    csv_file_path = 'db/monster_abilities.csv'

    CSV.foreach(csv_file_path, {headers: true}) do |row|
    Ability.create!(row.to_hash)
    puts "#{row['name']} added!"
    end
    Before running this script, we need to generate the Ability model and its schema. Since there are only three attributes, we can easily create the migration script in one shot:
    $ rails generate model Ability name:string rank:integer description:string
    This command will create this migration file:
    class CreateAbilities < ActiveRecord::Migration[6.0]
    def change
    create_table :abilities do |t|
    t.string :name
    t.integer :rank
    t.string :description

    t.timestamps
    end
    end
    end
    Now we can run the migration and then seed the database:
    $ rails db:migrate
    $ rails db:seed
    And we've added our second table to the database with 147 abilities. (Wait, I thought there were over 200 abilities. Good catch. We'll get to that in a bit.) However, we should have a link between this new ability table and the monster table because every monster's many abilities should all be one of the abilities in the ability table.

    Associating Monsters' Abilities with the Abilities Table

    What we want to do in order to associate each monster's abilities with the abilities table is make each ability in the monster table a reference instead of a string, and point those references at the correct specific abilities in the abilities table. To accomplish this association, we'll need to change both the monster table migration and the monster model part of the seed script.

    First, we can change all of the t.string declarations in the monster table migration to t.references. Changing only the abilities that end in "_passive" is sufficient for now because those are the abilities we created in the abilities table. The references for these abilities will actually be foreign keys from the abilities table, so we need to tell Rails that at the end of the migration:
    class CreateMonsters < ActiveRecord::Migration[6.0]
    def change
    create_table :monsters do |t|
    t.string :name
    # ... The other monster attributes ...
    t.references :default_passive1
    t.references :default_passive2
    t.references :default_passive3
    t.references :default_passive4
    t.string :default_skill
    t.string :special_notes
    t.references :lv_02_passive
    t.string :lv_02_skill
    # ... The rest of the lv XX abilities ...

    t.timestamps
    end

    add_foreign_key :monsters, :abilities, column: :default_passive1_id, primary_key: :id
    add_foreign_key :monsters, :abilities, column: :default_passive2_id, primary_key: :id
    add_foreign_key :monsters, :abilities, column: :default_passive3_id, primary_key: :id
    add_foreign_key :monsters, :abilities, column: :default_passive4_id, primary_key: :id
    add_foreign_key :monsters, :abilities, column: :lv_02_passive_id, primary_key: :id
    # ... All of the other _passive abilities foreign keys ...
    end
    end
    I know this seems like tedious busy work, but it's necessary for setting up the database. Some of it can be done with regex replace tools in some editors or generated with a script. I just brute-forced it and got pretty good at changing numbers really rapidly. Be sure to remove lv_xx_passive abilities that don't exist later in the list.

    Because we changed this migration, we need to rerun it, but now it references the ability migration so when we rollback the migrations, we need to change the date in the file name on the monster migration to be after the ability migration before we run the migrations forward again. Otherwise, Rails will complain.
    $ rails db:rollback STEP=2
    $ [rename the monster migration file to a date after the ability migration file]
    $ rails db:migrate
    We're not quite done with preparing the database, yet, because the Rails models need to know about this association, too. The app/models/monster.rb file needs to know that the passive ability attributes are associated with the Ability model using the belongs_to association:
    class Monster < ApplicationRecord
    belongs_to :default_passive1, class_name: 'Ability', optional: true
    belongs_to :default_passive2, class_name: 'Ability', optional: true
    belongs_to :default_passive3, class_name: 'Ability', optional: true
    belongs_to :default_passive4, class_name: 'Ability', optional: true
    belongs_to :lv_02_passive, class_name: 'Ability', optional: true
    # ... The rest of the lv_XX_passive attributes ...
    end
    It may sound weird that a monster belongs to an ability, but that's the type of association we want where the link points from the monster attribute to the ability. It's the same as if we had an Author model and a Book model, and the book belongs to the author that wrote it. The link would be in the book with the author's ID. That same link direction is what we want with monsters having links to ability IDs, so belongs_to it is.

    We also need to add associations to the Ability model so that we can follow links from abilities to monsters. That association is done with has_many, which also seems a bit weird, but oh well:
    class Ability < ApplicationRecord
    has_many :default_passive1_monsters, :class_name => 'Monster', :foreign_key => 'default_passive1'
    has_many :default_passive2_monsters, :class_name => 'Monster', :foreign_key => 'default_passive2'
    has_many :default_passive3_monsters, :class_name => 'Monster', :foreign_key => 'default_passive3'
    has_many :default_passive4_monsters, :class_name => 'Monster', :foreign_key => 'default_passive4'
    has_many :lv_02_passive_monsters, :class_name => 'Monster', :foreign_key => 'lv_02_passive'
    # ... The rest of the lv_XX_passive attributes ...
    end

    That was the easy, if mindlessly tedious, part. The next step is trickier. We want to change the db seed script so that when the monsters are created, the passive abilities in the monster table are references to the abilities table, and we want to catch any instances where the ability doesn't exist, meaning there was a typo or an omission. We need to make sure the ability table is populated first in the script, so we have access to those abilities when we're importing the monsters. Then, for each passive ability for each monster we search the ability table for that ability's name, and assign it to the corresponding ability attribute for the monster. That assignment creates the proper reference. If the ability isn't found, we print an error and return so the error can be fixed in the FAQ text file. We'll then have to rerun the ability or monster parser and try the import again. Here's what this process looks like in code, including the ability import:
    csv_file_path = 'db/monster_abilities.csv'

    CSV.foreach(csv_file_path, {headers: true}) do |row|
    Ability.create!(row.to_hash)
    puts "#{row['name']} added!"
    end

    csv_file_path = 'db/monsters.csv'

    CSV.foreach(csv_file_path, {headers: true}) do |row|
    monster = row.to_hash
    monster.keys.select { |key| key.ends_with? '_passive' }.each do |key|
    if monster[key]
    monster[key] = Ability.find_by(name: monster[key])
    if monster[key].nil?
    puts "ERROR: monster #{monster['name']} #{key} not found!"
    return
    end
    puts "Found #{key} #{monster[key].name}"
    end
    end
    Monster.create!(monster)
    puts "#{row['name']} added!"
    end
    This code is pretty much written as just described, but note that we check if the monster[key] exists before doing anything with it. Remember that these are optional attributes, so they can be nil for any given monster ability. If we run this script, we'll find right away that Cactuaroni has an ability "Critical: Haste (RL)" that doesn't exist in the ability table. We could add this ability to the FAQ and rerun the parser, but notice that there is an ability "Critical: Haste" already in the table. This issue of the non-red-locked ability existing in the table but not the red-locked version is fairly common, so we could solve the problem with code instead of tediously rerunning the scripts to find all of the instances where the red-locked ability is missing. All we have to do is search for the base ability, copy it, change the rank to 99, and tack " (RL)" onto the name. This process can be done like so:
    CSV.foreach(csv_file_path, {headers: true}) do |row|
    monster = row.to_hash
    monster.keys.select { |key| key.include? '_passive' }.each do |key|
    if monster[key]
    if monster[key].ends_with?(' (RL)') && Ability.find_by(name: monster[key]).nil?
    ability_name = monster[key][0..-6]
    puts "Searching for #{key} ability #{ability_name}"
    ability = Ability.find_by(name: ability_name).dup
    ability['name'] = monster[key]
    ability['rank'] = '99'
    ability.save
    puts "Ability #{ability['name']} added!"
    end

    monster[key] = Ability.find_by(name: monster[key])
    if monster[key].nil?
    puts "ERROR: monster #{monster['name']} #{key} not found!"
    return
    end
    puts "Found #{key} #{monster[key].name}"
    end
    end
    Monster.create!(monster)
    puts "#{row['name']} added!"
    end
    If the monster ability ends with " (RL)" and it's not in the ability table, then we copy the base ability and create the corresponding red-locked ability from it. We've sidestepped a bunch of dreary work with a little bit of targeted code!

    Now after running this script to seed the database, we will find a few more errors in the FAQ to fix. Four red-locked abilities are missing, so we'll have to add Perpetual Poison, Resist Damage +05%, Bonus CP, and Feral Speed. I found definitions for these abilities simply by googling them. The Resist Elements +20% ability that Twilight Odin has was also missing. I guessed at the rank of 8 for this one because I couldn't find it. Resist Elements +30% has a rank of 9 and Resist Elements +05% has a rank of 5, so it's most likely rank 7 or 8. Finally, there were three typos: the ability "Auto: Enfire (RL)" should not have the colon, the ability "Auto Haste (RL)" should be hyphenated, and the ability "ATB: Advantage (RL)" should not have the colon. After everything is fixed, we have a complete ability table with 218 abilities, and every passive monster ability is linked correctly to the ability table.


    That was quite a lot of work, some of it tedious, but we accomplished a lot. We generated a list of passive abilities from the FAQ, validated that data, imported it into a new table in the database, and linked all of those abilities to the monsters that can learn them in the monster table. This process can be repeated for the much smaller tables that are left to create, and that is what we'll do next time.

    Thursday, April 9, 2020

    HOTT 52 - Battle 4 - Attack On An'Burkag's Encampment

    I fought my HOTT 52 Week 4 battle over lunch today and had a blast! I tried out the Magician unit for the first time. After the battle report, I'll give you my thoughts on this unit and how it plays out.

    To create my armies I have tables that represent armies when they're all militia, all regular/professional, or a mix of the two. Regular armies have (more) elements like heroes, magicians, blades and knights. Militia armies have more of riders, warbands, spears and hordes, plus the cleric that sees to the needs of the common folk pressed into service!

    This battle, I wanted to mix it up, so I rolled randomly for regular/militia or militia/regular, then rolled to see which exact army was to be used. The human army would be a militia/regular army. The Orc army, led by the War Chief An'Burkag, would be regular/militia.

    Humans
    General/Spear x1 (2 AP)
    Spears x3 (6 AP)
    Shooters x2 (4 AP)
    Riders x2 (4 AP)
    Knights x2 (4 AP)
    Blades x2 (4 AP)
    Orcs
    General/Knights x1 (2 AP)
    Knights x1 (2 AP)
    Blades x4 (8 AP)
    Spear x2 (4 AP)
    Shooters x2 (4 AP)
    Magician x1 (4 AP)



    The orcs were supposed to have Spear x3 and Hordes x2, but I swapped them out for the Orc Shaman as my Magician experiment.





    The orcs lined up outside of their camp, howling and cursing at the humans who appeared on the horizon. They anchored their right flank with an impassable hill.

    The human commander put her knights and riders on the flanks with the intent of sending the speedy riders to threaten the stronghold or Orc reserves.



    The Orc Shaman Oz'turk, seeing the hateful Human Knights to their left, moved over to that flank, preparing foul spells to cast at them. Meanwhile, An'Burkag detailed a unit of Orc Spears to line up with him to prevent the Human Riders from flanking them or attacking their camp!



    The Human army rolled down on top of the orc defense and pushed them all back!



    Undaunted, the orcs and goblins gnashed their teeth, gripped their cold iron weapons and charged back at the humans, pushing them back!

    (It was an amazing story the dice told! Every orc unit on the line recoiled when it was the human's bound, and almost the same in reverse when it was the orc's bound!)



    The human knights, seeing a potential trap, charged at the flanking Orc knights. With nowhere to go, the retreating Orc Knights crashed into the melee between goblin and human archers. The unit broke up! Unfortunately, the impetuous Human knights made a mistake in turning to attack the Orc Shaman. When they attacked, they were overcome by his foul magicks!

    (In HOTT, if a Knight (Kn) attacks a Magician and suffers a minor defeat in that same bound, they are destroyed! I had to reread that rule several times to make sure I understood it. It's subtle in the wording... if the Magician had charged/initiated contact, then the way the rule reads, the Kn would not have been destroyed!)



    The Orcs also protected their rear/right flank, with the Orc spear destroying the light Rider cavalry. The orc line, however, suffered several defeats and began to fragment.



    The human army pressed their advantage and pushed the orcs back. Warchief An'Burkag raced to reinforce the line and the Shaman Oz'turk rained spells and curses on both the flanking knights and units in the humans army, but they had little effect.



    In the end, the orcs were unable to mount a serious defense and An'Burkag was forced to sound the horns of retreat. The humans marched upon the remains of the camp and burned it all to the ground, celebrating a hard fought victory!

    Orcs: 12AP lost, Humans: 8 AP lost.

    I rolled quite a few 6 to 1 combat rolls, in favor of the humans, which almost always spells defeat for the orcs. Once the line developed gaps, the bonuses for overlaps from the intact human line spelled the story out. Which was surprising, as the defeats on the flank really blunted whatever advantage I thought the humans would need.

    So.. the Magician unit. Fun! It's basically an advanced artillery unit, being able to launch spells at 3x the distance of shooters, a little further than regular artillery. It does require PIPs though, so it requires a choice to be made between moving a unit or two, or using spells. Unfortunately, out of 4 attempts at a spell, only one had any sort of effect (a recoil), so the results weren't as good as I hoped. I was taken by surprise at the result of the Magician killing the Knight unit, though! I'll remember that for the future.

    I don't know how this would have turned out if the Orcs had kept their Hordes and extra Spear unit, but it was a fun element to play with. Now I need to try a Cleric... and I need to buy 15mm human Wizards and Clerics! I don't have any!

    PS. If you're curious about my force composition tables, as well as how I conduct my fantasy wargames campaigns, here's a link to my rules. https://drive.google.com/open?id=18CUi40wW6OSm4FhTzdOoH-ct0NxW9jImGyF-okiMOrk