张墨轩的技术宅

不忘初心,方得始终

Docker Desktop for Windows(以wsl2为后端) 简要分析

前置知识1: linux启动过程是内核+boot文件系统先启动,等内核加载好后,卸载boot文件系统,加载根文件系统(包含各种发行版套件)

前置知识2: linux内核和linux操作系统其实是有区别的,对于一个完整的linux操作系统来说,其实是由linux内核+发行版套件构成

前置知识3: 一般来说,虚拟机分成核心和管理软件两部分,管理软件可以管理虚拟硬件,镜像等等

前置知识4:
wsl1: 是在应用层模拟了POSIX API行为,在应用层看上去像是linux,实际不是真的linux
wsl2: (用的Hyper-V核心,和微软定制的linux内核),是真的linux


常规的虚拟机模式:
windows -> VirtualBox/vmware/Hyper-V --> (linux内核+boot文件系统+linux根文件系统)
常规情况下,你在虚拟机上安装系统,比如通过iso镜像安装系统,这个iso镜像里面就包括了(linux内核+boot文件系统+linux根文件系统)

wsl2模式:
windows -> (Hyper-V核心+微软定制linux内核+微软定制boot文件系统) --> linux根文件系统(微软定制,包含各种发行套件)
可以将wsl2看成是微软高度优化过的一种linux专用虚拟机,wsl2是(Hyper-V核心+微软定制linux内核+微软定制boot文件系统)组合构成,在wsl2上安装系统,比如安装Ubuntu,其实只是安装包含Ubuntu套件的根文件系统而已,如下:
windows -> wsl2(Hyper-V核心+微软定制linux内核+微软定制boot文件系统) -> linux根文件系统(包含ubuntu套件)

可以用如下命令查询发行版的版本:
cat /etc/issue
Ubuntu 22.04.1 LTS \n \l


Docker Desktop for Windows:
windows -> wsl2(Hyper-V核心+微软定制linux内核+微软定制boot文件系统) -> linux根文件系统(包含基于LinuxKit构建的Docker套件)

可以用如下命令查询发行版的版本:
cat /etc/issue

Welcome to LinuxKit

## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
{ / ===-
\______ O __/
\ \ __/
\____\_______/



可以看到,上面两个例子中的linux操作系统是共享一个内核的,也就是wsl2中微软定制的linux内核,可以分别在两个系统的命令行中用命令查看,都是同样的输出:
cat /proc/version
Linux version 5.15.90.1-microsoft-standard-WSL2 (oe-user@oe-host) (x86_64-msft-linux-gcc (GCC) 9.3.0, GNU ld (GNU Binutils) 2.34.0.20200220) #1 SMP Fri Jan 27 02:56:13 UTC 2023


总结:
1.安装wsl2,本质是启用Hyper-V核心和安装微软定制的linux内核和boot文件系统
2.在wsl2上安装Ubuntu,本质是下载安装包含Ubuntu发行版内容的linux根文件系统
3.安装Docker Desktop for Windows,本质是安装包含LinuxKit定制的docker容器套件的linux根文件系统

当你在windows下运行容器:
windows(真机) -> wsl2(Hyper-V核心+微软定制linux内核+微软定制boot文件系统) -> linux根文件系统(基于LinuxKit构建的Docker套件) -> 各种容器应用

当你在windows下运行Ubuntu,并在Ubuntu下管理容器:
windows(真机) -> wsl2(Hyper-V核心+微软定制linux内核+微软定制boot文件系统) -> linux根文件系统(包含ubuntu套件) -> docker客户端 -> 虚拟机间通讯

此时,你的windows系统上实际至少跑了2台虚拟机!

第五弹(磁盘保护+穿透更新)

技术虽然每天日新月异,但是对技术的热爱却从未改变,总结过去,展望未来。对于一些公共场所的带硬盘的Windows PC,比如酒店或者网吧,既需要保护硬盘系统不被破坏,又需要对上面的一些软件进行更新,比如游戏。本模块是我以前开发的三层游戏更新平台客户端的核心模块,能保护磁盘的同时又可以更新指定的游戏或者软件。

演示截图:

第四弹(Windows安全防火墙)

技术虽然每天日新月异,但是对技术的热爱却从未改变,总结过去,展望未来。这次发的是2008年开发的一个Windows安全组件,主要包括两个部分,一是内核驱动防火墙,通过白名单机制,阻止未授权的驱动的加载,并且通过HOOK SSDT的方式保护自身进程的安全,二是ARP防火墙,它通过NDIS中间层驱动捕获以太数据报文,过滤掉其中的恶意ARP报文。自夸一下,当年项目代码很工整,并且自定义了C++ stub,用于实现ddk下编译C++代码 :)

截图如下:

第二弹(Dotfuscator_x86)

又有挺久没有更新了,这次发的是一个32位windows下x86指令集的扭曲加密小工具,这个小工具是2008年做的,当时因为需要给开发的windows驱动进行一些防护,防止其他人逆向,当时能给驱动加壳的软件还不是很多,比较厉害的就是刘涛涛的扭曲加密,不过因为各种原因最后在高人的指点下自己弄了个简单的代码扭曲加密小工具用于驱动代码的保护,虽然远远谈不上完美,但是勉强也能用,呵呵。 


因为年代久远有些细节也记不清楚了,基本思路就是比如将jmp会替成 jnz xxx jz xxx, call变成push xxx,jmp target这种代码,然后可以通过多次循环变换将生成的代码进一步进行分解变换以提高代码扭曲的程度。需要扭曲加密的代码块在编译前用宏  START_MUTATE() 和 END_MUTATE() 包裹住。比如:

