RunDll Exporters

One of the most interesting classes of functions that are exported by DLLs are functions that use the RunDll interface (this archived article describes it).

Thanks to traditional (today kinda old-school) programming conventions, many coders name their exported functions compatible with the RunDll interface in a way that makes them easy to identify. Basically, they often include the reference to ‘rundll’ in a function name.

Knowing that, we can make an attempt to comb through many ‘good’ DLLs to discover a list of libraries where some of the APIs they export… follow this simple naming convention. Analysis of my small DLL repo gave me the results shown below.

Looking at these results I can immediately see that some of them are very familiar Windows API names (f.ex. Control_RunDLL variants), but there are many others that are mostly unknown. And many of libraries that export these functions come from other vendors than Microsoft, the exported APIs have almost no documentation and a minimalistic footprint online – basically, googling some of them brings very limited results.

I have a gut feeling that at least some of them are good lolbin potentials.

549  _InfEngUnInstallINFFile_RunDLL@16
485  InfEngUnInstallINFFile_RunDLL
426  RunDLLEntryW
353  RunDll32Interface
252  ShowHidPropPageRunDll32
252  BluetoothUpdateSendToRunDll32
195  DelNodeRunDLL32
167  RunDLL32EP
133  ShowHidPropPageRunDll32W
132  UninstADrvRunDll
114  Rundll_Dial
 62  RunDLLEntry
 62  Control_RunDLL
 60  SHHelpShortcuts_RunDLL
 60  PrintersGetCommand_RunDLL
 60  OpenAs_RunDLL
 58  SRS_InitializeEndpoints_Rundll32
 58  SRS_CleanupEndpoints_Rundll32
 58  SHHelpShortcuts_RunDLLW
 58  SHHelpShortcuts_RunDLLA
 58  PrintersGetCommand_RunDLLW
 58  PrintersGetCommand_RunDLLA
 58  OpenAs_RunDLLW
 58  OpenAs_RunDLLA
 58  Control_RunDLLW
 58  Control_RunDLLA
 54  RunDllDoPreInstall
 54  Control_FillCache_RunDLL
 52  Control_FillCache_RunDLLW
 52  Control_FillCache_RunDLLA
 50  BTINS_RunDll
 46  _RunDLLEntry@16
 41  ShellExec_RunDLLW
 41  ShellExec_RunDLLA
 41  ShellExec_RunDLL
 41  CplRunDll32
 41  Control_RunDLLAsUserW
 40  RunDll
 39  RunDllW
 38  SHCreateLocalServerRunDll
 38  Options_RunDLLW
 38  Options_RunDLLA
 38  Options_RunDLL
 38  AppCompat_RunDLLW
 32  Activate_RunDLL
 30  RunDll32ShimW
 30  HomeNetWizardRunDll
 25  TestRunDll
 24  ctCVWUtilityRunDLL32EP
 24  ctCVWIntroRunDLL32EP
 24  RundllUninstallA
 24  RundllInstallA
 23  ctCVWConsoleRunDLL32EP
 22  ctCVWParentalRunDLL32EP
 22  RunDllPromptForReboot
 20  SetupRunDll32Entry
 20  SelectSetupRunDll32Ex
 20  RunDllRegister
 19  UpgradePrinterRunDll32Ex
 18  RunDLL_InstallOEMDeviceEx
 18  RunDLL_InstallOEMDevice
 17  DelNodeRunDLL32W
 17  DelNodeRunDLL32A
 16  RunDLL_ExtractCabinetFile
 16  RunDLL32_UnregisterApplication
 16  RunDLL32_RegisterApplication
 16  RunDLL32_FilterRunOnceExRegistration
 15  RunDllEntryPoint
 13  SxsRunDllInstallAssemblyW
 13  SxsRunDllInstallAssembly
 12  IID_IShellRunDll
 11  usb_uninstall_service_np_rundll
 11  usb_install_service_np_rundll
 11  usb_install_driver_np_rundll
 11  TestPrint_RunDLLW
 11  RunDLL_InstallMultipleOEMDevicesEx
 11  @Jvjclutils@RunDll32Internal$qqruix17System@AnsiStringt2t2i
 11  @Jvjclutils@RunDLL32$qqrx17System@AnsiStringt1t1oi
 10  usb_touch_inf_file_np_rundll
  9  @Registryscan@TRegistryScan@HandleRundll32$qqr17System@AnsiStringo
  9  @PRunDLLCommand@saveGuts$xqr6RWFile
  9  @PRunDLLCommand@restoreGuts$qr6RWFile
  9  @PRunDLLCommand@newSpecies$xqv
  9  @PRunDLLCommand@myAtom
  9  @PRunDLLCommand@isA$xqv
  9  @PRunDLLCommand@copy$xqv
  9  @PRunDLLCommand@classIsA$qv
  9  @PRunDLLCommand@binaryStoreSize$xqv
  9  @PRunDLLCommand@UnExecute$qv
  9  @PRunDLLCommand@SetThirdParam$qrx9RWCString
  9  @PRunDLLCommand@SetSecondParam$qrx9RWCString
  9  @PRunDLLCommand@SetFunctionType$q12ERunDLLTypes
  9  @PRunDLLCommand@SetFunctionName$qrx9RWCString
  9  @PRunDLLCommand@SetFirstParam$qrx9RWCString
  9  @PRunDLLCommand@SetDLLName$qrx9RWCString
  9  @PRunDLLCommand@GetVersion$xqv
  9  @PRunDLLCommand@GetThirdParam$xqv
  9  @PRunDLLCommand@GetSecondParam$xqv
  9  @PRunDLLCommand@GetFunctionType$xqv
  9  @PRunDLLCommand@GetFunctionName$xqv
  9  @PRunDLLCommand@GetFirstParam$xqv
  9  @PRunDLLCommand@GetDLLReturnValue$xqv
  9  @PRunDLLCommand@GetDLLName$xqv
  9  @PRunDLLCommand@Execute$qv
  9  @PRunDLLCommand@$beql$xqrx11MPersistent
  9  @PRunDLLCommand@$bdtr$qv
  9  @PRunDLLCommand@$bctr$qv
  9  @PRunDLLCommand@$bctr$qrx14PRunDLLCommand
  9  @PRunDLLCommand@$basg$qrx14PRunDLLCommand
  8  _Java_com_sun_java_accessibility_AccessBridge_runDLL@8
  8  InstallSecurityPromptRunDllW
  8  DeviceProperties_RunDLLW
  8  DeviceProperties_RunDLLA
  7  DriverStoreRunDllW
  7  DeviceProblenWizard_RunDLLW
  7  DeviceProblenWizard_RunDLLA
  7  ?runDll@SV_HttpdAPI@@AAE_NPBD@Z
  6  extract_RunDLL
  6  WOW64Uninstall_RunDLLW
  6  UsersRunDll
  6  SxspRunDllDeleteDirectoryW
  6  SxspRunDllDeleteDirectory
  6  S3Disp_RunDll
  6  Rundll32RegisterServer
  6  RunDllProcW
  6  RunDllProcA
  6  RunDllEntry
  6  RunAsNewUser_RunDLLW
  6  PrepareDiscForBurnRunDllW
  6  LaunchMSHelp_RunDLLW
  6  AddNetPlaceRunDll
  6  @Jcldotnet@RunDll32ShimW$qqsxuixuipbxi
  5  RunDll_SetDefaultPrinter
  5  RunDll32Main
  5  PublishRunDll
  5  PassportWizardRunDll
  5  CscPolicyProcessing_RunDLLW
  5  CSCOptions_RunDLLW
  5  CSCOptions_RunDLLA
  5  CSCOptions_RunDLL
  4  update_start_rundll_old
  4  update_start_rundll
  4  uninstall_start_rundll_old
  4  uninstall_start_rundll
  4  rundll32_shellexec
  4  Wizard_RunDLL
  4  Rundll_EntryPoint
  4  DiskCopyRunDllW
  4  DiskCopyRunDll
  4  CI3_CreateShortcut_RUNDLL_32
  3  fnRunDll32
  3  _rundll32_shellexec@16
  3  WdipLaunchRunDLLUserHost
  3  Rundll32
  3  RunDll_UpdateDriver
  3  RunDll_Reenumerate
  3  RunDllHardwareTest
  3  RunDllA
  3  News_RunDLL
  3  NdfRunDllHelpTopic
  3  NdfRunDllDuplicateIPOffendingSystem
  3  NdfRunDllDuplicateIPDefendingSystem
  3  NdfRunDllDiagnoseWithAnswerFile
  3  NdfRunDllDiagnoseNetConnectionIncident
  3  NdfRunDllDiagnoseIncident
  3  Mail_RunDLL
  3  LogOffRunDLL
  3  Java_com_sun_java_accessibility_AccessBridge_runDLL
  3  CreateRegWizard_RunDll
  3  BoxedAppSDK_RunDll32_Callback
  2  rundll_analyze
  2  rundllIsAdmin
  2  _RunDll
  2  _RunDLLReport@16
  2  ShowRunDLLW
  2  ShowRunDLL
  2  ServiceRunDll
  2  RunDll32
  2  RunDLL_SaveImageFile
  2  RunDLL_RemoveDevice
  2  RunDLL_MountFileW
  2  RunDLL_MountFile
  2  RunDLL_DoCleanupW
  2  RunDLL_DoCleanupA
  2  RunDLLReport
  2  NotifyDevicesNeedRebootRunDllW
  2  ICRemoveByRundll
  2  ICInstallByRundll
  2  GetRunDllModule
  2  FromRunDll
  2  DllUnregisterServer_RunDll
  2  DllRegisterServer_RunDll
  2  DllIsRegisterServer_RunDll
  1  rundll_install_npq2f_srv2003
  1  rundll_install_npq2f
  1  rundll_install_ex
  1  rundll_install
  1  rundll_config
  1  dtuSerialRunDll
  1  dpuRunDllXML
  1  _RunDLL_SaveImageFile@16
  1  _RunDLL_RemoveDevice@16
  1  _RunDLL_MountFileW@16
  1  _RunDLL_MountFile@16
  1  UsersRunDllW
  1  Rundll32Call
  1  RunDllUnregisterMAPI
  1  RunDllRegisterMAPI
  1  RunDllInterfaceW
  1  RunDLLCommand
  1  RunDLL
  1  RegisterRunDll
  1  NetAccWizRunDll
  1  MigrateRunDll32
  1  ManageCardSpace_RunDll
  1  InstallReg_RunDLL
  1  ImportInformationCard_RunDllW
  1  ImportInformationCard_RunDllA

