一.封包,协议

什么是封包?封包就是按照某种协议组成的一段字节集.这个很容易理解

那为什么要按照某种协议呢?这个我们一会再说

所有联网的软件,包括网络游戏,客户端和服务端进行通信都是通过封包进行的.

大家可以想象成2个人打电话,封包就是说话的内容.

而协议呢?就是封包的语种, 2个人打电话一个用日语,一个用英文

もしもし、韓さんです。 那边接电话 What are you talking? 这怎么交流啊?

好,那么我们定好协议,本通电话我们全部采用中文,这样就可以正常交流了对吧?

这就是协议

一句话概括, 联网软件,客户端和服务端通过约定好的协议用封包来进行通信.

二.收发包

上面了解了什么是封包协议,我们来看下什么是收发包

网络游戏和单机游戏的区别就在于时刻要和服务器进行通信,也就是收发包.我们发给服务器的叫做发包, 我们收到服务器发给我们的叫做收包,

当然这是相当于我们而言.反之一样.

来一个简单的例子:

我们进入游戏说一句:你好! 为什么别的玩家可以看到?

通过这个流程我们就会了解收发包以及服务器的作用.

首先我们发送要和周围人说你好的封包给服务器,

服务器收到我们的封包以后, 一般会给我们回一个封包,说OK没问题,当然也可能高冷的不回啊.

然后服务器会执行代码判断我周围都有哪些玩家,然后对这些玩家逐个发包

这些玩家收到服务器的封包以后,就会看到我们的喊话内容了.

了解了什么是收发包

我们就要知道调用什么函数来实现.

这里的三大快递公司

就是三个发包函数 send sendto WSASend 以及对应的 3个收包函数 recv recvfrom WSARecv

用哪一组都是可以的.

例如 口袋西游是 send recv

幻想神域是 WSASend WSARecv

三.心跳包

上面了解了封包协议,收发包,我们来看下什么是心跳包?

心跳包也是封包,只是一种比较特殊的封包.

他可以证明我们的客户端还活着,顾名思义,有心跳就是活着.

举个简单的例子:

2个人打电话, A: 喂在吗? B:... A:在吗? B....A:挂了. 多次没得到回应直接就挂断了.

心跳包也一样,当服务器给我们发包,我们没有回复的时候,服务器就有可能把我们强制踢下线. 当然也有可能我们定时主动发送

1-2次服务器可能会认为偶尔丢包,如果2个以上应该就会直接认为我们掉线,直接强制踢下线了.

心跳包有很多好处:

1.一定程度防止脱机外挂

2.掉线立刻下线, 比如以前的老游戏,电脑蓝屏关机了,由于没有自然下线, 心跳也特别少,人物可能还在上面很长一段时间,这是不安全的,比如传奇2

3.可以隐藏一些数据在心跳包中.

四.针对发包函数的保护方法

了解了封包的概念,我们发现发包函数非常重要,

监听了发包函数就相当于掌握了所有功能实现的方法,可以替代所有call.监听了收包函数就相当于掌握了所以数据信息.因为数据本质来源于服务器

同时通过调用关系我们还知道,可以通过发包函数返回到所有的功能函数.

这是连新手的都知道的2点,所以一定要进行防护

针对这2点我们可以做什么防护呢?

那么第一件事,当然是把这个函数给尽量的隐藏起来

有的时候我们会发现,三大发包函数都不断!

那么是不是这个游戏不用这3个函数发包呢?当然不是,我们只能用这三个发包函数发包,没有其他的方法。

那就是重新实现发包函数,自己重新写一个发包函数,不用系统提供的成品函数了。

说的简单点就是,代码还是这三个发包函数的代码,只是换了个位置,搬家了,不直接调用这3个函数了。

知道这个原理以后就很容易处理了

方法一

既然还是之前的代码,那么我们到他的更内层函数下断就可以了

既WSPSend,相当于用内层函数当特征码。

函数体内的虚函数

X64结构不一样,但是一样是虚函数进去 找到WSPSend

方法二