发现VC8.0的一个BUG

前段日子在用VC8.0调试程序的过程中发现了它的一BUG, 今天有空决定把它写出来. 看看下面这段代码:
程序代码:[ 复制代码到剪贴板 ]
    int A = 255789;
    float B = (float)A/(float)100;
    float C = B * 100;
    printf("result: %f \n\n", C);

在VC6.0下不管是Debug方式编译,还是Release方式编译 执行结果都正确.  如图:

按此在新窗口打开图片

然而在VC8.0下不管是Debug方式编译,还是Release方式编译 执行结果都有点怪. 如图:

按此在新窗口打开图片

装了VS80sp1的补丁后问题依旧,  比较了一下发现问题就出现在第二条语句float B = (float)A/(float)100; 
看了看生成的汇编码, 下面是vc6 Debug编译的

程序代码:[ 复制代码到剪贴板 ]
__real@4@4005c800000000000000 dd 1.0e2

//int A = 255789;
mov     [ebp+A], 255789

//float B = (float)A/(float)100;
fild    [ebp+A]
fdiv    ds:__real@4@4005c800000000000000 //操作的数是DWORD型, 定义在上面
fst     [ebp+B]


下面是vc8 Debug编译的

程序代码:[ 复制代码到剪贴板 ]
__real@4059000000000000 dq 1.0e2 

//int A = 255789;
mov     [ebp+A], 255789

//float B = (float)A/(float)100;
fild    [ebp+A]
fdiv    ds:__real@4059000000000000  //操作的数是QWORD型, 定义在上面
fstp    [ebp+B]


   众所周知,  IA32 CPU的浮点运算是基于栈的,  浮点单元包括8个浮点寄存器,  每个浮点寄存器的位宽都是80位,  和普通寄存器不同的是, 它们被当成一个浅栈(shallow stack)来对待,  这些寄存器分别标识为st0,st1~~直到st7. 其中st0在栈顶. 当压入栈中的值超过8个时,  栈底的值就会消失. (具体请参看<<深入理解计算机系统>>). 浅显点来讲就是利用某些浮点指令把被操作的数PUSH到浮点栈中,等CPU运算完成后再利用某些浮点指令把结果从栈中POP出来.  扯远了, ,  继续看我们的问题, 比较上面2段汇编代码就会发现他们有2处不同:
   第一处就是fdiv指令, 在VC6中它操作的是DWORD型的数, 在VC8中它操作的是QWORD(QWORD大小为8个字节), 在VC中类型float代表单精度浮点数, 占用大小为4个字节也就是32位, double类型代表双精度浮点数, 占用大小为8个字节也就是64位. 通过我的测试发现在VC8中 (1)float B = (float)A/(float)100; (2)float B = (float)A/(double)100; 这2条语句用VC8编译后居然没有任何区别, 内存中100都是用64位的双精度浮点数表示的, 而同样的语句用VC6编译的话 那么语句(1)100就会用32位单精度浮点数表示, 而语句(2)就会用64位的双精度浮点数表示, 我认为这应该是VC8的一个BUG. 
   第二处不同就是 fst,fstp的区别, 先看看这2条指令的说明.
FST
 指令格式:FST  STReg/MemReal
 指令的功能:将协处理器堆栈栈顶的数据传送到目标操作数中。在进行数据传送时,系统自动根据控制寄存器中舍入控制位的设置把栈顶浮点数舍入成相应精度的数据。
FSTP
 指令格式:FSTP  STReg/MemReal
 该指令的功能与FST相类似,所不同的是:指令FST执行完后,不进行堆栈的弹出操作,即:堆栈不发生变化,而指令FSTP执行完后,则需要进行堆栈的弹出操作,堆栈将发生变化。

 经过我的测试, 这个地方的不同才是导致运算结果有区别的直接原因, 哪怕把前面那2段汇编代码改成一样,只保留FST和FSTP的不同, 执行后还是会产生不同的结果, 不知为何. 各位有时间也可以自己测试一下, 知道原因了一定要告诉我. 我这里有个浮点指令体系的说明, 是以前网上下的, 觉的写的也还浅显易懂就收集了, 这里把它做为附件也一并附上  

点击下载此文件


错误 C1076

前段日子在windows平台下开发了一BT软件,其中用到了libtorrent这个开源的BT库,当时用的版本是0.12版,
好久没去看发现libtorrent库已经升级到了0.13版,看了看发布报告,发现这个版本比0.12版解决了很多BUG,并且增加了很多新功能,所以决定试一试,把自己使用的版本也更新到0.13版,说弄就弄,做了一系列工作后,开始编译,居然报了个C1076的错误,猜想可能是0.13版使用了更多的模版代码所引起的

按此在新窗口打开图片

查了查资料:

致命错误 C1076
错误消息 (编译器限制 : 达到内部堆限制;使用 /Zm 指定更高的限制)
此错误可能是由过多符号或过多模板实例化引起的。
解决此问题的方法是:
使用 /Zm 选项设置编译器内存限制。
消除不需要的包含文件。
消除不需要的全局变量,例如,动态分配内存而不是声明一个大数组。
消除未使用的声明。
将大函数拆分为更小的函数。
将大类拆分为更小的类。
将当前文件拆分成更小的文件。
如果在生成开始后立即发生 C1076,则说明为 /Zm 指定的值对程序而言可能太高。请减小 /Zm 的值。

呵呵,果然如我猜想的那样. 在编译指令当中多加了条/Zm200, 随即解决问题.


«1»

Powered By Z-Blog 2.2 Prism Build 140101

Copyright phonegap.me Rights Reserved.