I don’t include sample hashes this time as I don’t want to make your life easier.

Enter Sandbox 30: Static Analysis gone wrong

This series is quite old, and I kinda abandoned it at some stage, but today I am reviving it to talk about … static analysis…

Let’s be honest – last 2 decades changed the way we do malware analysis, and for many reasons:

  • groundbreaking developments in decompilation,
  • groundbreaking developments in deobfuscation,
  • groundbreaking developments in devirtualisation,
  • groundbreaking developments in emulation,
  • groundbreaking developments in sandboxing,
  • groundbreaking developments in Satisfiability Modulo Theory (SMT) solvers,
  • groundbreaking developments in GenAI,
  • demonopolisation and democratisation of reverse engineering tools aka a lot more tools available in general, and even if some are still commercial, they are often cheaper, and many that are free — are literally game changers, and generally speaking… the tooling today is far more accessible than it was 20 years ago,
  • emergence of many advanced (and often free) mature malware-oriented sandboxing, hooking and emulation toolkits,
  • development of many free tools/techniques enables us to decompile, debundle many installers or compiled scripts,
  • software (including malware) developers walking away from protectors, packers and wrappers of yesterday – today it’s often no longer worth it,
  • emergence of tools like Detect It Easy, Yara/Yara-X, Capa, Floss, Bulk Extractor, and many forensic tools that allow us to perform a lot of file format-parsing tasks associated with preliminary static sample analysis focused on ‘low hanging fruits’ like:
    • reputational checks, signed binary checks,
    • determining the file format very precisely,
    • automated feature/functionality discovery/extraction/classification,
    • automatic payload decryption/extraction,
    • automatic config decryption/extraction,
    • full metadata parsing/extraction,
    • extraction of strings of interest hidden inside the code that in the past we could only find via dynamic analysis (f.ex. on stack), and of course,
    • large and rich libraries of yara rules help to immediately identify malware sample’s family if it has been already classified before,
    • older programming languages like Visual Basic, Delphi, C, C++ are now replaced by Go, Rust, Python, .NET, Windows Apps, Electron Apps,
  • emergence of SaaS and software delivered via browser only,
  • disabling OS / Software features by default helped to kill many attack vectors (macros, autorun.inf, etc.),
  • decreasing importance of email – it got replaced by IM software with rich features,
  • lots of new operating systems, new CPUs, and new architectures expanded the scope, and made Windows less important,
  • jailbreaking scene,
  • 0day/vulnerability discovery scene,
  • lolbins, RMMs and a wave of TTPs that focus on blending in with the environment,
  • advances in EDR-based detections,
  • advances in decoy-based detections,
  • lots of new protections built-in into browsers and file readers/viewers prevent old drive-by attacks,
  • smartphones and tablets taking over from desktop computers and laptops for many daily tasks,
  • 0days moving from endpoints to IoT, appliances, mobile devices,
  • security focus moving from an endpoint attack surface to identity solutions,
  • platformisation and a global move from ‘build’ to ‘buy’ lowered the bar for cybersecurity skills required to do the job,
  • etc.

