dart-windows / win32

Build Win32 apps with Dart!
https://win32.pub
BSD 3-Clause "New" or "Revised" License
735 stars 118 forks source link

Windows Defender Trojan Warning with v5.4.0 #841

Closed ryjacky closed 2 months ago

ryjacky commented 2 months ago

I am encountering a Trojan warning from Windows Defender when using the latest version 5.4.0 of the dart win32 library. This issue was identified in a virtual machine set up directly using Hyper-V Manager of the Windows 11 development image.

Steps to Reproduce:

Create a flutter project that depends on 5.4.0 of the dart win32 library. Add some where the following code and call it in the program:

static String? _getProcessFileName(int pid) {
    int processHandle = OpenProcess(
        PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION |
            PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ,
        FALSE,
        pid);
    if (processHandle != 0) {
      try {
        final buffer = wsalloc(MAX_PATH);
        if (GetModuleFileNameEx(processHandle, 0, buffer, MAX_PATH) > 0) {
          return buffer.toDartString();
        }
      } finally {
        CloseHandle(processHandle);
      }
    }

    return null;
  }

  static int _enumWindowsProc(int hWnd, int lParam) {
    // Don't enumerate windows unless they are marked as WS_VISIBLE
    if (IsWindowVisible(hWnd) == FALSE) return TRUE;

    final length = GetWindowTextLength(hWnd);
    if (length == 0) {
      return TRUE;
    }

    final buffer = wsalloc(length + 1);
    GetWindowText(hWnd, buffer, length + 1);
    final String windowTitle = buffer.toDartString();

    Pointer<Uint32> pid = calloc<Uint32>();
    GetWindowThreadProcessId(hWnd, pid);
    final String? processFileName = _getProcessFileName(pid.value);

    if (processFileName == null) {
      free(pid);
      free(buffer);
      return TRUE;
    }

    _taskBarProcessInfoMap[processFileName] = GUIProcess(
      processName: processFileName.split("\\").last.replaceAll(".exe", ""),
      exePath: processFileName,
      base64Icon: "",
      mainWindowTitle: windowTitle,
    );

    free(pid);
    free(buffer);

    return TRUE;
  }

  /// List the window handle and text for all top-level desktop windows
  /// in the current session.
  static Set<GUIProcess> enumerateWindows() {
    _taskBarProcessInfoMap.clear();

    final lpEnumFunc = NativeCallable<WNDENUMPROC>.isolateLocal(
      _enumWindowsProc,
      exceptionalReturn: 0,
    );
    EnumWindows(lpEnumFunc.nativeFunction, 0);
    lpEnumFunc.close();

    return _taskBarProcessInfoMap.values.toSet();
  }

Build the exe and run it in a virtual machine. Expected Behavior:

Windows Defender should not raise any warnings or flag the library as malicious.

Actual Behavior:

Windows Defender identifies the compiled exe which depends on wn32 5.4.0 as a Trojan and quarantines it.

Additional Information:

I was originally using version 5.1.1 and Windows Defender does not trigger any warnings. The following are screenshots showing the log from Windows Defender. image image

OS: image

halildurmus commented 2 months ago

package:win32 doesn't have version 4.5.0.

ryjacky commented 2 months ago

Oh no... my bad, it should be 5.4.0, I have updated the issue.

halildurmus commented 2 months ago

Thanks!

I can't reproduce this on my machine, though.

Screenshot 2024-04-18 101002

I've tried the following code in a brand-new Flutter app:

import 'dart:ffi';

import 'package:ffi/ffi.dart';
import 'package:win32/win32.dart';

final _taskBarProcessInfoMap = <String, GUIProcess>{};

class GUIProcess {
  const GUIProcess({
    required this.processName,
    required this.exePath,
    required this.base64Icon,
    required this.mainWindowTitle,
  });

  final String processName;
  final String exePath;
  final String base64Icon;
  final String mainWindowTitle;

  @override
  String toString() =>
      'GUIProcess(processName: $processName, exePath: $exePath, '
      'base64Icon: $base64Icon, mainWindowTitle: $mainWindowTitle)';
}

String? _getProcessFileName(int pid) {
  int processHandle = OpenProcess(
    PROCESS_ACCESS_RIGHTS.PROCESS_QUERY_INFORMATION |
        PROCESS_ACCESS_RIGHTS.PROCESS_VM_READ,
    FALSE,
    pid,
  );
  if (processHandle != 0) {
    try {
      final buffer = wsalloc(MAX_PATH);
      if (GetModuleFileNameEx(processHandle, 0, buffer, MAX_PATH) > 0) {
        return buffer.toDartString();
      }
    } finally {
      CloseHandle(processHandle);
    }
  }

  return null;
}

int _enumWindowsProc(int hWnd, int lParam) {
  // Don't enumerate windows unless they are marked as WS_VISIBLE
  if (IsWindowVisible(hWnd) == FALSE) return TRUE;

  final length = GetWindowTextLength(hWnd);
  if (length == 0) {
    return TRUE;
  }

  final buffer = wsalloc(length + 1);
  GetWindowText(hWnd, buffer, length + 1);
  final String windowTitle = buffer.toDartString();

  Pointer<Uint32> pid = calloc<Uint32>();
  GetWindowThreadProcessId(hWnd, pid);
  final String? processFileName = _getProcessFileName(pid.value);

  if (processFileName == null) {
    free(pid);
    free(buffer);
    return TRUE;
  }

  _taskBarProcessInfoMap[processFileName] = GUIProcess(
    processName: processFileName.split("\\").last.replaceAll(".exe", ""),
    exePath: processFileName,
    base64Icon: "",
    mainWindowTitle: windowTitle,
  );

  free(pid);
  free(buffer);

  return TRUE;
}

/// List the window handle and text for all top-level desktop windows
/// in the current session.
Set<GUIProcess> enumerateWindows() {
  _taskBarProcessInfoMap.clear();

  final lpEnumFunc = NativeCallable<WNDENUMPROC>.isolateLocal(
    _enumWindowsProc,
    exceptionalReturn: 0,
  );
  EnumWindows(lpEnumFunc.nativeFunction, 0);
  lpEnumFunc.close();

  return _taskBarProcessInfoMap.values.toSet();
}
ryjacky commented 2 months ago

hmm... I'm not sure if this matters but it doesn't happen on my devlopment machine as well, it's only when I take it to the virtual machine to make sure there's no dependency issues, this happened.

While the exact exe I'm having problem with is not a blank project with the above code, removing that part from the project makes the problem go away.

FYI this is the project I'm talking about: https://github.com/ryjacky/PieMenyu

halildurmus commented 2 months ago

Can you also try using v5.2.0 and v5.3.0 ?

halildurmus commented 2 months ago

I've reproduced this while working on something else with win32_runner. I've sent the compiled exe to Microsoft for analysis and it appears that they've removed the detection:

324262786-17ba28ad-dba3-4e84-ab1e-3084b757428d

After updating Windows Defender, it is no longer detected as a trojan on my machine.

image

ryjacky commented 2 months ago

Ah... sorry that I have been quite busy in the past few days and haven't been able to test out the other versions.

Just tested it out with both 5.4.0 and 5.3.0, it doesn't seem to be detected as a trojan anymore.

I will close this issue unless it happens again. Thanks for your help!