嘿!大家好啊~ 又是一个被床封印的早晨,被闹钟吵醒的时候才8:35,闭上眼睛睁开眼睛 我就穿越到了 11:07 有没有谁拥有和我一样的超能力?


前些日子的几篇笔记,分享了如何快乐Ctrl+F9定位游戏功能Call,和处理游戏在这方面设置的障碍,有没有学废了呢?


假设…啊 我们假设 张三学废了,然后…


然后…

张三就开始快乐的测试功能Call了,嗯 正当张三同学最快乐的时候,客户端弹出了一个小窗口…第三方检测了…张三同学被封号了。

(PS:初次了解封包,最好先看完之前的三篇笔记)

上面的故事真是闻者伤心听者落泪啊,不管是重写发包函数还是线程发包,虽然技法巧妙 但是只要清楚原理 细细分析,总会有漏网之鱼。

所以龙门前不会只有这些困难,后面也是布防甚多… 比如,功能Call的检测有什么变量检测 堆栈检测,恐怕是层层套娃 MV众多,解决起来更是烦躁无比啊!(欲扬先抑,先夸张一下)


那我们学废之前的技巧有啥子用罗?和上面那些花里胡哨的东西 中路对线?

当然不是,所谓黄河之水宜疏不宜堵,之前的三篇笔记都是铺垫 我们一开始的根本目的完全不是为了快乐的Ctrl+F9。


在揭晓谜底之前我们来观察一个珍贵无比的示例:

捕捉用户键盘消息——》技能相关的几个函数——》告诉服务器我要释放某某技能

一款网络游戏角色释放一个技能,大概就是上面这种流程。那比如我们找到了游戏的技能Call 调用它释放技能,会是什么样子?

会变成这样:

技能相关的几个函数——》告诉服务器我要释放某某技能

同学们,有没有看出来什么?哦,张三同学看出来了,同时它还有一个大胆的想法。

其实归根结底就是告诉服务器我干了啥,那我们能不能中间砍一刀 直接告诉服务我要释放啥啥技能,然后达到释放技能的目的?

当然可以,游戏实际上就是这样释放技能嘛,我们自然也可以这样做,以至于80%的功能 我们基本都能直接自己发送封包实现它。

哇哦!那这样做有什么具体的好处?

通过自己发包,试探变态功能这里就不讲了,我想辅Z通常大家有一个很关心的点(小声:嘿~~兄弟gua稳吗?)


如果把游戏检测比作地雷,那么游戏代码游戏领空就是雷区,想调用游戏功能call免不了遇到有埋雷的情况,而就算是排雷老手也免不了会踩上几颗。所谓究极的排雷方法,就是不走雷区到目的地,你那有雷我绕着走不就是了么。

这里也是同样的道理,我们走自己的代码通过send()系统发包函数发送封包实现功能,这样就避开了游戏领空的地雷,所以只要是完美的封包辅Z90%稳稳的很安心。

既然说到了“完美”二字,当然就有不那么完美的。毕竟自己发包实习功能,可不是说说就完成了。

让我们来观察一个珍贵无比的示例:

组包–》加密–》发包

游戏要发送一个封包,通常都要走这么一个流程。组包:把你做的坏事打包成小报告;加密:这是你和服务器的小秘密不能让别人知道;发包:把小报告邮寄给服务器。

明显的我们要想完美的达到目的主要有三大难点:

  1. 分析封包
  2. 寻找明文封包
  3. 寻找加密函数

**分析封包:**客户端告诉服务端我释放了某某技能,总不可能写得汉字吧,所以我们需要分析封包,知道释放技能应该以什么形式描述。

**找明文包:**系统发包函数上下断确实可以获得封包内容,但是多半是加密的,而且加密的密钥可能随时会发生变化,加密后的内容基本就是一团乱码无从下手没法分析,所以我们得找到内容还未被加密的地方。

**加密函数:**我们找到明文包了,也分析了放什么技能该发什么包。但是服务器可是要被加密后的封包啊,直接发送一个明文过去 服务器不认事小,第三方就不好了。我们要学客户端,把明文包加密一下,当然 得按游戏的加密方式,那么我们就得找到游戏的加密函数。

