文章目录
WeChatAppEx.exe 版本:2.0.6609.4
以融智云考学生端为例。
网上已经有关于微信小程序解密的非常优秀的文章,本着学习的目的便不参考相关内容。
笔者水平实在有限,如发现纰漏,还请读者不吝赐教。
如涉及侵权,请联系作者处理。
工具:火绒剑
首先看看打开一个小程序微信做了点什么,对微信进行火绒行为监控。因为小程序最初在PC端运行,必然会相关文件在客户机上释放,所以我们主要关注微信的文件读写行为。

注意到这里有类似文件释放的行为,在监控上访其实同样有读取此文件夹的行为,根据经验这其实就是一个简单的读取相关目录,发现没有相关程序逻辑文件后,主动请求服务器下载相关文件。
那我们的关注点来到\__APP__.wxapkg,根据前人的经验,这就是小程序的主要逻辑所在的地方。
其中可以监控到很多关于调用堆栈的信息,不过这些堆栈附近大概是文件释放相关逻辑,这并不是我们关注的重点。

我们用010Editor打开文件,任意一个二进制编辑器都可以。

可以看到明显程序逻辑被加密那么我们关注点就来到了这个文件的解密操作。
那么有两种思路
Findcrypt看看有没有比较明显的特征。**需要注意到的是,在WeChat中并没用打开这个wxapkg的相关操作。那么斗胆猜测可能是微信重新启动一个加载器对wxapkg进行装载。**那么我们进行火绒剑全局监控,看看有没对wxapkg进行读取的操作。

可以观察到名为WeChatAppEx.exe对文件做了两次读取的操作,根据经验我们关注第二次读取,双击File_read操作,查看调用栈

跟随堆栈,我们在IDA和X64dbg中分别定位此位置。RVA:0x678353d

可以看到此位置对文件进行了读取,我们在dbg中看看有没有文件的相关数据。附加到WeChatAppEx.exe,在对应位置下断点,并运行一个小程序。

轻而易举的断下,并且我们观察到文件大小非常接近打开的加密文件大小,并且在堆栈中出现了相关文件信息。
我们步过查看readBuf内的数据。


可以看到WeChatAppEx.exe读取了加密后的文件。那么我们不难理解WeChatAppEx.exe类似一个加载器,在运行时对文件进行解密装载。利用程序要对这一片内存进行读写的特征,我们对这篇内存区域下硬件访问断点(Xdbg的内存断点是针对内存页的,可能会断在奇怪的地方,可能是笔者不太会使用这种特性)

可以看到在RVA:2784F7A处断下,比较奇怪的是此处对文件的前8字节赋值为0,不太能理解,索性我们对没有修改的内存再次下硬件访问断点(不要忘记卸载之前的断点)。

再次断下时对之后的8个字节赋值为-1,同样比较难解,我们再次对剩余区域下硬件断点。

再次在RVA:676C91E处断下,可以看到这里有对字符串操作的相关指令,根据movsb指令的功能
指令:
MOVSB, MOVSW, MOVSD
描述:
移动字符串数据,复制由ESI寄存器寻址的内存地址处的数据至EDI寻址的内存地址处。
拓展的,我们分别观察RSI与RDI指向的内存区域

可以看到RSI指向的内存中有非完整的加密文件的十六进制形式(前8字节,即V1MMWXß被忽略),有意思的是,复制操作的指针并没有指向文件头部,而是指向了距离除去V1MMWXß之后的1024字节之后,这里有分块加密的特征,但是为什么程序没有在解密前1024字节处停下呢,可能是笔者疏忽或者程序先解密尾部部分在解密首部,既然已经到这里,我们不妨顺藤摸瓜。

可以看到正在向RDI中赋值数据,步过至字符串操作完成,我们再对这片内存区域下硬件访问断点。
之后再次在RVA:676C91E出断下,类似的,字符串操作完之后,我们再次对RDI下硬件访问断点。
再次运行之后再RVA:302772F处断下