In 2010 malware analysts’ skills were measured by the knowledge of debuggers, disassemblers, file formats, packers, etc. Now… we are in 2025 and let’s be honest… malware analysis process of today usually starts with a submission of a sample to a sandbox / sample analysis portal. And, sadly, it very often ends there!

This is where this post begins.

I am quite surprised that many automated malware analysis solutions do not process samples statically very well. They do not do in-depth file format analysis, they do not recognize corrupted files well, and often offer a false sense of security/value by offering a CLEAN verdict for files that simply need more …. reversing love.

See the below example.

I took Notepad.exe from Win10, truncated it with a hex editor, and then submitted it to a few online file analysis services. I am happy that some of them immediately marked the file as corrupted, but it didn’t stop them from running a full-blown dynamic analysis session on the file I submitted. And in terms of static analysis, some solutions went as far as to report lots of findings related to anti-reversing techniques, cryptography, and lots of far-fetched conclusions that are nonsensical in a context of a) a corrupted file, b) Notepad program (clearly non-malicious), and are simply not a true reflection of reality.

I kid you not, but a truncated notepad sample that will never execute was marked as

  • a program that can enumerate processes (because it references NtQuerySystemInformation function that is actually used by warbird protection that invokes this API with a SystemThrottleNotificationInformation/SystemPolicyInformation parameter),
  • a program that accepts drag & drop operations (true),
  • a program that has an ability to take screenshots (just because it references a CreateDC API function), which is not true,
  • and so on and so forth.

