《Windows PE权威指南》学习之第21章 EXE加密

news2026/5/2 11:27:27
EXE加密是软件保护范畴的一种技术通过对指定的PE文件进行加密可以增加逆向分析代码的难度在一定程度上保护软件代码的安全。EXE加密技术经常用于对软件的加壳处理通过PE分析软件对加密后的PE文件进行分析只能看到与补丁代码有关的信息原始的PE文件相关信息被隐藏同时如果在EXE补丁代码中使用一些技巧也能有效地增加PE反调试的难度达到保护软件的目的。21.1 基本思路加密一个PE文件的基本思路如下步骤1由补丁工具完成对目标PE文件数据目录表的修改并将原始数据目录表的内容转移到补丁代码中。步骤2由补丁工具完成对目标PE文件节区数据的加密。步骤3由补丁工具设置目标PE文件入口地址指向补丁代码。步骤4由补丁代码完成对目标PE文件数据目录表的还原。步骤5由补丁代码完成对目标PE文件节区数据的解密。步骤6由补丁代码完成对目标PE文件导入表中动态链接库的动态加载。步骤7由补丁代码完成对目标PE文件IAT的修正。加密以后的目标文件结构如图21-1所示。图21-1 加密以后的目标PE文件结构如图所示由于加密以后的数据目录表被清零通过结构分析工具如PEInfo查看目标PE文件时数据目录表中注册的数据类型都不会显示原始数据目录表会被补丁工具转移到补丁代码中。除数据目录表、SizeOfImage、最后一节的SizeOfRawData、函数入口地址AddressOfEntryPoint外目标PE文件的头部数据基本保持不变。目标PE文件的节区数据全部被加密保存但节区数据的长度不变补丁代码被选择附加到目标PE文件的最后一节中。21.2 加密算法加密算法是EXE加密的核心。本节首先介绍了常用的两类加密算法然后介绍了自行设计的可逆加密算法并给出了加密代码。21.2.1 加密算法的分类按照加密后的信息是否可以被还原常用的加密算法分为两大类❑ 不可逆加密算法❑ 可逆加密算法下面分别来介绍。1.不可逆加密算法不可逆加密算法的特征是加密过程中不需要使用密钥输入明文后由系统直接经过加密算法处理成密文。这种加密后的数据是无法被解密的只有重新输入明文并再次经过同样不可逆的加密算法处理得到相同的加密密文并被系统重新识别后才能真正解密。为了避免有人通过可逆算法得到加密前的信息在用户权限认证的系统中通常会使用一张不可逆加密的表表中存放着经过加密以后的用户密码。这个密码是任何人也破解不了的除非被他有幸猜中虽然不能还原最初的密码但可以通过对用户密码的再一次加密实现用户密码的验证。举个最简单的例子假设用户最初输入的密码是“123456”使用的不可逆加密算法是将每一位当成一个数字然后与3相除取余数作为每一位加密后的结果最终用户密码被加密成“120120”。很显然你无法正确地还原回去因为许多原始密码经过这种加密算法得到的值都是“120120”如原始密码为“789123”、“456123”等。但是这种加密算法却可以用在对用户的验证上即用户只要输入“123456”其加密以后的值一定是表中存储的值。很明显在这类加密过程中加密是自己解密还得是自己而所谓解密实际上就是重新加一次密所应用的“密码”也就是输入的明文。不可逆加密算法不存在密钥保管和分发问题非常适合在分布式网络系统上使用但因加密计算复杂工作量相当繁重通常只在数据量有限的情形下使用例如广泛应用在计算机系统中的口令加密利用的就是不可逆加密算法。近年来随着计算机系统性能的不断提高不可逆加密的应用领域正在逐渐增大。在计算机网络中应用较多不可逆加密算法的有许多例如RSA公司发明的MD5算法以及由美国国家标准局建议的不可逆加密标准SHSSecure Hash Standard安全散列信息标准等。2.可逆加密算法可逆加密算法又分为两大类“对称式”和“非对称式”。对称式加密 加密和解密使用同一个密钥通常称之为“Session Key ”。这种加密技术目前被广泛采用如美国政府所采用的DES加密标准就是一种典型的“对称式”加密法它的Session Key长度为56Bits。非对称式加密 加密和解密所使用的不是同一个密钥而是两个密钥一个称为“公钥”另一个称为“私钥”它们两个必须配对使用否则不能打开加密文件。这里的“公钥”是指可以对外公布的“私钥”则只能由持有人本人知道。它的优越性就在这里因为如果是在网络上传输加密文件对称式的加密方法就很难把密钥告诉对方不管用什么方法都有可能被别人窃听到。而非对称式的加密方法有两个密钥且其中的“公钥”是可以公开的不怕别人知道收件人解密时只要用自己的私钥即可以这样就很好地避免了密钥的传输安全性问题。最典型的可逆加密算法是异或运算。大家都知道对一个值连续异或两次其结果还是原值于是第一次异或被看成是加密第二次异或被看成是解密。对EXE进行加密必须使用一些可逆的加密算法即不仅能加密数据还要能还原数据。下面我们就自行开发一种简单的可逆加密算法。21.2.2 自定义可逆加密算法实例本节自行定义了一种可逆的加密算法基本思路是首先构造一个256字节的加密用基表该基表中的每一项的值都不重复这就意味着该基表中拥有了00h0ffh所有的字节值。有了这个基表以后再对PE中的每一个字节进行加密。加密方法非常简单将要加密的字节当做是基表的索引查找索引处的字节值该值即为加密后的值。加密算法示意见图21-2。图21-2 字节加密算法流程如图所示加密基表共有256项其中存储着256个不同的值并且这些值不是按顺序排列的。例如待加密的字节为50h将这个值当成是基表的索引查找该表50h处的字节是98h找到的这个值即为加密以后的值。解密的过程刚好相反根据要解密的字节98h遍历基表如果找到与之相等的值则记录此处的索引值该索引值50h即为解密后的字节。特别注意 基表的最后一个字节固定为00h。这个值的设置与基表的构造方式有关详见下一节。21.2.3 构造加密基表基表的组成包括255个随机数均不重复和最后一个00h字节。其中随机数的构造方法如下;生成加密基表 mov dwCount,0 mov edi,offset EncryptionTable .while TRUE invoke _getAByte mov byte ptr [edi],al inc edi inc dwCount .break .if dwCount0ffh .endw开始时加密基表所有表项值均被初始化为00h函数_getAByte得到一个与当前表项中已存在的所有值均不重复的字节并将其添加到表项。以下是函数_getAByte的定义_getAByte proc local ret pushad loc1: ; 取随机数 invoke _getRandom,1,255 mov ret,eax ; 判断随机数是否在基表中 invoke _isExists,eax .if eax ;如果在则重新获取随机数 jmp loc1 .endif popad mov eax,ret ;如果不在基表则返回 ret _getAByte endp函数_getAByte首先取一个1255之间的数注意这里舍弃了0因为0被设置为基表索引255处这样做主要是为了方便函数_isExists的运行。取指定范围随机数的方法如下_getRandom proc _dwMin:dword, _dwMax:dword local dwRet:dword pushad ; 取得随机数种子当然可用别的方法代替 invoke GetTickCount mov ecx, 19 ; X ecx 19 mul ecx ; eax eax X add eax, 37 ; eax eax YY 37 mov ecx, _dwMax ; ecx 上限 sub ecx, _dwMin ; ecx 上限 - 下限 inc ecx ; Z ecx 1得到了范围 xor edx, edx ; edx 0 div ecx ; eax eax mod Z余数在edx里面 add edx,_dwMin mov dwRet, edx popad mov eax, dwRet ; eax Rand_Number ret _getRandom endp获取随机数的基本算法为随机数下限随机数*1937mod上限-下限 1随机数1255之间获取以后首先通过函数_isExists判断该值是否在基表中已经存在。以下是函数_isExists的详细代码_isExists proc _byte local ret pushad mov esi,offset EncryptionTable mov ecx,0 .while TRUE mov al,byte ptr [esi] .if al0 mov ret,FALSE .break .endif mov ebx,_byte .if albl mov ret,TRUE .break .endif inc esi inc ecx .if ecx0ffh mov ret,FALSE .break .endif .endw popad mov eax,ret ret _isExists endp函数_isExists将循环检测基表结束条件有两个一是当函数在基表中找到了相同的值则返回TRUE另一个条件是函数已经完全遍历完基表未发现相同的值返回FALSE如果在基表中碰到00h则表示已经到了基表末尾返回FALSE。这里解释了上一节最后提出的问题即为什么要在基表的最后一个位置存放固定的字节00h。通过以上方法由计算机自动完成填充一个基表以下字节码是代码chapter21\HelloWorld.exe运行期获取的一个基表内容。可以看到基表中任何一项的值都是介于00h0FFh的每个值都不相等且基表的最后一个字节是00h。00401000 63 94 B2 E3 02 33 64 82 B3 E4 03 敳?3d偝? 00401010 34 52 83 B4 D2 04 35 53 84 A2 D3 05 23 54 85 A3 4R兇?5S劉?#T叄 00401020 D4 F2 24 55 73 A4 D5 F3 25 43 74 A5 C3 F4 26 44 则$Usふ?Ctッ?D 00401030 75 93 C4 F5 14 45 76 C5 15 46 95 C6 16 65 96 E5 u撃?Ev?F暺┬e栧 00401040 17 66 B5 E6 36 67 B6 06 37 86 B7 07 56 87 D6 08 ┤f垫6g?7喎●V囍■○○ 00401050 57 A6 D7 27 58 A7 F6 28 77 A8 F7 47 78 C7 F8 48 WψX (w Gx区H 00401060 97 C8 18 49 98 E7 19 68 99 E8 38 69 B8 E9 39 88 椚↑I樼├h欒8i搁9 00401070 B9 09 3A 89 D8 0A 59 8A D9 29 5A A9 DA 2A 79 AA ?:壺.Y娰)Z┶y 00401080 F9 2B 7A C9 FA 4A 7B CA 1A 4B 9A CB 1B 6A 9B EA ?z生J{?K毸←j涥 00401090 1C 6B BA EB 3B 6C BB 0B 3C 8B BC 0C 5B 8C DB 0D k弘;l?嫾.[屰. 004010A0 5C AB DC 2C 5D AC FB 2D 7C AD FC 4C 7D CC FD 4D \ ,] -| L} 听 M 004010B0 9C CD 1D 4E 9D EC 1E 6D 9E ED 3D 6E BD EE 3E 8D 溚N濎-m烅n筋 00401090 6D 9E ED 3D 6E BD EE 3E 8D BE 0E 3F 8E DD 0F 5E -| L} 0溚N濎 004010A0 8F DE 2E 5F AE DF 2F 7E AF FE 30 7F CE FF 4F 80 忁._ / 0 ?O€ 004010B0 CF 1F 50 9F D0 20 6F A0 EF 21 70 BF F0 40 71 C0 ?P熜 o狅!p筐q 004010C0 BE 0E 3F 8E DD 0F 5E 8F DE 2E 5F AE DF 2F 7E AF ??庉¤^忁._ / 004010D0 FE 30 7F CE FF 4F 80 CF 1F 50 9F D0 20 6F A0 EF ? ?O€?P熜 o狅 004010E0 21 70 BF F0 40 71 C0 10 41 90 C1 11 60 91 E0 12 !p筐q?A惲▲戉↑↓ 004010F0 61 B0 E1 31 62 B1 01 32 81 51 D1 A1 22 F1 72 42 a搬1b?2丵选駌B 00401100 C2 92 13 E2 00 聮!!?.构造加密基表的完整代码可以从随书文件chapter21\HelloWorld.asm中获得。21.2.4 利用基表测试加密数据接下来我们就用上节生成的基表进行数据加密的测试。首先在数据段中定义两部分数据一部分为加密前的数据szSrc另一部分为加密后的数据szDst。为了测试只定义了8个字节加密前的数据随便取一些值加密后的数据全部初始化为00h。加密数据的代码见代码清单21-1。代码清单21-1 加密数据的函数_encrptItchapter21\HelloWorld.asm138 ;------------------------------- 139 ; 加密算法可逆算法字节数不变 140 ; 入口参数 141 ; _src:要加密的字节码起始地址 142 ; _dst:生成加密后的字节码起始地址 143 ; _size:要加密的字节码的数量 144 ;------------------------------- 145 _encrptIt proc _src,_dst,_size 146 local ret 147 148 pushad 149 ;开始按照基表对字节进行加密 150 mov esi,_src 151 mov edi,_dst 152 .while TRUE 153 mov al,byte ptr [esi] 154 xor ebx,ebx ;将获取的值变成索引存储到ebx中 155 mov bl,al 156 mov al,byte ptr EncryptionTable[ebx] ;按索引值从基表里取出加密后的值 157 mov byte ptr [edi],al 158 159 inc esi 160 inc edi 161 dec _size 162 .break .if _size0 163 .endw 164 popad 165 ret 166 _encrptIt endp开始加密前esi指向要加密的数据edi指向存储加密结果的缓冲区行152163是一个循环循环次数为要加密数据的字节个数。行153155将获取的值当成一个索引存储在ebx寄存器中行156157按照该索引值从基表中取出加密后的值存储到结果缓冲区。以下是运行函数后的数据加密结果00403000 13 15 A0 00 17 !┴?┤ 00403010 01 00 FF 84 D3 AC 63 23 94 63 00 ┌. 動琧#攃....从列出的字节码可以看出加密前后数据的对应关系请对比以下所列自行查看基表看这些值是否真正符合我们的加密算法。加密前字节← →加密后字节 ---------------------------------------- 13← →84 15← → D3 A0← → AC 00← → 63 17← → 23 01← → 94 00← → 63 FF← → 0021.3 开发补丁工具补丁工具主要完成的操作包括❑ 处理目标PE文件的数据目录表向补丁代码传递原始数据目录表。❑ 生成加密基表向补丁代码传递加密基表及其他要传递给补丁代码的参数。❑ 加密节区数据。❑ 将补丁代码附加到目标PE文件的最后一节。本节相关文件在随书文件的目录chapter21\b中下面分别介绍。21.3.1 转移数据目录由于导入表的数据全部存储在节区而补丁工具会对这些数据进行加密处理破坏原有的导入表结构造成PE加载器加载目标PE文件失败因此必须事先将目标程序的数据目录表的导入表项设置为0即告诉加载器凡是目标PE中涉及的所有动态链接库不需要操作系统加载器来处理。不仅导入表的数据这样处理数据目录表中的其他数据也需要这样处理。修改数据目录表的方法是将所有项的RVA均设置为0。下面来看一个例子通过第17章介绍的方法将一个最简单的HelloWorld补丁打到记事本程序中打补丁的过程输出如下信息补丁程序D:\masm32\source\chapter21\patch.exe 目标PE程序C:\notepad.exe 补丁代码段大小00000196 PE文件大小00010400 对齐以后的大小00010400 目标文件最后一节在文件中的起始偏移00008400 目标文件最后一节对齐后的大小00008200 新文件大小00010600 补丁代码中的E9指令后的操作数修正为ffff4208最终生成的补丁程序为chapter21\patch_notepad.exe由于对原始notepad.exe程序的节数据没有进行加密处理所以使用OD加载该程序后内存空间分配情况见表21-1。表21-1 未加密前的进程内存空间分配表如表所示第17行为进程的PE文件头部分1820行为进程的其他节所在的起始地址和结束地址。下面将文件头部数据目录表项全部清零重新使用OD加载程序测试内存布局以下是清零以后的记事本的数据目录表字节码00000150 00 00 00 00 00 00 00 00 ........ 00000160 04 76 00 00 C8 00 00 00 00 B0 00 00 20 7F 00 00 .v........ ... 00000170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000180 00 00 00 00 00 00 00 00 50 13 00 00 1C 00 00 00 ........P....... 00000190 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001A0 00 00 00 00 00 00 00 00 A8 18 00 00 40 00 00 00 .............. 000001B0 50 02 00 00 D0 00 00 00 00 10 00 00 48 03 00 00 P..........H... 000001C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 000001D0 00 00 00 00 00 00 00 00 ........从字节码可以看出记事本程序数据目录中的以下数据类别项被定义导入表、资源表、调试信息、加载配置、绑定引入和IAT。现在把以上加黑部分全部清零然后用OD加载此时的内存空间分配见表21-2。表21-2 加密后的进程地址空间分配表从表21-1和表21-2的对比来看如果把数据目录中所有的内容全部清零目标程序patch_notepad.exe自身不含导入的其他动态链接库加载进内存后的空间分配是一致的如两个表的黑体部分所示。这就意味着对数据目录表清零的操作不影响进程自身模块最终加载进内存的空间分配。被加密的目标PE最终还是要运行的所以在运行前必须将数据目录表的内容恢复原样这就要求必须首先保存目标PE文件的数据目录表的原始内容保存的首选位置是嵌入到目标PE文件的补丁程序。代码清单21-2该代码使用了随书文件chapter17\bind.asm为补丁工具中模拟清除数据目录内容及备份内容到补丁程序的代码代码清单21-2 补丁工具中转移目标PE文件原始数据目录表内容的函数_openFile chapter21\b\bind.asm1264 1265 ;---------------------------到此为止数据复制完毕 1266 1267 ;清空目标文件中的数据目录表的内容 1268 ;并把该内容填充到补丁代码处 1269 ;该部分内容在补丁代码起始5个字节处共16个目录项 1270 ;该部分操作全部在lpDstMemory中完成 1271 1272 mov esi,lpDstMemory 1273 assume esi:ptr IMAGE_DOS_HEADER 1274 add esi,[esi].e_lfanew 1275 assume esi:ptr IMAGE_NT_HEADERS 1276 1277 ;复制数据目录表内容到补丁字节码处 1278 mov eax,[esi].OptionalHeader.NumberOfRvaAndSizes 1279 sal eax,3 1280 mov ecx,eax 1281 mov dwDDSize,ecx 1282 add esi,78h 1283 mov dwDDStart,esi 1284 1285 mov edi,lpDstMemory 1286 add edi,dwNewFileAlignSize 1287 add edi,5 ;跳转指令长度定位到补丁程序中保存数据目录表的位置 1288 rep movsb 1289 1290 ;清空目标文件数据目录表内容 1291 mov edi,dwDDStart 1292 mov ecx,dwDDSize 1293 mov al,0 1294 rep stosb 1295如上所示行12771288负责将目标PE文件的原始目录表的数据共78h个字节复制到补丁代码指定的位置。行12901294则将目标PE文件的数据目录表的内容全部置0。实现该功能的相关测试文件请参照随书文件目录chapter21\b。其中bind.asm是补丁工具 patch.asm为补丁程序patch_notepad.exe是生成的打了补丁的记事本程序。使用FlexHex查看补丁后的记事本程序会发现记事本数据目录中的所有内容均被移动到了补丁代码中。21.3.2 传递程序参数要传给补丁程序的参数包括解密用的基表以及补丁前最后一节在文件中的大小。基表用于补丁程序解密使用补丁前最后一节在文件中的大小需要事先传递给补丁程序因为一旦补丁被打上该值会发生变化。向补丁程序传递参数的相关代码见代码清单21-3。1296 ;初始化加密基表 1297 invoke _encrptAlg 1298 1299 ;将基表复制到补丁代码中 1300 mov esi,offset EncryptionTable 1301 mov edi,lpDstMemory 1302 add edi,dwNewFileAlignSize 1303 add edi,5 ;5个字节的跳转指令 1304 add edi,168 ;168个数据目录表长度 1305 mov ecx,256 1306 rep movsb 1307 … 1327 1328 ;将最后一节在文件中对齐后的大小存储到补丁代码位置 1329 1330 mov edi,lpDstMemory 1331 assume edi:ptr IMAGE_DOS_HEADER代码清单21-3 补丁工具向补丁程序传递参数chapter21\b\bind.asm1332 add edi,[edi].e_lfanew 1333 assume edi:ptr IMAGE_NT_HEADERS 1334 ;获取节的个数 1335 movzx eax,[edi].FileHeader.NumberOfSections 1336 mov dwSections,eax 1337 add edi,sizeof IMAGE_NT_HEADERS 1338 1339 dec eax 1340 mov ecx,sizeof IMAGE_SECTION_HEADER ;定位到最后一个节 1341 mul ecx 1342 add edi,eax 1343 assume edi:ptr IMAGE_SECTION_HEADER 1344 mov ecx,[edi].SizeOfRawData 1345 1346 mov edi,lpDstMemory 1347 add edi,dwNewFileAlignSize 1348 add edi,5 ;5个字节的跳转指令 1349 add edi,168 ;168个数据目录表长度 1350 add edi,257 ;基表 1351 mov dword ptr [edi],ecx如上所示行1297调用函数_encrptAlg创建加密用的基表。行12991306将获得的基表内容复制到补丁程序的指定位置共256个字节。行13281344得到目标PE文件最后一节的SizeOfRawData行13461351将该参数传递给补丁程序。补丁程序chapter21\b\patch.asm中存放加密基表和目标PE文件最后一节的SizeOfRawData两个参数定义如下jmp start ;补丁程序起始代码该指令占了5个字节0000h ;保存目标程序的相关信息 dstDataDirectory dd 32 dup(0) ; 原始目标程序的数据目录表0005h EncryptionTable db 256 dup(0),0 ; 加密基表 0085h dwLastSectionSize dd ? ; 最后一节的尺寸以字节计如上所示补丁程序中保存的与目标PE有关的信息主要包括三个原始数据目录表、解密用的基表和目标PE文件最后一节的尺寸。以下节选了运行期该位置显示的数据内容00010400 E9 FC 05 00 00【00 00 00 00 00 00 00 00 04 76 00 ............v. 00010410 00 C8 00 00 00 00 B0 00 00 20 7F 00 00 00 00 00 ....... ...... 00010420 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010430 00 00 00 00 00 50 13 00 00 1C 00 00 00 00 00 00 .....P.......... 00010440 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010450 00 00 00 00 00 A8 18 00 00 40 00 00 00 50 02 00 ...........P.. 00010460 00 D0 00 00 00 00 10 00 00 48 03 00 00 00 00 00 ........H...... 00010470 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00010480 00 00 00 00 00 】【73 91 C2 F3 12 43 61 92 C3 E1 13 .....s.Ca. 00010490 44 62 93 B1 E2 14 32 63 94 B2 E3 02 33 64 82 B3 Db.2c.3d 000104A0 E4 03 34 52 83 B4 D2 04 35 53 84 A2 D3 05 23 54 .4R.5S.#T 000104B0 85 A3 D4 F2 24 55 A4 D5 25 74 A5 F4 26 75 C4 F5 $U%tu 000104C0 45 76 C5 15 46 95 C6 16 65 96 E5 17 66 B5 E6 36 Ev.F.e.f6 000104D0 67 B6 06 37 86 B7 07 56 87 D6 08 57 A6 D7 27 58 g.7.V.WX 000104E0 A7 F6 28 77 A8 F7 47 78 C7 F8 48 97 C8 18 49 98 (wGxH.I 000104F0 E7 19 68 99 E8 38 69 B8 E9 39 88 B9 09 3A 89 D8 .h8i9.: 00010500 0A 59 8A D9 29 5A A9 DA 2A 79 AA F9 2B 7A C9 FA .Y)Zyz 00010510 4A 7B CA 1A 4B 9A CB 1B 6A 9B EA 1C 6B BA EB 3B J{.K.j.k; 00010520 6C BB 0B 3C 8B BC 0C 5B 8C DB 0D 5C AB DC 2C 5D l..[.\,] 00010530 AC FB 2D 7C AD FC 4C 7D CC FD 4D 9C CD 1D 4E 9D -|L}M.N 00010540 EC 1E 6D 9E ED 3D 6E BD EE 3E 8D BE 0E 3F 8E DD .mn.? 00010550 0F 5E 8F DE 2E 5F AE DF 2F 7E AF FE 30 7F CE FF .^._/0. 00010560 4F 80 CF 1F 50 9F D0 20 6F A0 EF 21 70 BF F0 40 O€.P o!p 00010570 71 C0 10 41 90 C1 11 60 E0 B0 31 01 81 51 D1 A1 q.A.1.Q 00010580 22 F1 72 42 00】00【00 00 01 00】00 00 00 00 00 00 rB............如上所示第一个【】包含了目标程序的数据目录表第二个【】是解密用基表内容第三个【】是目标程序最后一节的实际字节码长度。21.3.3 加密节区内容补丁工具负责对目标PE文件节区数据的加密工作。首先计算出要加密数据的范围从第一个节开始到文件末尾的数据全部要进行加密。由于加密采用不变长度算法能保证PE加载器在加载数据到内存以后所有的字节码位置保持相对不变在进行解密以后不需要重新计算代码中与定位有关的数据所以比较方便。对节区数据进行加密的代码见代码清单21-4。1308 ;加密节区数据 1309 ;------------------------------ 1310 ;首先计算加密范围 1311 ;取第一个节的文件起始偏移 1312 1313 mov edi,lpDstMemory 1314 assume edi:ptr IMAGE_DOS_HEADER 1315 add edi,[edi].e_lfanew 1316 add edi,sizeof IMAGE_NT_HEADERS 1317 1318 assume edi:ptr IMAGE_SECTION_HEADER 1319 mov eax,[edi].PointerToRawData 1320 1321 mov ecx,dwFileSize1 1322 sub ecx,eax 1323 add eax,lpDstMemory ;起始偏移 1324 mov esi,eax 1325 1326 invoke _encrptIt,esi,esi,ecx代码清单21-4 加密节区数据chapter21\b\bind.asm函数_encrptIt在21.2.4节已经讲解过了传入参数esi为第一个节在文件中的起始地址ecx为从该位置到文件尾的长度。程序运行到此目标文件中的指定范围的数据均被加密。经过同一个加密算法生成的目标程序使用了相同的补丁程序所以使用一些PE分析工具得到的结果除了文件头部描述不相同外其他地方的描述基本是相同的。使用PEInfo小工具分析加密以后的记事本程序结果显示如下文件名D:\masm32\source\chapter21\b\patch_notepad.exe ----------------------------------------- 运行平台 0x014c 节的数量 3 文件属性 0x010f 建议装入基地址 0x01000000 文件执行入口(RVA地址) 0x13000 节的名称 未对齐前真实长度 内存中的偏移(对齐后的) 文件中对齐后的长度 文件中的偏移 节的属性 ----------------------------------------------------------------------------- .text 00007748 00001000 00007800 00000400 60000020 .data 00001ba8 00009000 00000800 00007c00 c0000040 .rsrc 00009000 0000b000 00008800 00008400 c0000060 未发现该文件有导入函数 未发现该文件有导出函数 未发现该文件有重定位信息 未发现该文件有资源表从分析结果来看这里显示的内容根本不是原始的记事本程序用FlexHex查看字节码也只是看到一堆乱码而已。补丁工具还有最后一个功能即负责将补丁代码嵌入到目标PE文件的最后一节。由于这部分功能在第17章中有详细描述在此略过具体代码请参照随书文件chapter21\b\bind.asm。21.4 处理补丁程序前几章讲过的补丁程序实现的功能都与目标PE无关而这次补丁程序的代码针对的对象是目标程序本身所以需要额外对目标程序的数据进行如下处理❑ 对目标PE数据目录进行还原。❑ 对目标PE加密的节区的数据进行解密。❑ 对目标PE导入表中的动态链接库实施动态加载。❑ 对目标PE的IAT中的值进行修正。以下将对这四项处理进行逐一介绍。21.4.1 还原数据目录表对EXE加密必须保证加密以后的PE文件可以正常运行但补丁工具却将其数据目录表全部清零所以在运行目标程序前补丁程序要做的第一件事情就是依据事先保存在补丁程序中的目标PE文件的原始数据目录表信息恢复目标PE文件的数据目录表。代码清单21-5演示了还原目标PE文件数据目录表的整个过程447 ;获取目标进程的基地址 448 mov eax,offset dwImageBase 449 add eax,ebx 450 451 push eax 452 lea edx,_getImageBase代码清单21-5 还原数据目录表chapter21\b\patch.asm453 add edx,ebx 454 call edx 455 mov dwImageBase[ebx],eax 456 457 458 ;还原目标进程的数据目录表 459 mov esi,dwImageBase[ebx] 460 add esi,[esi3ch] 461 add esi,78h 462 push esi 463 464 assume fs:nothing 465 mov eax,fs:[20h] 466 mov hProcessID[ebx],eax 467 468 469 push hProcessID[ebx] 470 push FALSE 471 push PROCESS_ALL_ACCESS 472 call _openProcess 473 mov hProcess[ebx],eax ;找到的进程句柄在hProcess中 474 475 476 477 ;设置文件头部分为可读可写可执行 478 lea edx,hOldPageValue 479 add edx,ebx 480 push edx 481 push PAGE_EXECUTE_READWRITE 482 ;获取SizeOfImage大小 483 push esi 484 mov esi,dwImageBase[ebx] 485 add esi,[esi3ch] 486 assume esi:ptr IMAGE_NT_HEADERS 487 mov edx,[esi].OptionalHeader.SizeOfImage 488 pop esi 489 push edx ;设置页面大小 490 push dwImageBase[ebx] 491 push hProcess[ebx] 492 call _virtualProtectEx 493 494 pop esi 495 push NULL 496 push 168 497 mov edx,offset dstDataDirectory 498 add edx,ebx 499 push edx 500 push esi 501 push hProcess[ebx] 502 call _writeProcessMemory大致思路是通过OpenProcess函数传递PROCESS_ALL_ACCESS参数打开目标进程。然后使用WriteProcessMemory将补丁代码中保存的数据目录表项全部写回到目标进程头部数据目录表位置。代码行447455获取目标PE的基地址存储到变量dwImageBase中。行464466从fs:[20h]位置处取出进程的ID号存储到变量hProcessID中。行469473调用OpenProcess函数打开目标进程。行477492调用函数VirtualProtectEx将目标进程文件头部内存页设置为可读可写。行494502调用函数WriteProcessMemory将esi指向的数据写入变量dstDataDirectory指向的位置即还原目标进程的数据目录表。数据目录表被还原以后程序还无法正常运行因为此时数据目录表中的数据项指向的位置都是经过加密以后的数据对正常的目标进程而言这些数据根本无法识别。接下来要做的就是将节区对应的数据进行解密。21.4.2 解密节区内容加密的EXE程序运行前需要先将补丁工具加密的节区数据解密。由于加密使用了不变长度的算法解密后的节区数据大小和解密前一样所以解密时不需要额外构造解密用的缓冲区相对比较简单。首先来看解密函数。1.解密函数_UnEncrptIt解密函数的运行依赖于加密时创建的基表。解密以字节为单位进行每个字节的解密与其他字节没有关系解密后的数据大小与解密前大小一致。解密函数见代码清单21-6。代码清单21-6 解密函数_UnEncrptItchapter21\b\patch.asm65 ;------------------------------- 66 ; 解密算法可逆算法字节数不变 67 ; 入口参数 68 ; _src:要解密的字节码起始地址 69 ; _size:要加密的字节码的数量 70 ;------------------------------- 71 _UnEncrptIt proc _src,_size,_writeProcessMemory 72 local ret 73 local dwTemp 74 75 pushad 76 ;开始按照基表对字节进行加密 77 mov esi,_src 78 .while TRUE 79 mov al,byte ptr [esi] 80 mov edi,offset EncryptionTable 81 add edi,ebx 82 mov dwTemp,0 83 .while TRUE ;查找基表索引在dwTemp中 84 mov cl,byte ptr [edi] 85 .break .if alcl ;如果找到则退出 86 inc dwTemp 87 inc edi 88 .endw 89 90 ;用解密后的字节更新字节码 91 mov ecx,dwTemp 92 mov byte ptr dbEncrptValue[ebx],cl 93 94 ;使用远程写入 95 push NULL 96 push 1 97 mov edx,offset dbEncrptValue 98 add edx,ebx 99 push edx 100 push esi ;?? 101 push hProcess[ebx] 102 call _writeProcessMemory 103 104 inc esi 105 dec _size 106 .break .if _size0 107 .endw 108 popad 109 ret 110 _UnEncrptIt endp解密算法其实很简单查找加密基表如果在基表中找到指定字节码则记录该位置在基表中的索引这个索引值即为解密后的字节码然后用这个字节码替换原来位置的字节值。2.解密过程程序将所有节的数据均进行一遍解密还原目标程序为原始内容然后才启动其他诸如动态加载DLL、修正IAT等操作。代码清单21-7是解密数据的代码。504 ;解密数据 505 mov edi,dwImageBase[ebx] 506 assume edi:ptr IMAGE_DOS_HEADER 507 add edi,[edi].e_lfanew 508 assume edi:ptr IMAGE_NT_HEADERS 509 ;获取节的个数 510 movzx eax,[edi].FileHeader.NumberOfSections 511 mov dwSections[ebx],eax 512 add edi,sizeof IMAGE_NT_HEADERS 513 514 dec eax 515 mov ecx,sizeof IMAGE_SECTION_HEADER ;定位到最后一个节 516 mul ecx 517 add edi,eax 518 assume edi:ptr IMAGE_SECTION_HEADER 519 520 mov first,1 521 522 .while TRUE 523 mov esi,[edi].VirtualAddress 524 add esi,dwImageBase[ebx] ;要解密的起始地址 525 526 .if first ;如果是最后一节补丁工具更改了此处的大小 527 ;必须使用由补丁工具传入的原始值 528 mov ecx,dwLastSectionSize[ebx] 529 mov first,0 530 .else ;如果是其他节则使用SizeOfRawData代码清单21-7 解密节区数据过程chapter21\b\patch.asm531 mov ecx,[edi].SizeOfRawData 532 .endif 533 534 push _writeProcessMemory 535 push ecx 536 push esi 537 mov edx,offset _UnEncrptIt 538 add edx,ebx 539 call edx 540 541 dec dwSections[ebx] 542 sub edi,sizeof IMAGE_SECTION_HEADER 543 .break .if dwSections[ebx]0 544 .endw如上所示代码行505518通过文件头部描述获取节的数量并存储在变量dwSections中然后将指针定位到最后一节。由于此时CPU的控制权尚处于补丁程序手里所以最后一节的SizeOfRawData是被补丁工具修改以后的大小。如果用这个大小来解密数据势必会越界导致后面的补丁代码被修改所以最后一个节要解密的数据大小由补丁工具传进来的变量dwLastSectionSize决定其他的节要解密的数据大小则直接通过每个节的SizeOfRawData来决定。行522544是解密目标进程所有节的一个循环。其中变量first如果为1表示当前处理的是最后一节否则从最后一节向前处理直到循环次数达到节的总数为止。节区的数据解密完成是否就可以跳转到目标进程的入口地址处运行了呢答案是否定的。因为加密的目标PE文件在最初被操作系统加载器加载进内存时是误认为该文件没有导入表的导入表项被设置为0。所以记事本代码中用到的所有动态链接库在记事本进程地址空间中还不存在。如果补丁代码只还原完数据目录表将加密的节区解密就直接转移到原始入口地址处运行还不会成功补丁代码需要继续完成对记事本导入表中登记的各模块的动态载入以及IAT的修正后才会成功。21.4.3 加载目标DLL加密的目标PE文件被加载进内存后其原始的导入信息丢失。加载器不会将其导入表中的动态链接库加载进进程的地址空间这个操作必须由补丁程序代为完成。首先利用小工具程序PEInfo查看notepad.exe导入表中都引入了哪些动态链接库这些链接库包括comdlg32.dll、SHELL32.dll、WINSPOOL.DRV、COMCTL32.dll、msvcrt.dll、ADVAPI32.dll、KERNEL32.dll、GDI32.dll和USER32.dll。但是从OD加载的记事本进程地址空间中看到的链接库SECUR32.DLL、USP10.DLL、SHLWAPI.DLL、RPCRT4.DLL、LPK. DLL、IMM32.DLL并未出现在NOTEPAD.EXE的导入表定义中。这说明这些动态链接库的导入应该是包含在其他动态链接库中的例如❑ WINSPOOL.DRV→RPCRT4.DLL❑ COMDLG32.DLL→SHLWAPI.DLL❑ LPK.DLL→USP10.DLL接下来要为补丁代码增加动态加载DLL功能。通过遍历目标程序的导入表调用LoadLibraryA函数将导入表中列出的所有模块加载到内存中并记录各模块的基地址。详细代码见代码清单21-8。278 ;获取目标进程的基地址 279 mov eax,offset dwImageBase 280 add eax,ebx 281 282 push eax 283 lea edx,_getImageBase 284 add edx,ebx 285 call edx 286 mov dwImageBase[ebx],eax 287 288 ;遍历目标进程导入表 289 mov edi,offset dstDataDirectory 290 add edi,ebx 291 add edi,8 ;定位到导入表项 292 293 mov eax,dword ptr [edi] ;获取VirtualAddress 294 ;未做判断假设处理的PE文件均有导入表 295 add eax,dwImageBase[ebx] ;所在内存偏移 296 297 mov edi,eax ;计算引入表所在文件偏移位置 298 assume edi:ptr IMAGE_IMPORT_DESCRIPTOR 299 300 mov eax,dword ptr [edi].Name1 ;取第一个动态链接库名字字符串所在的RVA值 301 add eax,dwImageBase[ebx] ;在内存定位只需加上基地址即可 302 ;invoke _messageBox,NULL,eax,NULL,MB_OK 303 304 ;动态加载该DLL 305 invoke _loadLibrary,eax 306 mov dwModuleBase[ebx],eax代码清单21-8 动态加载目标进程导入表中登记的DLLchapter21\patch2.asm只加载一个DLLcomdlg32.dll的示例可以调试chapter21\bindC.exe文件。在OD环境下对比动态加载前后的内存分配可以看到comdlg32.dll被正确地加入到地址0x76320000起始处。以下是加载所有的动态链接库代码chapter21\a\patch.asm...... mov edi,eax ;计算引入表所在文件偏移位置 assume edi:ptr IMAGE_IMPORT_DESCRIPTOR .while [edi].Name1 ;循环结束条件只需简单判断Name1是否为0即可 push edi mov eax,dword ptr [edi].Name1 ;取第一个动态链接库名字字符串所在的RVA值 add eax,dwImageBase[ebx] ;在内存定位只需加上基地址即可 ;动态加载该DLL invoke _loadLibrary,eax mov dwModuleBase[ebx],eax ;修正从该链接库引入的函数IAT项 ;----------------------------- pop edi add edi,sizeof IMAGE_IMPORT_DESCRIPTOR .endw离最终可运行的目标越来越近了最后一步是修正目标文件的IAT。21.4.4 修正目标IAT引入的动态链接库被动态加载到内存以后接下来要做的就是修正目标进程中的IAT内容。从导入表中得到每个函数的名称字符串然后从加载的模块基地址获取该函数在内存的VA值并填到对应的IAT位置。下面开始IAT修复工作具体代码见代码清单21-9。代码清单21-9 修正目标进程IAT的_updateIAT函数chapter21\a\patch.asm240 ;------------------ 241 ; 修正IAT表 242 ; 传入全局变量参数 243 ; dwModuleBase 模块的地址 244 ; dwImageBase 进程基地址 245 ;------------------ 246 _updateIAT proc _lpIID,_writeProcessMemory 247 local dwCount 248 249 pushad 250 mov dwCount,0 251 252 mov edi,_lpIID 253 assume edi:ptr IMAGE_IMPORT_DESCRIPTOR 254 255 ;获取函数名字字符串 256 mov esi,[edi].OriginalFirstThunk 257 add esi,dwImageBase[ebx] 258 .while TRUE 259 mov eax,[esi] 260 .break .if !eax 261 add eax,dwImageBase[ebx] 262 add eax,2 ;跳过hint/name中的hint 263 264 ;此时eax指向了函数字符串 265 lea edx,_getApi ;获取函数地址 266 add edx,ebx 267 push eax 268 push dwModuleBase[ebx] 269 call edx 270 ;add eax,dwImageBase[ebx] ;获取函数VA值 271 272 ;将函数地址覆盖IAT对应位置 273 push esi 274 push eax 275 mov esi,[edi].FirstThunk 276 add esi,dwImageBase[ebx] ;esi指向IAT表开始 277 278 mov eax,dwCount ;求索引对应偏移 279 sal eax,2 280 add esi,eax 281 pop eax 282 283 284 mov dwIATValue[ebx],eax 285 ;使用远程写入 286 push NULL 287 push 4 ; 写入长度 288 mov edx,offset dwIATValue 289 add edx,ebx 290 push edx ; 写入的值所在缓冲区 291 push esi ; 写入起始地址 292 push hProcess[ebx] 293 call _writeProcessMemory 294 295 ;mov dword ptr [esi],eax ;将函数VA值写入IAT 296 pop esi 297 298 inc dwCount 299 add esi,4 300 .endw 301 302 popad 303 ret 304 _updateIAT endp调用函数_updateIAT前目标程序的数据目录表数据已经得到恢复。dwModuleBase中存放了当前动态加载的模块的基地址dwImageBase中存放了目标进程的基地址。hProcess为打开的目标进程为内存写作准备的句柄。函数传入两个参数参数1是_lpIID该参数指向当前导入描述IMAGE_IMPORT_DESCRIPTOR结构参数2为WriteProcessMemory的函数VA。函数首先通过IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk找到函数名字字符串注意要跳过Hint/Name前的2个字节值然后通过调用_getApi函数获取该函数指令字节码所在内存的地址并将该地址保存到IAT的相关项位置。通过以上几步对EXE加密、解密、运行的任务就完成了。运行补丁后的chapter21\b\path_notepad.exe程序先出现补丁代码中的对话框提示然后出现记事本程序。通过PEInfo小工具查看该程序显示的信息中大部分与记事本程序无关。----------------------------------测试--------------------------------打开补丁程序patch_a.exe打开PE文件notepad.exe生成bind21a.exe在C盘目录下用peInfo工具查看运行打开是正常的说明运行时解密bind21b.exe测试打开PE文件notepad.exe记事本程序生成bind21b.exe生成在C盘目录下用peInfo工具查看运行21.5 小结本章以记事本为例向大家介绍了一种加密EXE文件的思路。方法是对目标PE文件的节区进行加密运行时由补丁代码接管控制权实施解密并重新组织原程序的运行。本章用到了第17章中介绍的补丁工具。本实例的加密方法很简单大家可以根据实际需要对加密算法进行改进以满足一些特殊需要。注意本章介绍的代码只适合存在导入表且导入函数均为名称导入的目标PE程序如果大家想让其适应更多的程序请根据所学知识自行修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2552401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…