观察到我们下断点的内存区域已经出现了../image字样,这无疑是非常让人兴奋的,可能这就是文件解密的位置。事实确实如此,我们取消硬件断点,在上一步断下出的循环中循环几次简单分析就可以确定,这确实是一个解密点,而且是非常简单的异或解密。事实上,这一部分解密过程与微信图片解密相同。

现在,我们找到了密文的1024字节之后部分的解密例程。我们默认忽略前8字节的处理,读者可自行分析,装载器并没有对前8字节做过多处理,在解密过程中只是简单的忽略。
那么我们rbp所对应的秘钥0x34从何而来呢,追踪异或Key使我们接下来要做的事情。
有请尊敬的IDA先生,我们在IDA转到RVA:302772A,即异或解密位置追踪Key从何而来。

根据分析,a3即是xorKey,至于a3*0x1010101010… 目的是用int8类型的值a3填满rbp至8个字节,进行8个字节分组异或。我们对a3进行简单的重命名—>xorKey_

可以看到xorKey作为参数被传入(为提高辨识度,笔者对此函数进行简单的重命名)

查看对此函数的引用,有两个运行时调用,还有一个直接调用,处于防止跟飞情况,我们对此函数头下断来找到调用点。

文件再次断下,并在堆栈中回溯,我们来到调用点RVA:302759B
在IDA中我们了解到xorKey在异或解密函数ContextDecode中作为第三个参数传递,且类型为int8,根据fastcall调用约定,我们关注寄存器R8:000000000014EE34,最后一个字节,即0x34。他是怎么来的呢,我们向上分析

VA:00007FF632C97589处对r8最后一个字节进行了赋值,我们看[rax+rcx-0x2]是什么。

可以看到rcx指向一个字符串,实际上这是微信小程序的ID,即AppID,进行简单的分析,rax是AppID的长度,而减去0x2后,[rax+rcx-0x2]指向的是AppID字符串的倒数第二个字符,将字符所对应的ascii码赋值给r8,这样,我们xorKey就拿到了。我们在everything找搜索对应AppId:wxf2a0156c0235fc4c

可以证实上面的说法。至此,尾部部分解密告一段落。
我们再次来到RVA:0x678353d上方的ReadFile处下断,因为根据之前分析,此处有全部密文出现,同样我们忽视前8字节,对剩余内存内容下硬件访问断点,尝试寻找前1024字节解密位置。

重新在此处断下(RVA:676C91E),同之前分析,补过字符串操作之灵后,我们跟随目的地(RDI)内存区域,对其下硬件访问断点。

再次在此处断下(RVA:676C91E)断下,重复上述步骤,在目的地地址下断。
运行之后再RVA:40DFE处断下

这里就有比较令人兴奋的字段:the iv:16bytes,部分加密需要一个向量,我们不妨猜测,这里就是加密函数,我们在IDA中来到对应位置。

之前笔者已经对一些变量进行分析并且重命名,所以看起来似乎一目了然,这些变量的命名,我们之后逐步分析但不是现在的关注的重点。
引起我们注意的是类似的汇编指令aesdeclast xmm2, xmm1,注意到字样“aes”就可以怀疑密文首部采用的是aes加密,事实上确实采用的是这用加密,从学习的角度,我们假设并不知情相关特征。
既然到了这一步,不妨运行看看相关内存区域有没有明文信息。