Let’s be clear – mapping presence of APIs in the sample’s import table or as a string referencing API name found in a sample’s body to actual ‘threats’ or TTPs is an absurdity that is omnipresent in sandbox reports today and should be corrected asap. This could have worked in 2010, but today these sort of ‘determinations’ must be seen as poor indicators.

And as an analyst, I’d actually like to see why the sample was marked as corrupted. I’d also like to see the context of the far-fetched API-matching claims as well. You can’t list many Windows API in a negative context (like f.ex. CreateDC that notepad uses for… printing) unless you really can prove that it is indeed present in the code to deliver some malicious functionality… It strikes us as an over-simplistic approach that is focused more on the quantity of the findings than the overall quality of the report.

This is where old-school reversing comes in.

A long time ago I wrote my own PE file parser that I always run on all PE samples that I analyze, first. Because I wrote it, I fully control what it tells me, and since I used this tool to analyze many files over the years, corrected it on many occasions, learned a lot about PE file format intricacies on the way, and I have incorporated a lot of PE file format checks into it.

Running it on my truncated Notepad sample I immediately get many red flags:

(Raw Offset + Raw size of '.data '=0002EC00>filesize=0002DE00
(Offset to Raw size of '.pdata '=0002EC00>filesize=0002DE00
(Offset to Raw size of '.didat '=0002FE00>filesize=0002DE00
(Offset to Raw size of '.rsrc '=00030000>filesize=0002DE00
(Offset to Raw size of '.reloc '=00030C00>filesize=0002DE00
(wrong appdata ofs/size=0002EC00,00000000)
(.rsrc File Offset 00030000 <> DataDirectoryResourceOffset = 00000000

Seeing this kind of result immediately alters the way I do my sample analysis:

  • I, for sure, can’t run/test/debug/analyze it.
  • I, for sure, can’t trust any sandbox report generated for this sample.
  • I may need to ask about the source of the file.

My point is… if we want to sandbox/automate sample analysis, let’s do it in a smarter way. File format parsing is an extremely complex topic. If you look at Detect It Easy program’s data base, you will find a huuuuge number of file-typing routines that try to analyze various file types and return the best verdict possible.

So what can we do today?

Ask Sandbox vendors to do a more thorough static analysis that check file’s basic properties and at the most basic level, verifies if we have enough data in a submitted file to cover all the sections listed in a PE header…