找明文包:

(一)理论

明文包的寻找是我们的首要任务,以此为跳板才能分析封包 和 定位 加密函数,如何寻找?不如再来欣赏一下这个珍贵无比的示例:

组包–》加密–》发包

我们可以观察到,从组包完成到加密的这段过程,封包内容是明文的未加密的。通过面对对象的编程原则,我们可以猜测这几个步骤在代码中的表现形式,就是几个对应的函数。

或者说组包完成后,封包数据会通过函数一层一层传递,函数怎么传递数据到另外一个函数?没错,当然是通过参数。那么我们是不是能够在发包处下断,使用传统手艺Ctrl+F9逐层返回所有经过的Call(函数),通过分析他们的参数内容,来找到明文的封包内容?


正确!不过在准备撸起袖子加油干之前,我们还有一个问题需要解决,我们怎么分辨出明文封包内容?要知道这些东西可不会写着I is 明文包,它们在内存之中的形式就是一串数字,而堆栈中所有函数的任何参数也都是一堆数字,怎么办?

想象一下你在茫茫人海中,光凭一个模糊的照片去寻找一个人,这该有多么困难~但是如果那个人的长相特立独行…比如头上长个角?再顶两只猫耳朵…是不是就简单多了?我相信眼尖的你肯定一眼就认出他了。


道理是一样的,那封包里面会不会有这种引起围观的奇异种?

我想大多数游戏都是有的,也就是喊话功能 或者 说聊天功能,你想想看在游戏中你和队友聊天,客户端是不是一定需要发送一个装有聊天内容的封包给服务器,这样服务器才能更新到其他玩家的客户端上。

我想大家应该都知道吧,实际上电脑只能储存数字…而字符是通过一张表把一些数字定义为字符,如图:

通过表图我们可以看到,字符 1 的对应数字是49,以十六进制表示 0x31。那么我们喊话一串“11111111111111”断下,它在封包内容里面就是一串 31 31 31 31 31 31…

Nice~ 纸上谈兵不如真刀真枪的干一架,请出我们的老伙计私服版的幻想神域2 带来一段珍贵无比的简单实例。


(二)实战

先在上面笔记说到的跳出线程发包代码段下断,可以当发包下断可以断到功能函数,当然我们并是要Ctrl+F9去找Call,而是返回上一层寻找未加密的封包内容。

喊话“1111111111”断下。



Ctrl+F9返回上一层调用。


这个call看起来好像只有一个参数,具体还是需要观察堆栈变化才能确定。Call指令下断,断下后F8单步步过 观察堆栈esp增加了多少。

增加了0x4 看来确实只有一个参数,Call指令下断 喊话“11111”断下,分析一下这个参数。


好大的值,像这种值一般是一个地址,这说明这个参数实际上是一个结构体之类的东西,我们数据窗口中查看分析一下。



这个结构体里面的成员也像是地址,我们挨个进去看一下,第一个:


这…显然是虚表函数之类的,反正没有31 31 31 不管他…再看看第二个:


哇,哦~ 一串的31 31 31 31看来我们运气不错只返回了一层我们就找到了明文的封包,如果要确认的话也简单。我们再次再次喊话断下,把这个明文内容修改一下会怎样呢?


放开断点。


我们输入的是 “1111111111111111”,居然喊出了 “1111155555111111”这就是逆向的魅力,这也就说明我们确实找到了明文封包的位置了。

我们不如发散一下思维,如果我们调用这个Call是不是就可以直接发送明文内容实现功能了,而不必再费力寻找和分析加密函数了?

的确,从实现功能的角度讲,只要分析透了这个函数是能够实现所有的功能,但是这样还是过了游戏代码 并非“完美”的,我们从学习角度出发接着分析。

找加密Call:

(一)理论

加密call在哪?想想我们跳出线程发包代码段那,封包内容应该已经被加密了,因为下面的代码再走就是把封包内容复制到全局变量里面,然后被发包线程发送出去了。游戏不可能让发包线程里面有明文内容,不然我们是不是就不用幸苦跳出发包线程了,直接分析明文的封包内容 岂不美哉?