通过三个函数的特征码到对应模块中进行搜索,这里注意的是这个重新实现的发包函数,可能在原来的模块中ws2_32,也可能在游戏自己的某个模块中。

所以搜索的时候要注意.

特征码比如头部这一段

可能会有微小变化,所以我们要尝试

同时也可能搜索到多个位置,全部下断,测试即可.

总之两种方法思路是一致的,都是找原代码的特征而已。

对发包函数第二个保护方案就是加密

如果不加密,直接监听发包函数就可以获得所有的信息了.

比如直接hook send函数

例如WPE等简单工具就是hook的发包函数

所以必须进行加密

加密也一定要进行动态加密,就是同样的结果加密后内容是不同的.那么以上方法也就没有任何作用了.

五.对发包函数第三个保护方案就是线程发包

正常的游戏调用流程是

功能函数1---》功能函数2---》组包过程---》明文封包---》加密---》发包函数

所谓组包过程就是把功能函数的参数按照格式组成一个字节集

明文封包就是组装完成但是没有进行加密的封包

最后一步加密发送.

通过上面的调用关系,我们能看出来

这样只要找到发包函数,所以功能函数以及加密解密函数随意返回,随意分析

那怎么样避免呢?线程发包!

线程发包原理

就是说用2条线程来控制以上流程,让逆向者不能直接返回

第一条线程

功能函数1---》功能函数2---》组包---》明文封包---》封包写到某一个地址中

第二条线程

得到那个地址中的内容---》加密---》发包函数

当然两条线程分工不一定是严格这样写的,也可能明文封包在第二条线程中

这样的发包会有什么的特征呢?

1.无论什么功能,堆栈返回都是一样的,因为第二条线程的调用过程,什么功能都一样了

2.第二条线程由于不断循环,断的可能会比较频繁

六.线程发包跳出循环线程

先不管线程通信都有哪些方法,我们先来一个锻炼,然后我们再详细分析原理.

上面我们知道了线程发包的原理,那么这2条线程唯一的关联就是封包内容

所以我们直接追封包内容来源即可.就有线索从第二条线程返回到第一条线程了.

1.包内容地址如果是固定的,我们直接下断就能跳出去了,相当于用全局变量做线程之间通信

这里是变化的

那么我们追其来源

edi+2888 开始不发生变化了

根据我们的思路,要找变化的值来源, 那么我们在该地址上下写入断,

找到写入来源以后 我们看是否跳出了线程

发现并未跳出线程,还在原来的线程里.

2.那么我们还要继续追其来源

edx开始不变化了,对EDX下断

跳出了线程,我们可以通过这里下断,断到功能函数了

返回就是明文发包call

而这里就可以当成我们的"send"了

七.整理完全线程发包流程

1.跳出线程在这个位置,我们来整理下整体流程

线程1将ebp写入一个地址.

ebp 是会变的是动态申请的,存放的封包结构

这个地址是全局类对象指向封包结构的指针

线程2将线程1写入的 封包结构取出来

edi+2880就是全局类对象指向封包结构的指针

取出来给edx,我们依然写ebp 为了知道是线程1断出去的ebp

[[ebp]+8] 又写入 全局类型对象 +2888的位置

全局类对象+2888的位置 取出来给ebx

取出来的就是前面的 [[ebp]+8]

[[ebp+8]+4] 就是是封包内容

八.验证发包内容

我们下断可以验证一下 里面的封包内容是否是最后发送的内容,这个必须验证,防止中间又有多次加密

如果不相同,我们还要在这里对封包下访问断,追踪他经历了什么

方法很简单:

我们同时在这个位置下断 和WSASend 下断

对比两个位置封包是否一样

分别下条件断

byte ptr[[[EBP+8]+4]] != 0x0F

喊话11对比

2B771F00 C2C70011

2B771F04 C0105E58

2B771F08 D805FBB8

2B771F0C 910B1805

2B771F10 6F043A08

$ ==> >C2C70011

$+4 >C0105E58

$+8 >D805FBB8

$+C >910B1805

$+10 >6F043A08

$+14 >404B0000

