Windows 10: 将程序命名为main.exe会导致弹出窗口显示

20

在Windows 10上,当我们创建一个名为 main.exe 的程序或将一个程序重命名为 main.exe 时,程序会弹出一个窗口,如下图所示:


Notepad ++中的某些弹出窗口,多么奇妙?


有两种不同的弹出窗口:

- 游戏栏(法语和英语版本):

Appuyer sur Win + G pour ouvrir la barre jeu

Press Win + G to open Game bar

- 截屏:

Appuyer sur Win + Alt +Impr.écran pour prendre une capture d'écran

(英文:按 Win + Alt + PrintScreen 进行屏幕截图)


我最初是在使用 Python 和 cx_freeze 时发现这个问题的,

我已经测试了多个程序,包括(如上所示)将 Notepad++.exe 重命名为 main.exe,并且每次都有一个弹出窗口,

我们还可以注意到弹出窗口会交替出现(一个游戏弹出窗口,然后是一个截屏弹出窗口,接着又是一个游戏弹出窗口...),

我在虚拟机中运行了 Windows 10,但正如下面所述,这个问题也发生在物理机器上。

对于这个问题,你有什么想法吗?

注意: BoltClock也进行了测试(在物理机上),他发现,在他的计算机上,只有"Main.exe"存在这种行为,而在我的计算机上,无论"main"的大小写分布如何(例如:它可以使用main.exe,Main.exe或者甚至是MaIN.exe),这种行为都会发生。


这很有趣......我真的很想看看你的程序,这样我就可以测试它。 - danidee
你安装了雷蛇游戏增强器或Cortex吗? - Ignacio Vazquez-Abrams
3
这绝对是与Xbox应用程序相关的本地Windows 10弹出窗口。我在启动某些游戏时会看到它。我刚刚在我的物理机器上使用一些WinForms和WPF exes复制了这个问题。但奇怪的是,它并不适用于所有大小写组合 - 例如,仅对于"Main.exe"而不是"main.exe"才有效。依然很有趣。 - BoltClock
猜测你需要一个清单来指定Windows 10兼容性,以便抑制此行为。 - Harry Johnston
2
我无法复现。我正在运行最新的预览版本14328.1000,也许问题已经被修复了?(或者它只影响某些配置。) - Harry Johnston
显示剩余9条评论
1个回答

15
我在周末进行了一些调查,发现有超过2000个特殊exe名称会触发相同的行为,而不仅仅是main.exe
资源管理器有一个名为BroadcastDVR的组件(位于twinui dll中),在进程创建时,它会将可执行文件属性与游戏的“存储”进行比较,并在匹配时启动GameLauncher.exe
我还没有找到比较是在哪里完成的,因为它被隐藏在RPC调用后面,这很难逆向。
无论如何,explorer.exe对以下文件C:\Users\YOUR_USERNAME\AppData\Local\Microsoft\GamesDVR\KnownGameList.binC:\Windows\broadcastdvr中有一份副本)有一个句柄,其中列出了所有触发XBox录制弹出窗口的特殊可执行文件。你可以在这里看到main.exe条目(第1007条)。

enter image description here

我已经编写了一个010模板文件来解析条目列表,在我的电脑上有2089个条目。从反转二进制文件所见,有三种类型的条目:

  • “简单”条目,只匹配可执行文件名。 例如:main.exeai.exe

  • 更复杂的条目,需要在可执行文件名和存储exe的路径中包含一些字符串才能匹配。 例如:acu.exe必须位于刺客信条:团结的子文件夹中。

  • 有一些条目需要匹配附加字符串,但我还没有找到如何触发游戏DVR弹出窗口。

NB:Win32子系统不区分大小写,因此可执行文件名的大小写不重要。

这是模板(您可以从这里安装010 Editor,我想有一个评估期):

typedef struct  {
   BYTE Reserved[0x300];
}HEADER;