运行若干步之后,我们在RSI所指内存区域中发现明文特征,这与之前分析尾部解密中得出的明文十分类似,至此可以确定,这一部分逻辑即是对前1024字节进行解密的逻辑。
那么我们下一步要解决的问题是:”这是什么加密“,以便我们能找出秘钥,自行写出解密脚本。
我们百度aesdeclast xmm2, xmm1,看看能不能收获一些有用的信息。
下面是来自于互联网的一些资料:
AESDECLAST — Perform Last Round of an AES Decryption Flow
| Opcode/Instruction | Op/En | 64/32-bit Mode | CPUID Feature Flag | Description |
|---|---|---|---|---|
| 66 0F 38 DF /r AESDECLAST xmm1, xmm2/m128 | RM | V/V | AES | Perform the last round of an AES decryption flow, using the Equivalent Inverse Cipher, operating on a 128-bit data (state) from xmm1 with a 128-bit round key from xmm2/m128. |
| VEX.128.66.0F38.WIG DF /r VAESDECLAST xmm1, xmm2, xmm3/m128 | RVM | V/V | Both AES and AVX flags | Perform the last round of an AES decryption flow, using the Equivalent Inverse Cipher, operating on a 128-bit data (state) from xmm2 with a 128-bit round key from xmm3/m128; store the result in xmm1. |
Description ¶
This instruction performs the last round of the AES decryption flow using the Equivalent Inverse Cipher, with the round key from the second source operand, operating on a 128-bit data (state) from the first source operand, and store the result in the destination operand.
128-bit Legacy SSE version: The first source operand and the destination operand are the same and must be an XMM register. The second source operand can be an XMM register or a 128-bit memory location. Bits (MAXVL-1:128) of the corresponding YMM destination register remain unchanged.
VEX.128 encoded version: The first source operand and the destination operand are XMM registers. The second source operand can be an XMM register or a 128-bit memory location. Bits (MAXVL-1:128) of the destination YMM register are zeroed.
请注意描述中的加粗部分,其大概意思是aesdeclast xmm2, xmm1执行的是反向解密的最后一轮解密过程,xmm1是round Key(拓展秘钥,aes将用户设置的秘钥进行拓展以便于运算),而xmm2即是最后一轮解密的数据。
现在我们可以确定这一部分加密使用的是AES加密,我们正在分析的是其对应的解密部分。根据AES加密的对应的解密过程,最后一轮解密使用的round Key正是用户设定的秘钥,关于AES使用类似指令的介绍以及加解密的细节问题,笔者收集到一篇优质文章:
我们再次来到RVA:40DFE处
结合引用文章的介绍,我们大概可以得出:
那么我们接下来的关注点放到了拓展秘钥缓冲区,我们跟随秘钥缓冲区的生成会进入到秘钥拓展例程,在那里,我们大概率可以拿到Key,我们在IDA中追踪秘钥缓冲区

v33对应的是Rcx,而拓展秘钥缓冲区即keyArry来自于函数外。我们对此函数头下断进行栈回溯。
函数头:

再次断下后(前几次断下并不能得到我们要的调用栈,因为相关参数中找不到密文缓冲区等特征),根据fastcall约定,秘钥应该是r9所指缓冲区,实际上,秘钥缓冲区最后十六个字节作为原始Key的一部分(16字节),即未拓展的Key,秘钥拓展例程通过原始Key进行秘钥拓展,不过不注意这个细节也没有关系,这在之后的分析中将会体现。

我们回溯到RVA:2811EB5

在此函数中,keyArry已经生成,那么我们继续栈回溯,来到VA:00007FF6444C1137

[rcx+0x10]即是keyArry,同样分析此函数,发现keyArry同样作为参数传入此函数,那么我们继续进行调用栈回溯。
回退到第二次调用栈,我们来到VA:00007FF6444C1398,如下图

同样的,[rcx+0x10]即是keyArry,同样是作为参数传递进来的,再次进行堆栈回溯,来到RVA:00000000027F15AB,如下图,这里再次进行了简单的转发,再次进行栈回溯。

来到RVA:285B20C,如下图
我们所说的keyArry是aes实例化的一个对象,里面存储有拓展秘钥。再此函数进行简单分析后发现,key同样来自函数外通过参数传递进来。

继续堆栈回溯-_-||,来到RVA:000000000285AE26

继续回溯,来到RVA:000000000285AED8,同样是一个简单的转发,继续回溯,来到RVA:0000000003026BF2
如下图

如图,[[v18]+0x8]指向秘钥,终于要计算秘钥了-_-||,本函数上方有对v18的相关操作,如下图

其实看到‘salt’这几个字符,对秘钥拓展熟悉的朋友应该能马上反应过来这里应该就是拓展秘钥的地方了。我们进到函数里
如图