发现是一样的那么我们在这里继续分析即可

九.明文发包call 和锁定加密call

我们现在跳出的位置 其实就是相当于"send"

正常可以返回到各种功能call, 如果使用call的话,我们现在已经足够了

如果想自己加密自己send发送封包,那么我们还需要分析加密解密过程,分析加密算法.

返回一层层看看 是否有明文内容,喊话是最容易看到明文的位置

返回的第一个call里面就看到了明文

那么说明 从这个call的头部 到第二次跳出的位置 中间就有加密call

如果这层发现不了明文,我们继续返回

当然我们也可以从ebp 一句一句逆向来源也是可以的

这个地方同样需要条件断 byte ptr[[[esp]+4]]!=0f

那么这地方就是明文发包call

找到了明文发包call,我们可以直接调用或则调用更外层功能函数实现各种功能,这之前我们都已经讲过了,这节课我们想用更好的方法

就是找到加密函数,我们自己加密封包,然后通过发包函数发送

十.加密call

加密函数的位置我们已经锁定了,就是在返回的call到我们断的位置之间

那么可以进行分析了

从call 断下以后 F7单独执行即可

发现了加密calll

以及分析出来加密call的参数

00B92266 2BF8 sub edi, eax

00B92268 83C5 02 add ebp, 2

00B9226B 55 push ebp ; 加密的地址

00B9226C 83C3 02 add ebx, 2

00B9226F 53 push ebx ; 加密的地址

00B92270 83C7 FE add edi, -2

00B92273 8D46 54 lea eax, dword ptr [esi+54]

00B92276 57 push edi ; 加密的长度

00B92277 50 push eax ; 秘钥 往上追 [[[[[00f84ba4]]+4]+0xC+8]]+54

00B92278 E8 83240000 call 00B94700 ; 加密call

00B9227D 8B9E 80280000 mov ebx, dword ptr [esi+2880]

全过程:

功能call参数进行组包,调用明文发包call, 明文发包call内部进行加密, 写入到一个全局类对象的属性中

线程2访问该属性,进行发包

十一.偷功能

偷功能很容易就是把程序的汇编代码复制到我们的程序内进行使用

我们自己组包,自己加密,自己发送封包

好处是,彻底不走游戏的任何代码.

复制出来的汇编代码需要进行初步处理才能写到内联汇编中

1.常数全部要加上0x

2.如果有call 还要自己分析出来继续执行内容 否则还是要以来原有程序

3.mov eax,[0x12345678] 类似这种容易错误的汇编代码也要转一下

4.跳转都标注出来

例如 jnz 12345678 他是要跳转到目的代码地址执行的,我们偷出来的代码已经不是原来的地址了

所以要通过标签修改跳转的地址。

我们把加密call的代码全部偷出来


push    ebp
push    ebx
push    esi
push    edi
mov     edi, dword ptr [esp+0x14]
mov     edx, dword ptr [esp+0x18]
mov     esi, dword ptr [esp+0x1C]
mov     ebp, dword ptr [esp+0x20]
xor     eax, eax
xor     ebx, ebx
cmp     edx, 0
je      Label1 =========================================
mov     al, byte ptr [edi]
mov     bl, byte ptr [edi+4]
add     edi, 8
lea     ecx, dword ptr [esi+edx]
sub     ebp, esi
mov     dword ptr [esp+0x18], ecx
inc     al
cmp     dword ptr [edi+0x100], -1
je      Label2================================================
mov     ecx, dword ptr [edi+eax*4]
and     edx, 0xFFFFFFFCje      Label3===============================================
lea     edx, dword ptr [esi+edx-4]
mov     dword ptr [esp+0x1C], edx
mov     dword ptr [esp+0x20], ebpLabel4:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
mov     ecx, dword ptr [edi+eax*4]
mov     ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [esp+0x20]
or      ebp, dword ptr [edi+edx*4]
ror     ebp, 8
xor     ebp, dword ptr [esi]
cmp     esi, dword ptr [esp+0x1C]
mov     dword ptr [ecx+esi], ebp
lea     esi, dword ptr [esi+4]
mov     ecx, dword ptr [edi+eax*4]
jb      Label4============================================
cmp     esi, dword ptr [esp+0x18]
je      Label5===================================
mov     ebp, dword ptr [esp+0x20]Label3:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecxinc     al
and     edx, 0x0FF
mov     edx, dword ptr [edi+edx*4]
xor     dl, byte ptr [esi]
lea     esi, dword ptr [esi+1]
mov     ecx, dword ptr [edi+eax*4]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label3==========================================
jmp     Label5========================================Label2:movzx   ecx, byte ptr [edi+eax]Label6:
add     bl, cl
movzx   edx, byte ptr [edi+ebx]
mov     byte ptr [edi+ebx], cl
mov     byte ptr [edi+eax], dl
add     dl, cl
movzx   edx, byte ptr [edi+edx]
add     al, 1
xor     dl, byte ptr [esi]lea     esi, dword ptr [esi+1]
movzx   ecx, byte ptr [edi+eax]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label6=============================================
Label5:
dec     al
mov     byte ptr [edi-4], bl
mov     byte ptr [edi-8], alLabel1:pop     edi
pop     esi
pop     ebx
pop     ebp
retn