然后我们返回一层,在传入参数里面发现了明文包,哪说明加密函数就应该在 上层之下,跳出线程发包代码段之上。哪我们的范围就小太多了,我们可以在函数头部跟踪明文内容,加密call必然会需要这个参数!

(二)实战

我们来到函数头部追踪一下明文内容。


在头部发现一个堆栈的值,直接赋值给寄存器。一般来说这种[esp+0xXX]要么是局部变量,要么是函数调用者传入的参数。而这里是函数头部,上面没有对[esp+0x14]初始化的代码,这说明它不是一个局部变量而是一个被传入的参数。

原因很简单,因为局部变量的空间都是在堆栈划分的,而堆栈被反复使用里面有非常多的垃圾数据,所有如果局部变量不初始化谁也不知道它的值是啥。

当然如果看见[esp+0xXX]而自己没有啥把握的话,最好去堆栈中观察确认一下,这是最稳妥的。

那么现在edi是参数首地址了,我们再往下看。[edi+0x4]赋值给了ebp,还记得我们上面寻找明文封包的分析么,这个参数是一个结构体,他的第二个成员就是封包内容首地址。[edi]是第一个成员,[edi+0x4]是第二个成员,那么现在ebp是明文封包内容首地址了。

再往下看,ebp和[edi+0x8]做比较,[edi+0x8]是第三个成员,这时候我想到了数组,存放数组首地址的地址+0x4通常是数组的尾地址,edi+0x8和edi+0x4 应该是这种情况。那么这个操作就很好理解了,应该是判断数组的长度…或者说里面有没有东西。

接着分析,盯住ebp这个封包内容,看有没有那个Call把它作为参数。


最后发现在范围内只有这个call把封包内容当作参数传入了,基本上可以断定它就是加密Call了。

很明显它有4个push的参数,前两个都是来源[edi+0x4] 至于为什么传2个一模一样的我们不管他,而第3个push 往前面翻一翻可以发现来自于 [edi+0x8]-[edi+0x4] 前面说过了这是尾地址减去首地址,应该是封包长度。

我相信你也看见了,它先把封包首地址+0x2 封包长度-0x2, 也就是说封包内容前2个字节是不加密的,来看看之前的封包截图。


我想前2个字节应该是代表这个封包前2字节之后的有效长度,之所以不加密应该是游戏服务器可以更好的读取和区分。

第4个push下断一看它是不断变化的,也不难猜 估计是密钥 加密就像是用密钥上锁,服务器拿到之前再用密钥打开。密钥应该是服务器隔一段时间更新下发一次,我们要想获得它得往上回溯得到它的偏移表达式和基地址,从里面读取就没问题了,基本功内容就不写出来了 这里没几个偏移就找到基地址了。

摸清楚了函数的参数,我们已经可以直接调用它来加密我们自己组装的明文包了,这样就可以用send()发给服务器了。同样的,这么做也并不“完美”,我们先进加密call里面去看看代码。


(代码太多就不放老长的图了,假装一下它是那种长图~)

我们发现这是一Call到底,里面没有调用任何其他的call,下断F8执行一遍之后也没有发现什么花里胡哨的跳转。那么我们另一条路就好走多了------偷功能! 顾名思义,我们要偷游戏加密call的功能。

怎么偷?其实也很简单,C++ 是支持内联汇编的,正好OD工具直接帮我们把汇编显示了出来,直接复制到我们的代码里面,只不过需要稍稍修改一下——JCC指令。

OD反汇编显示出来的JCC 跳转指令后面都是直接跟得立即数,内联汇编中并不支持这种写法,所以我们全部改成标签跳转就可以了,没什么技巧和技术 就是需要一点汇编基础和细心耐心。

如图:建议写一个裸函数,__asm 内联汇编把汇编复制进去就得勒。

(假装…一下…放个N长得图或者代码也没意思…)