这就非常明确了,我们百度Pbkdf2
PBKDF的全称是Password-Based Key Derivation Function,简单的说,PBKDF就是一个密码衍生的工具。既然有PBKDF2那么就肯定有PBKDF1,那么他们两个的区别是什么呢?PBKDF2是PKCS系列的标准之一,具体来说他是PKCS#5的2.0版本,同样被作为RFC 2898发布。它是PBKDF1的替代品,为什么会替代PBKDF1呢?那是因为PBKDF1只能生成160bits长度的key,在计算机性能快速发展的今天,已经不能够满足我们的加密需要了。所以被PBKDF2替换了。在2017年发布的RFC 8018(PKCS #5 v2.1)中,是建议是用PBKDF2作为密码hashing的标准。PBKDF2和PBKDF1主要是用来防止密码暴力破解的,所以在设计中加入了对算力的自动调整,从而抵御暴力破解的可能性。
PBKDF2的工作流程
PBKDF2实际上就是将伪散列函数PRF(pseudorandom function)应用到输入的密码、salt中,生成一个散列值,然后将这个散列值作为一个加密key,应用到后续的加密过程中,以此类推,将这个过程重复很多次,从而增加了密码破解的难度,这个过程也被称为是密码加强。
稍微阅读以上引用内容 ,对比此函数参数不难得出:
既然秘钥长度是256位,那么可以推测出加密算法是AES_256。
注意到的是,它的伪散列算法是可以替换的,那么我们下一步要找出的是它使用的是哪种散列算法
这里笔者简单使用js选几种常见的散列算法试一试
const crypto = require('crypto');
let appid = "wxf2a0156c0235fc4c";
crypto.pbkdf2(appid,"saltiest",1000,32,'sha1',(err, derivedKey) =>
{
if (err) throw err;
console.log("The password is ",derivedKey.toString('hex'));
});

对比拓展秘钥函数运行之后的返回值[[rax]+0x8]中的值:

