{
	"id": "5bac6fa6-df47-4697-9138-61c7a4984817",
	"created_at": "2026-04-10T03:20:53.656144Z",
	"updated_at": "2026-04-10T13:11:49.564679Z",
	"deleted_at": null,
	"sha1_hash": "c3a766d2b36db56dc38ca95a68e019fc5bb81b5c",
	"title": "The DGA of Pitou - Analyzing a Virtualized Algorithm",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 505285,
	"plain_text": "The DGA of Pitou - Analyzing a Virtualized Algorithm\r\nArchived: 2026-04-10 03:16:17 UTC\r\nThe domain generation algorithm (DGA) of Pitou runs in kernel mode and is protected by a virtual machine,\r\nwhich makes it the hardest DGA I have reverse-engineered so far.\r\nThis blog post shows, after an introduction, how the virtual machine of Pitou works. Afterward, reverse\r\nengineering of the bytecode is covered showing two approaches. As a result, the DGA was extracted and\r\nreimplemented in Python. Documentation of the full virtual instruction set in the Appendix completes the blog\r\npost.\r\nIntroduction\r\nIn my opinion, DGAs are one of the most accessible components of malware. One can usually reverse-engineer\r\nthe algorithm from an unpacked sample within a few hours. Why is this so?\r\n1. DGAs are usually not explicitly protected.\r\n2. DGAs are relatively easy to localize. Be it via API calls, such as DnsQuery and gethostbyname or by\r\npatterns, e.g., add al, 61h .\r\n3. DGAs are usually concise and easy to read. All the more so if decompilers such as Hex Rays are used —\r\nthey can produce results that are presumably very close to the source code.\r\n4. DGAs are mostly coherent. For example, the letters of the domain name are determined in a loop one after\r\nthe other, by mapping a random number to the letter a-z.\r\nThe DGA of Pitou is different in all four aspects:\r\n1. The entire domain generation algorithm, including seeding, is virtualized. Virtual machines are a\r\nparticularly effective form of code protection and challenging to analyze, or at the very least incredibly\r\ntime-consuming.\r\n2. Pitou is a rootkit with dynamically resolved API calls. Furthermore, Pitou uses NDIS hooking to obscure\r\nnetwork communication. The usual DGA patterns also do not work for the reason given in point 1.\r\n3. The DGA is pretty long, with a complicated date-based calculation for seeding.\r\n4. The DGA has two major bugs, which make it much more difficult to understand.\r\nThese reasons, especially the first, make Pitou by far the hardest domain generation I have ever analyzed.\r\nPrevious Work\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 1 of 84\n\nThis blog post focuses exclusively on the analysis of the DGA of Pitou. All other aspects are deliberately not\r\ncovered.\r\nIn August 2014, the F-Secure researchers published an excellent report on Pitou — I highly recommend reading\r\nthe report to learn more about the characteristics of Pitou. The accompanying blog post briefly answers the most\r\nimportant questions concerning Pitou. The report has a dedicated section on the DGA that mentions a few\r\nproperties of the algorithm. However, the algorithm itself is not listed, nor are details of the virtual machine that\r\nruns the DGA.\r\nIn January 2016, Symantec published an entry on their security center. It lists 20 domains that the malware might\r\nconnect to, without mentioning that these are DGA domains with limited lifetime.\r\nIn January 2018, TG Soft’s Research Centre (C.R.A.M.) published a blog post on Pitou. The post lists four\r\ndomains but does not mention, let alone discuss, the domain generation algorithm that produced those domains.\r\nFourteen days before this blog post was released, Brad Duncan published a diary entry on the SANS Internet\r\nStorm Blog titled Rig Exploit Kit send Pitou.B Trojan. He also wrote two entries on his personal blog Malware-Traffic-Analysis.net about two different Pitou samples. This shows that Pitou and its DGA are still relevant — 5\r\nyears after F-Secure’s report. As the Table at the end of the blog post shows, the DGA with the original seed is still\r\nin use, which can probably be attributed to the good protection of the DGA.\r\nExample Domains\r\nThe following Wireshark screenshot shows the 20 domains queried on June 20th, 2019:\r\nThe domains use some uncommon TLDs such as .mobi and .me, and stand out as artificially generated, despite\r\nconsonants and vowels somewhat alternating to give pronounceable names.\r\nAnalyzed Sample\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 2 of 84\n\nI reverse-engineered the following file. It is detected as Pitou by ESET, Ikarus, and Microsoft:\r\nMD5\r\n28060e9574f457f267fab2a6e1feec92\r\nSHA1\r\n9529d4e33498dd85140e557961c0b2d2be51db95\r\nSHA256\r\n43483385f68ad88901031ce226df45b217e8ba555916123ab92f63a97aef1d0e\r\nfilesize\r\n522K\r\ncompile timestamp\r\n2017-10-31 10:15:25 UTC\r\nlink\r\nVirusTotal\r\nThe file unpacks to this binary, which is now also detected as Pitou by Avast, AVG, and Fortinet.\r\nMD5\r\n70d32fd5f467b5206126fca4798a2e98\r\nSHA1\r\n6561129fd718db686673f70c5fb931f98625a5f0\r\nSHA256\r\nf43a59a861c114231ad30d5f9001ebb1b42b0289777c9163f537ac8a7a016038\r\nfilesize\r\n405K\r\ncompile timestamp\r\n2017-08-22 10:24:10 UTC\r\nlink\r\nVirusTotal\r\nThe above executable then drops the rootkit. Pitou contains a 32-bit and a 64-bit module to support both\r\narchitectures.\r\n32-bit\r\nMD5\r\n9a7632f3abb80ccc5be22e78532b1b10\r\nSHA1\r\n2d964bb90f2238f2640cb0e127ce6374eaa2449d\r\nSHA256\r\nab3b7ffaa05a6d90a228199294aa6a37a29bb42c4257f499b52f9e4c20995278\r\nfilesize\r\n431K\r\ncompile timestamp\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 3 of 84\n\n2017-03-22 01:21:01 UTC\r\nlink\r\nVirusTotal\r\n64-bit\r\nMD5\r\n264a210bf6bdded5b4e35f93eca980c4\r\nSHA1\r\n8f6ff0dd9b38c633e6f13bde24ff01ab443191f6\r\nSHA256\r\nddb82094dec1fc7feaa4d987aee9cc0ec0e5d3eb26ba9264bb6ad4aa750ae167\r\nfilesize\r\n478K\r\ncompile timestamp\r\n2017-02-27 06:13:41 UTC\r\nlink\r\nVirusTotal\r\nI only looked at the 64-bit variant.\r\nThe Virtual Machine\r\nThis section covers the virtual machine that protects the DGA, as well as other functions of Pitou components.\r\nThe first part shows the main components of the virtual machine. The second part then discusses the properties of\r\nthe VM and its bytecode.\r\nComponents\r\nThis video of Tim Blazytko and Moritz Contag gives an excellent introduction to the main components of a virtual\r\nmachine, which are:\r\nVM Entry / VM Exit\r\nVM Dispatcher\r\nHandler Table\r\nVM Entry\r\nVM entry and exit are responsible for context switching. The VM entry copies the native context (registers and\r\nflags) to the virtual context. The VM is entered in the following screenshot:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 4 of 84\n\nFour arguments are passed to the virtualized DGA according to the x64 calling convention (in rcx , rdx , r8 ,\r\nand r9 ). I will revisit those arguments and their meanings when discussing the arguments to the DGA. The call\r\nto the DGA pushes a return value on the stack, which is later used in VM exit in a ret call to jump back to the\r\nnative code right after the call to the dga. The call leads to this native code island:\r\nThe call, which is 5 bytes long, is in the middle of the bytecode that the VM executes. The purpose of the call is to\r\npush the address of the following address (marked with entry_point_bytecode ) on the stack. This address is the\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 5 of 84\n\nentry point (which is not at the beginning of the bytecode) of the virtual DGA. The call then jumps to the start of\r\nthe virtual machine. The virtual machine is called from 46 different locations, meaning there are 46 different\r\nbytecode starting points, all probably implementing different components of Pitou, for example:\r\nSince native code, i.e., a call instruction, can be in the middle of the bytecode, the VM must be able to\r\nrecognize that instruction and skip it. We will see later how this is implemented.\r\nThe VM itself starts as follows:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 6 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 7 of 84\n\nThe native context is copied to the virtual context, which is accessed by the register rsi . The following content\r\nis copied:\r\nThe flags, by using pushfq\r\nThe general purpose registers rax , rbx , rcx , rdx , rdi , rsi and r8 through r15 . The\r\nregisters rip , rsp and rbp are not copied, as the VM itself uses those.\r\nThe XMM-registers, although none of the virtual instructions modifies them.\r\nThe last line of the screenshot, pop rax , removes the entry point to the virtual code from the stack. This entry\r\npoint is then also saved in the virtual context:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 8 of 84\n\nThe above screenshot also shows how VM entry checks if the entry point is inside the bytecode. I named the\r\nlowest address of the bytecode imagebase , and the highest address highest_addr . If the entry point is within\r\nthis range, the virtual instruction pointer is set to the entry point. This completes the VM entry.\r\nVM Dispatcher\r\nThe task of the dispatcher is to fetch and decode the instructions. Usually — and the VM of Pitou is no exception\r\n— a handler belonging to the opcode is called. This handler is also responsible for updating the context of the\r\nVM, especially the instruction pointer.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 9 of 84\n\nThe following screenshot shows the VM dispatcher. First, the virtual instruction pointer is read (1). If the control\r\nregister CR8 is set, the instruction pointer converts to an offset from the entry point, and a software interrupt is\r\ntriggered (2). The dispatcher then reads a byte at the instruction pointer. If it corresponds to E8 (4), the VM is at\r\na native call, as used for passing the entry point to the VM (see section VM Entry). The five bytes of the call are\r\nsimply skipped (5). All other byte values are valid bytecode. The 6 least significant bits correspond to the opcode,\r\nwhich refers to a function from the handler table. The VM then jumps to this function (6).\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 10 of 84\n\nThe 6 bit can represent up to 64 different functions, of which 0x28 is excluded because that corresponds to the\r\nleast significant 6 bits of the reserved byte 0xE8 . However, only the first 29 entries in the handler table point to\r\ndifferent routines; I labeled them instruction_00 to instruction_1C according to the primary opcode that\r\ninvokes them. As of opcode 0x1D , previous functions are reused, e.g., 0x1D uses the handler of opcode 0x03 .\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 11 of 84\n\nSome handler functions are accessed by exactly one opcode, and others have multiple opcodes. For example,\r\n0x07 , 0x47 , 0x87 , 0xc7 all map to the same handler.\r\nVM Exit\r\nThe virtual jmp/call/ret instruction (handler 0x02 ) also handles VM exit. The appendix discusses this handler in\r\ndetail. The next screenshot of parts of the handler shows how the native registers are restored from the virtual\r\ncontext. There isn’t much more to exiting the VM; the handler at the end just returns to the code following the VM\r\nentry, by using the address on the stack.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 12 of 84\n\nProperties\r\nThe virtual machine of Pitou is stack-based and executes 64-bit code. At least the 64-bit module does. The 32-bit\r\nmodule probably has 32-bit virtual instruction. This section describes the properties of the virtual machine. A\r\ncomplete list of all instructions can be found in the Appendix.\r\nRegisters\r\nThe VM uses virtual copies of the x64 general purpose registers and flags. As expected, it also has a virtual\r\ninstruction pointer, a virtual stack pointer, and a virtual base pointer. In addition, two state registers are available,\r\nwhich are used for jumps:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 13 of 84\n\nInstruction-Set\r\nThe virtual instructions have a variable length from 1 byte to 11 bytes. As section VM Dispatcher has shown, the\r\nlowest 6 bits of the first byte are the opcode that determines the handler function. The highest 2 bits can be used to\r\nselect variants within the handlers. The first byte is the only mandatory one. In fact, many virtual instructions are\r\nonly 1 byte long. Often determined by one of the two bits of the prefix, one or two optional specifier-bytes follow.\r\nThe format varies from instruction to instruction (for example, it could contain information about which memory\r\nsegment the instruction uses, whether the following operand is signed, or which size it has). After the optional\r\nspecifier, an optional operand might follow.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 14 of 84\n\nOperands are stored in little-endian order and can be bytes, words, double-words, or quad-words. Operands and\r\nthe specifier are XOR-encrypted with the following keys:\r\nBytes are encrypted with 0x57\r\nWords are encrypted with 0x13F1\r\nDouble-words are encrypted with 0x69B00B7A\r\nQuad-words are encrypted with 0x7EF5142A570C5298\r\nFor example, the operand AB 01 is encrypted to the value 0x125A ( 0x01AB XOR 0x13F1 ). Jump targets are\r\nrelative to the start of the virtual code. For example, if the virtual code starts at 0xFFFFF87EC582C000 , then a jump\r\nwith decrypted operand 0x123 would set the virtual instruction pointer to 0xFFFFF87EC582C123 . Instruction\r\n0x01, Instruction 0x04, Instruction 0x06 and Instruction 0x18 can make use of addresses relative to the virtual\r\ncode. One handler, Instruction 0x18, can also use addresses relative to the start of the executable. This allows the\r\nhandler to access memory outside of the VM. The DGA uses this addressing to read static strings such as the list\r\nof top level domains. This is the only instruction that is not position-independent, i.e., which would need to be\r\nrelocated, if the virtual code were to be placed elsewhere.\r\nLet’s look at an example: the virtual instruction JZ 0xfffff8800586c445 — jump to address\r\n0xfffff8800586c445 if the zero bit is set — is encoded as follows:\r\nConditional jumps are in handler 0x06 . This handler uses both prefix bits.\r\nThe first prefix bit determines whether the condition is unary ( 0 ), or a binary comparison ( 1 ). In our\r\ncase, we have a unary condition. The second prefix bit only applies to binary comparisons. All in all the\r\nfirst byte is 0x06\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 15 of 84\n\nThe first (most significant) two bits of the specifier determine how the flags are combined. 1 0 means the\r\ncondition is satisfied if all selected flags are set. The remaining 6 bits are a bitmask for various flags. The\r\nJZ instruction only has to look at the ZF flag, so only this bit is set in the bitmask. The whole specifier\r\nis 0xDF , which is then encrypted with key 0x57 to get the final value 0x88 .\r\nThe jump target is calculated as follows: Subtract the image base ( 0xfffff87EC582C000 ) from the target\r\n( 0xfffff8800586c445 ), then XOR encrypt the result (key 0x7EF5142A570C5298 ).\r\nVirtual Stack\r\nSince the virtual machine is stack-based, the stack is of particular importance. Although the VM emulates 64-bit\r\ncode, i.e., it handles values of 64-bit length at most, the stack is 128-bit wide. Each stack entry consists of two 64-\r\nbit slots. Most instructions only use the slot stored at the lower address, shown on the left in all stack plots. I refer\r\nto this slot as value or regular storage. The other slot, 64-bits higher than the first one, is often used to store the\r\naddress of the value in the left slot (value slot). I refer to this slot as the extra slot.\r\nAs usual, the stack follows the “last in first out” principle. It grows towards smaller addresses and shrinks towards\r\ngreater addresses. The virtual stack register variable points to the last item in the stack (full stack implementation).\r\nReverse Engineering\r\nThis section covers reverse engineering of the virtual machine. At first, I had written a disassembler but was not\r\nsatisfied with its output; respectively it was still too complicated to read. Especially the lack of tools for the user-defined instructions made analysis difficult.\r\nThe second approach was to bring the virtual code back to C code. This worked surprisingly well and resulted in\r\nan algorithm that was easy to understand despite complexity and bugs. I use a part of the DGA as an example to\r\nillustrate the four steps. The snippet corresponds to a simple mathematical statement, which can be written as a\r\nsingle line in C This should give a sense of how long the results of the individual steps are and how difficult they\r\nare to read.\r\nApproach 1: Disassembler\r\nThe usual steps to analyze a VM are as follows (see Practical Reverse Engineering: x86, x64, ARM, Windows\r\nKernel, Reversing Tools, and Obfuscation:\r\n1. Explore how the instructions are decoded from the bytecode, especially which bits are the opcode that\r\ndetermines the handler function. As shown in section VM dispatcher, the lowest 6 bits of the first byte\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 16 of 84\n\nspecify the handler that decodes the rest of the instruction and also sets the instruction pointer forward.\r\n2. Understand the architecture of the VM. This has already been described for the post part in section The\r\nVirtual Machine.\r\n3. Finally, the handlers have to be analyzed. This is the most time-consuming part of the analysis. In the case\r\nof Pitou, 29 different functions have to be reverse-engineered.\r\nThe following screenshot shows the first handler for opcode 0x00 . The function is not all that long and\r\nfortunately is not obfuscated.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 17 of 84\n\nThe following illustration shows the instruction encoding and the effect on the stack for opcode 0x00 (and the\r\naliases 0x2F and 0x35 ):\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 18 of 84\n\nThe fields of the encoded instruction have the following meaning:\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x00 , 0x2F or 0x35\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\nThe handler uses the first bit of the prefix to determine whether an optional size specification follows. If the bit is\r\nnot set, the operation is assumed to be 32-bit, and the instruction is only one byte long. If, on the other hand, the\r\nprefix bit is set, then another byte must follow, of which the lowest two bits determine the size of the operation.\r\n0 always stands for Byte (8bit), 1 for Word (16bit), 2 for Dword (32-bit, the default), and 3 for Qword (64-\r\nbit). The instruction pops two values from the stack, XORs them and pushes the result back on the stack, casting\r\nthe values to the specified size. It is straightforward to reimplement this handler by the disassembler:\r\n1. check if the has size bit is set, if yes, read the next byte and determine the data width based on the last\r\ntwo bits ( 0 means Word, etc.). Otherwise, default to size Dword.\r\n2. output of XOR \u003csize\u003e , e.g., XOR dword .\r\n3. if the has size bit is set, increase the instruction pointer by 2, if not by 1.\r\nAfter reimplementing all 29 handlers, the bytecode can be disassembled. The following excerpt from the\r\ndisassembled bytecode shows the virtual instructions and their effect on the stack for the x64 instruction: XOR\r\ndword edx, edx , i.e., zeroing register edx .\r\nFFFFF8800585817C NOP\r\n (empty stack)\r\nFFFFF8800585817D PUSH dword (rdx, addr(rdx))\r\n | rdx | addr(rdx) |\r\nFFFFF8800585817F PUSH dword (addr(rdx), addr(rdx))\r\n | addr(rdx) | addr(rdx) |\r\n | rdx | addr(rdx) |\r\nFFFFF88005858181 DREFH dword\r\n | rdx | addr(rdx) |\r\n | rdx | addr(rdx) |\r\nFFFFF88005858182 XOR dword\r\n | edx XOR edx | addr(rdx) |\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 19 of 84\n\nFFFFF88005858183 P2E dword\r\n (empty stack)\r\n1. The first instruction, NOP does nothing.\r\n2. The register rdx is pushed on the stack with handler 0x12, but using two different settings for the push\r\naddress flag: the first PUSH pushes the value of the register rdx to the value slot of the stack, the\r\nsecond PUSH pushes the address of register rdx (in both cases, the address is stored in the extra slot of\r\nthe stack entries).\r\n3. The address of register rdx on top of the stack is dereferenced by DREFH dword to obtain rdx . Both\r\nentries on the stack are now equal. Why the compiler didn’t push the register in the first place instead of\r\nthe address is not clear to me.\r\n4. The XOR dword instruction takes the values from the two topmost stack entries, xors them and pushes the\r\nresult back on the stack. The extra slot remains the same, i.e., at the value that was 1 entry from the top\r\nbefore the XOR operation.\r\n5. Finally, the P2E dword instruction pops entry from the stack, and moves the value slot ( edx XOR edx to\r\nthe memory given by the extra slot — addr(rdx) . This means, the virtual register rdx is set to 0 .\r\nHere is the bytecode for our example snippet, which, as mentioned in the introduction, corresponds to a simple\r\nmathematical statement in the DGA:\r\nThe disassembler produces the following lines from the above bytecode:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 20 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 21 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 22 of 84\n\nThe first number is the address of each line. The number in the blue square is the handler. Next follow the bytes\r\nthat make up the instruction. Most are 1 or 2 bytes long, but there are also much longer instructions such as the\r\nconditional jump with 10 bytes length. The mnemonic of the instruction follows after the bytecode, with a bit of\r\nsyntax highlighting. Since I quickly abandoned the disassembler approach, not all mnemonics are particularly well\r\nchosen and meaningful. They are all listed in the Appendix though.\r\nOne could probably extract the DGA from the output of the disassembler with some patience. However, I did not\r\ntry for long, because:\r\n1. The output of the disassembler is very long, altogether 3681 lines.\r\n2. The virtual machine uses special instructions, like the P2E or DREFH , as shown above. These are new\r\nand would need practice to grasp quickly.\r\n3. There are no tools that display the output nicely, e.g., a graph view would help to see the code flow.\r\nThere are multiple ways to make the disassembly more readable, for instance:\r\n1. Add patterns to reduce the number of lines. For example, the 6 lines that make up the XOR edx, edx code\r\nabove are quite common. Clever pattern matching could reduce the number of lines drastically.\r\n2. Implement the disassembler as an IDA Pro processor module. This would give a nice graph view, with all\r\nthe commenting tools available in IDA to make the code more readable.\r\nI didn’t pursue these ideas because I had a more tempting goal in mind: to decompile the virtual code to C. You\r\ncan run the disassembler yourself with the Python script on Github:\r\npython3 main.py disassembly -o pitou.dis\r\nApproach 2: Decompiler\r\nThis section shows how to convert the virtual code back to C. The plan is to first translate the bytecode to x64\r\nassembly, then assemble that to an x64 binary. IDA Pro can then open the binary, and produce x64 disassembly as\r\nwell as C code with the Hex Rays plugin. The purpose of those steps is to be able to use existing tools, in\r\nparticular, the Hex Rays decompiler. This is especially worthwhile because the DGA uses many instances of\r\ninteger division by multiplication, which are hard to read in assembly but are very well handled by decompilers.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 23 of 84\n\nStep 1: Dynamic Binary Translator\r\nThe only one of the four steps for which there exist no tools is the translation of the bytecode of the VM to x64\r\nassembly. The task of binary translation is to recompile the sequence of virtual instructions to x64 assembly.\r\nI decided to do dynamic translation by emulating the bytecode and simultaneously outputting the corresponding\r\nx64 instructions on the fly. To emulate the virtual instructions, I used the disassembler from step 1. This provides\r\nthe decoding of the instructions as well as the recursive traversal of the code. Since the VM is stack-based, the\r\nstack has to be emulated. Two elements per stack slot are tracked:\r\n1. An immediate or a register name. I labeled this the value and extra for the two slots of the stack.\r\n2. Only if (1) is a register, also the sequence of assembly instructions that lead to the register value on the\r\nstack. This list of instructions can be empty. I labelled the list of instructions value instructions and extra\r\ninstructions.\r\nThe following sections show how to emulate most categories of virtual instructions. I hope this clarifies the\r\nmethod. Either way, the source code of my binary translator can be referenced on Github for more details.\r\nUnary Operations\r\nThe virtual instructions that model unary operations are NEG; INC and DEC; NOT; and SHR, SHL, ROR, ROL,\r\nSAR, and SAL. They are all decompiled in the following manner:\r\n1. Pop a value from the stack. In the following example rcx .\r\n2. If the value is a register, cast it according to the size of the instruction, e.g., Dword would cast rcx to\r\necx . The cast register becomes the value of the new stack entry.\r\n3. Concatenate all value instructions from the popped stack element, if there are any. Then add \u003cmnemonic\u003e\r\n\u003csize\u003e \u003cvalue (cast)\u003e , e.g., NEG DWORD ecx . Set the sequence of instructions as the value instructions\r\nof the new stack entry.\r\n4. Copy the extra and extra instructions from the popped stack entry to the extra and extra instructions slots\r\nof the new entry.\r\n5. Push the new stack entry on the stack.\r\nThe decompiler only affects the stack; it does not write any output. The following table shows an example stack\r\nbefore the NEG handler is called (with size flag unset):\r\nvalue value instructions extra extra instructions\r\nrcx\r\nADD rcx, 10\r\nSHR rcx, 2\r\naddr(rcx)\r\nAnd after the handler is called:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 24 of 84\n\nvalue value instructions extra extra instructions\r\necx\r\nADD rcx, 10\r\nSHR rcx, 2\r\nNEG DWORD ecx\r\naddr(rcx)\r\nBinary Operations\r\nThere are 7 virtual instructions that perform a binary operation: XOR, SBB, SUB, OR, AND, CMP, and ADD,\r\nBinary arithmetic instructions are all handled similar to unary operations:\r\n1. Pop two values from the stack. In the following example [r10 + 1] and rax , with the first popped\r\nvalue being the source of the instruction, and the second being the destination.\r\n2. If the destination value is a register, cast it according to the size of the instruction, e.g., Word would cast\r\nrax to al . The cast target register becomes the value of the new stack entry.\r\n3. Concatenate all value instructions from the two popped stack elements, if there are any. Then add\r\n\u003cmnemonic\u003e \u003csize\u003e \u003ctarget value (cast)\u003e, \u003csrc value\u003e , e.g., XOR byte al, [r10 + 1] . Set the\r\nsequence of instructions as the value instructions.\r\n4. Copy the values extra and extra instructions from the second popped stack entry to the extra and extra\r\ninstructions slots of the new entry.\r\n5. Push the new stack entry on the stack.\r\nAs for the unary operations, the decompiler only changes the stack and does not write any output. The following\r\ntable shows the stack before the XOR handler is called, with size flag set and the size byte set to 0 (representing\r\nByte):\r\nvalue value instructions extra extra instructions\r\n[r10 + 1] SHR r10d, 2 r10 ADD QWORD r10, 1\r\nrax SHL rax, 2 addr(rax)\r\nThe XOR-operation changes the stack to:\r\nvalue value instructions extra extra instructions\r\nal\r\nSHR r10d, 2\r\nSHL rax, 2\r\nXOR BYTE al, [r10 + 1]\r\naddr(rax)\r\nMove Operations\r\nVirtual instructions that write data are M2E, M2V, P2E, and P2V. For example, the M2E instruction moves the\r\ndata in the value slot of the topmost stack to the memory location specified by the extra slot. Often, this extra slot\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 25 of 84\n\ncontains the address of a virtual register, which results in the value being moved to a register. For example, if\r\napplied to the following stack, the content of rsp would be written to rax :\r\nvalue value instructions extra extra instructions\r\nrsp SUB QWORD rsp, 8 addr(rax)\r\nSHL QWORD rax, 3\r\nADD QWORD rax, 1\r\nThis move corresponds to the assembly MOV rax, rsp . However, rsp and rax have been changed on the\r\nstack, as can be seen in the value instructions and extra instructions columns: these transformations have been\r\napplied on the stack. These operations need to be realized by the decompiler by simply writing them out before\r\nthe MOV statement:\r\nSHL QWORD rax, 3\r\nADD QWORD rax, 1\r\nSUB QWORD rsp, 8\r\nMOV rax, rsp\r\nIn almost all bytecode sequences of the VM, the destination and source are the same. In these cases, the MOV is\r\nremoved, e.g., MOV rsp, rsp would not be written. Now that the instructions have been realized, they are\r\nremoved from the respective stack slots if the handler did not already pop the stack entirely (like P2E , P2V do).\r\nThe value and extra columns stay the same.\r\nvalue value instructions extra extra instructions\r\nrsp addr(rax)\r\nPopping from the stack work the same way. A POP dword rax on this stack\r\nvalue value instructions extra extra instructions\r\nrdx SHR rdx, 2 addr(rdx)\r\nresults in an empty stack, and the binary translation writes:\r\nSHR rdx, 2\r\nMOV DWORD eax, edx\r\nThere is a small problem with this approach. Consider these three lines of instructions that the above move might\r\nproduce (the first two lines stem from realizing the value instructions field, the last line is the actual move).\r\nSHL QWORD rax, 2\r\nADD QWORD rax, r11\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 26 of 84\n\nMOV QWORD rcx, rax\r\nThe issue here is that the first line modifies rax , which is then used again by the second instruction. The original\r\nvirtual register, however, would not have been changed, as the operations all happen on the stack. In this example,\r\nthere is a simple fix: The target of the operation, rcx , is not used in the previous assembly lines. It can,\r\ntherefore, serve as a substitute for register rax . This leads to an additional line of assembly, MOV rcx, rax , to\r\ncopy the value over. In return, the MOV rcx, rax can be omitted, since the calculations already refer to rcx :\r\nMOV rcx, rax\r\nSHL QWORD rcx, 2\r\nADD QWORD rcx, r11\r\nUnfortunately, this does not always work, as the following example shows:\r\nSHL QWORD rax, 1\r\nADD QWORD rax, rax\r\nIn this case, the calculations operate on the target rax of the move instruction, which is therefore eliminated. The\r\nfinal target rax is the same as the tainted register. In those cases, the binary translation uses r15 as the\r\ntemporary register. This register could, of course, be in use, so it is first stored on the stack, and then restored at\r\nthe end. I chose [rsp-1000] arbitrarily. Since the virtual machine does not use the native stack except for RET ,\r\nthis should not cause any problems. After r15 is saved, it receives the value of the tainted register rax with\r\nMOV r15, rax . Then follow the two original lines with rax replaced by 15 . Finally, r15 is moved back to\r\nrax and r15 is restored from the stack:\r\nMOV [rsp-1000], r15\r\nMOV r15, rax\r\nSHL QWORD r15, 1\r\nADD QWORD r15, rax\r\nMOV rax, r15\r\nMOV r15, [rsp-1000]\r\nJumps and Calls\r\nThe JMP, CALL and RET handler is in essence a unary operation with some additional steps. First, the value is\r\npopped from the stack, so\r\nvalue value instructions extra extra instructions\r\n0xFFFFF8800588FCBA 0xFFFFF8800588FCBA\r\nresults in an empty stack. If the value is a bytecode address, then decompiler prepends _addr_ to the hex string\r\nto produce a label for the jump target, i.e.:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 27 of 84\n\nJMP _addr_FFFFF8800588FCBA\r\nAt the target address, the label is also written:\r\n ADD QWORD rsp, 8\r\n RET\r\n_addr_FFFFF8800588FCBA:\r\n MOV DWORD eax, 1\r\nThe target can also be a symbolic expression. Of these, [rsp] is interesting, because JMP [rsp] is essentially\r\nRET . The decompiler snippet above shows an example of a RET being used in place of a JMP [rsp] .\r\nConditional Jumps\r\nThe conditional jump IF x → y is straightforward to implement: Determine the mnemonic according to the table\r\nin the previous section, and convert the destination to an absolute address, if given as a relative offset. The\r\nhandling of the jump target is the same as for handler 0x02. An example output would be:\r\nJNZ _addr_FFFFF8800588FCBA\r\nThe virtual data type conversions in handler 0x0B just need to be converted to the corresponding mnemonic and\r\noutput.\r\nDiscarding Stack Elements\r\nThe handler POPD removes n elements from the stack, and outputs all instructions of the value and extra slot, e.g.:\r\nvalue value instructions extra extra instructions\r\nr10d AND DWORD r10d, r10d addr(r10)\r\nr8d XOR DWORD r8d, r8d addr(r8)\r\nebx AND DWORD ebx, ebx addr(rbx)\r\nbecomes\r\nvalue value instructions extra extra instructions\r\nebx AND DWORD ebx, ebx addr(rbx)\r\nwith these lines written by the binary translation:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 28 of 84\n\nAND DWORD r10d, r10d\r\nXOR DWORD r8d, r8d\r\nSwapping Stack Elements\r\nThe handler STACKSWP swaps the two topmost value slots of the stack — including value instructions. For\r\nexample,\r\nvalue value instructions extra extra instructions\r\nr10d AND DWORD r10d, r10d addr(r10)\r\nr8d XOR DWORD r8d, r8d addr(r8)\r\nbecomes\r\nvalue value instructions extra extra instructions\r\nr8d XOR DWORD r8d, r8d addr(r10)\r\nr10d AND DWORD r10d, r10d addr(r8)\r\nNo x64 assembly is output.\r\nDereferencing\r\nThe dereferencing happens exclusively on the stack, and no output is generated. For example:\r\nvalue value instructions extra extra instructions\r\nrax\r\nADD QWORD rax, rsp\r\nADD QWORD rax, 56\r\naddr(rax)\r\nturns into\r\nvalue value instructions extra extra instructions\r\n[rsp + rax + 56] rax\r\nADD QWORD rax, rsp\r\nADD QWORD rax, 56\r\nThe translation also replaces the ADD instructions with + if dereferencing happens. Hence, in many cases, the\r\ninstructions can be cleared out and moved to the value part.\r\nVirtual Instructions that don’t change the stack\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 29 of 84\n\nTwo handlers do nothing: NOP, TRIPLE, and two handlers that change state variables that don’t seem to matter:\r\nSET1, and STATE. The binary translation does nothing for these four handlers.\r\nMultiplying and Division\r\nMultiplication (MUL) and division (DIV) are special: First, the two virtual registers rax and rdx are copied to\r\nthe native registers. Then a value is popped from the stack, e.g.,\r\nvalue value instructions extra extra instructions\r\nrcx addr(rcx)\r\nturns into an empty stack. Then MUL \u003csize\u003e \u003cpopped value\u003e or DIV \u003csize\u003e \u003cpopped value\u003e is executed, for\r\ninstance, MUL BYTE cl . The result is not pushed back on the stack; instead, the virtual context stores the two\r\nnative registers rax and rdx .\r\nSigned multiplication (IMUL) works differently. The instructions pops two values from the stack. The first is set\r\nto rdx , the second to rax . Then IMUL Byte dl , IMUL Word dx , IMUL Dword edx , or IMUL Qword rdx is\r\ncalculated, depending on the size. Finally, rdx then rax are pushed back on the stack. For example, the stack\r\nbefore:\r\nvalue value instructions extra extra instructions\r\nr9 addr(r9)\r\nrax addr(rax)\r\nAnd the stack after:\r\nvalue value instructions extra extra instructions\r\nrdx addr(rax)\r\nrax addr(r9)\r\nThe process generates these two lines of assembly:\r\nMOV rdx, r9d\r\nIMUL DWORD rdx\r\nPushing\r\nThe two PUSH instructions in handler 0x18 and handler 0x12 push an immediate, a register or the address of a\r\nregister on the stack. The binary translation only changes the stack without writing any output. For instance,\r\npushing register rbp would change this stack\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 30 of 84\n\nvalue value instructions extra extra instructions\r\nrax addr(rax)\r\nto this stack:\r\nvalue value instructions extra extra instructions\r\nrbp addr(rbp)\r\nrax addr(rax)\r\nxDIAGy\r\nThe strange instructions MDIAG , MDIAGA , PDIAG , PDIAGA just affect the stack, and the binary translation only\r\nneeds to move stack entries around. For example, PDIAG on this stack:\r\nvalue value instructions extra extra instructions\r\nr9 ADD QWORD r9, -1 addr(r9)\r\nrax addr(rax)\r\nleads to:\r\nvalue value instructions extra extra instructions\r\nrax r9 ADD QWORD r9, -1\r\nExample: Binary Translation\r\nThe following snippet shows the disassembly together with the resulting x64 assembly (marked by ▶ ).\r\nFFFFF880058766F6 NOP\r\nFFFFF880058766F7 PUSH dword (addr(r8), addr(r8))\r\nFFFFF880058766F9 DREFH dword\r\nFFFFF880058766FA PUSH dword (r8, addr(r8))\r\nFFFFF880058766FC AND dword\r\nFFFFF880058766FD POPD\r\n ▶ AND DWORD r8d, r8d\r\nFFFFF880058766FE NOP\r\nFFFFF880058766FF STATE 1\r\nFFFFF88005876700 IF NOT ZF -\u003e JMP 0xFFFFF8800587C8F8\r\n ▶ JNZ _addr_FFFFF8800587C8F8\r\nFFFFF8800587670A NOP\r\nFFFFF8800587670B PUSH dword (51EB851Fh, 51EB851Fh)\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 31 of 84\n\nFFFFF88005876710 POP dword rax\r\n ▶ MOV DWORD eax, 1374389535\r\nFFFFF88005876712 NOP\r\nFFFFF88005876713 PUSH dword (addr(r9), addr(r9))\r\nFFFFF88005876715 DREFH dword\r\nFFFFF88005876716 PUSH dword (rax, addr(rax))\r\nFFFFF88005876718 IMUL dword\r\n ▶ MOV rdx, r9\r\n ▶ IMUL DWORD r9d\r\nFFFFF88005876719 POP dword rdx\r\nFFFFF8800587671B POP dword rax\r\nFFFFF8800587671D NOP\r\nFFFFF8800587671E PUSH dword (addr(rdx), addr(rdx))\r\nFFFFF88005876720 DREFH dword\r\nFFFFF88005876721 PUSH byte (5h, 5h)\r\nFFFFF88005876724 SAR dword\r\nFFFFF88005876726 P2E dword rdx\r\n ▶ SAR DWORD edx, 5\r\nFFFFF88005876727 NOP\r\nFFFFF88005876728 PUSH dword (addr(rdx), addr(rdx))\r\nFFFFF8800587672A DREFH dword\r\nFFFFF8800587672B POP dword rax\r\n ▶ MOV DWORD eax, edx\r\nFFFFF8800587672D NOP\r\nFFFFF8800587672E PUSH dword (addr(rax), addr(rax))\r\nFFFFF88005876730 DREFH dword\r\nFFFFF88005876731 PUSH byte (1Fh, 1Fh)\r\nFFFFF88005876734 SHR dword\r\nFFFFF88005876735 P2E dword rax\r\n ▶ SHR DWORD eax, 31\r\nFFFFF88005876736 NOP\r\nFFFFF88005876737 PUSH dword (rdx, addr(rdx))\r\nFFFFF88005876739 PUSH dword (addr(rax), addr(rax))\r\nFFFFF8800587673B DREFH dword\r\nFFFFF8800587673C ADD dword\r\nFFFFF8800587673D P2E dword rdx\r\n ▶ ADD DWORD edx, eax\r\nFFFFF8800587673E NOP\r\nFFFFF8800587673F PUSH dword (addr(rdx), addr(rdx))\r\nFFFFF88005876741 DREFH dword\r\nFFFFF88005876742 PUSH byte (64h, 64h)\r\nFFFFF88005876745 IMUL dword\r\n ▶ MOV rax, 100\r\n ▶ IMUL DWORD edx\r\nFFFFF88005876746 POPD\r\nFFFFF88005876747 PUSH dword (addr(rdx), addr(rdx))\r\nFFFFF88005876749 PDIAG\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 32 of 84\n\nFFFFF8800587674A P2E dword rdx\r\n ▶ MOV DWORD edx, eax\r\nFFFFF8800587674B NOP\r\nFFFFF8800587674C PUSH dword (r9, addr(r9))\r\nFFFFF8800587674E PUSH dword (addr(rdx), addr(rdx))\r\nFFFFF88005876750 DREFH dword\r\nFFFFF88005876751 CMP dword\r\nFFFFF88005876752 POPD\r\n ▶ CMP DWORD r9d, edx\r\nFFFFF88005876753 NOP\r\nFFFFF88005876754 STATE 1\r\nFFFFF88005876755 IF NOT ZF -\u003e JMP 0xFFFFF88005867CB8\r\n ▶ JNZ _addr_FFFFF88005867CB8\r\nFFFFF8800587675F NOP\r\nFFFFF88005876760 PUSH dword (51EB851Fh, 51EB851Fh)\r\nFFFFF88005876765 POP dword rax\r\n ▶ MOV DWORD eax, 1374389535\r\nFFFFF88005876767 NOP\r\nFFFFF88005876768 PUSH dword (addr(r9), addr(r9))\r\nFFFFF8800587676A DREFH dword\r\nFFFFF8800587676B PUSH dword (rax, addr(rax))\r\nFFFFF8800587676D IMUL dword\r\n ▶ MOV rdx, r9\r\n ▶ IMUL DWORD r9d\r\nAs you can see from the listing above, the binary translation dramatically reduces the number of instructions. The\r\noriginal 3681 lines of disassembly are condensed into 786 x64 instructions, a reduction of about 80%. The\r\nprogress can also be seen in the code fragment used as an example throughout this post. This disassembly turns\r\ninto these lines of x64 assembly:\r\n_addr_FFFFF880058766F6:\r\n AND DWORD r8d, r8d\r\n JNZ _addr_FFFFF8800587C8F8\r\n MOV DWORD eax, 1374389535\r\n MOV rdx, r9\r\n IMUL DWORD r9d\r\n SAR DWORD edx, 5\r\n MOV DWORD eax, edx\r\n SHR DWORD eax, 31\r\n ADD DWORD edx, eax\r\n MOV rax, 100\r\n IMUL DWORD edx\r\n MOV DWORD edx, eax\r\n CMP DWORD r9d, edx\r\n JNZ _addr_FFFFF88005867CB8\r\n MOV DWORD eax, 1374389535\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 33 of 84\n\nMOV rdx, r9\r\n IMUL DWORD r9d\r\n SAR DWORD edx, 7\r\n MOV DWORD eax, edx\r\n SHR DWORD eax, 31\r\n ADD DWORD edx, eax\r\n MOV rax, 400\r\n IMUL DWORD edx\r\n MOV DWORD edx, eax\r\n CMP DWORD r9d, edx\r\n JNZ _addr_FFFFF8800587C8F8\r\n JMP _addr_FFFFF88005867CB8\r\n_addr_FFFFF88005867CB8:\r\n MOV DWORD eax, 1\r\n JMP _addr_FFFFF88005852C11\r\n_addr_FFFFF8800587C8F8:\r\n XOR DWORD eax, eax\r\n JMP _addr_FFFFF88005852C11\r\nThis snippet is much more readable already. Mainly still missing is a representation of the code flow in a graph, as\r\nwell as better handling of the optimized integer division, which make up most of the lines.\r\nStep 2: Assembler\r\nIn the first step, x64 assembly was created. To be able to analyze it with IDA Pro, it must first be converted into an\r\nexecutable. For this, I used the Netwide Assembler (NASM). The code from the previous section only needs to be\r\namended with two section headers. The data section, where I copied in data that the VM reads from the native\r\ncontext, and the text section before the actual code. Although the DGA is a function, I used it directly as an\r\nentry point for the binary.\r\nsection .data\r\ndata_FFFFF8800589E540 dd 31,28,31,30,31,30,31,31,30,31,30,31\r\ndata_FFFFF8800589E570 dd 31,29,31,30,31,30,31,31,30,31,30,31\r\n...\r\nsection .text\r\nglobal _start\r\nThe code on Github already adds those lines if you run:\r\npython3 main.py nasm -o pitou.asm\r\nThe resulting file can then be compiled with:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 34 of 84\n\nnasm -f elf64 pitou.asm\r\nld pitou.o -o pitou.bin\r\nOf course, this makes our code unreadable at first, because it is now in a binary format again. Our example snippet\r\nis now:\r\nStep 3: Disassembler\r\nThe executable file from the previous step can now be opened and disassembled in IDA. Our excerpt now looks as\r\nfollows. In contrast to step 1, we have a graph view and the possibility to add comments.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 35 of 84\n\nStep 4: Decompiler\r\nFinally, Hex Rays can decompile the disassembly to C code. Our example snippet looks as follow:\r\nThe very long disassembly from Approach 1 became a single line of C code, which corresponds to this statement\r\nIs a year a leap year?\r\nYou can run the decompiler yourself with the Python script on Github:\r\npython3 main.py nasm -o pitou.nasm\r\nThe DGA\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 36 of 84\n\nThis section covers reverse engineering of the DGA using the output from the process in the previous section. A\r\nPython reimplementation is presented as the result of the reverse engineering. The script can generate the domains\r\nfor any given date.\r\nDGA Caller\r\nTo understand the DGA, you must first look at the native code that calls the VM:\r\nAt the top of the picture you can see the call of the virtual DGA with the five arguments that are passed:\r\nr8d: The current day, for instance 2 for the 2nd.\r\nedx: The current month, for instance 3 for March.\r\necx: The current year, for instance 2019 .\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 37 of 84\n\nrsi: Domain number, starting with 0.\r\nr9: Memory address that receives the generates domain.\r\nThe domain nr rsi is set to r12d in the first line of the screenshot, with r12d being zero. The loop generates\r\nexactly 20 domain, until rsi reaches 20.\r\nIDA Pro Graph View\r\nThe number of assembler lines that the dynamic binary translation generated is 80% less than the number of\r\nvirtual instructions. Nevertheless, the DGA is still very long, as the following two images show. They show the\r\nDGA itself, as well as a function called from it that does date-based seeding.\r\nDGA Main Function\r\nThis is the DGA itself:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 38 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 39 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 40 of 84\n\nDGA Seeding\r\nThis is the date-based function for seeding:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 41 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 42 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 43 of 84\n\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 44 of 84\n\nThe DGA could now easily be analyzed with the disassembly graph of IDA, as it reveals the structure and control\r\nflow of the functions. The real advantage of IDA in this case, however, is the Hex Rays decompiler. As mentioned\r\nbefore, the DGA uses a lot of optimized integer divisions, so-called divisions by invariant integers. Those are\r\nannoying to read in disassembly, but very nicely handled by Hex Ray’s decompiler plugin.\r\nIDA Pro Hex Rays\r\nThe DGA is reverse-engineered based completely on the output of the Hex Rays decompiler.\r\nPseudo Days since Epoch\r\nThe date function called by the DGA for seeding is analyzed first. It is based on the following assumptions\r\nregarding the arguments that it receives:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 45 of 84\n\nr8d : The one-based day, so the first day of month is 1.\r\nedx : The zero-based month. That is. January has the value 0 and December the value 11 .\r\necx : The four-digit year.\r\nThe assumption that the month is zero-based is wrong. The month is passed one-based, so with January equal to 1.\r\nThe first part of this section analyzes the function as if the assumption about the arguments were correct. The\r\nsecond part then examines the effects of the unexpected values for the month.\r\nFirst of all the whole output of Hex Rays, with hidden casting and declerations:\r\nsigned __int64 __usercall days_since_epoch@\u003crax\u003e(int month@\u003cedx\u003e, int year@\u003cecx\u003e, int day@\u003cr8d\u003e)\r\n{\r\n // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-\"+\" TO EXPAND]\r\n retaddr = v4;\r\n month_o = month;\r\n extra_years = month / 12;\r\n year_f = extra_years + year;\r\n month_fixed = (-12 * extra_years + month_o);\r\n if ( month_fixed \u003c 0 )\r\n {\r\n month_fixed = (month_fixed + 12);\r\n --year_f;\r\n }\r\n month_fixed_2 = month_fixed;\r\n day_0_based = (day - 1);\r\n if ( day - 1 \u003c 0 )\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 46 of 84\n\n{\r\n year_plus_1900 = year_f + 1900;\r\n do\r\n {\r\n month_fixed = (month_fixed - 1);\r\n if ( --month_fixed_2 \u003c 0 )\r\n {\r\n month_fixed = 11LL;\r\n --year_f;\r\n --year_plus_1900;\r\n month_fixed_2 = 11LL;\r\n }\r\n is_leap_year = !(year_plus_1900 % 4)\r\n \u0026\u0026 (year_plus_1900 != 100 * (year_plus_1900 / 100) || year_plus_1900 == 400 * (year_plus_1900\r\n *(\u0026stack_ - 125) = a5;\r\n a5 = *(\u0026stack_ - 125);\r\n day_0_based = (month_lengths_common_year[month_fixed_2 + 12 * is_leap_year] + day_0_based);\r\n }\r\n while ( day_0_based \u003c 0 );\r\n }\r\n month_f = month_fixed;\r\n year_f_plus_1900 = year_f + 1900;\r\n while ( 1 )\r\n {\r\n year_div4_ = year_f_plus_1900 % 4;\r\n leap_year = year_div4_\r\n || year_f_plus_1900 == 100 * (year_f_plus_1900 / 100) \u0026\u0026 year_f_plus_1900 != 400 * (year_f_plus_190\r\n *(\u0026stack_ - 125) = a5;\r\n tmp = *(\u0026stack_ - 125);\r\n if ( day_0_based \u003c month_lengths_common_year[month_f + 12 * leap_year] )\r\n break;\r\n leap_year_ = !year_div4_\r\n \u0026\u0026 (year_f_plus_1900 != 100 * (year_f_plus_1900 / 100)\r\n || year_f_plus_1900 == 400 * (year_f_plus_1900 / 400));\r\n *(\u0026stack_ - 125) = tmp;\r\n a5 = *(\u0026stack_ - 125);\r\n index = month_f++ + 12 * leap_year_;\r\n day_0_based = (day_0_based - month_lengths_common_year[index]);\r\n if ( month_f == 12 )\r\n {\r\n month_f = 0LL;\r\n ++year_f;\r\n ++year_f_plus_1900;\r\n }\r\n }\r\n years_since_epoch = year_f - 1970;\r\n day_1_based = day_0_based + 1;\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 47 of 84\n\nyear_mod4 = year_f % 4;\r\n year_mod4_is_1 = year_mod4 \u0026\u0026 year_mod4 \u003c 2;\r\n days_beg_year_with_rule_div4 = year_mod4_is_1 + years_since_epoch / 4 + 365 * (year_f - 1970);\r\n year_mod100 = year_f % 100;\r\n c1 = year_f % 100 \u0026\u0026 year_mod100 \u003c 70;\r\n days_beg_year_rule_div100 = -c1 - years_since_epoch / 100 + days_beg_year_with_rule_div4;\r\n year_mod400 = year_f % 400;\r\n c2 = year_mod400 \u0026\u0026 year_mod400 \u003c 370;\r\n c3 = (1374389535LL * years_since_epoch) \u003e\u003e 32;\r\n days_in_months = 0LL;\r\n days_beg_year_with_rule_div400 = c2 + (c3 \u003e\u003e 31) + (c3 \u003e\u003e 7) + days_beg_year_rule_div100;\r\n for ( i = 0LL; i \u003c month_f; days_in_months = (month_lengths_common_year[i_] + days_in_months) )\r\n {\r\n is_leap_year_1 = !year_mod4 \u0026\u0026 (year_mod100 || !year_mod400);\r\n *(\u0026stack_ - 125) = tmp;\r\n tmp = *(\u0026stack_ - 125);\r\n i_ = i++ + 12 * is_leap_year_1;\r\n }\r\n *(\u0026stack_ - 125) = tmp;\r\n return days_in_months + days_beg_year_with_rule_div400 + day_1_based - 1;\r\nIf the arguments were correct\r\nIf the date components are within the expected range (again, they aren’t, but for now we assume that they are),\r\nthen the calculations at the beginning are omitted, as is the whole if-block ( if(day -1 \u003c 0) ). The while(1)\r\nblock also has no effect, since the break statement always triggers.\r\nThe code also contains lines with *(\u0026stack_ - 125) , these are relics of the temporary stack variable [rsp-1000] which the binary translation writes sometimes, see the Binary Translation Section. All lines with *\r\n(\u0026stack_ - 125) can be removed. This leads to this simplified code:\r\nsigned __int64 __usercall days_since_epoch@\u003crax\u003e(int month@\u003cedx\u003e, int year@\u003cecx\u003e, int day@\u003cr8d\u003e)\r\n{\r\n year_f = year;\r\n day_0_based = (day - 1);\r\n month_f = month;\r\n year_f_plus_1900 = year_f + 1900;\r\n years_since_epoch = year_f - 1970;\r\n day_1_based = day_0_based + 1;\r\n year_mod4 = year_f % 4;\r\n year_mod4_is_1 = year_mod4 \u0026\u0026 year_mod4 \u003c 2;\r\n days_beg_year_with_rule_div4 = year_mod4_is_1 + years_since_epoch / 4 + 365 * (year_f - 1970);\r\n year_mod100 = year_f % 100;\r\n c1 = year_f % 100 \u0026\u0026 year_mod100 \u003c 70;\r\n days_beg_year_rule_div100 = -c1 - years_since_epoch / 100 + days_beg_year_with_rule_div4;\r\n year_mod400 = year_f % 400;\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 48 of 84\n\nc2 = year_mod400 \u0026\u0026 year_mod400 \u003c 370;\r\n c3 = (1374389535LL * years_since_epoch) \u003e\u003e 32;\r\n days_in_months = 0LL;\r\n days_beg_year_with_rule_div400 = c2 + (c3 \u003e\u003e 31) + (c3 \u003e\u003e 7) + days_beg_year_rule_div100;\r\n for ( i = 0LL; i \u003c month_f; days_in_months = (month_lengths_common_year[i_] + days_in_months) )\r\n {\r\n is_leap_year_1 = !year_mod4 \u0026\u0026 (year_mod100 || !year_mod400);\r\n i_ = i++ + 12 * is_leap_year_1;\r\n }\r\n return days_in_months + days_beg_year_with_rule_div400 + day_1_based - 1;\r\nThe code calculates the days since epoch, i.e., 1 January 1970. First, it determines the number of years since 1970.\r\nyear_f = year;\r\nday_0_based = (day - 1);\r\nmonth_f = month;\r\nyear_f_plus_1900 = year_f + 1900;\r\nyears_since_epoch = year_f - 1970;\r\nNext, the code determines the number of days to the beginning of the provided year since epoch by considering\r\neach year divisible by 4 a leap year. So for instance, if the function is called for November 2nd, 2017, then it\r\ncalculates the days to January 1, 2017.\r\nday_1_based = day_0_based + 1;\r\nyear_mod4 = year_f % 4;\r\nyear_mod4_is_1 = year_mod4 \u0026\u0026 year_mod4 \u003c 2;\r\ndays_beg_year_with_rule_div4 = year_mod4_is_1 + years_since_epoch / 4 + 365 * (year_f - 1970);\r\nThen the code corrects the fact that every year divisible by 100 is not a leap year:\r\n year_mod100 = year_f % 100;\r\n c1 = year_f % 100 \u0026\u0026 year_mod100 \u003c 70;\r\n days_beg_year_rule_div100 = -c1 - years_since_epoch / 100 + days_beg_year_with_rule_div4;\r\nAnd finally the code accounts for the rule that every year divisible by 400 is again a leap year:\r\n year_mod400 = year_f % 400;\r\n c2 = year_mod400 \u0026\u0026 year_mod400 \u003c 370;\r\n c3 = (1374389535LL * years_since_epoch) \u003e\u003e 32;\r\n days_in_months = 0LL;\r\n days_beg_year_with_rule_div400 = c2 + (c3 \u003e\u003e 31) + (c3 \u003e\u003e 7) + days_beg_year_rule_div100;\r\nNow the code correctly determined the number of days to the beginning of the year. Finally, it uses a loop to add\r\nup the days in each elapsed month:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 49 of 84\n\nfor ( i = 0LL; i \u003c month_f; days_in_months = (month_lengths_common_year[i_] + days_in_months) )\r\n {\r\n is_leap_year_1 = !year_mod4 \u0026\u0026 (year_mod100 || !year_mod400);\r\n i_ = i++ + 12 * is_leap_year_1;\r\n }\r\nThe month_length_common_year lists the number of days per month in a common year, immediately followed by\r\nthe list of days in a leap year. The term 12 * is_leap_year_1 switches to the leap year array if necessary.\r\nFinally, the code adds up the days to the beginning of the year, the days in past months, and the current day,\r\nsubtracting 1 to get the days since epoch:\r\n return days_in_months + days_beg_year_with_rule_div400 + day_1_based - 1;\r\nImpact of the actual arguments\r\nThe above code works perfectly fine when the months are zero-based. However, the function\r\nRtlTimeToTimeFields is used by the caller to get the date values, see DGA caller.This function returns months\r\nfrom 1 to 12. What happens to the epoch function, if it runs on these dates?\r\nCase 1: not December, not the end of the month\r\nDates that are not in December and not after the 28th of the month result in the number of days to the\r\ncorresponding day in the next month. So for instance (with dates dd.mm.yyyy ):\r\nreal date treated as result\r\n28.3.2019 28.4.2019 0x465E\r\n1.9.2017 1.10.2017 0x4420\r\n1.1.2014 1.2.2014 0x30A1\r\n30.11.2019 30.12.2019 0x4754\r\nCase 2: not December, end of the month\r\nShifting dates to the next month will cause a problem if the day in the next month does not exist. For example,\r\nMarch 31 would shift to April 31, which does not exist. In those instances, the while(1) loop that we skipped\r\nbefore comes into effect:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 50 of 84\n\nwhile ( 1 )\r\n {\r\n year_div4_ = year_f_plus_1900 % 4;\r\n leap_year = year_div4_\r\n || year_f_plus_1900 == 100 * (year_f_plus_1900 / 100) \u0026\u0026 year_f_plus_1900 != 400 * (year_f_plus_190\r\n if ( day_0_based \u003c month_lengths_common_year[month_f + 12 * leap_year] )\r\n break;\r\n leap_year_ = !year_div4_\r\n \u0026\u0026 (year_f_plus_1900 != 100 * (year_f_plus_1900 / 100)\r\n || year_f_plus_1900 == 400 * (year_f_plus_1900 / 400));\r\n index = month_f++ + 12 * leap_year_;\r\n day_0_based = (day_0_based - month_lengths_common_year[index]);\r\n if ( month_f == 12 )\r\n {\r\n month_f = 0LL;\r\n ++year_f;\r\n ++year_f_plus_1900;\r\n }\r\n }\r\nIt has the test\r\nday_0_based \u003c month_lengths_common_year[month_f + 12 * leap_year]\r\nwhich checks, if the day exists in the current month. If it does not, then the date properly overflows to the next\r\nmonth. The code could even shift dates by several months, e.g., April 91 to June 30. Leap years are also correctly\r\nhandled, see the last to rows in the following table:\r\nreal date treated as result\r\n31.3.2019 1.5.2019 0x4661\r\n30.11.2019 30.12.2019 0x4754\r\n31.1.2019 3.3.2019 0x4626\r\n31.1.2020 2.3.2020 0x4793\r\nCase 3: December\r\nFor dates in December, the start of the function is relevant:\r\n month_o = month;\r\n extra_years = month / 12;\r\n year_f = extra_years + year;\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 51 of 84\n\nmonth_fixed = (-12 * extra_years + month_o);\r\n if ( month_fixed \u003c 0 )\r\n {\r\n month_fixed = (month_fixed + 12);\r\n --year_f;\r\n }\r\nThe variable extra_years is 1 , which increments the year by one. The month value is reduced by 12 ( -12 *\r\nextra_years + month_o) , i.e., becomes 0 which stands for January. We therefore get:\r\nreal date treated as result\r\n6.12.2019 6.1.2020 0x475B\r\nThe DGA Function\r\nThe DGA looks as follows. Again, the output is very long, although only few lines are superfluous. The\r\ncomponents of the algorithm are examined individually afterward.\r\n__int64 __usercall dga@\u003crax\u003e(__int64 months@\u003crdx\u003e, __int64 year@\u003crcx\u003e, __int64 domaint_output@\u003cr9\u003e, int days@\u003cr\r\n{\r\n domain_out = domain_output;\r\n vars30 = \u0026vars38;\r\n vars28 = a4;\r\n vars20 = a3;\r\n vars18 = a6;\r\n vars10 = a7;\r\n vars8 = a8;\r\n j = 0LL;\r\n domain = a5;\r\n v20 = year;\r\n random_numbers = 0;\r\n magic_number = 0xDAFE02C;\r\n days_since_1970_broken = days_since_epoch(months, year, days);\r\n consonants = *pConsonants;\r\n LOBYTE(v20) = v20 - 108;\r\n retaddr = v20;\r\n i = 0;\r\n v25 = \u0026v72;\r\n seed_value = domain_nr / 3 + days_since_1970_broken;\r\n if ( !*pConsonants )\r\n {\r\n consonants = (ExAllocatePool)(\u0026v72, domain, 23LL, 0LL);\r\n *pConsonants = consonants;\r\n if ( decrypt_consonants )\r\n {\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 52 of 84\n\nkey = 0x3365841C;\r\n key_index = 0LL;\r\n addr_encrypted_consonants = \u0026encrypted_consonants;\r\n do\r\n {\r\n key_index_1 = key_index;\r\n ++consonants;\r\n ++addr_encrypted_consonants;\r\n key_byte = *(\u0026key + key_index);\r\n *(consonants - 1) = *(addr_encrypted_consonants - 1) ^ *(\u0026key + key_index);\r\n key_index = (key_index + 1) \u0026 0x80000003;\r\n *(\u0026key + key_index_1) = 2 * key_byte ^ (key_byte \u003e\u003e 1);\r\n if ( key_index \u003c 0 )\r\n key_index = ((key_index - 1) ^ 0xFFFFFFFC) + 1;\r\n }\r\n while ( addr_encrypted_consonants \u003c \u0026encrypted_consonants_end );\r\n consonants = *pConsonants;\r\n }\r\n }\r\n vowels = *pVowels;\r\n if ( !*pVowels )\r\n {\r\n vowels = (ExAllocatePool)(\u0026v72, domain, *pVowels + 7LL, 0LL);\r\n *pVowels = vowels;\r\n if ( decrypt_vowels )\r\n {\r\n key = -967459448;\r\n key_index_2 = 0LL;\r\n addr_encrypted_vowels = \u0026encrypted_vowels;\r\n do\r\n {\r\n key_index_3 = key_index_2;\r\n ++vowels;\r\n ++addr_encrypted_vowels;\r\n key_byte_1 = *(\u0026key + key_index_2);\r\n *(vowels - 1) = *(addr_encrypted_vowels - 1) ^ *(\u0026key + key_index_2);\r\n key_index_2 = (key_index_2 + 1) \u0026 0x80000003;\r\n *(\u0026key + key_index_3) = 2 * key_byte_1 ^ (key_byte_1 \u003e\u003e 1);\r\n if ( key_index_2 \u003c 0 )\r\n key_index_2 = ((key_index_2 - 1) ^ 0xFFFFFFFC) + 1;\r\n }\r\n while ( addr_encrypted_vowels \u003c \u0026encrypted_vowels_end );\r\n vowels = *pVowels;\r\n }\r\n }\r\n tlds = pTLDs;\r\n if ( !pTLDs )\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 53 of 84\n\n{\r\n tlds = (ExAllocatePool)(\u0026v72, domain, pTLDs + 38, 0LL);\r\n pTLDs = tlds;\r\n if ( decrypt_tlds )\r\n {\r\n key = 2131189013;\r\n key_index_4 = 0LL;\r\n addr_encrypted_tlds = \u0026encrypted_tlds;\r\n do\r\n {\r\n v44 = key_index_4;\r\n ++tlds;\r\n ++addr_encrypted_tlds;\r\n key_byte_2 = *(\u0026key + key_index_4);\r\n *(tlds - 1) = *(addr_encrypted_tlds - 1) ^ *(\u0026key + key_index_4);\r\n key_index_4 = (key_index_4 + 1) \u0026 0x80000003;\r\n *(\u0026key + v44) = 2 * key_byte_2 ^ (key_byte_2 \u003e\u003e 1);\r\n if ( key_index_4 \u003c 0 )\r\n key_index_4 = ((key_index_4 - 1) ^ 0xFFFFFFFC) + 1;\r\n }\r\n while ( addr_encrypted_tlds \u003c \u0026encrypted_tlds_end );\r\n tlds = pTLDs;\r\n }\r\n }\r\n tlds_1 = tlds;\r\n v30 = \u0026v72 - tlds;\r\n do\r\n {\r\n v67 = *tlds_1;\r\n tlds_1 = (tlds_1 + 1);\r\n *(tlds_1 + v30 - 1) = v67;\r\n }\r\n while ( v67 );\r\n if ( tlds )\r\n {\r\n (ExFreePool)(\u0026v72, domain, v30, tlds);\r\n pTLDs = 0LL;\r\n }\r\n v17 = 1LL;\r\n v73 = \u0026v72;\r\n if ( v72 )\r\n {\r\n do\r\n {\r\n if ( *v25 == 44 )\r\n {\r\n *v25 = 0;\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 54 of 84\n\n*\u0026tld_array[8 * v17 - 49] = v25 + 1;\r\n v17 = (v17 + 1);\r\n }\r\n v25 = (v25 + 1);\r\n }\r\n while ( *v25 );\r\n }\r\n counter_ = domain_nr;\r\n round_seed_to_nearset_10 = 10 * (seed_value / 0xA);\r\n seed_value = round_seed_to_nearset_10;\r\n HIWORD(v39) = HIWORD(round_seed_to_nearset_10);\r\n LOWORD(v39) = ((0xDAFE02Cu \u003e\u003e domain_nr) * (domain_nr - 1)) * round_seed_to_nearset_10;\r\n LOBYTE(v39) = (v39 \u0026 1) + 8;\r\n domain_length = v39;\r\n if ( v39 \u003e 0 )\r\n {\r\n v41 = BYTE1(seed_value);\r\n addr_random_numbers = \u0026the_random_numbers;\r\n do\r\n {\r\n ip1 = i++;\r\n v50 = (ip1 \u003e\u003e 31) \u0026 3;\r\n v51 = v50 + ip1;\r\n v52 = (v51 \u003e\u003e 2);\r\n v53 = (v51 \u0026 3) - v50;\r\n if ( v53 )\r\n {\r\n v54 = v53 - 1;\r\n if ( v54 )\r\n {\r\n v55 = v54 - 1;\r\n if ( v55 )\r\n {\r\n if ( v55 == 1 )\r\n {\r\n ++random_numbers;\r\n v56 = (round_seed_to_nearset_10 \u003c\u003c v52) ^ (v41 \u003e\u003e v52);\r\n v57 = v52;\r\n counter_ = domain_nr;\r\n addr_random_numbers = (addr_random_numbers + 1);\r\n *(addr_random_numbers - 1) = v56 * (*(\u0026magic_number + v57) \u0026 0xF) * (domain_nr + 1);\r\n }\r\n else\r\n {\r\n counter_ = domain_nr;\r\n }\r\n }\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 55 of 84\n\nelse\r\n {\r\n ++random_numbers;\r\n v15 = (v41 \u003c\u003c v52) ^ (round_seed_to_nearset_10 \u003e\u003e v52);\r\n v16 = v52;\r\n counter_ = domain_nr;\r\n addr_random_numbers = (addr_random_numbers + 1);\r\n *(addr_random_numbers - 1) = v15 * (*(\u0026magic_number + v16) \u003e\u003e 4) * (domain_nr + 1);\r\n }\r\n }\r\n else\r\n {\r\n v31 = v52;\r\n v32 = v52;\r\n counter_ = domain_nr;\r\n ++random_numbers;\r\n addr_random_numbers = (addr_random_numbers + 1);\r\n *(addr_random_numbers - 1) = (*(\u0026magic_number + v31) \u0026 0xF) * (retaddr \u003c\u003c v32) * (domain_nr + 1);\r\n }\r\n }\r\n else\r\n {\r\n v63 = v52;\r\n v64 = v52;\r\n counter_ = domain_nr;\r\n ++random_numbers;\r\n addr_random_numbers = (addr_random_numbers + 1);\r\n *(addr_random_numbers - 1) = (*(\u0026magic_number + v63) \u003e\u003e 4) * (retaddr \u003e\u003e v64) * (domain_nr + 1);\r\n }\r\n }\r\n while ( random_numbers \u003c domain_length );\r\n domain = domain_out;\r\n if ( domain_length \u003e 0 )\r\n {\r\n while ( 1 )\r\n {\r\n v34 = j;\r\n j = (j + 1);\r\n v35 = *(\u0026the_random_numbers + v34);\r\n if ( (v35 \u0026 0x80u) == 0 )\r\n break;\r\n *(++domain - 1) = *(consonants + (v35 % 21));\r\n if ( j \u003e= domain_length )\r\n goto append_tld;\r\n v36 = j;\r\n j = (j + 1);\r\n *(++domain - 1) = *(vowels + (*(\u0026the_random_numbers + v36) % 5));\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 56 of 84\n\nif ( j \u003e= domain_length )\r\n goto append_tld;\r\n r = *(\u0026the_random_numbers + j);\r\n LOBYTE(r) = r \u0026 64;\r\n if ( r )\r\n {\r\n *(++domain - 1) = *(vowels + (r % 5));\r\n_addr_FFFFF880058745FC:\r\n j = (j + 1);\r\n }\r\n if ( j \u003e= domain_length )\r\n goto append_tld;\r\n }\r\n *domain = *(vowels + (v35 % 5));\r\n domain += 2;\r\n *(domain - 1) = *(consonants + (*(\u0026the_random_numbers + j) % 21));\r\n goto _addr_FFFFF880058745FC;\r\n }\r\n }\r\nappend_tld:\r\n *domain = '.';\r\n tld = *\u0026tld_array[8 * ((counter_ ^ round_seed_to_nearset_10 ^ 0xDAFE02C) % 9) - 49];\r\n dmtld = domain - tld;\r\n do\r\n {\r\n result = *tld;\r\n tld = (tld + 1);\r\n *(tld + dmtld) = result;\r\n }\r\n while ( result );\r\n return result;\r\n}\r\nSeeding\r\nThe main part of seeding is in function days_since_epoch . This value is combined with the domain counter and\r\nrounded to 10 day intervals:\r\n days_since_1970_broken = days_since_epoch(months, year, days);\r\n ...\r\n seed_value = domain_nr / 3 + days_since_1970_broken;\r\n ...\r\n round_seed_to_nearset_10 = 10 * (seed_value / 10);\r\n seed_value = round_seed_to_nearset_10;\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 57 of 84\n\nThe seed stays the same for ten days, in most cases. It does not really matter that the number of days since epoch\r\nare calculated wrong, this value is only used for seeding and required to change daily. This is the case for almost\r\nall days, except for a few edge cases, where two days can have the same seed (for example January 29, 2019 and\r\nFebruary 1, 2019 return the same value). The broken calculation can also shorten or extend the 10 day window.\r\nThe longest windows is 13 days at the end of January, e.g., 2019-01-23 – 2019-02-04. Windows at the end of\r\nFebruary are shorter, for example the 7 day window 2019-02-25 – 2019-03-03. In rare cases, the window is just 1\r\nday long, happening next 2025-01-31.\r\nSeeding also uses a magic number:\r\n magic_number = 0xDAFE02C;\r\nAccording to F-Secure, this means it is Pitou version 33. They list 0xDAFE02D as a second seed for version 31.\r\nEncrypted Strings\r\nThe DGA uses three encrypted strings: vowels, consonants, and TLDs. The DGA decrypts these strings the first\r\ntime it is run. The encryption is a rolling XOR with a four-byte key, which is updated every loop according to key\r\n= (key\u003c\u003c1) ^ (key \u003e\u003e1) :\r\n if ( !*pConsonants )\r\n {\r\n consonants = (ExAllocatePool)(\u0026v72, domain, 23LL, 0LL);\r\n *pConsonants = consonants;\r\n if ( decrypt_consonants )\r\n {\r\n key = 0x3365841C;\r\n key_index = 0LL;\r\n addr_encrypted_consonants = \u0026encrypted_consonants;\r\n do\r\n {\r\n key_index_1 = key_index;\r\n ++consonants;\r\n ++addr_encrypted_consonants;\r\n key_byte = *(\u0026key + key_index);\r\n *(consonants - 1) = *(addr_encrypted_consonants - 1) ^ *(\u0026key + key_index);\r\n key_index = (key_index + 1) \u0026 0x80000003;\r\n *(\u0026key + key_index_1) = 2 * key_byte ^ (key_byte \u003e\u003e 1);\r\n if ( key_index \u003c 0 )\r\n key_index = ((key_index - 1) ^ 0xFFFFFFFC) + 1;\r\n }\r\n while ( addr_encrypted_consonants \u003c \u0026encrypted_consonants_end );\r\n consonants = *pConsonants;\r\n }\r\n }\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 58 of 84\n\nSecond Level Domain Length\r\nThe length of the second level domain is calculated as follows:\r\n counter_ = domain_nr;\r\n round_seed_to_nearset_10 = 10 * (seed_value / 0xA);\r\n seed_value = round_seed_to_nearset_10;\r\n HIWORD(v39) = HIWORD(round_seed_to_nearset_10);\r\n LOWORD(v39) = ((0xDAFE02Cu \u003e\u003e domain_nr) * (domain_nr - 1)) * round_seed_to_nearset_10;\r\n LOBYTE(v39) = (v39 \u0026 1) + 8;\r\n domain_length = v39;\r\nThis leads to lengths of 8 or 9 characters.\r\nRandom Numbers\r\nThe seed is converted to random numbers. The seed is viewed as a 16bit value, which splits into four 4-bit values.\r\nThese values are then used to generate the letters of the domain. Because the more significant bits of the seed\r\nchange slowly, the domains mostly change at letter positions 3,4 and 7,8. For example, these are domains from\r\nJune 1, June 10, and June 20, 2019:\r\nzuoezaxa�.name\r\nzuopabma.org\r\nzuojabba.mobi\r\nWhy the � character in the domain zuoezaxa�.name ? Pitou contains a severe bug. Even if the length of the\r\nsecond level domain is picked out to be 9 characters, only 8 random numbers are calculated. The 9th is read from\r\nundefined memory. This means that the last character of the sld is undetermined. Domains with only 8 second\r\nlevel domain characters are ok.\r\nPicking Letters\r\nTwo arrays provide the letters of the second level domain: a list of vowels ( aeiou ) and a list of consonants\r\n( bcdfghjklmnpqrstvwxyz ). They are picked somewhat alternately to produce more natural-sounding names.\r\nPicking a TLD\r\nThe TLD is also picked from a set of hardcoded list: com , org , biz , net , info , mobi , us , name , me\r\nReimplementation in Python\r\nThe DGA is pretty messy, an even the reimplementation in Python is challenging to read.\r\nimport argparse\r\nfrom datetime import date, datetime, timedelta\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 59 of 84\n\nfrom calendar import monthrange\r\ndef date2seed(d):\r\n year_prime = d.year\r\n month_prime = (d.month + 1)\r\n day_prime = d.day\r\n if month_prime \u003e 12:\r\n month_prime -= 12\r\n year_prime += 1\r\n _, monthdays = monthrange(year_prime, month_prime)\r\n if day_prime \u003e monthdays:\r\n month_prime += 1\r\n day_prime -= monthdays\r\n if month_prime \u003e 12:\r\n month_prime -= 12\r\n year_prime += 1\r\n date_prime = date(year_prime, month_prime, day_prime)\r\n epoch = datetime.strptime(\"1970-01-01\", \"%Y-%m-%d\").date()\r\n return (date_prime - epoch).days\r\ndef dga(year, seed, counter, magic):\r\n seed_value = 10*( (counter//3 + seed) // 10)\r\n year_since = year - 1900\r\n random_numbers = []\r\n a = (magic \u003e\u003e counter)\r\n b = (counter - 1) \u0026 0xFF\r\n d = a*b \u0026 0xFF\r\n e = d*seed_value\r\n sld_length = 8 + (e \u0026 1)\r\n magic_list = []\r\n for i in range(4):\r\n magic_list.append((magic \u003e\u003e (i*8)) \u0026 0xFF)\r\n for i in range(8):\r\n imod = i % 4\r\n idiv = i // 4\r\n b1 = (seed_value \u003e\u003e 8) \u0026 0xFF\r\n b0 = seed_value \u0026 0xFF\r\n if imod == 0:\r\n m = magic_list[idiv] \u003e\u003e 4\r\n f = (year_since \u003e\u003e idiv)\r\n elif imod == 1:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 60 of 84\n\nm = magic_list[idiv] \u0026 0xF\r\n f = (year_since \u003c\u003c idiv)\r\n elif imod == 2:\r\n m = magic_list[idiv] \u003e\u003e 4\r\n f = (b1 \u003c\u003c idiv) ^ (b0 \u003e\u003e idiv)\r\n elif imod == 3:\r\n m = magic_list[idiv] \u0026 0xF\r\n f = (b0 \u003c\u003c idiv) ^ (b1 \u003e\u003e idiv)\r\n cp = (counter + 1)\r\n r = (m*f \u0026 0xFF) *cp\r\n random_numbers.append(r \u0026 0xFF)\r\n random_numbers.append(0xE0)\r\n r = random_numbers\r\n vowels = \"aeiou\"\r\n consonants = \"bcdfghjklmnpqrstvwxyz\"\r\n sld = \"\"\r\n while True:\r\n x = r.pop(0)\r\n if x \u0026 0x80:\r\n sld += consonants[x % len(consonants)]\r\n if len(sld) \u003e= sld_length:\r\n break\r\n x = r.pop(0)\r\n sld += vowels[x % len(vowels)]\r\n if len(sld) \u003e= sld_length:\r\n break\r\n x = r[0]\r\n if x \u0026 0x40:\r\n r.pop(0)\r\n sld += vowels[x % len(vowels)]\r\n if len(sld) \u003e= sld_length:\r\n break\r\n else:\r\n sld += vowels[x % len(vowels)]\r\n x = r.pop(0)\r\n sld += consonants[x % len(consonants)]\r\n if len(sld) \u003e= sld_length:\r\n break\r\n tlds = ['com', 'org', 'biz', 'net', 'info', 'mobi', 'us', 'name', 'me']\r\n \r\n \r\n q = (counter ^ seed_value ^ magic) \u0026 0xFFFFFFFF\r\n tld = tlds[q % len(tlds)]\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 61 of 84\n\nif len(sld) \u003e 8:\r\n lc = sld[-1]\r\n sld = sld[:-1]\r\n if lc in consonants:\r\n sld_c = [sld + c for c in consonants]\r\n else:\r\n sld_c = [sld + c for c in vowels]\r\n return [s + \".\" + tld for s in sld_c]\r\n else:\r\n return sld + \".\" + tld\r\nif __name__==\"__main__\":\r\n parser = argparse.ArgumentParser(description=\"DGA of Pitou\")\r\n parser.add_argument(\"-d\", \"--date\",\r\n help=\"date for which to generate domains, e.g., 2019-04-09\")\r\n parser.add_argument(\"-m\", \"--magic\", choices=[\"0xDAFE02D\", \"0xDAFE02C\"],\r\n default=\"0xDAFE02C\", help=\"magic seed\")\r\n args = parser.parse_args()\r\n if args.date:\r\n d = datetime.strptime(args.date, \"%Y-%m-%d\")\r\n else:\r\n d = datetime.now()\r\n for c in range(20):\r\n seed = date2seed(d)\r\n domains = dga(d.year, seed, c, int(args.magic, 16))\r\n if type(domains) == str:\r\n print(domains)\r\n else:\r\n l = len(domains[0]) + 1\r\n print(l*\"-\" + \"+\")\r\n for i, domain in enumerate(domains):\r\n if i == len(domains)//2:\r\n label = \"one of these\"\r\n print(\"{} +--{}\".format(domain, label))\r\n else:\r\n print(\"{} |\".format(domain))\r\n print(l*\"-\" + \"+\")\r\nFor all domains with sld length 9, the code print all possible domain names (see the bug in Random Number):\r\n▶ python3 dga.py -d 2019-06-10\r\n-------------+\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 62 of 84\n\nkoupoalab.me |\r\nkoupoalac.me |\r\nkoupoalad.me |\r\nkoupoalaf.me |\r\nkoupoalag.me |\r\nkoupoalah.me |\r\nkoupoalaj.me |\r\nkoupoalak.me |\r\nkoupoalal.me |\r\nkoupoalam.me |\r\nkoupoalan.me +--one of these\r\nkoupoalap.me |\r\nkoupoalaq.me |\r\nkoupoalar.me |\r\nkoupoalas.me |\r\nkoupoalat.me |\r\nkoupoalav.me |\r\nkoupoalaw.me |\r\nkoupoalax.me |\r\nkoupoalay.me |\r\nkoupoalaz.me |\r\n-------------+\r\nCharacteristics\r\nThe following table summarizes the properties of Pitou’s DGA.\r\nproperty value\r\ntype\r\nTDD (time-dependent-deterministic), to some extend TDN (time-dependent non-deterministic)\r\ngeneration scheme bit-shifting the seed\r\nseed magic value + current date\r\ndomain change\r\nfrequency\r\nmostly every 10 days, very rarly after 1 day, sometimes after 13 days\r\ndomains per day 20\r\nsequence sequential\r\nwait time between\r\ndomains\r\nNone\r\ntop level domains com , org , biz , net , info , mobi , us , name , me\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 63 of 84\n\nproperty value\r\nsecond level characters a-z\r\nsecond level domain\r\nlength\r\n8 or 9\r\nComparison with Public Reports.\r\nFor all the reports listed under Previous Work, I checked that any domains mentioned are actually covered by the\r\nDGA in this blog post. You can find the list of domains from 2015 - 2021 for the two seeds here: 0xdafe02c and\r\n0xdafe02d. All domains from the reports are covered by the DGA as I reimplemented it.\r\nPitou - The “silent” resurrection of the notorious Srizbi kernel spambot\r\nThe report by f-Secure does not list any Pitou DGA domains.\r\nBootkits are not dead. Pitou is back!\r\nThe report by C.R.A.M from Januar 15, 2018 lists four domains:\r\ndomain seed first generated last generated\r\nunpeoavax.mobi 0xDAFE02C 2017-10-04 2017-10-13\r\nilsuiapay.us 0xDAFE02C 2017-10-04 2017-10-13\r\nivbaibja.net 0xDAFE02C 2017-10-08 2017-10-17\r\nasfoeacak.info 0xDAFE02C 2017-10-08 2017-10-17\r\nRig Exploit Kit sends Pitou.B Trojan\r\nThe SANS Internet Storm Center diary entry from Brad Duncan, published 25 June 2019, links to a Pitou PCAP\r\non the excellent malware-traffic-analysis blog by the same author.\r\ndomain seed first generated last generated\r\nrogojaob.info 0xDAFE02C 2019-06-23 2019-07-01\r\nwiejlauas.info 0xDAFE02C 2019-06-18 2019-06-27\r\nyoevuajas.us 0xDAFE02C 2019-06-22 2019-06-30\r\nijcaiatas.name 0xDAFE02C 2019-06-19 2019-06-28\r\npiiaxasas.com 0xDAFE02C 2019-06-19 2019-06-28\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 64 of 84\n\ndomain seed first generated last generated\r\ncaoelasas.name 0xDAFE02C 2019-06-22 2019-06-30\r\nnaaleazas.net 0xDAFE02C 2019-06-23 2019-07-01\r\nepcioalas.info 0xDAFE02C 2019-06-20 2019-06-29\r\noltaeazas.mobi 0xDAFE02C 2019-06-20 2019-06-29\r\nsuudaacas.org 0xDAFE02C 2019-06-18 2019-06-27\r\ngiazfaeas.me 0xDAFE02C 2019-06-21 2019-06-30\r\nzuojabba.mobi 0xDAFE02C 2019-06-18 2019-06-27\r\nunufabub.net 0xDAFE02C 2019-06-21 2019-06-30\r\nufayubja.me 0xDAFE02C 2019-06-19 2019-06-28\r\nhuoseavas.name 0xDAFE02C 2019-06-17 2019-06-26\r\nirifyara.com 0xDAFE02C 2019-06-21 2019-06-30\r\nvaxeiayas.mobi 0xDAFE02C 2019-06-22 2019-06-30\r\nkooovaqas.biz 0xDAFE02C 2019-06-23 2019-07-01\r\ndienoalas.us 0xDAFE02C 2019-06-17 2019-06-26\r\namlivaias.us 0xDAFE02C 2019-06-20 2019-06-29\r\nBrad Duncan also wrote a second blog post on another Pitou sample, again he provides a PCAP with these Pitou\r\ndomains:\r\ndomain seed first generated last generated\r\namlivaias.us 0xDAFE02C 2019-06-20 2019-06-29\r\npiiaxasas.com 0xDAFE02C 2019-06-19 2019-06-28\r\nzuojabba.mobi 0xDAFE02C 2019-06-18 2019-06-27\r\nvaxeiayas.mobi 0xDAFE02C 2019-06-22 2019-06-30\r\ngiazfaeas.me 0xDAFE02C 2019-06-21 2019-06-30\r\noltaeazas.mobi 0xDAFE02C 2019-06-20 2019-06-29\r\nrogojaob.info 0xDAFE02C 2019-06-23 2019-07-01\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 65 of 84\n\ndomain seed first generated last generated\r\nirifyara.com 0xDAFE02C 2019-06-21 2019-06-30\r\nufayubja.me 0xDAFE02C 2019-06-19 2019-06-28\r\nnaaleazas.net 0xDAFE02C 2019-06-23 2019-07-01\r\ndienoalas.us 0xDAFE02C 2019-06-17 2019-06-26\r\nkooovaqas.biz 0xDAFE02C 2019-06-23 2019-07-01\r\nsuudaacas.org 0xDAFE02C 2019-06-18 2019-06-27\r\nwiejlauas.info 0xDAFE02C 2019-06-18 2019-06-27\r\nunufabub.net 0xDAFE02C 2019-06-21 2019-06-30\r\nyoevuajas.us 0xDAFE02C 2019-06-22 2019-06-30\r\nepcioalas.info 0xDAFE02C 2019-06-20 2019-06-29\r\nhuoseavas.name 0xDAFE02C 2019-06-17 2019-06-26\r\ncaoelasas.name 0xDAFE02C 2019-06-22 2019-06-30\r\nijcaiatas.name 0xDAFE02C 2019-06-19 2019-06-28\r\nTrojan.Pitou.B\r\nThe technical description of Pitou by Symantec lists 20 domains. Note that these use these stem from the other\r\nseed 0xDAFE02D :\r\ndomain seed first generated last generated\r\necqevaaam.net 0xDAFE02D 2016-01-06 2016-01-15\r\nyaefobab.info 0xDAFE02D 2016-01-09 2016-01-18\r\nalguubub.mobi 0xDAFE02D 2016-01-14 2016-01-23\r\ndueifarat.name 0xDAFE02D 2016-01-14 2016-01-23\r\nehbooagax.info 0xDAFE02D 2016-01-13 2016-01-22\r\nigocobab.com 0xDAFE02D 2016-01-08 2016-01-17\r\nutleeawav.us 0xDAFE02D 2016-01-14 2016-01-23\r\nwuomoalan.us 0xDAFE02D 2016-01-06 2016-01-15\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 66 of 84\n\ndomain seed first generated last generated\r\ncoosubca.mobi 0xDAFE02D 2016-01-09 2016-01-18\r\nseeuvamap.mobi 0xDAFE02D 2016-01-06 2016-01-15\r\nhioxcaoas.me 0xDAFE02D 2016-01-15 2016-01-24\r\nupxoearak.biz 0xDAFE02D 2016-01-07 2016-01-16\r\noxepibib.net 0xDAFE02D 2016-01-07 2016-01-16\r\nruideawaf.us 0xDAFE02D 2016-01-08 2016-01-17\r\nagtisaib.info 0xDAFE02D 2016-01-07 2016-01-16\r\nneaqaaxag.org 0xDAFE02D 2016-01-08 2016-01-17\r\npooexaxaq.org 0xDAFE02D 2016-01-15 2016-01-24\r\niyweialay.net 0xDAFE02D 2016-01-13 2016-01-22\r\nlaagubha.com 0xDAFE02D 2016-01-15 2016-01-24\r\nviurjaza.name 0xDAFE02D 2016-01-09 2016-01-18\r\nAppendix - Virtual ISA\r\nNot all variants are used by Pitou, and not all instructions are fully implemented. I created the illustrations with a\r\nPython script created for this purpose.\r\n0x00 - XOR\r\nPops two values from the stack, XORs them, and pushes the result back on the stack.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x00 , 0x2F or 0x35\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 67 of 84\n\nfield bits description\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x01 - M2E and M2V\r\nGets a value (and extra value) from the stack, and moves the value to the offset given by the extra slot from the\r\nstack ( M2E - move to extra) or to the offset from the instruction ( M2V - move to value).\r\nfield bits description\r\nhas size 1\r\n0 : no size and segment field, defaults to Dword, 1 : size and segment value\r\nfollows (1 byte)\r\nmove to\r\noffset\r\n1 0 : move to address from stack, 1 : move to relative offset given by offset\r\nopcode 6 must be 0x01\r\nsegment 6 (optional) 0 or 6 : data segment, 4 : fs, 5 : gs\r\nsize 2 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\noffset 64 (optional) destination of the mov instruction\r\n0x02 - JMP, CALL and RET\r\nPop an address from the stack. Then jump or call this address. The flag stay in vm determines, whether the call\r\nis to bytecode or to native code. The difference between jump or call affects the native stack: the call pushes the\r\n(native) instruction pointer on the native stack. This allows calls to native code to return to the VM handler. With\r\nthe help of the decompiler, some JMP statements are renamed to RET (see Decompiler section).\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 68 of 84\n\nfield bits description\r\ncall or jump 1 0 : jump, 1 : call\r\nstay in vm 1 0 : external (native) address, 1 : vm address\r\nopcode 6 must be 0x02 , 0x24 or 0x25\r\n0x03 - POPD\r\nIncrease the stack pointer, i.e., discard stack elements.\r\nfield bits description\r\nmultiple 1 0 : discard 1 element, 1 : discard n elements given as operand (1 byte)\r\nopcode 6 must be 0x03 , 0x1D , 0x2A or 0x3A\r\nnr 8 (optional) number of stack elements to discard\r\n0x04 - DREFH\r\nDereference an address from the stack or given as an argument. The resulting address and the address itself are\r\nstored on the stack (as value and extra data respectively).\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 69 of 84\n\nfield bits description\r\nhas size 1\r\n0 : no size and segment field, defaults to Dword, 1 : size and segment\r\nvalue follows (1 byte)\r\ndref from stack or\r\nimmediate\r\n1 0 : dereference from stack, 1 : dereference from argument\r\nopcode 6 must be 0x04 , 0x1E , 0x23 or 0x33\r\nsegment 3 (optional) 0 or 6 : data segment, 4 : fs, 5 : gs\r\nsigned 1\r\n(optional) 0 : dereferenced value is unsigned, 1 : dereferenced value is\r\nsigned\r\nsize 2\r\n(optional) size of the address, 0 : Byte, 1 : Word, 2 : DWord, 3 :\r\nQWord\r\naddress 64 (optional) address of the value\r\n0x05 - SBB\r\nPops two values from the stack, subtracts the first from the second and pushes the result back on the stack. Also\r\nsubtracts the carry flag, i.e., simulates assembly instruction SBB .\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 70 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x05 , 0x2E or 0x32\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x06 - IF x → y\r\nConditional jump to a target address (relative or absolute). Can consider one or two sets of flags from the\r\nFLAGS register.\r\nfield bits description\r\nbinary\r\ncomparison\r\n1 0 : use one flag, 1 : use two flags\r\nand / or 1 how to treat two flags, 0 : OR operation, 1 : AND operation\r\nopcode 6 must be 0x06\r\noperation 1 2\r\n0 : true if at least one but not all flags set, 1 : true if no flag set, 2 : true if all or\r\nno flags set, 3 : true if all flags set\r\nbitmask 1 3 0 : CF, 1 : PF, 2 : AF, 3 : ZF, 4 : SF, 5 : OF\r\noperation 2 2 (optional) see operation 1\r\nbitmask 2 3 (optional) see bitmask 1\r\ndestination 64 (optional) relative destination address\r\nFor example, the conditional jump JLE is taken when\r\nIF ZF OR NOT (SF \u0026 OF) AND (SF | OF)\r\nWhich can be split into two conditions\r\n1. ZF : bitmask 1 set to 001000 , with operation 1 set to 3 .\r\n2. NOT (SF \u0026 OF) AND (SF | OF) : bitmask 2 set to 110000 , with operation 2 set to 0 .\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 71 of 84\n\nThe two conditions are combined with OR , i.e., and / or is set to 0 .\r\nThe handler could check very complex combinations of flags. At least for the DGA, however, only the following\r\nchecks are used which all can be mapped to an x86/x64 assembly instruction:\r\ncomparison\r\nand /\r\nor\r\nop\r\n1\r\nbm 1\r\nop\r\n2\r\nbm 2 result assembly\r\nunary - 1 010000 - - NOT SF JNS\r\nunary - 3 010000 - - SF JS\r\nunary - 2 110000 - - ``(SF \u0026 OF) OR NOT (SF OF)``\r\nunary - 1 001000 - - NOT ZF JNZ\r\nunary - 0 110000 - - ``NOT (SF \u0026 OF) AND (SF OF)``\r\nbinary or 3 010000 0 110000\r\n``ZF OR NOT (SF \u0026 OF)\r\nAND (SF\r\nOF)``\r\nunary - 3 010000 - - ZF JZ\r\nunary - 3 000001 - - CF JB\r\n0x07 - NOP\r\nDoes not affect the VM.\r\nfield bits description\r\nopcode 6 must be 0x07 or 0x37\r\n0x08 - MUL\r\nCopy the two virtual registers rax and rdx to the native counterpart. Pop a value from the stack and run MUL,\r\nwhich multiplies the stack value with the native rax . Then the two native registers rax and rdx are copied\r\nback to their respective virtual registers.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 72 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x08 , 0x22 or 0x2D\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x09 - DIV\r\nSame as MUL, except DIV is run instead of MUL.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x09\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x0A - STACKSWP\r\nSwap the value of the two topmost stack elements. The extra field is not affected by this instruction, so probably\r\nthe instruction will only be applied to stack elements where the extra field is unused.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 73 of 84\n\nfield bits description\r\nopcode 6 must be 0x0A or 0x3C\r\n0x0B - CWD, CDW …\r\nExtends the sign bit of al , ax , eax or rax .\r\nfield bits description\r\nhas\r\nsize\r\n1\r\n0 : no size field, defaults to CWDE for 32-bit and CDQ for 64-bit, 1 : size value follows\r\n(1 byte)\r\n64-bit 1 0 : use 32 bit conversions, 1 : use 64-bit conversions\r\nopcode 6 must be 0x0B , 0x20 or 0x36\r\nsize 8 (optional) size of the operation, see table below\r\nThe 8 combinations of flags has size , 64-bit and size map to these x64 instructions. The colum\r\nextension shows which register is sign extended to what register.\r\n| has size | 64-bit | size | mnemonic | extension | | — | — | — | — | | 0 | 0 | x | CWDE | ax to eax | 1 | 0 | 1 | CBW\r\n| al to ax | 1 | 0 | 2 | CWDE | ax to eax | 1 | 0 | 3 | CDQE | eax to rax | 0 | 1 | x | CDQ | eax to\r\nedx:eax | 1 | 1 | 1 | CWD | ax to dx:ax | 1 | 1 | 2 | CDQ | eax to edx:eax | 1 | 1 | 3 | CQO | rax to\r\nrdx:rax\r\n0x0C - NOP\r\nNot implemented, extracts three values but does nothing\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 74 of 84\n\nEncoding\r\nfield bits description\r\nopcode 6 must be 0x0C or 0x29\r\nc 3 an unused 3 bit value\r\nb 3 an unused 3 bit value\r\na 2 an unused 2 bit value\r\n0x0D - IMUL\r\nPop two values from the stack, multiply them as signed values, and push the result back on the stack.\r\nEncoding\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x0D or 0x3E\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x0E - NEG\r\nPop a value from the stack and negate (NEG) it, then push the result back on the stack.\r\nEncoding\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 75 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x0E\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\nDecompiler\r\nSee unary\r\n0x0F - SUB\r\nPop two values from the stack, subtract them (without borrow) and push the result back on the stack.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x0F , 0x34 or 0x39\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x10 - OR\r\nPops two values from the stack, ORs them and pushes the result back on the stack.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 76 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x10 , 0x21 , 0x26 or 0x30\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x11 - INC and DEC\r\nPops a value from the stack and increments (INC) or decrements (DEC) it, then pushes the result back on the\r\nstack.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\ndec or inc 1 0 : decrement, 1 : increment\r\nopcode 6 must be 0x11 or 0x38\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x12 - PUSH and POP\r\nPush or pop a register to or from the stack. The push can either push the value or the address of the register to the\r\nstack, while the extra field is always set to the address of the register.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 77 of 84\n\nfield bits description\r\npush or pop 1 0 : pop from stack to register, 1 : push register to stack\r\npush address 1 (optional) 0 : push register value, 1 : push register address\r\nopcode 6 must be 0x12 , 0x27 or 0x2B\r\nnot used 1 (unused)\r\nnot used 2 (unused)\r\nregister 5 register index, see below\r\nThe register is referenced by an index: 0: rax , 1: rcx , 2: rdx , 3: rbx , 4: rsp , 5: rbp , 6: rsi , 7: rdi ,\r\n8: r8 , 9: r9 , 10: r10 , 11: r11 , 12: r12 , 13: r13 , 14: r14 , 15: r15 , 16: eflags , 17: field_88 , 18:\r\nvirtual instruction pointer, 19: virtual stack base, 20: virtual stack pointer\r\n0x13 - CMP\r\nPop two values from the stack, compare them, and set the virtual RFLAGS ( CF , PF …) accordingly. Push the\r\nlast value back on the stack, which is then often discarded in the next virtual instruction. The following snippet\r\nfrom the disassembly shows how r11 is compared to Ch . Note that the address of r11 is pushed and then\r\ndereferenced in the following instruction to get r11 on the stack, altough there is also a version of PUSH that\r\npushes the value itself, without requiring the DRERH instruction.\r\nE7 BC PUSH qword (addr(r11), addr(r11))\r\n9E 54 DREFH qword\r\n98 57 5B PUSH byte Ch\r\nBB 54 CMP\r\n3A POPD\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 78 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x13 or 0x3B\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x14 - SET1\r\nSet a virtual flag to 1 . I did not examine how this flag is used, it had no influence on reversing the DGA.\r\nfield bits description\r\nopcode 6 must be 0x14 or 0x3D\r\n0x15 - xDIAGy\r\nGet ( MDIAG / MDIAGA ) or pop ( PDIAG / PDIAGA ) a value from the stack. Then either move the value ( MDIAG /\r\nPDIAG ) or the address of the value ( MDIAGA / PDIAGA ) to the extra part on top of the stack. Hopefully the\r\nfollowing picture can help to illustrate the complicated instructions:\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 79 of 84\n\nfield bits description\r\npop or get 1\r\n0 : variants P* that pop the value from the stack, 1 : variants M* that get the value\r\nfrom stack\r\nuse\r\naddress\r\n1\r\n0 : move the value to the extra field, 1 : move the address of the value to the extra\r\nfield.\r\nopcode 6 must be 0x15\r\n0x16 - STATE\r\nSet the flag state1 , and either set or unset state2 . The two states together define the addressing mode.\r\nfield bits description\r\nvalue 1 0 : set state2 to 0 , 1 : set state2 to 1\r\nopcode 6 must be 0x16 or 0x2C\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 80 of 84\n\n0x17 - ADD\r\nPop two values from the stack, add them and push the result back on the stack.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x17\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x18 - PUSH\r\nPush the value given as the operand on the stack. The operand can be 1, 2, 4, or 8 bytes long and can be signed or\r\nunsigned.\r\nfield bits description\r\nhas\r\nsize\r\n1\r\n0 : no size and signed field, defaults to Dword and unsigned , 1 : 1 sign flag and 2\r\nbit size value follow\r\nopcode 6 must be 0x18 or 0x31\r\nsigned 1\r\n(optional) 0 : the value is unsigned, 1 : the value is signed, i.e., the value\r\nneeds to be sign extended if not 64 bit already\r\nsize 2 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 81 of 84\n\nfield bits description\r\nvalue\r\n8, 16, 32\r\nor 64\r\nimmediate value to push on the stack\r\n0x19 - P2E and P2V\r\nPop a value (and extra value) from the stack, and move the value to the extra value from the stack ( P2E - pop to\r\nextra) or to the offset from the instruction ( P2V - pop to value).\r\nfield bits description\r\nhas size 1\r\n0 : no size and segment field, defaults to Dword, 1 : size and segment value\r\nfollows (1 byte)\r\nmove to\r\noffset\r\n1 0 : move to address from stack, 1 : move to relative offset given in offset\r\nopcode 6 must be 0x19\r\nsegment 6 (optional) 0 or 6 : data segment, 4 : fs, 5 : gs\r\nsize 2 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\noffset 64 (optional) destination of the mov instruction\r\n0x1A - NOT\r\nPop a value from the stack, perform a bitwise NOT operation, i.e., reverses all bits, and push the result back on\r\nthe stack.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 82 of 84\n\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x1A or 0x1F\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x1B - AND\r\nPop two values from the stack, AND them and push the result back on the stack.\r\nfield bits description\r\nhas size 1 0 : no size field, defaults to Dword, 1 : size value follows (1 byte)\r\nopcode 6 must be 0x1B\r\nsize 8 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\n0x1C - SHR, SHL …\r\nPop a value from the stack, shift the value, and push the result back on the stack.\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 83 of 84\n\nfield bits description\r\nhas size 1\r\n0 : no size and type field, defaults to Dword and SHL/SHR, 1 : 6 bit type and 2 bit size\r\nvalue follows\r\ndirection 1 direction of shift, 0 : shift right, 1 : shift left\r\nopcode 6 must be 0x1C\r\ntype 6\r\n(optional) type of shift, 0 : SHL or SHR shift, 2 : ROL or ROR, 3 : SAL or SAR; if\r\nhas size is not set, then this field does not exist and the type of shift defaults to SHL\r\nand SHR respectively.\r\nsize 2 (optional) size of the operation, 0 : Byte, 1 : Word, 2 : DWord, 3 : QWord\r\nSource: https://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nhttps://johannesbader.ch/2019/07/the-dga-of-pitou/\r\nPage 84 of 84\n\nTo understand At the top of the DGA, the picture you must first you can see the look at the native call of the virtual code that DGA with calls the VM: the five arguments that are passed:\nr8d: The current day, for instance 2 for the 2nd.\nedx: The current month, for instance 3 for March. \necx: The current year, for instance 2019 . \n    Page 37 of 84",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://johannesbader.ch/2019/07/the-dga-of-pitou/"
	],
	"report_names": [
		"the-dga-of-pitou"
	],
	"threat_actors": [],
	"ts_created_at": 1775791253,
	"ts_updated_at": 1775826709,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/c3a766d2b36db56dc38ca95a68e019fc5bb81b5c.pdf",
		"text": "https://archive.orkl.eu/c3a766d2b36db56dc38ca95a68e019fc5bb81b5c.txt",
		"img": "https://archive.orkl.eu/c3a766d2b36db56dc38ca95a68e019fc5bb81b5c.jpg"
	}
}