这样就可以调用我们偷出来的函数加密了,由于是直接复制出来的,所以效果和原版一模一样。不过在我们用参数最少最简单的发包函数send()夹带私货之前,我们还需要打通最后一公里,先看看send()函数的定义。


第2 第 3 个参数也就是封包内容和长度我们有,第4个参数 常量0 我们不用管他,但是第一个参数需要解决一下,这代表东西要送到那里去。

好在三大发包函数第一个参数都是SOCKET套接字描述符,游戏里面使用的WSASend 函数当然是的,所以咱们可以直接Ctrl+G到WSASend里面下断点Ctrl+F9到调用处回溯第一个参数。


ECX看来就是我们要找的SOCKET了,都帮我们标注出来了,暖暖的很贴心~!随便往上翻一个偏移,来到了这,如图:


游戏为了获取SOCKET调用了这个函数,不过OD已经帮我们标注出来了,那我们肯定没必要进Call追数据了,OD认识的肯定是系统的API函数,我们直接上网查查这个函数是咋回事,然后学着游戏的样子调用它就ok了。


第二个参数游戏直接传入了常量0x15 所以我们不用关心它,第一个参数是一个窗口句柄…我们怎么才能方便的获取它呢?

既然它是一个窗口句柄那么可以使用 FindWindow() 系统API函数获得,但是这个函数需要一个窗口类名,或者窗口标题…嗯,这就不得不借用Visual Studio自带的工具Spy++了。


Spy++根据窗口句柄的数值查询到窗口的类名或者标题名,而窗口句柄的数值我们可以在游戏调用GetWindowLongW的地方下断复制出来(可惜下次打开游戏会变化,不然也不用这么麻烦了),然后扔到Spy++查询一下就OK了,如图:





这下就得到类名拉,只需要通过FindWindow()传入这个类名就可以获得我们想要的那个窗口句柄了~!

接下来只需要把我们上面讲的内容有机的结合一下,就可以封装成我们自己的明文发包函数拉~!

(三)代码示例