最终代码


__declspec(naked) void 加密call(DWORD 秘钥,DWORD 加密长度,DWORD 加密地址,DWORD 加密地址2)
{__asm
{
push    ebp
push    ebx
push    esi
push    edi
mov     edi, dword ptr [esp+0x14]
mov     edx, dword ptr [esp+0x18]
mov     esi, dword ptr [esp+0x1C]
mov     ebp, dword ptr [esp+0x20]
xor     eax, eax
xor     ebx, ebx
cmp     edx, 0
je      Label1
mov     al, byte ptr [edi]
mov     bl, byte ptr [edi+4]
add     edi, 8
lea     ecx, dword ptr [esi+edx]
sub     ebp, esi
mov     dword ptr [esp+0x18], ecx
inc     al
cmp     dword ptr [edi+0x100], -1
je      Label2
mov     ecx, dword ptr [edi+eax*4]
and     edx, 0xFFFFFFFCje      Label3
lea     edx, dword ptr [esi+edx-4]
mov     dword ptr [esp+0x1C], edx
mov     dword ptr [esp+0x20], ebpLabel4:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
mov     ecx, dword ptr [edi+eax*4]
mov     ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [edi+eax*4]
or      ebp, dword ptr [edi+edx*4]
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecx
inc     al
and     edx, 0x0FF
ror     ebp, 8
mov     ecx, dword ptr [esp+0x20]
or      ebp, dword ptr [edi+edx*4]
ror     ebp, 8
xor     ebp, dword ptr [esi]
cmp     esi, dword ptr [esp+0x1C]
mov     dword ptr [ecx+esi], ebp
lea     esi, dword ptr [esi+4]
mov     ecx, dword ptr [edi+eax*4]
jb      Label4
cmp     esi, dword ptr [esp+0x18]
je      Label5
mov     ebp, dword ptr [esp+0x20]Label3:
add     bl, cl
mov     edx, dword ptr [edi+ebx*4]
mov     dword ptr [edi+ebx*4], ecx
mov     dword ptr [edi+eax*4], edx
add     edx, ecxinc     al
and     edx, 0x0FF
mov     edx, dword ptr [edi+edx*4]
xor     dl, byte ptr [esi]
lea     esi, dword ptr [esi+1]
mov     ecx, dword ptr [edi+eax*4]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label3
jmp     Label5Label2:movzx   ecx, byte ptr [edi+eax]Label6:
add     bl, cl
movzx   edx, byte ptr [edi+ebx]
mov     byte ptr [edi+ebx], cl
mov     byte ptr [edi+eax], dl
add     dl, cl
movzx   edx, byte ptr [edi+edx]
add     al, 1
xor     dl, byte ptr [esi]lea     esi, dword ptr [esi+1]
movzx   ecx, byte ptr [edi+eax]
cmp     esi, dword ptr [esp+0x18]
mov     byte ptr [ebp+esi-1], dl
jb      Label6
Label5:
dec     al
mov     byte ptr [edi-4], bl
mov     byte ptr [edi-8], alLabel1:pop     edi
pop     esi
pop     ebx
pop     ebp
retn}}

