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.

Being a tool while using a tool

This case is kinda DFIR-fascinating.

There is an unwritten rule in the DFIR world that says – always check the results provided by one tool, with another tool, or manually…

Well… it all sounds nice in theory, until we come across a case that will change it all.

So…

If you use many different tools, and on regular basis, be warned that this case will destroy your faith in them…

Ready?

Let’s go!

I have been using Total Commander for over 2 decades. I absolutely love this tool, and can’t imagine working with gazillion of files and samples that I play with on regular basis, without using it.

But recently, I got fooled by it.

When you download the Signal desktop client installer for Windows (v7.39), you can browse its contents with Total Commander+its (various) archive plugins to see the following output:

I was specifically interested in the Signal.exe binary so I used TC to copy Signal.exe to my temporary work folder.

To my surprise, the sigcheck reported that this binary was compiled for… ARM processors!

Verified:       Signed
Signing date:   01:00 2025-01-23
Publisher:      Signal Messenger, LLC
Company:        Signal Messenger, LLC
Description:    Signal
Product:        Signal
Prod version:   7.39.0.0
File version:   7.39.0
MachineType:    64-bit ARM

Huh?

I was puzzled.

I literally downloaded what I believed to be an installer of Signal that was meant for Intel-based Windows, but now I am seeing the ARM binary inside it!

<Anxiety level intensifies>

I then tried the very same approach with the installer of the older version of Signal (7.38), but the result was the same….

What’s going on here? I wondered…

I must make a note here that the Signal setup program is using the Nullsoft installer to deliver the software to users. And in the reverse engineering world, once you recognize the installer type, the natural step in analysis is to decompile the script used by the installer.

Using the older version of 7z (7z_1505) that extracts the [NSIS].nsi script file I got the following output listing all the embedded files inside the most recent Signal installer:

$PLUGINSDIR\app-64.7z
$PLUGINSDIR\app-arm64.7z
$PLUGINSDIR\nsExec.dll
$PLUGINSDIR\nsis7z.dll
$PLUGINSDIR\SpiderBanner.dll
$PLUGINSDIR\StdUtils.dll
$PLUGINSDIR\System.dll
$PLUGINSDIR\WinShell.dll
$R0\Uninstall Signal.exe
$PLUGINSDIR\installerHeaderico.ico
[NSIS].nsi

Huh…

As you can see, there are two embedded 7z files listed above:

  • $PLUGINSDIR\app-64.7z
  • $PLUGINSDIR\app-arm64.7z

The first one is Intel-based, and the second one is ARM-based.

The [NSIS].nsi script references them here (using the respective 7z file depending on the architecture):

label_796:
  StrCmp $_40_ ARM64 0 label_799
  SetOverwrite on
  AllowSkipFiles on
  File $PLUGINSDIR\app-arm64.7z
  Goto label_802
label_799:
  StrCmp $_40_ 64 0 label_802
  File $PLUGINSDIR\app-64.7z
  Goto label_802

Kinda surprisingly, we can actually locate these 2 7z files inside the main installer file at the following offsets:

  • 0x0003C57B – app-arm64.7z
  • 0x087904C4 – app-64.7z

and after carving/extraction, browsing them with Total Commander we can reveal their content as shown below:

ARM (app-arm64.7z):

INTEL (app-64.7z):

Do you see where it is going?

With files/installers using many embedded files, the Total Commander’s (+its plugins’) visibility seems to be limited only to the first embedded archive, in this case it is the app-arm64.7z file! (in fact, it’s a bit more complicated in case TC or its plugins can parse PE file/their sections of the sample, adding an additional layer in a game of nested dolls).

When I look at that original Signal installer again now I can see the Total Commander (+its plug-ins) only see the first embedded archive. As a result, I see the Intel-targeting setup file embedding the ARM-targeting file shown in TC. The proper handling would include full file analysis of the installer and recognition of all embedded archives as virtual subfolders… at least.

The bottom line is this:

  • Let’s admit it, file formats are complicated, especially if they are mixed/overlapping
  • Trust, but verify — use multiple tools to extract/parse installer scripts, analyze/compare their outputs
  • Don’t trust GUI-only programs
  • Question what you see (in my case: the Intel-CPU targeting installer including ARM binaries as seen by TC in the installer’s body looked odd)
  • Analyze as many properly formatted file types as possible on a file format level to spot anomalies and inconsistencies in the future
  • Use carving and static analysis tools on samples: extracted sections, embedded media files, executables, configuration files, URLs, IPs. github repository addresses, PDB paths, etc. – this can add a lot of intelligence value long term