{
	"id": "897918e2-6948-42f8-93aa-cb4eb224cdd0",
	"created_at": "2026-04-06T00:12:01.999467Z",
	"updated_at": "2026-04-10T03:20:47.934835Z",
	"deleted_at": null,
	"sha1_hash": "71da5ff60f693b4dbf04c5345fe28ef9ac744324",
	"title": "Native function and Assembly Code Invocation",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 173187,
	"plain_text": "Native function and Assembly Code Invocation\r\nBy itayc\r\nPublished: 2022-09-21 · Archived: 2026-04-05 22:25:04 UTC\r\nSeptember 21, 2022\r\nResearch by: Jiri  Vinopal.\r\nIntroduction\r\nFor a reverse engineer, the ability to directly call a function from the analyzed binary can be a shortcut that\r\nbypasses a lot of grief. While in some cases it is just possible to understand the function logic and reimplement it\r\nin a higher-level language, this is not always feasible, and it becomes less feasible the more the logic of the\r\noriginal function is fragile and sophisticated. This is an especially sore issue when dealing with custom hashing\r\nand encryption — a single off-by-one error somewhere in the computation will cause complete divergence of the\r\nfinal output, and is a mighty chore to debug.\r\nIn this article, we walk through 3 different ways to make this “shortcut” happen, and invoke functions directly\r\nfrom assembly. We first cover the IDA Appcall feature which is natively supported by IDA Pro, and can be used\r\ndirectly using IDAPython. We then demonstrate how to achieve the same feat using Dumpulator; and finally, we\r\nwill show how to get that result using emulation with Unicorn Engine. The practical example used in this article is\r\nbased on the “tweaked” SHA1 hashing algorithm implemented by a sample of the MiniDuke malware.\r\nModified SHA1 Hashing algorithm implemented by MiniDuke\r\nThe modified SHA1 algorithm in the MiniDuke sample is used to create a per-system encryption key for the\r\nmalware configuration. The buffer to be hashed contains the current computer name concatenated with DWORDs\r\nof all interface descriptions, e.g. 'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte' .\r\nThis function ( SHA1Hash ) uses the same constants as the original SHA1 for both the initial digest and\r\nintermediate stages, but produces different outputs.\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 1 of 32\n\nFigure 1: MiniDuke SHA1Hash function constants\r\nSince the constants used are all the same in the original and modified SHA1, the difference must occur somewhere\r\nin one of the function’s 1,241 assembly instructions. We cannot say whether this tweak was introduced\r\nintentionally but the fact remains that malware authors are growing fonder of inserting “surprises” like this, and it\r\nfalls to analysts to deal with them. To do so, we must first understand in what form the function expects its input\r\nand produces its output.\r\nAs it turns out, the Duke-SHA1 assembly uses a custom calling convention where the length of buffer to be\r\nhashed is passed in the ecx register and the address of the buffer itself in edi . A value is technically also\r\npassed in eax but this value is identically 0xffffffff whenever the executable invokes the function, and we\r\ncan treat it as a constant for our purposes. Interestingly, the malware also sets the buffer length ( ecx ) to 0x40\r\nevery time it invokes this function, effectively hashing only the first 0x40 bytes of the buffer.\r\nFigure 2: SHA1Hash function arguments\r\nThe resulting 160-bit SHA1 hash value is returned in 5 dwords in registers (from high dword to low: eax , edx\r\n, ebx , ecx , esi ). For example, the buffer DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN\r\nInteWAN Inte has a Duke-SHA1 value of 1851fff77f0957d1d690a32f31df2c32a1a84af7 , returned as\r\nEAX:0x1851fff7 EDX:0x7f0957d1 EBX:0xd690a32f ECX:0x31df2c32 ESI:0xa1a84af7 .\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 2 of 32\n\nFigure 3: Example produced SHA1 Hash of buffer\r\nAs explained before, hunting down the exact place(s) where the logic of SHA1 and Duke-SHA1 diverge and then\r\nreimplementing Duke-SHA1 in Python is an excellent way to waste a day, and possibly a week. Instead, we will\r\nuse several approaches to “plug into” the function’s calling convention and invoke it directly.\r\nIDA – Appcall\r\nAppcall is a feature of IDA Pro which allows IDA Python scripts to call functions inside the debugged program\r\nas if they were built-in functions. This is very convenient, but it also suffers from the typical curse of convenient\r\nsolutions, which is a very sharp spike in difficulty of application when the use case gets somewhat unusual or\r\ncomplex. Alas, such is the case here: while passing a buffer length in ecx and a buffer in edi is par for the\r\ncourse, the 160-bit return value split across 5 registers is not your typical form of function output, and Appcall\r\nrequires some creative coercion to cooperate with what we want it to do here.\r\nWe proceed by creating a custom structure struc_SHA1HASH which holds the values of 5 registers, and is used as\r\na return type of the function prototype:\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\n# ------------------Struct Creation ------------------\r\nsid = idc.get_struc_id(STRUCT_NAME)\r\nif (sid != -1):\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 3 of 32\n\nidc.del_struc(sid)\r\nsid = idc.add_struc(-1, STRUCT_NAME, 0)\r\nidc.add_struc_member(sid, \"_EAX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EDX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ECX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ESI_\", -1, idc.FF_DWORD, -1, 4)\r\nSTRUCT_NAME = \"struc_SHA1HASH\" # ------------------Struct Creation ------------------ sid =\r\nidc.get_struc_id(STRUCT_NAME) if (sid != -1): idc.del_struc(sid) sid = idc.add_struc(-1, STRUCT_NAME, 0)\r\nidc.add_struc_member(sid, \"_EAX_\", -1, idc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_EDX_\", -1,\r\nidc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ECX_\", -1, idc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_ESI_\", -1,\r\nidc.FF_DWORD, -1, 4)\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\n# ------------------Struct Creation ------------------\r\nsid = idc.get_struc_id(STRUCT_NAME)\r\nif (sid != -1):\r\n idc.del_struc(sid)\r\nsid = idc.add_struc(-1, STRUCT_NAME, 0)\r\nidc.add_struc_member(sid, \"_EAX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EDX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ECX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ESI_\", -1, idc.FF_DWORD, -1, 4)\r\nFigure 4: IDA Structure Window – “struc_SHA1HASH”\r\nNow with the structure definition in place, we are poised to invoke the magic incantation that will allow Appcall\r\nto interface with this function prototype, as seen in the PROTO value below.\r\nPlain text\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 4 of 32\n\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# ------------------Initialization ------------------\r\nFUNC_NAME = \"SHA1Hash\"\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\nPROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx, 12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e,\r\nBYTE *buffer@\u003cedi\u003e);\".format(STRUCT_NAME, FUNC_NAME) # specify prototype of SHA1Hash function\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------Initialization ------------------ FUNC_NAME = \"SHA1Hash\" STRUCT_NAME =\r\n\"struc_SHA1HASH\" PROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx, 12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e,\r\nconst int@\u003ceax\u003e, BYTE *buffer@\u003cedi\u003e);\".format(STRUCT_NAME, FUNC_NAME) # specify prototype of\r\nSHA1Hash function SHA1BUFF_LEN = 0x40 CONSTVAL = 0xffffffff\r\n# ------------------Initialization ------------------\r\nFUNC_NAME = \"SHA1Hash\"\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\nPROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx, 12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e, const int@\u003ceax\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\nAs IDA Appcall relies on the debugger, to invoke this logic we first need to write a script that will start the\r\ndebugger, make required adjustments to the stack and do other required housekeeping.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# ------------------ Setting + Starting Debugger ------------------\r\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 5 of 32\n\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustment\r\n# ------------------ Setting + Starting Debugger ------------------ idc.load_debugger(\"win32\",0) # select Local\r\nWindows Debugger idc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nidc.start_process(\"\",\"\",\"\") # start process with default options idc.wait_for_next_event(idc.WFNE_SUSP, 3) #\r\nwaits until process get suspended on entrypoint eip = idc.get_reg_value(\"eip\") # get EIP idc.run_to(eip + 0x1d) #\r\nlet the stack adjust itself (execute few instructions) idc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until\r\nprocess get suspended after stack adjustment\r\n# ------------------ Setting + Starting Debugger ------------------\r\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustme\r\nFigure 5: IDA View – Stack adjusting\r\nUsing Appcall is the last step, and there are several ways to utilize it to call functions. We can call the function\r\ndirectly without specifying a prototype, but this highly relies on a properly typed function in IDA’s IDB. The\r\nsecond way is to create a callable object from the function name and a defined prototype. This way we can call a\r\nfunction with a specific prototype, no matter what type is set in the IDB, as shown below:\r\nPlain text\r\nCopy to clipboard\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 6 of 32\n\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN\r\nInte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nretValue = SHA1Hash(buffLen, const, inBuff)\r\neax = malduck.DWORD(retValue._EAX_)\r\nedx = malduck.DWORD(retValue._EDX_)\r\nebx = malduck.DWORD(retValue._EBX_)\r\necx = malduck.DWORD(retValue._ECX_)\r\nesi = malduck.DWORD(retValue._ESI_)\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object inBuff =\r\nAppcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN const = CONSTVAL retValue = SHA1Hash(buffLen, const, inBuff) eax =\r\nmalduck.DWORD(retValue._EAX_) edx = malduck.DWORD(retValue._EDX_) ebx =\r\nmalduck.DWORD(retValue._EBX_) ecx = malduck.DWORD(retValue._ECX_) esi =\r\nmalduck.DWORD(retValue._ESI_)\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nretValue = SHA1Hash(buffLen, const, inBuff)\r\neax = malduck.DWORD(retValue._EAX_)\r\nedx = malduck.DWORD(retValue._EDX_)\r\nebx = malduck.DWORD(retValue._EBX_)\r\necx = malduck.DWORD(retValue._ECX_)\r\nesi = malduck.DWORD(retValue._ESI_)\r\nThe full script to call Duke-SHA1 using Appcall is reproduced below.\r\nPlain text\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 7 of 32\n\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer)\r\nimport idc, malduck\r\nfrom idaapi import Appcall\r\n# ------------------Initialization ------------------\r\nFUNC_NAME = \"SHA1Hash\"\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\nPROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx, 12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e,\r\nBYTE *buffer@\u003cedi\u003e);\".format(STRUCT_NAME, FUNC_NAME) # specify prototype of SHA1Hash function\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------Struct Creation ------------------\r\nsid = idc.get_struc_id(STRUCT_NAME)\r\nif (sid != -1):\r\nidc.del_struc(sid)\r\nsid = idc.add_struc(-1, STRUCT_NAME, 0)\r\nidc.add_struc_member(sid, \"_EAX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EDX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ECX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ESI_\", -1, idc.FF_DWORD, -1, 4)\r\n# ------------------ Setting + Starting Debugger ------------------\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 8 of 32\n\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustment\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN\r\nInte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nretValue = SHA1Hash(buffLen, const, inBuff)\r\neax = malduck.DWORD(retValue._EAX_)\r\nedx = malduck.DWORD(retValue._EDX_)\r\nebx = malduck.DWORD(retValue._EBX_)\r\necx = malduck.DWORD(retValue._ECX_)\r\nesi = malduck.DWORD(retValue._ESI_)\r\n# ------------------ RESULTS ------------------\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (eax, edx,\r\nebx, ecx, esi))\r\n# ------------------ Exiting Debugger ------------------\r\nidc.exit_process()\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample # SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values) # SHA1\r\nHASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer) import\r\nidc, malduck from idaapi import Appcall # ------------------Initialization ------------------ FUNC_NAME =\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 9 of 32\n\n\"SHA1Hash\" STRUCT_NAME = \"struc_SHA1HASH\" PROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx,\r\n12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e, BYTE *buffer@\u003cedi\u003e);\".format(STRUCT_NAME,\r\nFUNC_NAME) # specify prototype of SHA1Hash function SHA1BUFF_LEN = 0x40 CONSTVAL = 0xffffffff #\r\n------------------Struct Creation ------------------ sid = idc.get_struc_id(STRUCT_NAME) if (sid != -1):\r\nidc.del_struc(sid) sid = idc.add_struc(-1, STRUCT_NAME, 0) idc.add_struc_member(sid, \"_EAX_\", -1,\r\nidc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_EDX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_ECX_\", -1,\r\nidc.FF_DWORD, -1, 4) idc.add_struc_member(sid, \"_ESI_\", -1, idc.FF_DWORD, -1, 4) # ------------------ Setting\r\n+ Starting Debugger ------------------ idc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point idc.start_process(\"\",\"\",\"\") #\r\nstart process with default options idc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get\r\nsuspended on entrypoint eip = idc.get_reg_value(\"eip\") # get EIP idc.run_to(eip + 0x1d) # let the stack adjust\r\nitself (execute few instructions) idc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended\r\nafter stack adjustment # ------------------ Arguments + Execution ------------------ SHA1Hash =\r\nAppcall.proto(FUNC_NAME, PROTO) # creating callable object inBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte') buffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL retValue = SHA1Hash(buffLen, const, inBuff) eax = malduck.DWORD(retValue._EAX_)\r\nedx = malduck.DWORD(retValue._EDX_) ebx = malduck.DWORD(retValue._EBX_) ecx =\r\nmalduck.DWORD(retValue._ECX_) esi = malduck.DWORD(retValue._ESI_) # ------------------ RESULTS --------\r\n---------- print(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (eax,\r\nedx, ebx, ecx, esi)) # ------------------ Exiting Debugger ------------------ idc.exit_process()\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by M\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer\r\nimport idc, malduck\r\nfrom idaapi import Appcall\r\n# ------------------Initialization ------------------\r\nFUNC_NAME = \"SHA1Hash\"\r\nSTRUCT_NAME = \"struc_SHA1HASH\"\r\nPROTO = \"{:s} __usercall {:s}@\u003c0:eax, 4:edx, 8:ebx, 12:ecx, 16:esi\u003e(int buffLen@\u003cecx\u003e, const int@\u003ceax\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------Struct Creation ------------------\r\nsid = idc.get_struc_id(STRUCT_NAME)\r\nif (sid != -1):\r\n idc.del_struc(sid)\r\nsid = idc.add_struc(-1, STRUCT_NAME, 0)\r\nidc.add_struc_member(sid, \"_EAX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EDX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_EBX_\", -1, idc.FF_DWORD, -1, 4)\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 10 of 32\n\nidc.add_struc_member(sid, \"_ECX_\", -1, idc.FF_DWORD, -1, 4)\r\nidc.add_struc_member(sid, \"_ESI_\", -1, idc.FF_DWORD, -1, 4)\r\n# ------------------ Setting + Starting Debugger ------------------\r\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustme\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nretValue = SHA1Hash(buffLen, const, inBuff)\r\neax = malduck.DWORD(retValue._EAX_)\r\nedx = malduck.DWORD(retValue._EDX_)\r\nebx = malduck.DWORD(retValue._EBX_)\r\necx = malduck.DWORD(retValue._ECX_)\r\nesi = malduck.DWORD(retValue._ESI_)\r\n# ------------------ RESULTS ------------------\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (eax, edx, ebx, ecx, esi\r\n# ------------------ Exiting Debugger ------------------\r\nidc.exit_process()\r\nAnd some sample output:\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 11 of 32\n\nFigure 6: Script execution – “IDA Appcall” producing the same SHA1 Hash values as the\r\nMiniDuke sample\r\nThe above is fine if we just want to use the invoked function as a black box, but sometimes we may want access to\r\nregistry values in a specific state of execution, and specifying the prototype as above is something of a chore.\r\nHappily, both these downsides can be mitigated, as we will see below.\r\nAs IDA Appcall relies on the debugger and can be invoked right from IDAPython, we can invoke Appcall from\r\nthe debugger and gain more granular control over its execution. For example, we can make Appcall hand control\r\nback to the debugger during execution by setting a special option for Appcall – APPCALL_MANUAL .\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\nSHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will cause the debugger to\r\nbreak on function entry and gives the control to debugger\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN\r\nInte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 12 of 32\n\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1Hash)\r\n# ------------------ Arguments + Execution ------------------ SHA1Hash = Appcall.proto(FUNC_NAME, PROTO) #\r\ncreating callable object SHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will\r\ncause the debugger to break on function entry and gives the control to debugger inBuff =\r\nAppcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN const = CONSTVAL SHA1Hash(buffLen, const, inBuff) # invoking Appcall and\r\nbreaking on function entry (SHA1Hash)\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\nSHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will cause the debugger to break\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1H\r\nThis way we can make use of Appcall to prepare arguments, allocate a buffer and later restore the previous\r\nexecution context. We can also avoid specifying the structure type for the return value (type it as void ) as this\r\nwill be handled by the debugger. There are more ways to get the return values of the function, so as we are now\r\ncontrolling the debugger, we can use (for example) a conditional breakpoint to print desired values in a specific\r\nstate of execution (such as on return).\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# ------------------Set conditional BP on Return ------------------\r\ndef SetCondBPonRet():\r\ncond = \"\"\"import idc\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" %\r\n(idc.get_reg_value(\"eax\"), idc.get_reg_value(\"edx\"), idc.get_reg_value(\"ebx\"), idc.get_reg_value(\"ecx\"),\r\nidc.get_reg_value(\"esi\")))\r\nreturn True\r\n\"\"\"\r\nfunc = idaapi.get_func(idc.get_name_ea_simple(FUNC_NAME))\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 13 of 32\n\nbpt = idaapi.bpt_t()\r\nbpt.ea = idc.prev_head(func.end_ea) # last instruction in function -\u003e should be return\r\nbpt.enabled = True\r\nbpt.type = idc.BPT_SOFT\r\nbpt.elang = 'Python'\r\nbpt.condition = cond # with script code in condition we can get or log any values we want\r\nidc.add_bpt(bpt)\r\nreturn bpt # return breakpoint object -\u003e will be deleted later on\r\n# ------------------Set conditional BP on Return ------------------ def SetCondBPonRet(): cond = \"\"\"import idc\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" %\r\n(idc.get_reg_value(\"eax\"), idc.get_reg_value(\"edx\"), idc.get_reg_value(\"ebx\"), idc.get_reg_value(\"ecx\"),\r\nidc.get_reg_value(\"esi\"))) return True \"\"\" func = idaapi.get_func(idc.get_name_ea_simple(FUNC_NAME)) bpt =\r\nidaapi.bpt_t() bpt.ea = idc.prev_head(func.end_ea) # last instruction in function -\u003e should be return bpt.enabled =\r\nTrue bpt.type = idc.BPT_SOFT bpt.elang = 'Python' bpt.condition = cond # with script code in condition we can\r\nget or log any values we want idc.add_bpt(bpt) return bpt # return breakpoint object -\u003e will be deleted later on\r\n# ------------------Set conditional BP on Return ------------------\r\ndef SetCondBPonRet():\r\n cond = \"\"\"import idc\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (idc.get_reg_value(\"eax\r\nreturn True\r\n\"\"\"\r\n func = idaapi.get_func(idc.get_name_ea_simple(FUNC_NAME))\r\n bpt = idaapi.bpt_t()\r\n bpt.ea = idc.prev_head(func.end_ea) # last instruction in function -\u003e should be return\r\n bpt.enabled = True\r\n bpt.type = idc.BPT_SOFT\r\n bpt.elang = 'Python'\r\n bpt.condition = cond # with script code in condition we can get or log any va\r\n idc.add_bpt(bpt)\r\n return bpt # return breakpoint object -\u003e will be deleted later on\r\nWe can restore the previous state (before Appcall invocation) at any desired moment of execution by calling\r\ncleanup_appcall() . So in our case, right after hitting the conditional breakpoint.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 14 of 32\n\nEnlighterJS 3 Syntax Highlighter\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1Hash)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidaapi.continue_process() # debugger has control now so continue to hit the new conditional breakpoint\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidc.del_bpt(bpt.ea) # deleting the previously created conditional breakpoint\r\nAppcall.cleanup_appcall() # clean Appcall after hitting the conditional breakpoint -\u003e return\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1Hash)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) idaapi.continue_process() # debugger has control now so continue\r\nto hit the new conditional breakpoint idc.wait_for_next_event(idc.WFNE_SUSP, 3) idc.del_bpt(bpt.ea) # deleting\r\nthe previously created conditional breakpoint Appcall.cleanup_appcall() # clean Appcall after hitting the\r\nconditional breakpoint -\u003e return\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1H\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidaapi.continue_process() # debugger has control now so continue to hit the new co\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidc.del_bpt(bpt.ea) # deleting the previously created conditional breakpoint\r\nAppcall.cleanup_appcall() # clean Appcall after hitting the conditional breakpoint\r\nThe full script is reproduced below.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer)\r\nimport idc, idaapi\r\nfrom idaapi import Appcall\r\n# ------------------ Initialization ------------------\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 15 of 32\n\nFUNC_NAME = \"SHA1Hash\"\r\nPROTO = \"void __usercall {:s}(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e, BYTE\r\n*buffer@\u003cedi\u003e);\".format(FUNC_NAME) # specify prototype of SHA1Hash function\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------Set conditional BP on Return ------------------\r\ndef SetCondBPonRet():\r\ncond = \"\"\"import idc\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" %\r\n(idc.get_reg_value(\"eax\"), idc.get_reg_value(\"edx\"), idc.get_reg_value(\"ebx\"), idc.get_reg_value(\"ecx\"),\r\nidc.get_reg_value(\"esi\")))\r\nreturn True\r\n\"\"\"\r\nfunc = idaapi.get_func(idc.get_name_ea_simple(FUNC_NAME))\r\nbpt = idaapi.bpt_t()\r\nbpt.ea = idc.prev_head(func.end_ea) # last instruction in function -\u003e should be return\r\nbpt.enabled = True\r\nbpt.type = idc.BPT_SOFT\r\nbpt.elang = 'Python'\r\nbpt.condition = cond # with script code in condition we can get or log any values we want\r\nidc.add_bpt(bpt)\r\nreturn bpt # return breakpoint object -\u003e will be deleted later on\r\n# ------------------ Setting + Starting Debugger ------------------\r\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nbpt = SetCondBPonRet() # setting the conditional breakpoint on function return\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 16 of 32\n\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustment\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\nSHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will cause the debugger to\r\nbreak on function entry and gives the control to debugger\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN\r\nInte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1Hash)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidaapi.continue_process() # debugger has control now so continue to hit the new conditional breakpoint\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidc.del_bpt(bpt.ea) # deleting the previously created conditional breakpoint\r\nAppcall.cleanup_appcall() # clean Appcall after hitting the conditional breakpoint -\u003e return\r\n# ------------------ Exiting Debugger ------------------\r\nidc.exit_process()\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample # SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values) # SHA1\r\nHASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer) import\r\nidc, idaapi from idaapi import Appcall # ------------------ Initialization ------------------ FUNC_NAME =\r\n\"SHA1Hash\" PROTO = \"void __usercall {:s}(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e, BYTE\r\n*buffer@\u003cedi\u003e);\".format(FUNC_NAME) # specify prototype of SHA1Hash function SHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff # ------------------Set conditional BP on Return ------------------ def SetCondBPonRet():\r\ncond = \"\"\"import idc print(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x\r\nESI:0x%x\" % (idc.get_reg_value(\"eax\"), idc.get_reg_value(\"edx\"), idc.get_reg_value(\"ebx\"),\r\nidc.get_reg_value(\"ecx\"), idc.get_reg_value(\"esi\"))) return True \"\"\" func =\r\nidaapi.get_func(idc.get_name_ea_simple(FUNC_NAME)) bpt = idaapi.bpt_t() bpt.ea =\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 17 of 32\n\nidc.prev_head(func.end_ea) # last instruction in function -\u003e should be return bpt.enabled = True bpt.type =\r\nidc.BPT_SOFT bpt.elang = 'Python' bpt.condition = cond # with script code in condition we can get or log any\r\nvalues we want idc.add_bpt(bpt) return bpt # return breakpoint object -\u003e will be deleted later on # ------------------\r\nSetting + Starting Debugger ------------------ idc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point bpt = SetCondBPonRet() #\r\nsetting the conditional breakpoint on function return idc.start_process(\"\",\"\",\"\") # start process with default options\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint eip =\r\nidc.get_reg_value(\"eip\") # get EIP idc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions)\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustment # ---------\r\n--------- Arguments + Execution ------------------ SHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating\r\ncallable object SHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will cause\r\nthe debugger to break on function entry and gives the control to debugger inBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte') buffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL SHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry\r\n(SHA1Hash) idc.wait_for_next_event(idc.WFNE_SUSP, 3) idaapi.continue_process() # debugger has control now\r\nso continue to hit the new conditional breakpoint idc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidc.del_bpt(bpt.ea) # deleting the previously created conditional breakpoint Appcall.cleanup_appcall() # clean\r\nAppcall after hitting the conditional breakpoint -\u003e return # ------------------ Exiting Debugger ------------------\r\nidc.exit_process()\r\n# IDAPython script to demonstrate Appcall feature on modified SHA1 Hashing algorithm implemented by M\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer\r\nimport idc, idaapi\r\nfrom idaapi import Appcall\r\n# ------------------ Initialization ------------------\r\nFUNC_NAME = \"SHA1Hash\"\r\nPROTO = \"void __usercall {:s}(int buffLen@\u003cecx\u003e, const int@\u003ceax\u003e, BYTE *buffer@\u003cedi\u003e);\".format(FUNC_N\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------Set conditional BP on Return ------------------\r\ndef SetCondBPonRet():\r\n cond = \"\"\"import idc\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (idc.get_reg_value(\"eax\r\nreturn True\r\n\"\"\"\r\n func = idaapi.get_func(idc.get_name_ea_simple(FUNC_NAME))\r\n bpt = idaapi.bpt_t()\r\n bpt.ea = idc.prev_head(func.end_ea) # last instruction in function -\u003e should be return\r\n bpt.enabled = True\r\n bpt.type = idc.BPT_SOFT\r\n bpt.elang = 'Python'\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 18 of 32\n\nbpt.condition = cond # with script code in condition we can get or log any va\r\n idc.add_bpt(bpt)\r\n return bpt # return breakpoint object -\u003e will be deleted later on\r\n# ------------------ Setting + Starting Debugger ------------------\r\nidc.load_debugger(\"win32\",0) # select Local Windows Debugger\r\nidc.set_debugger_options(idc.DOPT_ENTRY_BPT) # break on program entry point\r\nbpt = SetCondBPonRet() # setting the conditional breakpoint on function return\r\nidc.start_process(\"\",\"\",\"\") # start process with default options\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended on entrypoint\r\neip = idc.get_reg_value(\"eip\") # get EIP\r\nidc.run_to(eip + 0x1d) # let the stack adjust itself (execute few instructions\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3) # waits until process get suspended after stack adjustme\r\n# ------------------ Arguments + Execution ------------------\r\nSHA1Hash = Appcall.proto(FUNC_NAME, PROTO) # creating callable object\r\nSHA1Hash.options = Appcall.APPCALL_MANUAL # APPCALL_MANUAL option will cause the debugger to break\r\ninBuff = Appcall.byref(b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte')\r\nbuffLen = SHA1BUFF_LEN\r\nconst = CONSTVAL\r\nSHA1Hash(buffLen, const, inBuff) # invoking Appcall and breaking on function entry (SHA1H\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidaapi.continue_process() # debugger has control now so continue to hit the new co\r\nidc.wait_for_next_event(idc.WFNE_SUSP, 3)\r\nidc.del_bpt(bpt.ea) # deleting the previously created conditional breakpoint\r\nAppcall.cleanup_appcall() # clean Appcall after hitting the conditional breakpoint\r\n# ------------------ Exiting Debugger ------------------\r\nidc.exit_process()\r\nDumpulator\r\nDumpulator is a python library that assists with code emulation in minidump files. The core emulation engine of\r\ndumpulator is based on Unicorn Engine, but a relatively unique feature among other similar tools is that the entire\r\nprocess memory is available. This brings a performance improvement (emulating large parts of analyzed binary\r\nwithout leaving Unicorn), as well as making life more convenient if we can time the memory dump to when the\r\nprogram context (stack, etc) required to call the function is already in place. Additionally, only syscalls have to be\r\nemulated to provide a realistic Windows environment (since everything actually is a legitimate process\r\nenvironment).\r\nA minidump of the desired process could be captured with many tools (x64dbg – MiniDumpPlugin, Process\r\nExplorer, Process Hacker, Task Manager) or with the Windows API ( MiniDumpWriteDump). We can use the\r\nx64dbg – MiniDumpPlugin to create a minidump in a state where almost all in the process is already set for SHA1\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 19 of 32\n\nHash creation, right before the SHA1Hash function call. Note that timing the dump this way is not necessary, as\r\nthe environment can be set up manually in dumpulator after taking the dump; it is just convenient.\r\nFigure 7: Creation of minidump using “x64dbg – MiniDumpPlugin”\r\nDumpulator not only has access to the entire dumped process memory but can also allocate additional memory,\r\nread memory, write to memory, read registry values, and write registry values. In other words, anything that an\r\nemulator can do. There is also a possibility to implement system calls so code using them can be emulated.\r\nTo invoke Duke-SHA1 via Dumpulator, we need to specify the address of the function which will be called in\r\nminidump and its arguments. In this case, the address of SHA1Hash is 0x407108 .\r\nFigure 8: Opening produced minidump in IDA\r\nAs we do not want to use already set values in the current state of minidump, we define our own argument values\r\nfor the function. We can even allocate a new buffer which will be used as a buffer to be hashed. The decidedly\r\nelegant code to do this is shown below.\r\nPlain text\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 20 of 32\n\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# Python script to demonstrate dumpulator on modified SHA1 Hashing algorithm implemented by MiniDuke\r\nmalware sample\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer)\r\nfrom dumpulator import Dumpulator\r\n# ------------------Initialization ------------------\r\nFUNC_ADDR = 0x407108 # address of SHA1Hash function in MiniDuke\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------ Setting + Starting Dumpulator ------------------\r\ndp = Dumpulator(\"miniduke.dmp\", quiet=True)\r\ninBuff = b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'\r\nbufferAddr = dp.allocate(64)\r\ndp.write(bufferAddr, inBuff)\r\n#dp.regs.ecx = SHA1BUFF_LEN # possible to set the registers here\r\n#dp.regs.eax = CONSTVAL\r\n#dp.regs.edi = bufferAddr\r\n#dp.call(FUNC_ADDR)\r\ndp.call(FUNC_ADDR, regs= {\"eax\": CONSTVAL, \"ecx\": SHA1BUFF_LEN, \"edi\": bufferAddr})\r\n# ------------------ RESULTS ------------------\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (dp.regs.eax,\r\ndp.regs.edx, dp.regs.ebx, dp.regs.ecx, dp.regs.esi))\r\n# Python script to demonstrate dumpulator on modified SHA1 Hashing algorithm implemented by MiniDuke\r\nmalware sample # SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values) # SHA1 HASH\r\nArguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer) from\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 21 of 32\n\ndumpulator import Dumpulator # ------------------Initialization ------------------ FUNC_ADDR = 0x407108 #\r\naddress of SHA1Hash function in MiniDuke SHA1BUFF_LEN = 0x40 CONSTVAL = 0xffffffff # ------------------\r\nSetting + Starting Dumpulator ------------------ dp = Dumpulator(\"miniduke.dmp\", quiet=True) inBuff =\r\nb'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte' bufferAddr =\r\ndp.allocate(64) dp.write(bufferAddr, inBuff) #dp.regs.ecx = SHA1BUFF_LEN # possible to set the registers here\r\n#dp.regs.eax = CONSTVAL #dp.regs.edi = bufferAddr #dp.call(FUNC_ADDR) dp.call(FUNC_ADDR, regs=\r\n{\"eax\": CONSTVAL, \"ecx\": SHA1BUFF_LEN, \"edi\": bufferAddr}) # ------------------ RESULTS ------------------\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (dp.regs.eax,\r\ndp.regs.edx, dp.regs.ebx, dp.regs.ecx, dp.regs.esi))\r\n# Python script to demonstrate dumpulator on modified SHA1 Hashing algorithm implemented by MiniDuke\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer\r\nfrom dumpulator import Dumpulator\r\n# ------------------Initialization ------------------\r\nFUNC_ADDR = 0x407108 # address of SHA1Hash function in MiniDuke\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\n# ------------------ Setting + Starting Dumpulator ------------------\r\ndp = Dumpulator(\"miniduke.dmp\", quiet=True)\r\ninBuff = b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'\r\nbufferAddr = dp.allocate(64)\r\ndp.write(bufferAddr, inBuff)\r\n#dp.regs.ecx = SHA1BUFF_LEN # possible to set the registers here\r\n#dp.regs.eax = CONSTVAL\r\n#dp.regs.edi = bufferAddr\r\n#dp.call(FUNC_ADDR)\r\ndp.call(FUNC_ADDR, regs= {\"eax\": CONSTVAL, \"ecx\": SHA1BUFF_LEN, \"edi\": bufferAddr})\r\n# ------------------ RESULTS ------------------\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (dp.regs.eax, dp.regs.ed\r\nExecution of this script will produce correct Duke-SHA1 values.\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 22 of 32\n\nFigure 9: Script execution – “Dumpulator” producing the same SHA1 Hash values as the MiniDuke\r\nsample\r\nEmulation – Unicorn Engine\r\nFor the emulation approach, we can use any kind of CPU emulator (ex. Qiling, Speakeasy, etc.) which is able to\r\nemulate x86 assembly and has bindings for Python language. As we do not need any higher abstraction level\r\n(Syscalls, API functions) we can use the one which most of the others are based on – Unicorn Engine.\r\nUnicorn is a lightweight, multi-platform, multi-architecture CPU emulator framework, based on QEMU, which is\r\nimplemented in pure C language with bindings for many other languages. We will be using Python bindings. Our\r\ngoal is to create an independent function SHA1Hash which can be called like any other ordinary function in\r\nPython, producing the same SHA1 hashes as the original one in MiniDuke. The idea behind the implementation\r\nwe use is pretty straightforward — we simply extract the opcode bytes of the function and use them via the CPU\r\nemulation.\r\nExtracting all bytes of original function opcodes can be done simply via IDAPython or using IDA→Edit→Export\r\nData.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# IDAPython - extracting opcode bytes of SHA1Hash function\r\nimport idaapi, idc\r\nSHA1HashAddr = idc.get_name_ea_simple(\"SHA1Hash\")\r\nSHA1Hash = idaapi.get_func(SHA1HashAddr)\r\nSHA1HASH_OPCODE = idaapi.get_bytes(SHA1Hash.start_ea, SHA1Hash.size())\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 23 of 32\n\nSHA1HASH_OPCODE.hex()\r\n# Output: '0f6ec589cb8dad74a3[...]'\r\n# IDAPython - extracting opcode bytes of SHA1Hash function import idaapi, idc SHA1HashAddr =\r\nidc.get_name_ea_simple(\"SHA1Hash\") SHA1Hash = idaapi.get_func(SHA1HashAddr) SHA1HASH_OPCODE\r\n= idaapi.get_bytes(SHA1Hash.start_ea, SHA1Hash.size()) SHA1HASH_OPCODE.hex() # Output:\r\n'0f6ec589cb8dad74a3[...]'\r\n# IDAPython - extracting opcode bytes of SHA1Hash function\r\nimport idaapi, idc\r\nSHA1HashAddr = idc.get_name_ea_simple(\"SHA1Hash\")\r\nSHA1Hash = idaapi.get_func(SHA1HashAddr)\r\nSHA1HASH_OPCODE = idaapi.get_bytes(SHA1Hash.start_ea, SHA1Hash.size())\r\nSHA1HASH_OPCODE.hex()\r\n# Output: '0f6ec589cb8dad74a3[...]'\r\nFigure 10: Using IDA “Export data” dialog to export opcode bytes of SHA1Hash function\r\nAs in the previous approaches, we need to set up the context for execution. In this case this means preparing\r\narguments for the function, and setting addresses for our extracted opcodes and input buffer.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 24 of 32\n\n# ------------------Initialization ------------------\r\n# remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e UC_ERR_FETCH_UNMAPPED -\u003e no ret\r\naddress on stack\r\nSHA1HASH_OPCODE = b\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\"\r\nOPCODE_ADDRESS = 0x400000\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\nBUFFERADDR = OPCODE_ADDRESS + 0x200000\r\n# ------------------Initialization ------------------ # remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e\r\nUC_ERR_FETCH_UNMAPPED -\u003e no ret address on stack SHA1HASH_OPCODE =\r\nb\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\" OPCODE_ADDRESS = 0x400000 SHA1BUFF_LEN\r\n= 0x40 CONSTVAL = 0xffffffff BUFFERADDR = OPCODE_ADDRESS + 0x200000\r\n# ------------------Initialization ------------------\r\n# remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e UC_ERR_FETCH_UNMAPPED -\u003e no ret add\r\nSHA1HASH_OPCODE = b\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\"\r\nOPCODE_ADDRESS = 0x400000\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\nBUFFERADDR = OPCODE_ADDRESS + 0x200000\r\nNote that the last retn instruction should be deleted from the extracted opcode listing in order to not transfer\r\nback execution to the return address on the stack, and the stack frame should be manually set up by specifying\r\nvalues for ebp and esp . All these things are shown in the final Python script below.\r\nPlain text\r\nCopy to clipboard\r\nOpen code in new window\r\nEnlighterJS 3 Syntax Highlighter\r\n# Python script to demonstrate Unicorn emulator on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer)\r\nfrom unicorn import *\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 25 of 32\n\nfrom unicorn.x86_const import *\r\ndef GetMinidukeSHA1(inBuff:bytes) -\u003e Uc:\r\n# ------------------Initialization ------------------\r\n# remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e UC_ERR_FETCH_UNMAPPED -\u003e no ret\r\naddress on stack\r\nSHA1HASH_OPCODE = b\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\"\r\nOPCODE_ADDRESS = 0x400000\r\nSHA1BUFF_LEN = 0x40\r\nCONSTVAL = 0xffffffff\r\nBUFFERADDR = OPCODE_ADDRESS + 0x200000\r\n# ------------------ Setting + Starting Emulator ------------------\r\ntry:\r\nmu = Uc(UC_ARCH_X86, UC_MODE_32) # set EMU architecture and mode\r\nmu.mem_map(OPCODE_ADDRESS, 0x200000, UC_PROT_ALL) # map memory for SHA1Hash function\r\nopcodes, stack etc.\r\nmu.mem_write(OPCODE_ADDRESS, SHA1HASH_OPCODE) # write opcodes to memory\r\nmu.mem_map(BUFFERADDR, 0x1000, UC_PROT_ALL) # map memory for input to be hashed\r\nmu.mem_write(BUFFERADDR, inBuff) # write input bytes to memory\r\nmu.reg_write(UC_X86_REG_ESP, OPCODE_ADDRESS + 0x100000) # initialize stack (ESP)\r\nmu.reg_write(UC_X86_REG_EBP, OPCODE_ADDRESS + 0x100000) # initialize frame pointer (EBP)\r\nmu.reg_write(UC_X86_REG_EAX, CONSTVAL) # set EAX register (argument) -\u003e CONSTVAL\r\nmu.reg_write(UC_X86_REG_ECX, SHA1BUFF_LEN) # set ECX register (argument) -\u003e SHA1BUFF_LEN\r\nmu.reg_write(UC_X86_REG_EDI, BUFFERADDR) # set EDI register (argument) -\u003e BUFFERADDR to be\r\nhashed\r\nmu.emu_start(OPCODE_ADDRESS, OPCODE_ADDRESS + len(SHA1HASH_OPCODE)) # start emulation of\r\nopcodes\r\nreturn mu\r\nexcept UcError as e:\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 26 of 32\n\nprint(\"ERROR: %s\" % e)\r\n# ------------------ RESULTS ------------------\r\ninBuff = b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'\r\nmu = GetMinidukeSHA1(inBuff)\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" %\r\n(mu.reg_read(UC_X86_REG_EAX), mu.reg_read(UC_X86_REG_EDX), mu.reg_read(UC_X86_REG_EBX),\r\nmu.reg_read(UC_X86_REG_ECX), mu.reg_read(UC_X86_REG_ESI)))\r\n# Python script to demonstrate Unicorn emulator on modified SHA1 Hashing algorithm implemented by\r\nMiniDuke malware sample # SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values) # SHA1\r\nHASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer) from\r\nunicorn import * from unicorn.x86_const import * def GetMinidukeSHA1(inBuff:bytes) -\u003e Uc: # ------------------\r\nInitialization ------------------ # remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e\r\nUC_ERR_FETCH_UNMAPPED -\u003e no ret address on stack SHA1HASH_OPCODE =\r\nb\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\" OPCODE_ADDRESS = 0x400000 SHA1BUFF_LEN\r\n= 0x40 CONSTVAL = 0xffffffff BUFFERADDR = OPCODE_ADDRESS + 0x200000 # ------------------ Setting +\r\nStarting Emulator ------------------ try: mu = Uc(UC_ARCH_X86, UC_MODE_32) # set EMU architecture and\r\nmode mu.mem_map(OPCODE_ADDRESS, 0x200000, UC_PROT_ALL) # map memory for SHA1Hash\r\nfunction opcodes, stack etc. mu.mem_write(OPCODE_ADDRESS, SHA1HASH_OPCODE) # write opcodes to\r\nmemory mu.mem_map(BUFFERADDR, 0x1000, UC_PROT_ALL) # map memory for input to be hashed\r\nmu.mem_write(BUFFERADDR, inBuff) # write input bytes to memory mu.reg_write(UC_X86_REG_ESP,\r\nOPCODE_ADDRESS + 0x100000) # initialize stack (ESP) mu.reg_write(UC_X86_REG_EBP,\r\nOPCODE_ADDRESS + 0x100000) # initialize frame pointer (EBP) mu.reg_write(UC_X86_REG_EAX,\r\nCONSTVAL) # set EAX register (argument) -\u003e CONSTVAL mu.reg_write(UC_X86_REG_ECX,\r\nSHA1BUFF_LEN) # set ECX register (argument) -\u003e SHA1BUFF_LEN mu.reg_write(UC_X86_REG_EDI,\r\nBUFFERADDR) # set EDI register (argument) -\u003e BUFFERADDR to be hashed\r\nmu.emu_start(OPCODE_ADDRESS, OPCODE_ADDRESS + len(SHA1HASH_OPCODE)) # start emulation of\r\nopcodes return mu except UcError as e: print(\"ERROR: %s\" % e) # ------------------ RESULTS ------------------\r\ninBuff = b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte' mu =\r\nGetMinidukeSHA1(inBuff) print(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x\r\nESI:0x%x\" % (mu.reg_read(UC_X86_REG_EAX), mu.reg_read(UC_X86_REG_EDX),\r\nmu.reg_read(UC_X86_REG_EBX), mu.reg_read(UC_X86_REG_ECX), mu.reg_read(UC_X86_REG_ESI)))\r\n# Python script to demonstrate Unicorn emulator on modified SHA1 Hashing algorithm implemented by Min\r\n# SHA1 HASH is stored in EAX, EDX, EBX, ECX, ESI (return values)\r\n# SHA1 HASH Arguments -\u003e ECX = 0x40 (buffLen), EAX = 0xFFFFFFFF (const), EDI = BYTE *buffer (buffer\r\nfrom unicorn import *\r\nfrom unicorn.x86_const import *\r\ndef GetMinidukeSHA1(inBuff:bytes) -\u003e Uc:\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 27 of 32\n\n# ------------------Initialization ------------------\r\n # remove \"retn\" instruction from SHA1Hash function opcodes or -\u003e UC_ERR_FETCH_UNMAPPED -\u003e no ret\r\n SHA1HASH_OPCODE = b\"\\x0f\\x6e\\xc5\\x89\\xcb\\x8d\\xad\\x74\\xa3...........................\"\r\n OPCODE_ADDRESS = 0x400000\r\n SHA1BUFF_LEN = 0x40\r\n CONSTVAL = 0xffffffff\r\n BUFFERADDR = OPCODE_ADDRESS + 0x200000\r\n # ------------------ Setting + Starting Emulator ------------------\r\n try:\r\n mu = Uc(UC_ARCH_X86, UC_MODE_32) # set EMU architectu\r\n mu.mem_map(OPCODE_ADDRESS, 0x200000, UC_PROT_ALL) # map memory for SHA1\r\n mu.mem_write(OPCODE_ADDRESS, SHA1HASH_OPCODE) # write opcodes to me\r\n mu.mem_map(BUFFERADDR, 0x1000, UC_PROT_ALL) # map memory for inpu\r\n mu.mem_write(BUFFERADDR, inBuff) # write input bytes t\r\n \r\n mu.reg_write(UC_X86_REG_ESP, OPCODE_ADDRESS + 0x100000) # initialize stack (E\r\n mu.reg_write(UC_X86_REG_EBP, OPCODE_ADDRESS + 0x100000) # initialize frame po\r\n mu.reg_write(UC_X86_REG_EAX, CONSTVAL) # set EAX register (a\r\n mu.reg_write(UC_X86_REG_ECX, SHA1BUFF_LEN) # set ECX register (a\r\n mu.reg_write(UC_X86_REG_EDI, BUFFERADDR) # set EDI register (a\r\n mu.emu_start(OPCODE_ADDRESS, OPCODE_ADDRESS + len(SHA1HASH_OPCODE)) # start emulation of\r\n return mu\r\n except UcError as e:\r\n print(\"ERROR: %s\" % e)\r\n# ------------------ RESULTS ------------------\r\ninBuff = b'DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN InteWAN Inte'\r\nmu = GetMinidukeSHA1(inBuff)\r\nprint(\"SHA1 HASH RET VALUES: EAX:0x%x EDX:0x%x EBX:0x%x ECX:0x%x ESI:0x%x\" % (mu.reg_read(UC_X86_REG_\r\nThe script output can be seen below.\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 28 of 32\n\nFigure 11: Script execution – “Unicorn Engine” producing the same SHA1 Hash values as the\r\nMiniDuke sample\r\nConclusion\r\nAll the above-described methods for direct invocation of assembly have their advantages and disadvantages. We\r\nwere particularly impressed by the easy-to-use Dumpulator which is free, fast to implement, and highly effective.\r\nIt is well suited for writing universal string decryptors, config extractors, and other contexts where many different\r\nlogic fragments have to be called in sequence while preserving a hard-to-set-up context.\r\nThe IDA Appcall feature is one of the best solutions in situations where we would like to enrich the IDA database\r\ndirectly with results produced by the invocation of a specific function. Syscalls could be a part of such a function\r\nas Appcall is usually used in real execution environments – using a debugger. One of the greatest things about\r\nAppcall is the fast and easy context restoration. As Appcall relies on a debugger and could be used together with\r\nIDAPython scripting, it could even in theory be used as a basis for a fuzzer, feeding random input to functions in\r\norder to discover unexpected behavior (i.e. bugs), though the performance overhead might make this approach not\r\nvery practical.\r\nUsing pure emulation via Unicorn Engine is a universal solution for the independent implementation of specific\r\nfunctionality. With this approach, it is possible to take a part of the code as-is and use it with no connection to the\r\noriginal sample. This method does not rely on a runnable sample and there is no problem to re-implement\r\nfunctionality for just a part of the code. This approach may be harder to implement for functions that are not a\r\ncontiguous, easily-dumpable block of code. For part of code where APIs or syscalls occur, or the execution\r\ncontext is much harder to set up, the previously mentioned methods are usually a preferable choice.\r\nPros and Cons Summary\r\nIDA Appcall\r\nPROS:\r\nNatively supported by IDA\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 29 of 32\n\nPossible to use with IDAPython right in the context of IDA.\r\nNatively understands higher abstraction layer so Windows APIs and syscalls can be a part of the invoked\r\nfunction.\r\nCan be used on corrupted code/file with Bochs emulator IDB emulate feature (non-runnable sample).\r\nThe combination of the Appcall feature and scriptable debugger is very powerful, giving us full control\r\nat any moment of Appcall execution.\r\nCONS:\r\nPrototypes of more sophisticated functions using custom calling conventions ( __usercall ) are harder to\r\nimplement.\r\nInvoked assembly needs to be a function, not just part of code.\r\nDumpulator\r\nPROS:\r\nVery easy-to-use. Code making use of it is Pythonic and fast to implement.\r\nIf a minidump is obtained in a state where all context is already set, with no need to map memory or set\r\nthings like a stack, frame pointer, or even arguments, Dumpulator can leverage this to de-clutter the\r\ninvocation code even further.\r\nUnderstands the higher abstraction layer and allows use of syscalls (though some may need to be\r\nimplemented manually).\r\nEnables lower access level to modify the context in a similar way to usual emulation.\r\nCan be used to emulate part of code (does not have to be a function)\r\nCONS:\r\nRequires a minidump of the desired process to be worked on, which in turn requires a runnable binary\r\nsample.\r\nEmulation – Unicorn Engine\r\nPROS:\r\nThe most independent solution, requires only the interesting assembly code.\r\nLow access level to set and modify context.\r\nCan be used to emulate part of code fully independently. Allows free modification and patching of\r\ninstructions on the fly.\r\nCONS:\r\nHarder to map memory and set the context of the emulation engine correctly.\r\nNo out-of-the-box access to higher abstraction layer and system calls.\r\nReferences\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 30 of 32\n\nIDA – Appcall\r\nhttps://hex-rays.com/blog/introducing-the-appcall-feature-in-ida-pro-5-6/\r\nhttps://hex-rays.com/blog/practical-appcall-examples/\r\nhttps://www.hex-rays.com/wp-content/uploads/2019/12/debugging_appcall.pdf\r\nDumpulator\r\nhttps://github.com/mrexodia/dumpulator\r\nhttps://github.com/mrexodia/MiniDumpPlugin\r\nUnicorn Engine\r\nhttps://github.com/unicorn-engine/unicorn\r\nhttps://github.com/unicorn-engine/unicorn/tree/master/bindings/python\r\nSamples + Scripts (password:infected)\r\n1. Original MiniDuke sample: VirusTotal, miniduke_original.7z\r\n2. Unpacked MiniDuke sample: miniduke_unpacked.7z\r\n3. MiniDuke minidump: miniduke_minidump.7z\r\n4. All scripts mentioned in the article: IDAPython_PythonScripts.7z\r\nBLOGS AND PUBLICATIONS\r\nCheck Point Research Publications\r\nGlobal Cyber Attack Reports\r\nThreat Research\r\nFebruary 17, 2020\r\n“The Turkish Rat” Evolved Adwind in a Massive Ongoing Phishing Campaign\r\nWe value your privacy!\r\nBFSI uses cookies on this site. We use cookies to enable faster and easier experience for you. By continuing to\r\nvisit this website you agree to our use of cookies.\r\nACCEPT\r\nREJECT\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 31 of 32\n\nSource: https://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nhttps://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/\r\nPage 32 of 32\n\nevery time it invokes Figure 2: SHA1Hash this function, effectively function arguments hashing only the first 0x40 bytes of the buffer. \nThe resulting 160-bit SHA1 hash value is returned in 5 dwords in registers (from high dword to low: eax , edx\n, ebx , ecx , esi ). For example, the buffer DESKTOP-ROAC4IJ\\x00MicrWAN WAN MicrWAN MicrWAN InteWAN\nInteWAN Inte has a Duke-SHA1 value of 1851fff77f0957d1d690a32f31df2c32a1a84af7  , returned as\nEAX:0x1851fff7 EDX:0x7f0957d1 EBX:0xd690a32f ECX:0x31df2c32 ESI:0xa1a84af7 . \n  Page 2 of 32   \n\nSHA1HashAddr = SHA1Hash = idaapi.get_func(SHA1HashAddr) idc.get_name_ea_simple(\"SHA1Hash\")  \nSHA1HASH_OPCODE = idaapi.get_bytes(SHA1Hash.start_ea,  SHA1Hash.size())\n  Page 23 of 32",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"Malpedia"
	],
	"references": [
		"https://research.checkpoint.com/2022/native-function-and-assembly-code-invocation/"
	],
	"report_names": [
		"native-function-and-assembly-code-invocation"
	],
	"threat_actors": [],
	"ts_created_at": 1775434321,
	"ts_updated_at": 1775791247,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/71da5ff60f693b4dbf04c5345fe28ef9ac744324.pdf",
		"text": "https://archive.orkl.eu/71da5ff60f693b4dbf04c5345fe28ef9ac744324.txt",
		"img": "https://archive.orkl.eu/71da5ff60f693b4dbf04c5345fe28ef9ac744324.jpg"
	}
}