十二.不走游戏代码自己发送封包

我们简单分析一个喊话封包,开始自己调用不走任何游戏代码

send头文件

#include "winsock.h"

有问题记得加上如下代码

#pragma comment(lib,"ws2_32.lib")


void HXSYDialog::OnBnClickedButton16()
{byte a[100]  = {0x11,0x00,0x7E,0x00,0x00,0x00,0x00,0x02,0x00,0x31,0x31,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x60,0xA8,0x6C};
DWORD 包长 = 0x13;
DWORD 包地址 = (DWORD)a;
DWORD 加密地址 = 包地址 + 2;
DWORD 加密长度 = 包长 - 2;
DWORD 秘钥 = 0;
__asm
{mov ecx,0x00f84ba4mov ecx,[ecx]mov ecx,[ecx]mov ecx,[ecx+0x4]mov ecx,[ecx+0x14]mov ecx,[ecx]lea ecx,[ecx+0x54]
mov 秘钥,ecx}加密call(秘钥,加密长度,加密地址,加密地址);HWND 窗口句柄 =FindWindowA("Lapis Network Class",0);
DWORD A = GetWindowLongW(窗口句柄,-21);
DWORD S =*(DWORD*)(A+0x38);send(S,(const char*)包地址,包长,0);// TODO: 在此添加控件通知处理程序代码
}

我们分析几个明文包 ,然后调用测试

十三.收包

收包函数recv,recvfrom, WSARecv

(收包的参数包地址和长度 是准备接受的包地址 和最大包长)

这是必然的,因为调用前是不可能知道收到的内容和具体长度的,只有执行完毕才会获得

recv 的实际包长 在返回值eax

WSARecv 在第四个参数

十四.粘包

我们发现断的时间越长 收包越大 原因很简单 因为可能沾包

当我们线程停住的时候,服务器依然在给我们发包,这个时候就会一起接收到

那么停住的时间越长,包就粘的越长.

每个独立的封包都有很明显的划分方法,所以拆包并不难

十五.明文收包和解密call

收包我们收到的肯定也是服务器发给我们的加密封包

加密封包经过解密处理,把里面的数据写入到对应内存位置,这样我们就在界面上显示相应的反馈了.

明文收包和明文发包方向是反的

要找去哪里了,所以是下访问断追其去向

我们发现   lpbuffers  +4里的包地址  是固定的, 是否固定其实无所谓

那么我们想追解密函数 就要对  他进行访问断

当然 通过我们自己喊话等方式 让其断下

2个拷贝的位置都可以

再对拷贝的地址下访问断

返回就是解密call

同时也是明文收包的位置了

然后我们发现    加密和解密是同一个函数

加密和解密是一个可逆的过程

正常我们其实直接搜索call 就可以找到明文收包的位置了

十六.线程发包实现的原理和方法

上面的例子学习完毕以后,实际上大家可以在追线程发包的时候还会存在一些疑惑

为什么一定要追到不变的地址,还有什么其他情况等等

其实,

线程发包本质是两条线交互信息.

那么无论是全局变量还是全局类对象下面的属性

肯定都是不变的地址

通信必然要写到这里

当然还有线程其他的通信方式

我们以后学习更多线程的知识再进行讲解

