{
	"id": "32df52cd-a2f0-417b-abb3-b5ff6fc55678",
	"created_at": "2026-04-06T00:11:36.167614Z",
	"updated_at": "2026-04-10T03:36:37.023432Z",
	"deleted_at": null,
	"sha1_hash": "b592047277c62691e5914673f4135db9b65a9806",
	"title": "GraceWire / FlawedGrace malware adventure",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 353197,
	"plain_text": "GraceWire / FlawedGrace malware adventure\r\nBy Hugo Caron\r\nPublished: 2022-11-11 · Archived: 2026-04-05 16:40:38 UTC\r\nThis is some note about the Gracewire malware that I come across in last year during some investigation. Maybe\r\nthis will help people who are working on it. I’ve documented the persistence mechanism and the recovery\r\nmechanism for the Virtual File System (aka the configuration)\r\nThe persistence mechanism was well hidden beneath a lot of layers and allowed it to be fileless. They change the\r\nComHandler of an existing Windows Task schedule. Tools like autoruns still think it’s a Windows valid entry and\r\nhide it. The modification timestamp of the task schedule file can be used to find out the malware infection.\r\nGracewire is complex, Rolf Rolles publish a state-of-the-art IDB on one version of FlawedGrace/Gracewire. His\r\nwork saved me a lot of time in getting the information I needed.\r\nGracewire\r\nGracewire fileless loading version\r\nThis information comes from an infected system, where no malware was found directly on the disk.\r\nStage 1\r\nThe malware maintains persistence by updating an existing Windows task schedule\r\n\\Windows\\System32\\Tasks\\Microsoft\\Windows\\Registry\\RegIdleBackup (this task is hard-coded inside the\r\nmalware). It changes the ComHandler action with the UUID {CF8C0CD5-8DAA-4899-91FE-DF8DD3D165DE} .\r\nThey did not timestomp the file after updating it. So, it’s possible to find the persistence with a timeline analysis.\r\nIn the registry, they create the key HKLM:\\Software\\Classes\\CLSID\\{CF8C0CD5-8DAA-4899-91FE-DF8DD3D165DE}\\TreatAs is set to {972903D2-3A23-4C4D-A3D2-B6DE37AC983C} . The execution of the task schedule\r\nwill, in fact, execute the command saved in HKLM:\\Software\\Classes\\CLSID\\{972903D2-3A23-4C4D-A3D2-\r\nB6DE37AC983C}\\LocalServer .\r\nLocalServer contains the command below\r\nC:\\Windows\\SYstem32\\WindowsPowerShell\\v1.0\\powershell.exe -WindowStyle Minimized -c \"\u0026 {iex([System.Text.Encodi\r\nI’ve to hide the payload a little. This command will load and execute the PowerShell script saved in\r\nHKLM:\\Software\\Classes\\CLSID\\{972903D2-3A23-4C4D-A3D2-B6DE37AC983C}\\ProgID under the name {972903D2-\r\n3A23-4C4D-A3D2-B6DE37AC983C}\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 1 of 30\n\nThe two UUID derivate from a seed value, but we don’t know the seed value.\r\nStage 2\r\nInside the key HKLM:\\Software\\Classes\\CLSID\\{972903D2-3A23-4C4D-A3D2-B6DE37AC983C}\\ProgID the entry\r\nname {972903D2-3A23-4C4D-A3D2-B6DE37AC983C} has for value a PowerShell script.\r\nfunction DJEKGLRA{param($DJEKGLRB)[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($DJEKGLRB)\r\nThis script loads a DLL from HKLM:\\Software\\Classes\\CLSID\\{972903D2-3A23-4C4D-A3D2-\r\nB6DE37AC983C}\\VersionIndependentProgID with the name {972903D2-3A23-4C4D-A3D2-B6DE37AC983C} and\r\nexecute it inside the powershell.exe process.\r\nStage 3\r\nThis file is not on VT and I can’t share it.\r\nstage3.bin\r\nfile size 61440 (60.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nPE date 2012-12-28 02:58:07 GMT\r\nThis DLL is packed with a simple packer.\r\nstage3.unpack.bin\r\nfile size 36864 (36.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nPE date 2010-12-14 15:47:25 GMT\r\nThis DLL assumes that it’s running inside another thread than the main one; otherwise it will exit. The goal is to\r\nload the final payload from a registry key. The registry key used depends on the system information.\r\nThis file is later found in the VFS embed inside the installer (file /c ) with this information. I provide it on the\r\nGitHub repo in samples.zip.\r\nc.embded.stage3.dll\r\nfile size 36864 (36.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 2 of 30\n\nc.embded.stage3.dll\r\nmd5 18d1e87283eb975a76bb682e59dbaafa\r\nsha1 24a7988b43b76bc19a814ba5d44a8bd5fa6f54fc\r\nsha256 0e82f50477a4df52bdee361ef155d3c0496f2cd87523c17f492216e8ebceff9a\r\nimphash c438b77d56d8a538d975960a500f2199\r\nPE date 2014-08-07 02:26:11 GMT\r\nIt generates 3 UUID that derivate from a seed, a volume serial id, and computer name (script is here. Depending\r\non the usage, it will be formatted or not.\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x6f3ad240; desc: loader_reg_key; val: {8D7B6772-6772-8D7B-1C17-07FB\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x91fa4e91; desc: loader_reg_name; val: {73BBFBA3-FBA3-73BB-80C6-C705\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x350af376; desc: loader_enc_key; val: 44464bd744464bd7213d37a133224b\r\nSeed 0x6f3ad240 is used to generate the registry key SOFTWARE\\Classes\\CLSID\\{8D7B6772-6772-8D7B-1C17-\r\n07FB05037B8D}\r\nSeed 0x91fa4e91 is used to generate the value name of the registry key.\r\nThe value will contain a payload encrypted. The key to decrypt it come from the value generated by the seed\r\n0x350af376 . Due to how they implement it, the encryption is weak and can be simplified to a xor with a one-byte key. (python unpacker here)\r\nThe payload extracted will be called grace.loader.bin\r\nGraceWire loader payload\r\ngrace.loader.bin / 6f3ad240.dec.bin\r\nfile size 1146880 (1.1MiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 534362e1316c41dd0637f757c7766858\r\nsha1 0377a70765062922e9b6a2363b958a2dfc8b62f7\r\nsha256 a2da59241b1f6d898f0f32087b2684da2a38954063b2c0078a459171c27eab26\r\nimphash\r\nPE date 2012-12-28 02:58:07 GMT\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 3 of 30\n\nThe same packer as stage 3 is used.\r\n$ python3 grace_unpacker.py unpack -f ../samples/Gracewire_sample1/grace.loader.bin \u003e ../samples/Gracewire_sam\r\n$ file ../samples/Gracewire_sample1/grace.loader.unpack.bin\r\n../samples/Gracewire_sample1/grace.loader.unpack.bin: MS-DOS executable PE32+ executable (DLL) (GUI) x86-64, for\r\ngrace.loader.unpack.bin / 6f3ad240.unpack.hd.bin\r\nfile size 1122304 (1.1MiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 0a8ebb14016dc90af35c6360d73e126a\r\nsha1 cbaffd2383bf9fb518f7a7d13d25a7cc262c99ed\r\nsha256 bb151716c5755f4ddb39ee16f0041e42044e3b408abad1300c7107f727093df9\r\nimphash f8f56226b601010b649b11718ebc7593\r\nPE date 2015-05-28 06:31:14 GMT\r\nThis is the loader for the main module of GraceWire/FlawedGrace, which is in charge to get the configuration and\r\nthe main module, migrate to another process and execute the main module.\r\nTo work on this file, I wrote few scripts\r\ngracewire_loader_string_ida.py decrypt and add in comment the string in the IDB\r\nqiling_grace_loader_resolv.py Qiling script to find out the id, name of import used. This script used to\r\nwork, but now is failing. I update it to work on the recent Qiling version but during some decryption there\r\nis some junk, so it failed to resolv an import and don’t get fully initialized.\r\ngrace_loader_generate_uuid.py The script used to generate the UUID for a system common to the different\r\nlayer.\r\nThey used some obfuscation probably with LLVM to add a lot of junk code, between real instruction\r\ngrace_core_ida_obfuscation\r\nAfter removing some of the obfuscation with IDA\r\ngrace_core_ida_deobfuscation\r\nAt one moment my script was most likely okay and I try to be too aggressive in the deobfuscation so at the end\r\nthat was removing useful information. I lost the working version. The script is here for\r\nida_grace_core_deobfucation.py and ida_grace_core_deobfucation_old.py\r\nThe malware can load its configuration from at least two different places:\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 4 of 30\n\nFrom the resource of the DLL himself (resource name N ) encryption is AES and key is\r\ner0ewjflk3qrhj81\r\nFrom the registry using a similar system as we saw in stage 3\r\nIn this file, the resource section is corrupt, probably on purpose, so it’s not inside.\r\nThe configuration in the registry for the system can be found in SOFTWARE\\Classes\\CLSID\\{BB5B4C31-4C31-BB5B-3754-27CD46285BBB} (seed 0x591af903 ). This value is encrypted with AES-CBC (slightly modified see below)\r\nthe key is derivate from seed 0x46ed5316 and only the first 16 bytes or the UUID without separator are used so\r\n24E6ACA424E6ACA4 .\r\nI provide a copy of the decrypted registry key in grace_vfs_from_reg_93f4d91a.vfs\r\nThe decrypted data follow the DataHive structure describe latter.\r\nBefore passing the execution to the main module, the malware will copy the hive to a file in memory under the\r\nname Global\\7c1828b07c1828b0196354c60b7c28b0 or Local\\B028187C-187C-B028-6319-54C60B7C28B0 . This\r\ncome from the seed 0x5269ad4e but can be modified with the command line parameter cs . A mutex is created\r\nwith the name m1828B07C1828B0196354C60B7C28B0 using the seed 0x5269ad4e . This allows modules to get\r\naccess to the hive data.\r\nUUID and seed value for this payload:\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x46ed5316; desc: config_enc_key; val: A4ACE624E624A4AC\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x591af903; desc: config_reg_key; val: {BB5B4C31-4C31-BB5B-3754-27CD4\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x5269ad4e; desc: mutant; val: Local\\B028187C-187C-B028-6319-54C60B7C\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x5269ad4e; desc: mutant; val: Global\\7c1828b07c1828b0196354c60b7c28b\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x5269ad4e; desc: mutant; val: m1828B07C1828B0196354C60B7C28B0\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x93f4d91a; desc: unknown; val: {71B56C28-6C28-71B5-174D-C9075F08B571\r\ncn: WINDEV2210EVAL; vsn: 0xe241b532; seed: 0x6f6772e0; desc: unknown; val: {8D26C7D2-C7D2-8D26-BCB7-5AFBA5A3268D\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 5 of 30\n\nAt that point, I knew that it was loading some sort of VFS, but due to the obfuscation in this version, I wasn’t\r\nreally able to make quick progress. Since I was seeing some sort of configuration and 5 PE files, at that point that\r\nwas enough information.\r\nA month or two later, I had some free time and decided to try reversing the VFS and found Rolf Rolles’s post and\r\nIDB. I was able to quickly write a python script to dump the VFS.\r\n$ python3 grace_vfs.py -f ../samples/grace_vfs_from_reg_93f4d91a.vfs\r\nByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0)\r\npack 0x00000018 | name =\r\n entry 0x000000db | name = v | 0x1\r\n entry 0x000000ec | name = l2 | b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xff\\xff\\x00\\x00\\xb8\\x00\\x00\\x00\\x00\r\n entry 0x000aa0fe | name = p1 | b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xff\\xff\\x00\\x00\\xb8\\x00\\x00\\x00\\x00\r\n entry 0x000af110 | name = p2 | b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xff\\xff\\x00\\x00\\xb8\\x00\\x00\\x00\\x00\r\n entry 0x000b5122 | name = ni | AC0EF5AB240F4FA1E09AF46A4F789CD4\r\n entry 0x000b5183 | name = sr | 0x1\r\n entry 0x000b5195 | name = hv | 0x42b\r\n entry 0x000b51a7 | name = h | b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xff\\xff\\x00\\x00\\xb8\\x00\\x00\\x00\\x00\\\r\n entry 0x001cf1b8 | name = avt | 0x1\r\n entry 0x002cc25b | name = tlc | 0x0\r\n entry 0x002cc26e | name = au | 0x0\r\n entry 0x00341292 | name = se | 0x99533bfb\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 6 of 30\n\nentry 0x003412a4 | name = ve | 0x1\r\n entry 0x003412b6 | name = c | b'MZ\\x90\\x00\\x03\\x00\\x00\\x00\\x04\\x00\\x00\\x00\\xff\\xff\\x00\\x00\\xb8\\x00\\x00\\x00\\x00\\\r\n entry 0x003b62c7 | name = lo | 0x2\r\n pack 0x000b5154 | name = AC0EF5AB240F4FA1E09AF46A4F789CD4\r\n pack 0x00000038 | name = mt\r\n entry 0x0000005a | name = w | AC2696B99D5E44B21A9A4B5987DCC0E4\r\n pack 0x00000049 | name = se\r\n pack 0x0000008b | name = [0]\r\n entry 0x0000009d | name = p | 0x1bb\r\n entry 0x000000ae | name = h | 46.161.40.87\r\n pack 0x00000027 | name = mo\r\n pack 0x002cc1ed | name = us\r\n pack 0x002cc1fe | name = D08F22EDA5AB458E68B9C0D8508ECEDA\r\n entry 0x002cc22d | name = n | SYSTEM\r\n entry 0x002cc24a | name = p | 0x218\r\n pack 0x002cc280 | name = m\r\nI dump the VFS, I provide a copy in samples.zip samples/Gracewire_sample1/dump_vfs\r\ndump_vfs\r\n├── AC0EF5AB240F4FA1E09AF46A4F789CD4\r\n│ └── mt\r\n│ ├── se\r\n│ │ └── [0]\r\n│ │ ├── h\r\n│ │ └── p\r\n│ ├── us\r\n│ │ └── D08F22EDA5AB458E68B9C0D8508ECEDA\r\n│ │ ├── n\r\n│ │ └── p\r\n│ └── w\r\n├── au\r\n├── avt\r\n├── c\r\n├── h\r\n├── hv\r\n├── l2\r\n├── lo\r\n├── ni\r\n├── p1\r\n├── p2\r\n├── se\r\n├── sr\r\n├── tlc\r\n├── v\r\n└── ve\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 7 of 30\n\nInside we have the 5 payloads and some configuration fields.\r\nl2\r\nfile size 696320 (680.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 562dd15f883320fa04c0b5a9bdb003cd\r\nsha1 da74135a4c3630e6a5c7e0d7554bcdd370a76358\r\nsha256 ca03ff2ab99e9d0bac8e92b0697a2fea0f06d5384648551bb8648efa31f61ed9\r\nimphash bbcf353adf7d223bba7b33576d501b1d\r\nPE date 2014-09-05 01:38:14 GMT\r\nl2 is GraceWire main module\r\np1\r\nfile size 20480 (20.0KiB)\r\nfile magic PE32 executable (DLL) (GUI) Intel 80386, for MS Windows\r\nmd5 88695dbddd4fc57025b523f4fca268d7\r\nsha1 57ab5d9b5302644e91e3953062b40c5346b236e3\r\nsha256 f92dbf7943590c2c4011f911ba9ba445010c9d5895b5c8b57a5da9c8708c221d\r\nimphash\r\nPE date 2009-10-19 13:41:28 GMT\r\np1 is a 32bit DLL used to inject 32bit payload\r\np2\r\nfile size 24576 (24.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 b032fcb03d685b591054855572ac8f85\r\nsha1 869b38a87802af5628fe8a318323bfcb24229086\r\nsha256 a0286ea3521167642cbc73dbe1c23bc9870bc7a3012ee521be98b38836ce834e\r\nimphash\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 8 of 30\n\np2\r\nPE date 2018-09-08 05:36:12 GMT\r\np2 is a 64bit DLL used to inject 64bit payload\r\nh\r\nfile size 1155072 (1.1MiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 69d2507bbf73cf4fa6d6ca1647754f03\r\nsha1 93eafaa180b5085babee7b2bb85c0a349131328f\r\nsha256 0d37468eb7748e4f26f54e6858e2e5e2389ba3530552394abbd56bfeb873e5d0\r\nimphash\r\nPE date 2018-09-08 05:36:12 GMT\r\nh is the GraceWire Loader (packed) found in registry\r\nc\r\nfile size 479232 (468.0KiB)\r\nfile magic PE32+ executable (DLL) (GUI) x86-64, for MS Windows\r\nmd5 80a20106ced1a5d9f350b1401dbe7d14\r\nsha1 753561bf6da3cbb75711d109ed0e38b7abb28db8\r\nsha256 6d15a0807858dce0be652e480fa7f298482c7bbf2c1e116e6cf0a3d3df95180f\r\nimphash 7edbb1f08aaa2756392c6eb6a6201489\r\nPE date 2010-05-08 17:04:47 GMT\r\nc is the installer to setup the stage3 ?? two DLLs embedded\r\nAnother older version\r\nThis was another file I took a look when I wanted to start looking at the VFS [this file\r\nefcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74]\r\n[https://www.virustotal.com/gui/file/efcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74].\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 9 of 30\n\nefcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74\r\nfile size 564040 (550.8KiB)\r\nfile magic PE32 executable (GUI) Intel 80386, for MS Windows, Nullsoft Installer self-extracting archive\r\nmd5 4b9054475ff9aa15be35b42264715354\r\nsha1 a088dfaee1779878353a1dc347a91a892e5dfd74\r\nsha256 efcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74\r\nimphash 3abe302b6d9a1256e6a915429af4ffd2\r\nPE date 2018-01-30 03:57:45 GMT\r\nThe first layer is pack, once unpack we get this version. The PE timestamp is legit the same timestamp is found in\r\nother PE directory entries. A date in strings Nov 20 2017 10:53:33 is used when building the system information\r\nfor the field built\r\nefcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74_unpacked\r\nfile size 455680 (445.0KiB)\r\nfile magic PE32 executable (GUI) Intel 80386, for MS Windows\r\nmd5 b405d76e325c20d951e74b33781540ba\r\nsha1 eeb1313ae855af3642a56022eb6298a470d76671\r\nsha256 efea3b1ccea2a9f592631b282b62ba542d5eb73fd4ee1cecfe4efc379d215305\r\nimphash af1157c6aa4a47f92f955f129e023851\r\nPE date 2017-11-20 18:54:59 GMT\r\nThis sample doesn’t have a VFS/configuration inside, this version doesn’t load from the registry or resource.\r\nThe part in charge to load the configuration from the disk builds the path to the VFS by getting the value for\r\nCSIDL_COMMON_APPDATA (normally C:\\ProgramData ); then generate a UUID base on the drive serial number and\r\ncomputername. The algorithm to derivate the UUID is not the same in this one.\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 10 of 30\n\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 11 of 30\n\nThe function GenerateUuid start at 0x004323D0 and ends at 0x004325E3 .\r\nI used Qiling to execute the function to be able to generate UUID for a targeted system.\r\nThe interesting part is here, the complete script is here.\r\n # Set hook on GetVolumeInformationW because Qiling\r\n # implementation set a string instead of a DWORD in lpVolumeSerialNumber\r\n # I should push a pull request\r\n ql.os.set_api(\"GetVolumeInformationW\", my_GetVolumeInformationW, QL_INTERCEPT.CALL)\r\n # We allocate a buffer and set it as arg0 of the target function\r\n # IDA detect the calling convention as __thiscall so arg0 is ecx\r\n ptr = ql.mem.map_anywhere(256, minaddr=0x1000)\r\n # this string is set the default one set by the malware\r\n ql.mem.string(ptr, \"B597B8EF3F3F4BDE683FEFEF65479B0E\")\r\n ql.arch.regs.write(\"ecx\", ptr)\r\n #ql.arch.stack_push(ptr)\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 12 of 30\n\n# We set the sandbox profile to match the target VSN and computername\r\n ql.os.profile[\"VOLUME\"][\"serial_number\"] = f'{vsn:d}'\r\n ql.os.profile[\"SYSTEM\"][\"computername\"] = computername\r\n # The unpack version as a bug in the CRT (maybe a bad unpack)\r\n # we have to stop before the vsnprintf and dump the fmt parameter by hand\r\n # ql.run(begin=0x4323d0, end=0x4325d7)\r\n # data = ql.mem.read(ptr, 128)\r\n ql.run(begin=0x4323d0, end=0x4325c7)\r\n # We are at the call to vsnprintf we can dump the parameters\r\n [buffer, buffercount, maxcount, ptr_fmt, arg0, arg1, arg2, arg3, arg4, arg5] = \\\r\n [ ql.arch.stack_pop(), ql.arch.stack_pop(), ql.arch.stack_pop(),\r\n ql.arch.stack_pop(), ql.arch.stack_pop(), ql.arch.stack_pop(),\r\n ql.arch.stack_pop(), ql.arch.stack_pop(), ql.arch.stack_pop(),\r\n ql.arch.stack_pop()]\r\n \r\n # We read the format string from the ptr and format it\r\n fmt = ql.mem.string(ptr_fmt)\r\n uuid = fmt % ( arg0, arg1, arg2, arg3, arg4, arg5 )\r\n return uuid\r\n$ export QL_ROOTFS=$HOME/truenas/lab-re/qiling/rootfs\r\n$ python3 qiling_grace_uuid.py ../efcee275d23b6e71589452b1cb3095ff92b10ab68cd07957b2ad6be587647b74/efcee275d23b6\r\nWINDEV2210EVAL ; 0xe241b532 ; 99d912e9a7db7b98698cc3acefa57b98\r\nFor the test system, the file will be in C:\\ProgramData\\99d912e9a7db7b98698cc3acefa57b98.dat\r\nNow we can locate the VFS file.\r\nThe VFS for this version is encrypted in AES CBC and used the key c3oeCSIfx0J6UtcV\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 13 of 30\n\nCustom AES implementation\r\nThe implementation of AES CBC have a subtlety for the last block.\r\nimport aes # https://raw.githubusercontent.com/boppreh/aes/master/aes.py\r\n###\r\n# Grace custom AES\r\n###\r\nclass GraceAes(aes.AES):\r\n def decrypt_cbc(self, data):\r\n fp = io.BytesIO(data)\r\n dwLen = len(data)\r\n previous = fp.read(0x10)\r\n dst = b''\r\n while True:\r\n if fp.tell() \u003e= dwLen - 0x20:\r\n # Read last full block and decrypt it\r\n d = fp.read(0x10)\r\n val = self.decrypt_block(d)\r\n # Read the final block and xor with decrypted last full block\r\n d2 = fp.read(0x10)\r\n val2 = aes.xor_bytes(val, d2)\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 14 of 30\n\n# Append the decrypted block bytes to get a full block\r\n d2 += bytes(val[-(0x10-len(d2)):])\r\n # decrypt and CBC\r\n val = self.decrypt_block(d2)\r\n val = aes.xor_bytes(previous, val)\r\n dst += val\r\n dst += val2\r\n break\r\n # last case\r\n d = fp.read(0x10)\r\n val = self.decrypt_block(d) # decrypt\r\n val = aes.xor_bytes(previous, val) # do CBC\r\n dst += val\r\n previous = d\r\n return dst\r\nThe VFS\r\nNow that’s when I found Rolf Rolles’s IDB saved me a lot of time.\r\nHe calls those functions DataHive .\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 15 of 30\n\nSince he reversed all the internal structure, decoding the VFS was just a matter of copying the structure in a\r\npython script and follow the DataHive::Constructor method.\r\nclass VFS_ByteStreamHeaderPattern(CStruct_):\r\n __def__ = \"\"\"\r\n struct {\r\n uint32_t magic;\r\n uint16_t fixed4;\r\n uint8_t m64BitFlag;\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 16 of 30\n\nuint8_t dummy0;\r\n uint64_t dwHeaderLen;\r\n uint64_t zero;\r\n }\r\n \"\"\"\r\nclass VFS_SerializedEntry32(CStruct_):\r\n __def__ = \"\"\"\r\n struct {\r\n uint32_t dwNextEntryStreamPos;\r\n uint32_t dwDataStreamPos;\r\n uint32_t dwSerializedSize;\r\n uint8_t bValueType;\r\n uint8_t bEntryNameIsWideString;\r\n uint8_t wEntryNameLen;\r\n uint8_t dummy;\r\n }\r\n \"\"\"\r\nclass VFS_SerializedPack32(CStruct_):\r\n __def__ = \"\"\"\r\n struct {\r\n uint32_t dwStreamPos_NextSiblingPack;\r\n uint32_t dwStreamPos_FirstChildPack;\r\n uint32_t dwStreamPos_FirstEntry;\r\n uint8_t bEntryNameIsWideString;\r\n uint8_t wEntryNameLen;\r\n uint8_t dummy;\r\n }\r\n \"\"\"\r\nclass VFS_SerializedPack64(CStruct_):\r\n __def__ = \"\"\"\r\n struct {\r\n uint64_t dwStreamPos_NextSiblingPack;\r\n uint64_t dwStreamPos_FirstChildPack;\r\n uint64_t dwStreamPos_FirstEntry;\r\n uint8_t bEntryNameIsWideString;\r\n uint16_t wEntryNameLen;\r\n }\r\n \"\"\"\r\ndef VFS_StrData(data):\r\n if isinstance(data, bytes):\r\n return f'{data[0:0x10]}'\r\n elif isinstance(data, int):\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 17 of 30\n\nreturn f'0x{data:x}'\r\n else:\r\n return f'{data}'\r\ndef VFS_UnserializedData(fp, se):\r\n fp.seek(se.dwDataStreamPos)\r\n if se.bValueType == 0: # Bytes\r\n return fp.read(se.dwSerializedSize)\r\n elif se.bValueType == 1: # Int\r\n return se.dwDataStreamPos\r\n #return struct.unpack('\u003cI', fp.read(se.dwSerializedSize))[0]\r\n elif se.bValueType == 2: # Int64\r\n return struct.unpack('\u003cQ', fp.read(se.dwSerializedSize))[0]\r\n elif se.bValueType == 3: # String\r\n return fp.read(se.dwSerializedSize).decode()\r\n elif se.bValueType == 4: # WString\r\n return fp.read(se.dwSerializedSize).decode()\r\n return None\r\ndef VFS_BuildEntryMetaData32(fp, pos, depth=0, path='', cb=None):\r\n s = VFS_SerializedEntry32()\r\n while(True):\r\n fp.seek(pos)\r\n try:\r\n s.unpack(fp)\r\n except:\r\n return\r\n name = fp.read(s.wEntryNameLen)\r\n try:\r\n name = name.decode()\r\n except:\r\n logger.debug('failed to decode name')\r\n pass\r\n try:\r\n data = VFS_UnserializedData(fp, s)\r\n try:\r\n if cb: cb(f'{path}/{name}', data)\r\n except Exception:\r\n logger.exception(\"cb\")\r\n logger.debug(f'{\" \" * depth}entry 0x{pos:08x} | name = {name} | {VFS_StrData(data)}')\r\n except:\r\n logger.exception(\"failed to UnserializedData\")\r\n logger.debug(f'{\" \" * depth}entry 0x{pos:08x} | name = {name}')\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 18 of 30\n\nif not s.dwNextEntryStreamPos:\r\n return\r\n pos = s.dwNextEntryStreamPos\r\ndef VFS_BuildPackMetaData32(fp, pos, depth=0, path='', cb=None):\r\n s = VFS_SerializedPack32()\r\n while(True):\r\n fp.seek(pos)\r\n s.unpack(fp)\r\n fp.seek(pos + s.size -1)\r\n name = fp.read(s.wEntryNameLen)\r\n try:\r\n name = name.decode()\r\n except:\r\n pass\r\n # First name is empty so we want to avoid //\r\n if name != '':\r\n path_ = f'{path}/{name}'\r\n else:\r\n path_ = ''\r\n logger.debug(f'{\" \" * depth}pack 0x{pos:08x} | name = {name}')\r\n if s.dwStreamPos_FirstEntry:\r\n VFS_BuildEntryMetaData32(fp, s.dwStreamPos_FirstEntry, depth+1, path_, cb)\r\n if s.dwStreamPos_FirstChildPack:\r\n VFS_BuildPackMetaData32(fp, s.dwStreamPos_FirstChildPack, depth+1, path_, cb)\r\n if not s.dwStreamPos_NextSiblingPack:\r\n return\r\n \r\n pos = s.dwStreamPos_NextSiblingPack\r\nSomething interesting in the DataHive::Constructor is that the VFS start with a magic 0xE6F49DC4 .\r\nThe hunt\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 19 of 30\n\nI decide to try to find new samples in the hope to collect some VFS so I wrote some Yara rules and run it on VT\r\nimport \"pe\"\r\nrule gracewire_rsrc_names\r\n{\r\n condition:\r\n pe.number_of_resources \u003e= 1 and\r\n for any y in (0..pe.number_of_resources - 1): (\r\n pe.resources[y].name_string == \"XC\\x00\\x00\\x00\")\r\n}\r\nrule gracewire_vfs_header\r\n{\r\n strings:\r\n $magic = { c4 9d f4 e6 03 00 00 00 }\r\n condition:\r\n $magic\r\n}\r\nrule gracewire_packer_01\r\n{\r\n strings:\r\n $name = \"c.dll\"\r\n $ldrloaddll = { C6 44 ?? ?? 4C\r\n C6 44 ?? ?? 64\r\n C6 44 ?? ?? 72\r\n C6 44 ?? ?? 4c\r\n C6 44 ?? ?? 6f\r\n C6 44 ?? ?? 61\r\n C6 44 ?? ?? 64\r\n C6 44 ?? ?? 44\r\n C6 44 ?? ?? 6c\r\n C6 44 ?? ?? 6c }\r\n \r\n condition:\r\n $name and $ldrloaddll\r\n}\r\n// content:\"f93j5RFRjhf2ASfy\" or content:\"er0ewjflk3qrhj81\" or content:\"c3oeCSIfx0J6UtcV\" or content:\"kwREgu7324\r\nrule gracewire_keys\r\n{\r\n strings:\r\n $k1 = \"f93j5RFRjhf2ASfy\"\r\n $k2 = \"er0ewjflk3qrhj81\"\r\n $k3 = \"c3oeCSIfx0J6UtcV\"\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 20 of 30\n\n$k4 = \"kwREgu73245Nwg7842h\"\r\n $k5 = \"1220A51676E779BD877CBECAC4B9B8696D1A93F32B743A3E6790E40D745693DE58B1DD17F65988BEFE1D6C62D5416B25B\r\n $k6 = {12 20 A5 16 76 E7 79 BD 87 7C BE CA C4 B9 B8 69 6D 1A 93 F3 2B 74 3A 3E 67 90 E4 0D 74 56 93 DE 5\r\n condition:\r\n any of them\r\n}\r\nThe gracewire_keys are keys that I’ve collected. They are most of the time used to decrypt the resources or VFS.\r\nI get some hits, and I still get some from time to time, but everything is mostly old. Some samples have a small\r\nVFS in the resource, but the IOC are known and old. If it’s in the resource after the decryption, we need to inflate\r\nit lznt1 before parsing the VFS (@todo verify).\r\nOne hit was exciting because the rule was gracewire_vfs_header so an embed VFS without encryption.\r\ngracewire_vfs_header P2P/926b145b5bda585657326e0f08c9aebb1be698e4f617c08352da50532a989244\r\nIt was a sample I had never encountered before, but with the style of the Grace’s developer.\r\nA P2P botnet\r\n926b145b5bda585657326e0f08c9aebb1be698e4f617c08352da50532a989244\r\nfile size 548352 (535.5KiB)\r\nfile magic PE32 executable (DLL) (GUI) Intel 80386, for MS Windows\r\nmd5 029338d01927c127d703625a3cd3d46d\r\nsha1 61ea8be635f0e6b62e618fe4e4d23f3238847eb4\r\nsha256 926b145b5bda585657326e0f08c9aebb1be698e4f617c08352da50532a989244\r\nimphash 1ec7c4f47b9b2cc9fc83c06310bc0b21\r\nPE date 2021-05-24 12:27:57 GMT\r\nVT ratio detection 5\r\n⁄67\r\nVT scan_date 2022-01-09 23:15:54\r\nVT link link\r\nI start looking around the internet and I found out this was not very new, NCC Group had already published some\r\ninformation on it.\r\nhttps://research.nccgroup.com/2021/12/01/tracking-a-p2p-network-related-with-ta505/\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 21 of 30\n\nThe structures they provide help me to start writing a P2P client in Python, but at the end I was unable to finish it.\r\nAt the time of writing this post (2022⁄11) everything seem offline, and I was unable to find new IOC on VT to try to\r\nbootstrap the network.\r\nThe node configuration\r\nThe first configuration inside the sample is\r\n$ python3 ../tools2/grace_vfs.py -f 926b145b5bda585657326e0f08c9aebb1be698e4f617c08352da50532a989244.vfs.config\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /cx/nid 1CF094259E06664DA5504A5E1C551759\r\n4 \u003cclass 'int'\u003e /cx/dgx 0x4\r\n4 \u003cclass 'int'\u003e /cx/exe 0x2\r\n451 \u003cclass 'bytes'\u003e /cx/key b'-----BEGIN PUBLI'\r\n4 \u003cclass 'int'\u003e /cx/port 0xce5d\r\n4 \u003cclass 'int'\u003e /va/45.129.137.237 0x84ac\r\n4 \u003cclass 'int'\u003e /va/78.128.112.139 0x84ac\r\n/cx/nid is the network id\r\n/cx/exe DLL/exe\r\n/cx/key a RSA public key\r\n/cx/port the port to listen to on UDP\r\n/va the filename is the IP and the content of the entry the port to connect\r\nThe second one is\r\n$ python3 ../tools2/grace_vfs.py -f 926b145b5bda585657326e0f08c9aebb1be698e4f617c08352da50532a989244.vfs2.confi\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n4 \u003cclass 'int'\u003e /meta/app 0x1\r\n4 \u003cclass 'int'\u003e /meta/mod 0x1\r\n4 \u003cclass 'int'\u003e /meta/bld 0x1\r\n4 \u003cclass 'int'\u003e /meta/api 0x1\r\n4 \u003cclass 'int'\u003e /meta/llr 0x1\r\n4 \u003cclass 'int'\u003e /meta/llt 0x1\r\nRecords\r\nI was able to recover some records from peers.\r\nThis based on my vague souvenir.\r\nThis record is an update command received from one of the peers:\r\n/meta/pwd is the password to decrypt the record\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 22 of 30\n\n/meta/seal is some kind of signature (RSA ?) to avoid hijacking the botnet\r\n/meta/cfg is a another VFS inside\r\n/hash is the information to ask for file block for the update, each block is 0x1000 bytes and a hash is\r\nprovided.\r\n/drop I don’t remember, maybe some kind of blacklist to remove bad node?\r\n$ python3 ../../tools2/grace_vfs.py -f record_997378FCD959AA48893CB3BB84541841.bin\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /meta/net 1CF094259E06664DA5504A5E1C551759\r\n32 \u003cclass 'str'\u003e /meta/id 997378FCD959AA48893CB3BB84541841\r\n6 \u003cclass 'str'\u003e /meta/tag update\r\n4 \u003cclass 'int'\u003e /meta/sta 0x1d7523c51f1de80\r\n4 \u003cclass 'int'\u003e /meta/load 0x1df35\r\n4 \u003cclass 'int'\u003e /meta/snc 0x1d7523c51e5f7a0\r\n4 \u003cclass 'int'\u003e /meta/utl 0x0\r\n12 \u003cclass 'str'\u003e /meta/uni update.block\r\n32 \u003cclass 'str'\u003e /meta/pwd D486BB2FAB71BB44821A327124CA9233\r\n256 \u003cclass 'bytes'\u003e /meta/seal b'\\x89oy\\xed\\xa24\\xfcL\\xe5uN\\xf2sM\\xe4\\xd2'\r\n125 \u003cclass 'bytes'\u003e /meta/cfg b'\\xc4\\x9d\\xf4\\xe6\\x03\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\r\n4 \u003cclass 'int'\u003e /hash/dgst 0x4\r\n4 \u003cclass 'int'\u003e /hash/size 0x20\r\n4 \u003cclass 'int'\u003e /hash/0/ofs 0x0\r\n4 \u003cclass 'int'\u003e /hash/0/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/0/hash b'\\x82\\x8e\\xa2\\xa6w\\xbf\\x80g\\x1c\\x0b\\x99W\\xc3)\\x99\\xa5'\r\n4 \u003cclass 'int'\u003e /hash/1/ofs 0x1000\r\n4 \u003cclass 'int'\u003e /hash/1/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/1/hash b'=\\x82\"2\\xd7^\\xbb\\x8d:[\\xeb\\xef\\xf9\\x1c\\xbb\\xc0'\r\n4 \u003cclass 'int'\u003e /hash/2/ofs 0x2000\r\n4 \u003cclass 'int'\u003e /hash/2/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/2/hash b't\\xfc\\x82\\xfb\\xe4M}\\xe1\\x14\\xb1\\xe6r\\x0f\\xf0G\\xbe'\r\n4 \u003cclass 'int'\u003e /hash/3/ofs 0x3000\r\n4 \u003cclass 'int'\u003e /hash/3/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/3/hash b'NU\\xf2\\x00\\x9ea1\\x8dvi\\xcc\\x82\\xb38\\x8e\\xc0'\r\n4 \u003cclass 'int'\u003e /hash/4/ofs 0x4000\r\n4 \u003cclass 'int'\u003e /hash/4/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/4/hash b'#j\\x9c\\xe7\\xac\\xea\\xc7\\x8a\\n\\xf0\\xf7\\x98\\x95\u003c\\x01Q'\r\n4 \u003cclass 'int'\u003e /hash/5/ofs 0x5000\r\n4 \u003cclass 'int'\u003e /hash/5/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/5/hash b'\\xb9\\x97\\xa1jK\u003c\\xa8in)R\\xd2\\x87\\x8cLR'\r\n4 \u003cclass 'int'\u003e /hash/6/ofs 0x6000\r\n4 \u003cclass 'int'\u003e /hash/6/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/6/hash b'\\xd6Eil\\xa7\\xb4\\x8c\\x0e\\xb4\\x97\\x91\\xdb\\x8eC\\x155'\r\n4 \u003cclass 'int'\u003e /hash/7/ofs 0x7000\r\n4 \u003cclass 'int'\u003e /hash/7/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/7/hash b'\\xea\\x06\\xb0WF\\x06\\xfeUh\\xe5\u0026o\\xf5\\xab\\tH'\r\n4 \u003cclass 'int'\u003e /hash/8/ofs 0x8000\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 23 of 30\n\n4 \u003cclass 'int'\u003e /hash/8/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/8/hash b'\\xe1*\\x06\\xd6-\\xad\\xe5\\x9b\\xcb?\\x96\\x11\\xf6\\x88\\x86\\x8e'\r\n4 \u003cclass 'int'\u003e /hash/9/ofs 0x9000\r\n4 \u003cclass 'int'\u003e /hash/9/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/9/hash b'J\\x96[\\x05\\xafs2Q\\xed\u0026\\xb7$\\xf8\\nL\\x12'\r\n4 \u003cclass 'int'\u003e /hash/A/ofs 0xa000\r\n4 \u003cclass 'int'\u003e /hash/A/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/A/hash b'\\xd9\\x9c\\xa4L\\xbe\\xe3\\xe9\\x87\\x8a\\xd2\\x99J\\xe6P4\\x9a'\r\n4 \u003cclass 'int'\u003e /hash/B/ofs 0xb000\r\n4 \u003cclass 'int'\u003e /hash/B/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/B/hash b'||\\xfb\u003c\\x10Y\\xceV\\xe3C\\x97\\xa2\\xc0E\\xae\\x16'\r\n4 \u003cclass 'int'\u003e /hash/C/ofs 0xc000\r\n4 \u003cclass 'int'\u003e /hash/C/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/C/hash b')7\\xcdN\\xfe\\xc2\\x80\\xc32\\xf5Tu`\\x83\\xc5\\xd3'\r\n4 \u003cclass 'int'\u003e /hash/D/ofs 0xd000\r\n4 \u003cclass 'int'\u003e /hash/D/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/D/hash b'\\xa5\\xdc\\xf1D\\x86\\x9f\\x80\\x9c\\x7f\\xb7D\\xcd\\x19\\xea\\xd1Q'\r\n4 \u003cclass 'int'\u003e /hash/E/ofs 0xe000\r\n4 \u003cclass 'int'\u003e /hash/E/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/E/hash b'\\xcf\\xea,-)\\xf8!`8*\\xd7\\xabEW\u0026v'\r\n4 \u003cclass 'int'\u003e /hash/F/ofs 0xf000\r\n4 \u003cclass 'int'\u003e /hash/F/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/F/hash b'\\x83\\\\1WI\\x86\\x1aq\\x88~\\xdd\\xdf\\xc5+\\x9b3'\r\n4 \u003cclass 'int'\u003e /hash/10/ofs 0x10000\r\n4 \u003cclass 'int'\u003e /hash/10/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/10/hash b'\\xd2\\xe1\\xac/\\x83\\xe5\\xf9\\xe2\\xd3\\xde\\x119\\xe1\\xe4\\xb44'\r\n4 \u003cclass 'int'\u003e /hash/11/ofs 0x11000\r\n4 \u003cclass 'int'\u003e /hash/11/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/11/hash b'\\xf6\\xb7\\xed\\x8a\\xda\\x80\\xed\\x06\\x8e\\xa9\\xc9\\x9c\u003c\\x15m\\xd7'\r\n4 \u003cclass 'int'\u003e /hash/12/ofs 0x12000\r\n4 \u003cclass 'int'\u003e /hash/12/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/12/hash b'\\x03\\x11\\xff\\xf8\\xeb\\xd5\\xba2O\\xcdi\\x90\\xf5^\\xcc\\x8a'\r\n4 \u003cclass 'int'\u003e /hash/13/ofs 0x13000\r\n4 \u003cclass 'int'\u003e /hash/13/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/13/hash b'\u003c\\xddY\\x13\\xedI\\x11\\xa0\\xd1\\xaa\\xc5\\xd9\\xe2I\\xc4\\xe9'\r\n4 \u003cclass 'int'\u003e /hash/14/ofs 0x14000\r\n4 \u003cclass 'int'\u003e /hash/14/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/14/hash b'\\xad%~e\\xccU\\xbc\\x97\\xf5\\xe7#\\xe7[\\x1b\\xd36'\r\n4 \u003cclass 'int'\u003e /hash/15/ofs 0x15000\r\n4 \u003cclass 'int'\u003e /hash/15/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/15/hash b'\\xee\\xe2\\xf72\\xc5\\x8aJ\\x0ciOS\\xf8[\\xfc\\xbb8'\r\n4 \u003cclass 'int'\u003e /hash/16/ofs 0x16000\r\n4 \u003cclass 'int'\u003e /hash/16/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/16/hash b'1\\x90\\xcbu\\xdf7\\xbb\\xd6\\n\\xd8\\xf8\\x18\\xb6\\xf2\\x18\\xe0'\r\n4 \u003cclass 'int'\u003e /hash/17/ofs 0x17000\r\n4 \u003cclass 'int'\u003e /hash/17/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/17/hash b'\\xda\\xbc\\xecZ\\x93j\\xda\\x8ay\\x14\\xd5_\\xc4\\xa7\\xa5\"'\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 24 of 30\n\n4 \u003cclass 'int'\u003e /hash/18/ofs 0x18000\r\n4 \u003cclass 'int'\u003e /hash/18/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/18/hash b'\\x8f\\xdd\\xf9d\\xc0\\x1fq0_\\xe7\\xb1\\xf9.\\xec\\xef\\x18'\r\n4 \u003cclass 'int'\u003e /hash/19/ofs 0x19000\r\n4 \u003cclass 'int'\u003e /hash/19/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/19/hash b'\\xed/#\\x85\\xf4\\xf1\\xfe)\\xdah\\xe4Sw\\xad\\xf9\\xc2'\r\n4 \u003cclass 'int'\u003e /hash/1A/ofs 0x1a000\r\n4 \u003cclass 'int'\u003e /hash/1A/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/1A/hash b'\\x882`x*\\xf3\\xf8\\\\\\x80\\x05m=\u003e4\\xac\\xab'\r\n4 \u003cclass 'int'\u003e /hash/1B/ofs 0x1b000\r\n4 \u003cclass 'int'\u003e /hash/1B/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/1B/hash b'\\xc2\\xd36,\\xeb\\xeaN\\xc4BD\\xac\\xd9\\xf2k\\xe1\\x92'\r\n4 \u003cclass 'int'\u003e /hash/1C/ofs 0x1c000\r\n4 \u003cclass 'int'\u003e /hash/1C/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/1C/hash b'\\xf6\u003c\\xf5O\\xd0\\xa9\\x1ez}f_B\\x12\\xc7g\\x1c'\r\n4 \u003cclass 'int'\u003e /hash/1D/ofs 0x1d000\r\n4 \u003cclass 'int'\u003e /hash/1D/size 0xf35\r\n32 \u003cclass 'bytes'\u003e /hash/1D/hash b'm\\x96\\xcfC\\xd0Z88Ar\\xd3\\xce\\xa8\\x03\\xe8\\xbd'\r\n13 \u003cclass 'str'\u003e /drop/0/host 194.165.16.94\r\n4 \u003cclass 'int'\u003e /drop/0/port 0x8ad5\r\nFrom /meta/cfg\r\n32 \u003cclass 'str'\u003e /fp B88763DF8318F4962F0EFD398234DF96\r\n4 \u003cclass 'int'\u003e /rs 0x1\r\n4 \u003cclass 'int'\u003e /md 0x0\r\nPacket handler\r\nI rewrite kind of pkt_handler in python\r\n def pkt_handler(self, addr, pkt, pkt_data, data):\r\n if pkt.bFrameId == 0x10:\r\n logging.info(f'[#] echo recv')\r\n elif pkt.bFrameId == 0x7:\r\n logging.info(f'[#] fit one packet')\r\n hdr = NodePktDataHeader()\r\n hdr.unpack(data)\r\n logging.info(f'{hdr}')\r\n if hdr.bCmdId == 0x1:\r\n logging.info(f' [#] invitation packet')\r\n inv = NodePktInvitation()\r\n inv.unpack(data)\r\n logging.info(f' {inv}')\r\n elif hdr.bCmdId == 0x2:\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 25 of 30\n\nlogging.info(f' [#] add node to probes')\r\n inv = NodePktInvitation()\r\n inv.unpack(data)\r\n logging.info(f' {inv}')\r\n elif hdr.bCmdId == 0x03:\r\n logging.info(f' [#] Get a ping request')\r\n node_info = unserialized(data[hdr.size:])\r\n logging.info(f' node_info: {node_info} | {node_info[1]}/{node_info[0]} connections | {node_info\r\n # @todo implement response\r\n elif hdr.bCmdId == 0x4:\r\n logging.info(f' [#] node info')\r\n node_info = unserialized(data[hdr.size:])\r\n logging.info(f' node_info: {node_info} | {node_info[1]}/{node_info[0]} connections | {node_info\r\n elif hdr.bCmdId == 0x05:\r\n logging.info(f' [#] Get a new node')\r\n fp = io.BytesIO(data[hdr.size:])\r\n node_id = fp.read(0x10)\r\n node_port = struct.unpack('\u003cH', fp.read(2))[0]\r\n node_ip = fp.read(ord(fp.read(1))).decode()\r\n logging.info(f' node_id: {node_id.hex()} node: {node_ip}:{node_port:d}')\r\n n = { 'addr': [node_ip, node_port], 'node_id': node_id.hex()}\r\n is_exist = False\r\n for v in self.store['nodes']:\r\n if v['node_id'] == node_id.hex():\r\n is_exist = True\r\n break\r\n if not is_exist:\r\n logging.info(f' adding node to store')\r\n self.store['nodes'].append(n)\r\n self.send_invitation(n['addr'])\r\n elif hdr.bCmdId == 0x06:\r\n logging.info(f' [#] Get a record metadata')\r\n record_id = data[hdr.size:hdr.size+0x10]\r\n record_info = unserialized(data[hdr.size+0x10:])\r\n logging.info(f' record_id: {record_id.hex()} record_info: {record_info}')\r\n elif hdr.bCmdId == 0xa:\r\n logging.info(f' [#] Get a record')\r\n r = NodePktRecord()\r\n r.unpack(data)\r\n logging.info(f' {r}')\r\n else:\r\n logging.info(f' [#] unsupported hdr.bCmdId 0x{hdr.bCmdId:x}')\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 26 of 30\n\nelif pkt.bFrameId == 0xd:\r\n open(f'record_{pkt_data.dwUnk}_{pkt_data.dwCurId}_{pkt_data.dwTotal}.bin', 'wb').write(data)\r\n pass\r\n else:\r\n logging.info(f'[#] unsupported pkt.bFrameId 0x{pkt.bFrameId:x}')\r\nFile store on the filesystem\r\nOn execution, the malware use the file system to store information about the P2P network state. Most of the file\r\nfollow the VFS structure. In fs_files_from_sandbox.zip\r\n├── net.dsx\r\n├── node\r\n│ ├── 997378FCD959AA48893CB3BB84541841\r\n│ │ ├── l\r\n│ │ ├── m\r\n│ │ └── p\r\n│ └── BBBD9035B2A3CE4FB2A563F5FC6572DF\r\n│ ├── l\r\n│ ├── m\r\n│ └── p\r\n├── probes.dsx\r\n├── reports.dsx\r\n├── sessions\r\n│ └── 40DFE0CD66457646B3990B9B2160F622\r\n├── trash\r\n├── units\r\n│ ├── block\r\n│ │ └── block.dll\r\n│ └── exec\r\n│ ├── exec.dll\r\n├── units.dsx\r\n└── updates\r\nnet.dsx contain the node id\r\nnode contains record received from node, the sub key is the record id\r\nl unknown\r\nm contains the record meta data\r\np is the record data\r\nsession the files inside are VFS, but I don’t remember\r\nprobe.dsx contains other node information\r\nunits contains the unit it’s the payload recover from the record download from node in p\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 27 of 30\n\n$ python3 ../../tools2/grace_vfs.py -f net.dsx\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /gx/id 67E3945FF45AC644982765568986F30A\r\n$ python3 ../../tools2/grace_vfs.py -f units.dsx\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /blo/rec 997378FCD959AA48893CB3BB84541841\r\n100 \u003cclass 'str'\u003e /blo/exec C:\\Users\\John\\Desktop\\target\\units\\block\\block.dll\r\n4 \u003cclass 'int'\u003e /blo/meta/app 0x1\r\n4 \u003cclass 'int'\u003e /blo/meta/mod 0x2\r\n10 \u003cclass 'str'\u003e /blo/meta/name block\r\n4 \u003cclass 'int'\u003e /blo/meta/bld 0x1\r\n4 \u003cclass 'int'\u003e /blo/meta/hlt 0x1\r\n4 \u003cclass 'int'\u003e /blo/meta/api 0x1\r\n32 \u003cclass 'str'\u003e /ex/rec BBBD9035B2A3CE4FB2A563F5FC6572DF\r\n96 \u003cclass 'str'\u003e /ex/exec C:\\Users\\John\\Desktop\\target\\units\\exec\\exec.dll\r\n4 \u003cclass 'int'\u003e /ex/meta/app 0x1\r\n4 \u003cclass 'int'\u003e /ex/meta/mod 0x2\r\n8 \u003cclass 'str'\u003e /ex/meta/name exec\r\n4 \u003cclass 'int'\u003e /ex/meta/bld 0x1\r\n4 \u003cclass 'int'\u003e /ex/meta/hlt 0x1\r\n4 \u003cclass 'int'\u003e /ex/meta/api 0x1\r\n$ python3 ../../tools2/grace_vfs.py -f probes.dsx\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n4 \u003cclass 'int'\u003e /78.128.112.139/33964/curr 0x2\r\n4 \u003cclass 'int'\u003e /78.128.112.139/33964/max 0x5\r\n4 \u003cclass 'int'\u003e /78.128.112.139/33964/next 0x1d8228c5da8f7b0\r\n$ python3 ../../tools2/grace_vfs.py -f reports.dsx\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /0536FC541A7AC84DA4D94C46541B475A/rec 997378FCD959AA48893CB3BB84541841\r\n13 \u003cclass 'str'\u003e /0536FC541A7AC84DA4D94C46541B475A/ah 194.165.16.94\r\n4 \u003cclass 'int'\u003e /0536FC541A7AC84DA4D94C46541B475A/ap 0x8ad5\r\n4 \u003cclass 'int'\u003e /0536FC541A7AC84DA4D94C46541B475A/ec 0x0\r\n4 \u003cclass 'int'\u003e /0536FC541A7AC84DA4D94C46541B475A/atm 0x3e8\r\n4 \u003cclass 'int'\u003e /0536FC541A7AC84DA4D94C46541B475A/last 0x0\r\n4 \u003cclass 'int'\u003e /0536FC541A7AC84DA4D94C46541B475A/es 0x1d8228c1b9030f0\r\n$ python3 ../../tools2/grace_vfs.py -f sessions/40DFE0CD66457646B3990B9B2160F622\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n4 \u003cclass 'int'\u003e /g/c 0x1d8228bf4c4b400\r\n4 \u003cclass 'int'\u003e /g/l 0x1d8228c4b60f1c0\r\n16 \u003cclass 'bytes'\u003e /a/x b'\\x02\\x00\\x84\\xac-\\x81\\x89\\xed\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 28 of 30\n\n$ python3 ../../tools2/grace_vfs.py -f node/997378FCD959AA48893CB3BB84541841/m\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /meta/net 1CF094259E06664DA5504A5E1C551759\r\n32 \u003cclass 'str'\u003e /meta/id 997378FCD959AA48893CB3BB84541841\r\n6 \u003cclass 'str'\u003e /meta/tag update\r\n4 \u003cclass 'int'\u003e /meta/sta 0x1d7523c51f1de80\r\n4 \u003cclass 'int'\u003e /meta/load 0x1df35\r\n4 \u003cclass 'int'\u003e /meta/snc 0x1d7523c51e5f7a0\r\n4 \u003cclass 'int'\u003e /meta/utl 0x0\r\n12 \u003cclass 'str'\u003e /meta/uni update.block\r\n32 \u003cclass 'str'\u003e /meta/pwd D486BB2FAB71BB44821A327124CA9233\r\n256 \u003cclass 'bytes'\u003e /meta/seal b'\\x89oy\\xed\\xa24\\xfcL\\xe5uN\\xf2sM\\xe4\\xd2'\r\n125 \u003cclass 'bytes'\u003e /meta/cfg b'\\xc4\\x9d\\xf4\\xe6\\x03\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\r\n4 \u003cclass 'int'\u003e /hash/dgst 0x4\r\n4 \u003cclass 'int'\u003e /hash/size 0x20\r\n4 \u003cclass 'int'\u003e /hash/0/ofs 0x0\r\n4 \u003cclass 'int'\u003e /hash/0/size 0x1000\r\n32 \u003cclass 'bytes'\u003e /hash/0/hash b'\\x82\\x8e\\xa2\\xa6w\\xbf\\x80g\\x1c\\x0b\\x99W\\xc3)\\x99\\xa5'\r\n4 \u003cclass 'int'\u003e /hash/1/ofs 0x1000\r\n4 \u003cclass 'int'\u003e /hash/1/size 0x1000\r\n...\r\n$ python3 ../../tools2/grace_vfs.py -f node/BBBD9035B2A3CE4FB2A563F5FC6572DF/m\r\nVFS_ByteStreamHeaderPattern(magic=0xe6f49dc4, fixed4=0x3, m64BitFlag=0x0, dummy0=0x0, dwHeaderLen=0x18, zero=0x0\r\n32 \u003cclass 'str'\u003e /meta/net 1CF094259E06664DA5504A5E1C551759\r\n32 \u003cclass 'str'\u003e /meta/id BBBD9035B2A3CE4FB2A563F5FC6572DF\r\n6 \u003cclass 'str'\u003e /meta/tag update\r\n4 \u003cclass 'int'\u003e /meta/sta 0x1d7523c51fb7b70\r\n4 \u003cclass 'int'\u003e /meta/load 0x20d2f\r\n4 \u003cclass 'int'\u003e /meta/snc 0x1d7523c51f6c080\r\n4 \u003cclass 'int'\u003e /meta/utl 0x0\r\n11 \u003cclass 'str'\u003e /meta/uni update.exec\r\n32 \u003cclass 'str'\u003e /meta/pwd 2ECDEA5A357B5D4F968AD1EBF2B86B39\r\n256 \u003cclass 'bytes'\u003e /meta/seal b'!\\xb2N\\xc7*\\xcf\\xb3D/\\xea[\\xfc\\x0e\\xa5N\\xca'\r\n125 \u003cclass 'bytes'\u003e /meta/cfg b'\\xc4\\x9d\\xf4\\xe6\\x03\\x00\\x00\\x00\\x18\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\r\n4 \u003cclass 'int'\u003e /hash/dgst 0x4\r\n4 \u003cclass 'int'\u003e /hash/size 0x20\r\n4 \u003cclass 'int'\u003e /hash/0/ofs 0x0\r\n4 \u003cclass 'int'\u003e /hash/0/size 0x2000\r\n32 \u003cclass 'bytes'\u003e /hash/0/hash b'\u003c\\xf5\\xf3\\xe2\\x91\\xd3\\x07\\x05\\xdb8\\xde_\\xb3\\x12E\\xe2'\r\n4 \u003cclass 'int'\u003e /hash/1/ofs 0x2000\r\n4 \u003cclass 'int'\u003e /hash/1/size 0x2000\r\n...\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 29 of 30\n\nSource: https://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nhttps://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/\r\nPage 30 of 30",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://web.archive.org/web/20221115161556/https://blog.codsec.com/posts/malware/gracewire_adventure/"
	],
	"report_names": [
		"gracewire_adventure"
	],
	"threat_actors": [
		{
			"id": "5e6b31a6-80e3-4e7d-8b0a-d94897ce9b59",
			"created_at": "2024-06-19T02:03:08.128175Z",
			"updated_at": "2026-04-10T02:00:03.636663Z",
			"deleted_at": null,
			"main_name": "GOLD TAHOE",
			"aliases": [
				"Cl0P Group Identity",
				"FIN11 ",
				"GRACEFUL SPIDER ",
				"SectorJ04 ",
				"Spandex Tempest ",
				"TA505 "
			],
			"source_name": "Secureworks:GOLD TAHOE",
			"tools": [
				"Clop",
				"Cobalt Strike",
				"FlawedAmmy",
				"Get2",
				"GraceWire",
				"Malichus",
				"SDBbot",
				"ServHelper",
				"TrueBot"
			],
			"source_id": "Secureworks",
			"reports": null
		},
		{
			"id": "75d4d6a9-b5d1-4087-a7a0-e4a9587c45f4",
			"created_at": "2022-10-25T15:50:23.5188Z",
			"updated_at": "2026-04-10T02:00:05.26565Z",
			"deleted_at": null,
			"main_name": "TA505",
			"aliases": [
				"TA505",
				"Hive0065",
				"Spandex Tempest",
				"CHIMBORAZO"
			],
			"source_name": "MITRE:TA505",
			"tools": [
				"AdFind",
				"Azorult",
				"FlawedAmmyy",
				"Mimikatz",
				"Dridex",
				"TrickBot",
				"Get2",
				"FlawedGrace",
				"Cobalt Strike",
				"ServHelper",
				"Amadey",
				"SDBbot",
				"PowerSploit"
			],
			"source_id": "MITRE",
			"reports": null
		},
		{
			"id": "99cb4e5b-8071-4f9e-aa1d-45bfbb6197e3",
			"created_at": "2023-01-06T13:46:38.860754Z",
			"updated_at": "2026-04-10T02:00:03.125179Z",
			"deleted_at": null,
			"main_name": "TA505",
			"aliases": [
				"SectorJ04",
				"SectorJ04 Group",
				"ATK103",
				"GRACEFUL SPIDER",
				"GOLD TAHOE",
				"Dudear",
				"G0092",
				"Hive0065",
				"CHIMBORAZO",
				"Spandex Tempest"
			],
			"source_name": "MISPGALAXY:TA505",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		},
		{
			"id": "e447d393-c259-46e2-9932-19be2ba67149",
			"created_at": "2022-10-25T16:07:24.28282Z",
			"updated_at": "2026-04-10T02:00:04.921616Z",
			"deleted_at": null,
			"main_name": "TA505",
			"aliases": [
				"ATK 103",
				"Chimborazo",
				"G0092",
				"Gold Evergreen",
				"Gold Tahoe",
				"Graceful Spider",
				"Hive0065",
				"Operation Tovar",
				"Operation Trident Breach",
				"SectorJ04",
				"Spandex Tempest",
				"TA505",
				"TEMP.Warlock"
			],
			"source_name": "ETDA:TA505",
			"tools": [
				"Amadey",
				"AmmyyRAT",
				"AndroMut",
				"Azer",
				"Bart",
				"Bugat v5",
				"CryptFile2",
				"CryptoLocker",
				"CryptoMix",
				"CryptoShield",
				"Dridex",
				"Dudear",
				"EmailStealer",
				"FRIENDSPEAK",
				"Fake Globe",
				"Fareit",
				"FlawedAmmyy",
				"FlawedGrace",
				"FlowerPippi",
				"GOZ",
				"GameOver Zeus",
				"GazGolder",
				"Gelup",
				"Get2",
				"GetandGo",
				"GlobeImposter",
				"Gorhax",
				"GraceWire",
				"Gussdoor",
				"Jaff",
				"Kasidet",
				"Kegotip",
				"Kneber",
				"LOLBAS",
				"LOLBins",
				"Living off the Land",
				"Locky",
				"MINEBRIDGE",
				"MINEBRIDGE RAT",
				"MirrorBlast",
				"Neutrino Bot",
				"Neutrino Exploit Kit",
				"P2P Zeus",
				"Peer-to-Peer Zeus",
				"Philadelphia",
				"Philadephia Ransom",
				"Pony Loader",
				"Rakhni",
				"ReflectiveGnome",
				"Remote Manipulator System",
				"RockLoader",
				"RuRAT",
				"SDBbot",
				"ServHelper",
				"Shifu",
				"Siplog",
				"TeslaGun",
				"TiniMet",
				"TinyMet",
				"Trojan.Zbot",
				"Wsnpoem",
				"Zbot",
				"Zeta",
				"ZeuS",
				"Zeus"
			],
			"source_id": "ETDA",
			"reports": null
		}
	],
	"ts_created_at": 1775434296,
	"ts_updated_at": 1775792197,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/b592047277c62691e5914673f4135db9b65a9806.pdf",
		"text": "https://archive.orkl.eu/b592047277c62691e5914673f4135db9b65a9806.txt",
		"img": "https://archive.orkl.eu/b592047277c62691e5914673f4135db9b65a9806.jpg"
	}
}