可以验证散列算法是‘sha1’,对应我们在分析过程中的拓展秘钥的最后字节部分。
00001D5A002CD188 3D9D6AB5E94DDDE8 èÝMéµj.=
00001D5A002CD190 71122C7B6FFE09D6 Ö.þo{,.q
00001D5A002CD198 C3981F7A8828924E N.(.z..Ã
00001D5A002CD1A0 BD14C8D6E69FEA98 .ê.æÖÈ.½
00001D5A002CD1A8 3336977C3609F094 .ð.6|.63
00001D5A002CD1B0 1969D6DC01305252 RR0.ÜÖi.
至此,我们找到了秘钥的生成算法。
分组模式以及iv的寻找相对简单,只要对AES加密流程以及几种加密模式的区别熟悉就可以在加密函数(RVA:0000000000040C30)中分析出加密模式以及向量。这里笔者不再赘述。
经过简单分析,总结之前分析成果,有如下清单:
除去文件头8个字节,剩余1024解密算法:
秘钥算法:PBKDF2
盐值:saltiest
秘钥:小程序ID,wxf2a0156c0235fc4c
摘要算法:sha1
秘钥长度:32字节
解密算法:aes-256-cbc模式
初始化向量iv:74 68 65 20 69 76 3A 20 31 36 20 62 79 74 65 73 对应字符串:“the iv: 16 bytes” -_-||
1024字节之后的数据处理方式:
解密方式:异或解密
异或Key:微信appid字符串的第二个字符对应的ASCII码形式。
拿到解密出的文件后可以用相应的解包脚本进行解压,网上不乏解压脚本,遗憾的是笔者并没有找到能够彻底解压并且还原出微信开发者工具能够识别的对应各式的文件(微信开发者工具对js等的样式做了一层封装,想要能够调试源代码需要将解包后的文件还原成其能够识别的格式,网上确实有相关脚本,但大多比较老,微信开发工具对样式进行了更新,格式化出现了一些问题,笔者水平有限,就不去修复。)
下面给出不成熟的C++解密脚本
#include "PKCS7.h"
#include <openssl/evp.h>
#include<openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/crypto.h>
#include <iostream>
#include <string>
#include <fstream>
#include <filesystem>
using namespace std;
unsigned char iv[] = { 0x74,0x68,0x65,0x20,0x69,0x76,0x3A,0x20,0x31,0x36,0x20,0x62,0x79,0x74,0x65,0x73 };//iv
unsigned char recursive_keys[32] = { 0 };//计算AES秘钥
const unsigned char salt[] = "saltiest";//盐值
int main()
{
string app_id("wxf2a0156c0235fc4c");
/*cout << "Plz enter the AppID:" << endl;
cin >> app_id;*/
//计算递归秘钥
PKCS5_PBKDF2_HMAC_SHA1(app_id.c_str(),app_id.length(),salt,strlen((const char*)salt),1000,32, recursive_keys);
//cout << recursive_keys << endl;
string file_name("__APP__.wxapkg");
/*cout << "Plz enter the name of the file you want to decrypt :" << endl;
cin >> file_name;*/
//读取文件
int file_size = std::filesystem::file_size(file_name);
char* file_buf = new char[file_size] {0};
fstream fp(file_name.c_str(),std::ios::in|ios::binary);
if (!fp.is_open())
{
cout << "Sorry,please check that you entered the correct file name" << endl;
delete[] file_buf;
file_buf = nullptr;
return 0;
}
fp.read(file_buf, file_size);
fp.close();
//AES解密前1024字节内容(忽略文件头6个字节)
AES_KEY aes_key;
AES_set_decrypt_key((const unsigned char*)recursive_keys, 256, &aes_key);
AES_cbc_encrypt((const unsigned char*)file_buf+0x6, (unsigned char*)file_buf+0x6, 1024, &aes_key, iv, AES_DECRYPT);
PKCS7_unPadding* padding_result = removePadding(file_buf + 0x6, 1024);//解除填充
size_t diff = 1024 - padding_result->dataLengthWithoutPadding;//得到解除填充后与保持填充时明文的差值
//1024字节之后的密文解密
//找到异或Key
char xor_key = app_id.c_str()[app_id.length() - 2];
for (int i = 0; i < file_size - 0x6 - 0x400-diff; ++i)
{
file_buf[0x406 + i - diff] = file_buf[0x406 + i] ^ xor_key;
}
//将解密后的数据写入
fstream fp_out(file_name+"_plaintext",ios::out | ios::binary);
if (!fp_out.is_open())
{
cout << "File create failed." << endl;
fp_out.close();
delete[] file_buf;
file_buf = nullptr;
freeUnPaddingResult(padding_result);
return 0;
}
fp_out.write(file_buf+0x6,file_size-0x6-diff);
fp_out.close();
cout << "The file decryption is successful." << endl;
delete[] file_buf;
file_buf = nullptr;
freeUnPaddingResult(padding_result);
return 0;
}
WeChatAppEx_2.0.6609.4下载:
链接:https://pan.baidu.com/s/1N1f6DwOiIoElt9m1aN4uSw?pwd=vp03
提取码:vp03
供学习使用
前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现
目录0专栏介绍1平面2R机器人概述2运动学建模2.1正运动学模型2.2逆运动学模型2.3机器人运动学仿真3动力学建模3.1计算动能3.2势能计算与动力学方程3.3动力学仿真0专栏介绍?附C++/Python/Matlab全套代码?课程设计、毕业设计、创新竞赛必备!详细介绍全局规划(图搜索、采样法、智能算法等);局部规划(DWA、APF等);曲线优化(贝塞尔曲线、B样条曲线等)。?详情:图解自动驾驶中的运动规划(MotionPlanning),附几十种规划算法1平面2R机器人概述如图1所示为本文的研究本体——平面2R机器人。对参数进行如下定义:机器人广义坐标
网站的日志分析,是seo优化不可忽视的一门功课,但网站越大,每天产生的日志就越大,大站一天都可以产生几个G的网站日志,如果光靠肉眼去分析,那可能看到猴年马月都看不完,因此借助网站日志分析工具去分析网站日志,那将会使网站日志分析工作变得更简单。下面推荐两款网站日志分析软件。第一款:逆火网站日志分析器逆火网站日志分析器是一款功能全面的网站服务器日志分析软件。通过分析网站的日志文件,不仅能够精准的知道网站的访问量、网站的访问来源,网站的广告点击,访客的地区统计,搜索引擎关键字查询等,还能够一次性分析多个网站的日志文件,让你轻松管理网站。逆火网站日志分析器下载地址:https://pan.baidu.
一、机器人介绍 此处是基于MATLABRVC工具箱,对ABB-IRB-1200型号的微型机械臂进行正逆向运动学分析,并利Simulink工具实现对机械臂进行具有动力学参数的末端轨迹规划仿真,最后根据机械模型设计Simulink-Adams联合仿真。 图1.ABBIRB 1200尺寸参数示意图ABBIRB 1200提供的两种型号广泛适用于各作业,且两者间零部件通用,两种型号的工作范围分别为700 mm 和 900 mm,大有效负载分别为 7 kg 和5 kg。 IRB 1200 能够在狭小空间内能发挥其工作范围与性能优势,具有全新的设计、小型化的体积、高效的性能、易于集成、便捷的接
目录一.大致如下常见问题:(1)找不到程序所依赖的Qt库version`Qt_5'notfound(requiredby(2)CouldnotLoadtheQtplatformplugin"xcb"in""eventhoughitwasfound(3)打包到在不同的linux系统下,或者打包到高版本的相同系统下,运行程序时,直接提示段错误即segmentationfault,或者Illegalinstruction(coredumped)非法指令(4)ldd应用程序或者库,查看运行所依赖的库时,直接报段错误二.问题逐个分析,得出解决方法:(1)找不到程序所依赖的Qt库version`Qt_5'
提供3种Ubuntu系统安装微信的方法,在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信:ubuntu20.04安装最新版微信--可以支持微信最新版,但是适配的不是特别好;比如WeChartOCR.exe报错。2.原生微信安装:linux系统下的微信安装(ubuntu20.04)--微信适配的最好,反应最快,但是微信版本只到2.1.1,版本太老,很多功能都没有。3.深度deepin-wine6安装微信:ubuntu20.04+系统deepin-wine6安装新版微信--综合比较好,当前个人使用此种方法1个月,微信版本3.4;没什么大问题,尚可。一、WineHQ7.0安装微信
对传统的餐饮商家来说,小程序很好地解决了餐厅线下线上连接的问题,在引流获客、节约人力、营销宣传、塑造会员体系、改善消费体验等方面都有很大帮助。小程序点餐可以帮助餐饮企业节省一大把人力开支。一个包含扫码点单、菜品管理、优惠券推送、外卖配送的小程序,商家花几万元就能完成开发测试并投入。商家为什么要开通“扫码点餐”1.解决服务员不够用的问题。2.不怕顾客跑单漏单。3.在微信就能管理菜品、查看营业额。4.订单小票显示顾客桌号和已点菜品。5.可在“附近的小程序”找到您的门店。如今餐饮业常用的三种经营模式:1堂食点单模式客人通过小程序堂食点单。商家可以在微信扫码点餐小程序管理后台根据自己店内情况来设置不同
技术选型1,前端小程序原生MINA框架cssJavaScriptWxml2,管理后台云开发Cms内容管理系统web网页3,数据后台小程序云开发云函数云开发数据库(基于MongoDB)云存储4,人脸识别算法基于百度智能云实现人脸识别一,用户端效果图预览老规矩我们先来看效果图,如果效果图符合你的需求,就继续往下看,如果不符合你的需求,可以跳过。1-1,登录注册页可以看到登录页有注册入口,注册页如下我们的注册,需要管理员审核,审核通过后才可以正常登录使用小程序1-2,个人中心页登录成功以后,我们会进入个人中心页我们在个人中心页可以注册人脸,因为我们做人脸识别签到,需要先注册人脸才可以进行人脸比对,进