typedef struct  {
    WORD ByteLen;
    BYTE RawString[ByteLen];
    //local string sName=ReadWString(RawString);
} GAME_WSTR <read=ReadGame>;

typedef struct {
    DWORD Reserved;
    DWORD ByteLen;
    BYTE RawString[ByteLen] <fgcolor=cLtRed>;
} OPTION_STR  <read=ReadOption>;

typedef struct  {
   local int StartAddr = FTell();
   DWORD EntrySize;

   // Executable game name
   GAME_WSTR GameName <fgcolor=cLtBlue>;

   // Optional magic
   if (ReadUShort() == 0xca54)
        WORD OptReserved;

   // Optional structs based on switch values
   WORD AdditionalNamesCount;
   WORD SwitchOption2;

   // Additional names (probably like a hint).
   local int i =0;
   for (i = 0; i <  AdditionalNamesCount; i++){
        OPTION_STR Option;
        if (ReadUShort() == 0xca54)
            WORD OptReserved;
   }

   // Look for a magic
   local int Find20h = 0;
   while(!Find20h){
        Find20h = (0x20 == ReadByte());
        BYTE Res;
   }

   GAME_WSTR GameId;
   WORD Reserved;

   // Sometimes there is an additionnal name
   // sometimes not. I check the current entry
   // is at less than the EntrySize declared.
   if (FTell()-StartAddr < EntrySize)
   {
       switch (SwitchOption2)
       {
       case 3:
            OPTION_STR Option3;
            break;
       case 2:

            OPTION_STR Option2;
       case 1:
            break;
       }
    }

} ENTRY <read=ReadGameName>;

string ReadOption(OPTION_STR &Game)
{
    local wstring GameName = L"";
    local int i ;
    for (i= 0; 2*i < Game.ByteLen; i++){
        WStrcat(GameName, Game.RawString[2*i]);
    }
    return WStringToString(GameName);
}

string ReadGame(GAME_WSTR &Game)
{
    local wstring GameName = L"";
    local int i ;
    for (i= 0; 2*i < Game.ByteLen; i++){
        WStrcat(GameName, Game.RawString[2*i]);
    }
    return WStringToString(GameName);
}

string ReadGameName(ENTRY &Entry)
{
    local string GameName = ReadGame(Entry.GameName);
    local string OptionGameName = "";
    if (Entry.AdditionalNamesCount)
        OptionGameName = " : "+ReadOption(Entry.Option);

    return GameName + OptionGameName;
}

//------------------------------------------
LittleEndian();
Printf("Parse KnownGameList.bin Begin.\n");
HEADER UnkwownHeader <bgcolor=cLtGray>;
while(1)
{
    ENTRY Entry <bgcolor=cLtPurple>;
    //Printf("Entry : %s -> %d.\n",ReadGameName(Entry) ,Entry.AdditionalNamesCount);
}
Printf("Parse KnownGameList.bin End.\n");

如果这种行为让你感到烦恼,你可以通过将ShowStartup注册表键设置为0来全局禁用它。它位于HKEY_CURRENT_USER\Software\Microsoft\GameBar中。
我还没有找到如何禁用特定可执行文件触发它的方法,但可能只需查看twinui中的机器代码即可实现。
安全问题
我们面临的情况是,仅通过更改可执行文件的名称就可以启动进程。这可能是危险的。
游戏启动器命令行位于HKEY_LOCAL_MACHINE\Software\Microsoft\GameOverlay中,需要管理员级别才能写入,因此在这里没有UAC或完整性级别绕过可能。
(我没有找到来自msdn的权威链接,所以这里是一个SO答案确认它:What registry access can you get without Administrator privleges?

3
这...这是...他们甚至都懒得...我不知道该说什么了。任何名为“Runner.exe”或“Everything.exe”的应用程序(因此会影响搜索工具)都会受到这个弹出窗口的影响,顺便说一下,您无法禁用它。 - Gabriel Morin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接