bool F发送封包(byte *p, DWORD ndIndex,char *szStr)
{byte *data = p;DWORD nd包长 = ndIndex; DWORD nd加密地址 = (DWORD)data + 0x2; DWORD nd加密长度 = nd包长 - 0x2; DWORD nd密钥地址 = NULL;__try{// [[[[[0x0F84BA4]]+0x4]+0x0C+0x8]]+0x54   密钥公 0019FB90   00000B4C  |Socket = 0xB4Cnd密钥地址 = *(DWORD*)0x0F84BA4;        nd密钥地址 = *(DWORD*)nd密钥地址;      nd密钥地址 = *(DWORD*)(nd密钥地址 + 0x4);     nd密钥地址 = *(DWORD*)(nd密钥地址 + 0x0C + 0x8);     nd密钥地址 = *(DWORD*)nd密钥地址 + 0x54;F加密call(nd密钥地址, nd加密长度, nd加密地址, nd加密地址);      HWND hWnd = (HWND)FindWindowA("Lapis Network Class", NULL);      DWORD ndData = GetWindowLongW(hWnd, -21);      DWORD Socket = *(DWORD*)(ndData + 0x38);      send(Socket, (const char*)data, nd包长, 0);}__except (1)  {       F输出调试信息("幻想神域 %s\n\r", szStr);        return false;   }
}

HOOK拦截输出明文包内容:

(一)理论

完事具备,只欠东风。我们现在已经封装好了自己明文发包函数,就差内容了。那我们怎么才能知道,什么功能要传什么样的封包呢?比如吃药…难道直接传个“吃药”?

呃…其实说来也简单,我们不是找到明文封包了么,吃药断下我们把内容复制下来,多复制一些案例,细细分析总能猜出来。但是不同的动作会产生不同的封包,比如我们要实习一个自动打怪挂机、跑主线等,那工作量可就大了,如果都OD下断点复制,未免太麻烦了,所以我们需要一个更好的方式——HOOK。

HOOK简单来说其实就是想方设法改变程序原来的执行流程,我们这里的目标很简单,让cpu在运行加密函数之前,先运行我们的函数 我们函数里面输出明文包的内容 然后跳转回原来的地方让程序继续正常执行。

具体操作也很简单,如图:

其实所谓的代码和指令也数字,图上可知最左边是代码在内存中的地址,中间是代码在内存中真正的模样(数字),右边是OD帮我们把数字翻译成人能看懂的汇编代码。

那么既然我们知道了代码的本质(数值)和它的地址,所以我们可以通过地址修改数值来修改代码,从而改变代码流程。

比如把红框中的2句汇编代码修改成:0xE8XXXXXXXX —— E8XXXXXXXX 对应Call指令上图也可以看到,后面的数值是算出来的 等于 要跳转地址 - (当前地址+0x5)。要跳转的地址肯定是我们的函数啊,当前地址上图就有 舒服~

当然我们的函数必须是一个裸函数,而且开头要使用 pushad 指令来保存现场环境,干完坏事后 使用 popad指令还原现场,最后一定要记得一字不差的写上被我们破坏的那2条指令,在使用 ret 指令跳转回去 不然程序肯定崩溃。(篇幅有限这里只是讲个大概,有兴趣具体学习百度一搜就有)

这样在我们的裸函数里面,pushad popad中间就可以写代码输出明文的封包内容。

(二)参考代码

//HOOK
void HXSYDialog::OnBnClickedButton3()
{// TODO: 在此添加控件通知处理程序代码/*00B92C82    8B46 08         mov eax, dword ptr ds : [esi + 0x8]00B92C85    2BC1            sub eax, ecx00B92C87    83C0 FE         add eax, -0x2*/DWORD ndHOOKAddress = 0x00B92C82; DWORD ndHOOK函数指针 = (DWORD)FHOOK明文发包;   DWORD ndHOOK跳转值 = ndHOOK函数指针 - ndHOOKAddress - 5;DWORD old = 0;//改变内存页属性VirtualProtect((PVOID)ndHOOKAddress, 0x30, PAGE_EXECUTE_READWRITE, &old);//修改代码*(byte*)ndHOOKAddress = 0xE8; //Call*(DWORD*)(ndHOOKAddress + 1) = ndHOOK跳转值;//还原内存页属性VirtualProtect((PVOID)ndHOOKAddress, 0x30, old, &old);
}
//还原HOOK,一样的道理修改回去
void HXSYDialog::OnBnClickedButton4()
{// TODO: 在此添加控件通知处理程序代码/*00B92C82    8B46 08         mov eax, dword ptr ds : [esi + 0x8]  00B92C85    2BC1            sub eax, ecx    00B92C87    83C0 FE         add eax, -0x2*/DWORD ndHOOKAddress = 0x00B92C82;DWORD old = 0;    VirtualProtect((PVOID)ndHOOKAddress, 0x30, PAGE_EXECUTE_READWRITE, &old);   *(byte*)ndHOOKAddress = 0x8B;  *(DWORD*)(ndHOOKAddress + 1) = 0xC12B0846;    VirtualProtect((PVOID)ndHOOKAddress, 0x30, old, &old);
}//我们的函数DWORD
g_nd包长 = 0;
DWORD g_nd包Address = 0;
DWORD g_ndPid = NULL;
HANDLE g_hProcess = 0;
byte *g_byP = nullptr;
char g_szObj[0x1000];
char g_szStr[0x1000];
void __declspec(naked) FHOOK明文发包(){__asm    {       //保存寄存器 提升堆栈        pushad      //得到包地址 包长      mov eax, dword ptr[esi + 4]        mov g_nd包Address, eax       mov ecx, dword ptr[esi + 8]        sub ecx, eax        mov g_nd包长, ecx }//提升权限 F提升权限(TRUE);    //获得进程ID    GetWindowThreadProcessId(F获取游戏主窗口句柄(), &g_ndPid);//获得进程ID //打开进程    g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, g_ndPid);//打开进程 g_byP = new byte[g_nd包长]; //读出包内容 ReadProcessMemory(g_hProcess, (LPCVOID)g_nd包Address, g_byP, g_nd包长, 0);// p 封包字节集   *(WORD*)g_byP = g_nd包长 - 2;for (int i = 0; i < (int)g_nd包长; i++) {       sprintf_s(g_szObj, "%02X", g_byP[i]);     strcat_s(g_szStr, g_szObj); }//自己封装 支持多线程的printf()  F输出调试信息("幻想神域:明文发包  包长:%x   包内容:%s\r\n", g_nd包长, g_szStr);    //清空缓冲区 sprintf_s(g_szStr, "%s", "");   delete[] g_byP;__asm    {       popad       //还原两句 被HOOK的游戏代码       mov eax, dword ptr ds : [esi + 0x8]        sub eax, ecx        retn    }}
//自己封装 支持多线程的printf()
void F输出调试信息(char * pszFormat, ...)
{#ifdef _DEBUG   char szbufFormat[0x1000];   char szbufFormat_Game[0x1100] = "";  va_list argList;    va_start(argList, pszFormat);   vsprintf_s(szbufFormat, pszFormat, argList);    strcat_s(szbufFormat_Game, szbufFormat);    OutputDebugStringA(szbufFormat_Game);   va_end(argList);
#endif
}

(三) 实战

我们上面的HOOK代码会把封包内容输出出来,但是由于是DLL注入形式的代码,所以我们并没有控制台窗口,故不能直接看到输出。这时候我们就需要借助一个输出捕捉工具了——Dbgview:


只需要输入关键字,就可以捕捉所有进程带关键子的输出,非常好用。


我们试着嗑下如图位置的药品,看看会游戏会发送什么样的封包。


包长:16 包内容:14004F00000000000100000000000000000000000000
包长:16 包内容:14004F00000000000A00000000000000000000000000

很明显,”1400”是封包2字节后的有效长度 我们之前已经分析过了。”4F”应该是代表吃药这个动作,那么”1” 和 “A”根据药品的位置来看 应该就是药品在背包表格中的下标,其他都为0我们可以暂时无视它们。

基本摸清了吃药封包的结构,我们可以尝试封装一个通过药品下标吃药的函数了:

void F吃药Call(int i)
{   byte Data[0x16] = { 0x14,00,0x4F,00,00,00,00,0x00,00,00,00,00,00,00,00,00,00,00,00,00,00,00};  *(WORD*)(Data + 0x8) = i; F发送封包(Data, 0x16, "");
}

调用一下试试:


Nice~! 封包吃药成功~!(使用药水这几个字可不是我P上去的啊,游戏嗑药就这特性)

麻雀虽小,五脏俱全 ; 经历千辛万苦我们终于达成了目标!虽然只有一个简单的嗑药功能,但是这却是一个货真价实的封包辅Z!!!

总结:

封包辅Z也并不多神秘和神奇,只是省去了中间的过程,直接联系服务器。

优点: 不走游戏代码,相当于避开了所有的本地检测,极其稳定。
缺点: 需要做很多前期工作,实现麻烦、分析封包费时费力。

屈尊调用游戏功能函数“内存辅Z”研究速度快,认真逆向分析封包内容“封包辅Z”快乐而稳定!


用到的逆向工具、私服游戏、完整源码geihub链接 https://github.com/Li-lab-dev/-

揭秘封包辅助外G:封包外G初体验(四)相关推荐

  1. 网页游戏封包辅助技术

    易语言网页游戏封包辅助技术 链接: https://pan.baidu.com/s/1VUCZwllr62O0ZNlFDEOJ2A 提取码: niek

  2. mysql外键_MySQL外键约束(FOREIGN KEY)

    MySQL 外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用.对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表). 外键用 ...

  3. 常用G.657与G.652光纤的抗弯曲性能差距有多大?

    1 引言 G.657光纤又被称为弯曲损耗不敏感光纤,FTTH入户的那根比普通电话线还细的光缆里面用的就是G.657光纤.没有G.657光纤,也就没有FTTH的大规模应用.G.657光纤分为多个子类,当 ...

  4. MPB:西湖大学鞠峰组微生物群落胞内胞外吸附胞外游离水环境DNA的分离提取

    为进一步提高<微生物组实验手册>稿件质量,本项目新增大众评审环节.文章在通过同行评审后,采用公众号推送方式分享全文,任何人均可在线提交修改意见.公众号格式显示略有问题,建议电脑端点击文末阅 ...

  5. Opencv外参估计cvFindExtrinsicCameraParams2原理解析(四)

    背景介绍 在之前的博文中,已经在外参估计函数cvFindExtrinsicCameraParams2分别进行了单应性方法.DLT方法,单应性矩阵进一步估计旋转平移量做了原理解析.本文对opencv在估 ...

  6. 佛祖保佑 永无BUG ; 心外无法 法外无心

    登录linux命令行后出现的图形 复制图形代码到相应的文件中保存,重新登录即可出现. Usage: For Ubuntu: 12.04: Just copy the content from Budd ...

  7. 关于正则表达式 g,m 参数的总结,为了回答“正则表达式(/[^0-9]/g,'')中的/g是什么意思?”...

    为了解答"正则表达式(/[^0-9]/g,'')中的"/g"是什么意思?"这个问题,也为了能够便于大家对正则表达式有一个更为综合和深刻的认识,我将一些关键点和容 ...

  8. [转]关于正则表达式 g,m 参数的总结,为了回答“正则表达式(/[^0-9]/g,'')中的/g是什么意思?”...

    为了解答"正则表达式(/[^0-9]/g,'')中的"/g"是什么意思?"这个问题,也为了能够便于大家对正则表达式有一个更为综合和深刻的认识,我将一些关键点和容 ...

  9. iptables 状态策略 允许内网连接外网 拒绝外网主动连入内网 _ 笔记

    4种状态 new established related invalid NEW ( a连接b 在b没有回复前 都被称为NEW包) ESTABLISHED ( a和b 连接成功 只有一个连接时 称为E ...

  10. templet 显示字段外键对应名_主外键和外键约束

    主外键和外键约束 主键 主键(primary key):一列(或一组列),其值能够唯一区分表中每个行 . 外键 外键(foreign key) 外键为某个表中的一列,它包含另一个表的主键值,定义了两个 ...

最新文章

  1. ant导入Zookeeper到Eclipse错误path contains invalid character
  2. Action访问Servlet API的对象
  3. c++类指针赋值表达式必须是可修改的左值_C++进阶教程系列:全面理解C++中的类...
  4. nodjs npm 报错:Segmentation fault: 11
  5. zabbix server监控项报警提示:“Zabbix discoverer processes 75% busy”
  6. cisco路由器 三层交换机简单环境配置实例(图)
  7. java基础27 单例集合Collection及其常用方法
  8. Excel数据透视表中的值计算
  9. Docker备份镜像和重新载入镜像
  10. MongoDB数据同步工具mongosync
  11. 南京大学计算机专业复试面试,南京大学计算机CS专业复试超全PPT及真题、面试内部资料(离散编译上机面试)...
  12. Java并发编程面试题合集
  13. Burst(突发)信号
  14. 一个输入手机号获取验证码的页面,包含哪些测试流程?
  15. 【微信小程序开发】缓存Storage的存入与获取
  16. UUIDUtil获取八位UUID
  17. Android 透明度数值对应表
  18. 红米4a android 9 速度,雷军感叹科技进步速度太快!Redmi 9入门机性能已经相当于骁龙835...
  19. 实战PyQt5: 143-QChart图表之堆积柱状图
  20. N个实用的css代码

热门文章

  1. 有了这5个免费下载音乐的wangzhan,从此告别各种VIP
  2. javasript | 鼠标经过表格行变色
  3. 数学建模——摘要写作
  4. blast2go本地安装,一个防火墙设置引发的血案
  5. 泛泰 A870 4.1.2 刷第三方专用Recovery合集
  6. 科学计算机上lg怎么打,科学计算器对数
  7. 申请计算机助理的英语作文,助理面试英文自我介绍范文(通用5篇)
  8. 2021超级热门引流红包裂变微信分享朋友圈广告游戏源码
  9. QT设计风速仪上位机实例(串口读写数据处理数据下载)
  10. 生理学知识点总结--biologic