封包协议,加密解密,线程发包,recv,明文收包相关推荐

  1. 游戏安全UE4引擎之天堂W跳线程,HOOK明文收包发包并分析加密解密,实现send发包

    开始我们的UE4的跳线程之旅 简单了解游戏数据 拿到一个游戏,想去分析,在什么都不了解的情况下,我们只能先盲目的找2个简单的数据熟悉一下,之后再根据需求系统的分析,这是必然的一个顺序. 那么,先来找下 ...

  2. 图解HTTPS协议加密解密全过程

    为什么80%的码农都做不了架构师?>>>    我们都知道HTTPS能够加密信息,以免敏感信息被第三方获取.所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议. ...

  3. 用C#实现DES加密解密解决URL参数明文的问题

    啥也不说,直接上代码. 加密解码,封装到一个类,key可以自己修改. using System; using System.Security.Cryptography; using System.Te ...

  4. 【CTF杂项】常用的一些加密解密工具网站集合(收藏用)

    ASCII ASCII 转换 常规的密码学加密解密 凯撒 栅栏 维吉尼亚 密码 摩尔斯电码 base编码解码 base64编码说明 base16 base32 base36 base58 base62 ...

  5. 用实例给新手讲解易懂的RSA加密解密算法

    用实例给新手讲解易懂的RSA加密算法 RSA加密算法是最常用的非对称加密算法,CFCA在证书服务中离不了它.我查过论坛上很少这方面的介绍,恰好看到一本书中作者用实例对它进行了简化而生动的描述,使得高深 ...

  6. C 语言网络编程 — 内核协议栈收包/发包流程

    目录 文章目录 目录 关键技术 DMA sk_buff 结构体 Net driver Rx/Tx Ring Buffer Buffer Descriptor Table NAPI 收包机制 网卡多队列 ...

  7. 游戏封包的加密与解密算法的破解

    游戏封包的加密与解密算法的破解 破解封包的加密与解密算法是制作外挂的第一步,是外挂制作中最具技术含量的步骤,同样也是一个十分令人头痛的环节.如果加密与解密算法被成功地破解,那么外挂制作也就完成了一半. ...

  8. QQ协议TEA加密解密代码 C#

    网上找到的qq协议的TEA加密和解密说明如下: 也有很多源代码,但是感觉上代码比较复杂,不容易看.我用C#重写了下. 基于2008协议 * QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一 ...

  9. 游戏数据逆向分析系列课程之封包和线程发包

    1.何为封包(15分钟) 免费试学 2.封包的过程(15分钟) 3.心跳包的过滤(16分钟) 4.逍遥情缘(send)(14分钟) 5.发包和线程发包(6分钟) 6.永恒传奇(send)(29分钟) ...

最新文章

  1. Python 爬虫使用pyppeteer 网页进行向下翻页操作
  2. postmessage与sendmessage的区别
  3. clickhouse建库_ClickHouse高性能数据库
  4. [唐胡璐]VBS技巧 - Adding Quotes(为字符串加双引号)
  5. java计算奇数阶魔方阵
  6. 让你瞬间提高工作效率的常用js函数汇总
  7. 日本新研究:将光伏组件高温高湿试验速度提高70倍
  8. 想做点副业,一天200-300就行
  9. Linux下的进程池(3)
  10. shell脚本不换行刷新数据
  11. JavaScript中的输入输出语句
  12. 求解偏微分方程开源有限元软件deal.II学习--Step 10
  13. 学习笔记: 委托解析和封装,事件及应用
  14. 原来,一直没有完全理解range()函数
  15. spring较为常用注解
  16. docker nginx 反向代理
  17. 【Scratch编程案例教学】scratch消灭砖块 scratch编程案例教学 少儿编程教案
  18. MaxCompute-ODPS SQL报错:Quota not enough,配额组资源不足
  19. Linux下使用clang-format格式化C++代码
  20. canvas绘图有锯齿毛边问题的解决

热门文章

  1. vue热敏打印机_vue.js实现连接打印机
  2. 【工具推荐】最简单方法创建 README
  3. 为什么比你优秀比你成功的人,还比你更努力?
  4. 计算机中push英语是什么意思,push是什么意思_push的翻译_音标_读音_用法_例句_爱词霸在线词典...
  5. 我的2016--旅行路线篇
  6. Artifact XXX:war exploded: Artifact is being deployed, please wait...(已解决)
  7. 13个优秀的网站检测工具推荐
  8. Win10如何开启OneDrive文件存储?
  9. 机器学习:期望风险、经验风险、结构风险
  10. ubuntu 下 开启无线网卡的monitor mode