PC微信逆向之发送消息

  • 写在前面
  • 工具
  • 定位CALL地址
  • 调用
  • 生成DLL
  • 注入与外部调用
  • 注入部分代码
  • 写在后面

写在前面

最近在搞微信的发送消息CALL,跟着网上的教程,一步一步走,很容易定位到CALL的地址,在适当的地方用OD断下,修改压入的参数内容,消息内容或接收人成功改变,但在使用C++调用的时候,因为不懂汇编指令,所以踩了一些坑。

工具

微信 3.5.0.46
Windows10 Pro
OllyICE 1.10
Cheat Engine 7.0
Visual Studio 2019

定位CALL地址

这部分感觉自己讲不太明白,而且网上有很多现成的教程,找这个CALL的思路还是比较简单的,推荐阅读下面这篇文章:
CSDN:PC微信逆向:发送与接收消息的分析与代码实现
下面是我定位到的内容:

787D42EE    8D46 38         lea     eax, dword ptr [esi+38]          ; 取at结构体
787D42F1    6A 01           push    1                                ; 0x1
787D42F3    50              push    eax                              ; 群消息at好友,非at消息为0
787D42F4    57              push    edi                              ; 消息内容,[edi]
787D42F5    8D95 7CFFFFFF   lea     edx, dword ptr [ebp-84]          ; 接收人,[edx]
787D42FB    8D8D 58FCFFFF   lea     ecx, dword ptr [ebp-3A8]         ; 缓冲区,据说是类本身
787D4301    E8 7A793300     call    78B0BC80                         ; 发送消息CALL
787D4306    83C4 0C         add     esp, 0C                          ; 平衡堆栈

CALL的偏移:0x78B0BC80 - 0x78670000 = 0x49BC80
0x78670000是WeChatWin.dll的基地址,可以在OD中查看可执行模块获取

调用

拿到了汇编代码,下一步自然是调用,不过在这之前要搞清楚接收人和消息内容的结构,不然发送的东西CALL看不明白,微信可能就崩了。
消息内容结构:

0ABBFC1C  122B2CF8  UNICODE "123456"
0ABBFC20  00000006
0ABBFC24  00000006
0ABBFC28  00000000
0ABBFC2C  00000000

地址122B2CF8指向消息内容本身,所以结构体第一个元素是消息文本的指针,后面第一个6是消息文本的长度,第二个是消息最大长度,一般分配消息文本长度两倍大小,会多耗费一点内存,CALL执行完就回收了;再往后面就是0了,为了保证安全,可以在结构体末尾添加DWORD类型占位字符。
接收人结构:

012FE504  12290278  UNICODE "filehelper"
012FE508  0000000A
012FE50C  0000000A
012FE510  00000000
012FE514  00000000

可以看到该结构体跟消息内容基本一致,都是字符串地址加长度,在OD中还可以看到接收人结构体后面跟了一个消息内容结构体,似乎没什么用。
最终结构体应该是这个样子:

struct WxString
{// 存字符串wchar_t* buffer;// 存字符串长度DWORD length;// 字符串最大长度DWORD maxLength;// 补充两个占位符DWORD fill1;DWORD fill2;
};

因为是调用已有的函数,不需要加Hook,直接使用内联汇编调用就可以了:

void SendWxMessage(wchar_t* wsWxId,wchar_t* wsTextMsg) {// 1、构造参数// 构造接收者结构WxString wxWxid = { 0 };wxWxid.buffer = wsWxId;wxWxid.length = wcslen(wsWxId);// OD显示与字符串长度一致,但可以给大一点wxWxid.maxLength= wcslen(wsWxId) * 2;// 构造消息结构WxString wxTextMsg = { 0 };wxTextMsg.buffer = wsTextMsg;wxTextMsg.length = wcslen(wsTextMsg);// OD显示与字符串长度一致,但可以给大一点wxTextMsg.maxLength = wcslen(wsTextMsg) * 2;// 取出消息地址wchar_t** pWxmsg = &wxTextMsg.buffer;// 构造空bufferchar buffer[0x3A8] = { 0 };WxString wxNull = { 0 };// 2、获取DLL模块基址// 模块基址DWORD dllBaseAddress = (DWORD)GetModuleHandle(L"WeChatWin.dll");// 3、计算函数的内存地址// 函数偏移,不同的微信版本会有变化DWORD callOffset = 0x49BC80;// 函数内存地址DWORD callAddress = dllBaseAddress + callOffset;__asm {lea eax, wxNull;// 参数5:1push 0x1;// 参数4:空结构push eax;// 参数3:发送的消息,传递消息内容的地址mov edi, pWxmsg;push edi;// 参数2:接收人,传递结构体地址,要特别注意lea和mov的区别lea edx, wxWxid;// 参数1:空bufferlea ecx, buffer;// 调用函数call callAddress;// 堆栈平衡,否则会崩溃add esp, 0xC;}
}

上面容易踩坑的地方是edi和edx两处,一个是mov赋值,一个是用lea取有效地址,mov传递字符串地址,lea取结构体地址,如果lea取字符串地址,就变成了字符串地址的地址,这里比较绕,其实就是指针那点破事儿,对于汇编也不太了解,如有错误欢迎指正。
可以把mov替换成lea,那么edi也直接取结构体地址就行了,已验证通过,但不知道会不会出什么问题。

生成DLL

写好了调用函数,就要编译成DLL,然后注入到微信的内存空间,测试一下了,这部分直接给完整的代码:
pch.h

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。#ifndef PCH_H
#define PCH_H// 添加要在此处预编译的标头
#include "framework.h"
#include <string>
#include <iostream>
#include <io.h>
#include <fcntl.h>#endif //PCH_H
#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT void SendWxMessage(wchar_t* wsWxId, wchar_t* wsTextMsg);
BOOL CreateConsole(void);
// 外部调用的入口,必须export,不然无法计算函数地址
DLLEXPORT void SendWxMessageAPI(LPVOID lpParameter);

pch.cpp

// pch.cpp: 与预编译标头对应的源文件#include "pch.h"using namespace std;
#define STRUCT_OFFSET(stru_name, element) (unsigned long)&((struct stru_name*)0)->element// 当使用预编译的头时,需要使用此源文件,编译才能成功。
struct WxString
{// 存字符串wchar_t* buffer;// 存字符串长度DWORD length;//字符串最大长度DWORD maxLength;// 补充两个占位符DWORD fill1;DWORD fill2;
};// 外部调用时使用
struct RemoteParam
{DWORD wxid;DWORD wxmsg;
};// 启动一个控制台窗口,以便调试
BOOL CreateConsole(void) {if (AllocConsole()) {AttachConsole(GetCurrentProcessId());FILE* retStream;freopen_s(&retStream, "CONOUT$", "w", stdout);if (!retStream) throw std::runtime_error("Stdout redirection failed.");freopen_s(&retStream, "CONOUT$", "w", stderr);if (!retStream) throw std::runtime_error("Stderr redirection failed.");return 0;}return 1;
}// 测试用的函数
void testMessage(DWORD edx_, DWORD edi_,int s) {wcout.imbue(locale("chs"));printf("s->%d,edi->0x%08X,edx->0x%08X\n",s,edi_,edx_);unsigned long offset = STRUCT_OFFSET(WxString, buffer);// edx是通过lea取的结构体地址,所以直接强制类型转换WxString* wxWxid = (WxString*)edx_;// edi是结构体成员buffer地址,要反推结构体首地址,再进行强制类型转换printf("wxTextMsg结构体首地址为: 0x%08X\n", edi_ - offset);WxString* wxTextMsg = (WxString*)(edi_ - offset);// edi实际上是wchar_t**变量wcout << L"接收人wxid:" << wxWxid->buffer << "," << L"消息内容:" << *(wchar_t**)edi_ << endl;wcout << wxWxid->buffer << ",";printf("%d,%d,%d,%d\n", wxWxid->length, wxWxid->maxLength, wxWxid->fill1, wxWxid->fill2);wcout << wxTextMsg->buffer << ",";printf("%d,%d,%d,%d\n", wxTextMsg->length, wxTextMsg->maxLength, wxTextMsg->fill1, wxTextMsg->fill2);
}void SendWxMessageAPI(LPVOID lpParameter) {RemoteParam* rp = (RemoteParam*)lpParameter;wchar_t* wsWxId = (WCHAR*)rp->wxid;wchar_t* wsTextMsg = (WCHAR*)rp->wxmsg;SendWxMessage(wsWxId, wsTextMsg);
}void SendWxMessage(wchar_t* wsWxId,wchar_t* wsTextMsg) {// 1、构造参数// 构造接收者结构WxString wxWxid = { 0 };wxWxid.buffer = wsWxId;wxWxid.length = wcslen(wsWxId);// OD显示与字符串长度一致,但可以给大一点wxWxid.maxLength= wcslen(wsWxId) * 2;// 构造消息结构WxString wxTextMsg = { 0 };wxTextMsg.buffer = wsTextMsg;wxTextMsg.length = wcslen(wsTextMsg);// OD显示与字符串长度一致,但可以给大一点wxTextMsg.maxLength = wcslen(wsTextMsg) * 2;//取出消息地址wchar_t** pWxmsg = &wxTextMsg.buffer;// 构造空bufferchar buffer[0x3A8] = { 0 };WxString wxNull = { 0 };// 2、获取DLL模块基址// 模块基址DWORD dllBaseAddress = (DWORD)GetModuleHandle(L"WeChatWin.dll");// 3、计算函数的内存地址// 函数偏移,不同的微信版本会有变化DWORD callOffset = 0x49BC80;// 函数内存地址DWORD callAddress = dllBaseAddress + callOffset;// printf("发消息CALL地址:0x%08X\n", callAddress);// 一段测试代码,参数是按从右到左的顺序压入的/*__asm {push 0x1;mov edi, pWxmsg;push edi;lea edx, wxWxid;push edx;call testMessage;add esp, 0xC;}*/// 4、编写调用函数的代码/*787D42EE    8D46 38         lea     eax, dword ptr [esi+38]          ; 取at结构体787D42F1    6A 01           push    1                                ; 0x1787D42F3    50              push    eax                              ; 群消息at好友,非at消息为0787D42F4    57              push    edi                              ; 消息内容,[edi]787D42F5    8D95 7CFFFFFF   lea     edx, dword ptr [ebp-84]          ; 接收人,[edx]787D42FB    8D8D 58FCFFFF   lea     ecx, dword ptr [ebp-3A8]         ; 缓冲区,据说是类本身787D4301    E8 7A793300     call    78B0BC80                         ; 发送消息CALL787D4306    83C4 0C         add     esp, 0C                          ; 平衡堆栈*/__asm {lea eax, wxNull;// 参数5:1push 0x1;// 参数4:空结构push eax;// 参数3:发送的消息,传递消息内容的地址mov edi, pWxmsg;push edi;// 参数2:接收人,传递结构体地址,要特别注意lea和mov的区别lea edx, wxWxid;// 参数1:空bufferlea ecx, buffer;// 调用函数call callAddress;// 堆栈平衡,否则会崩溃add esp, 0xC;}printf("over\n");
}

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{switch (ul_reason_for_call){case DLL_PROCESS_ATTACH:{// DLL注入后,会执行到此处// CreateConsole();wchar_t* wsWxId = (WCHAR*)L"filehelper";wchar_t* wsTextMsg = (WCHAR*)L"发送的消息";SendWxMessage(wsWxId, wsTextMsg);DWORD pfunc = (DWORD)SendWxMessageAPI;printf("发送消息的函数地址:0x%08X\n",pfunc);}break;case DLL_THREAD_ATTACH:case DLL_THREAD_DETACH:case DLL_PROCESS_DETACH: break;}return TRUE;
}

注入与外部调用

DLL注入的部分,网上也有很多教程,此处不做过多讲解,思路如下:

  1. OpenProcess开启远程进程
  2. VirtualAllocEx在进程内开辟内存空间
  3. WriteProcessMemory在指定内存写入DLL的绝对路径
  4. CreateRemoteThread创建一个远程线程,让目标进程调用LoadLibrary
  5. 适当的时候卸载DLL(参考2-4,先GetModuleHandle,再FreeLibrary)

关键说一下怎么在外部调用发送消息的接口,在编译的DLL中,导出了SendWxMessageAPI这个函数,其实也可以不导出,只要提前算好该函数相对DLL基地址的偏移即可,具体思路如下:

  1. DLL注入微信
  2. CreateRemoteThread调用GetModuleHandle获取DLL在微信的基地址
  3. 加上算好的偏移,得到SendWxMessageAPI的地址
  4. WriteProcessMemory将消息内容和接收人写入远程进程,得到两处地址
  5. 组装结构体,保存第四步的两处地址
  6. WriteProcessMemory将结构体写入远程进程,得到结构体地址
  7. CreateRemoteThread调用SendWxMessageAPI,参数是第6步得到的结构体地址
  8. SendWxMessageAPI解引用指针,再从目标地址获取消息内容地址和接收人地址
  9. SendWxMessageAPI调用SendWxMessage,完成消息发送

看起来比较复杂,可能有人会问为什么不直接调用SendWxMessage,我理解的是,CreateRemoteThread只能传递一个LPVOID类型的参数,所以要用结构体来保存所有的参数,但是结构体中不能有指针,否则把结构体写入远程进程的时候,只是写了一些冰冷的数字进去,访问的时候还会出现NULL指针错误。

注入部分代码

injert.h

#pragma once
#include <iostream>
#include "stdlib.h"
#include <tchar.h>
#include <Windows.h>
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
#include <atlconv.h>
#include <tchar.h>
#include <sys/stat.h>using namespace std;bool Inject(DWORD dwId, WCHAR* szPath);
bool isFileExists_stat(string& name);
string wstring2string(wstring wstr);

main.cpp

#include "injert.h"// DLL中有一个同样的结构体
struct RemoteParam
{DWORD wxid;DWORD wxmsg;
};
// 参数1:申请的远程进程句柄,参数2:要调用的函数地址
void SendWxMessage(HANDLE hProcess, DWORD addrsend) {DWORD dwId = 0;DWORD dwWriteSize = 0;RemoteParam RemoteData;ZeroMemory(&RemoteData, sizeof(RemoteParam));LPVOID wxidaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);LPVOID wxmsgaddr = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);RemoteParam* paramAndFunc = (RemoteParam*)::VirtualAllocEx(hProcess, 0, sizeof(RemoteData), MEM_COMMIT, PAGE_READWRITE);if (!wxidaddr || !wxmsgaddr || !paramAndFunc || !addrsend)return;DWORD dwTId = 0;// wxid和wxmsg写入远程线程WCHAR* wxid = (WCHAR*)L"filehelper";if (wxidaddr)WriteProcessMemory(hProcess, wxidaddr, wxid, wcslen(wxid) * 2 + 2, &dwWriteSize);WCHAR* wxmsg = (WCHAR*)L"发送的消息";if (wxmsgaddr)WriteProcessMemory(hProcess, wxmsgaddr, wxmsg, wcslen(wxmsg) * 2 + 2, &dwWriteSize);// 结构体存储wxid和wxmsg的地址RemoteData.wxid = (DWORD)wxidaddr;RemoteData.wxmsg = (DWORD)wxmsgaddr;// 远程线程写入结构体if (paramAndFunc != NULL)printf("wxid地址:0x%08X,wxmsg地址:0x%08X\n", (DWORD)wxidaddr, (DWORD)wxmsgaddr);if (paramAndFunc) {if (!::WriteProcessMemory(hProcess, paramAndFunc, &RemoteData, sizeof(RemoteData), &dwTId)){printf("写入paramAndFunc失败!error:0x%08X\n", GetLastError());}else {printf("写入paramAndFunc成功!要写入的size:%d,实际写入的size:%d\n", sizeof(RemoteData), dwTId);}}else {printf("申请内存空间paramAndFunc失败!error:0x%08X\n", GetLastError());}printf("写入的结构体首地址:0x%08X\n", (DWORD)paramAndFunc);// 在此处打断点,然后用CE验证指针中的数据是否正确// system("pause");HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)addrsend, (LPVOID)paramAndFunc, 0, &dwId);if (hThread) {WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);}else {printf("调用消息发送函数失败!\n");}// 释放内存,释放后可以再次查看CEVirtualFreeEx(hProcess, wxidaddr, 0, MEM_RELEASE);VirtualFreeEx(hProcess, wxmsgaddr, 0, MEM_RELEASE);VirtualFreeEx(hProcess, paramAndFunc, 0, MEM_RELEASE);
}bool Inject(DWORD dwId, WCHAR* szPath)//参数1:目标进程PID  参数2:DLL路径
{//一、在目标进程中申请一个空间/*【1.1 获取目标进程句柄】*/HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);printf("目标窗口的句柄为:%d\n", (int)hProcess);/*【1.2 在目标进程的内存里开辟空间】*/LPVOID pRemoteAddress = VirtualAllocEx(hProcess,NULL,1,MEM_COMMIT,PAGE_READWRITE);//二、 把dll的路径写入到目标进程的内存空间中DWORD dwWriteSize = 0;/*【写一段数据到刚才给指定进程所开辟的内存空间里】*/if (pRemoteAddress){WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath) * 2 + 2, &dwWriteSize);}else {printf("写入失败!\n");return 1;}//三、 创建一个远程线程,让目标进程调用LoadLibraryHANDLE hThread = CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibrary,pRemoteAddress,NULL,NULL);if (hThread) {WaitForSingleObject(hThread, -1); //当句柄所指的线程有信号的时候,才会返回}else {printf("调用失败!\n");return 1;}CloseHandle(hThread);WCHAR* dllname = (WCHAR*)L"DllSendMessage.dll";WriteProcessMemory(hProcess, pRemoteAddress, dllname, wcslen(dllname) * 2 + 2, &dwWriteSize);// 调用GetModuleHandleWDWORD dwHandle, dwID;LPVOID pFunc = GetModuleHandleW;hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pRemoteAddress, 0, &dwID);if (hThread) {WaitForSingleObject(hThread, INFINITE);// 获取远程线程的返回值GetExitCodeThread(hThread, &dwHandle);}else {printf("GetModuleHandleW调用失败!\n");return 1;}CloseHandle(hThread);// 获取发送消息接口函数地址HMODULE hd = LoadLibrary(szPath);DWORD addrsend = 0;// 计算对应函数的地址,已经算好偏移就不需要加载DLL进本进程了if (hd) {DWORD localsendaddr = (DWORD)GetProcAddress(hd, "SendWxMessageAPI");printf("模块基址:0x%08X,函数地址:0x%08X,偏移:0x%08X\n", (DWORD)hd, localsendaddr, localsendaddr - (DWORD)hd);addrsend = dwHandle + localsendaddr - (DWORD)hd;printf("目标进程发送消息函数地址:0x%08X\n", addrsend);// 当前进程卸载DLLFreeLibrary(hd);}SendWxMessage(hProcess, addrsend);// 四、 【释放申请的虚拟内存空间】VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE);// 释放console窗口,不然关闭console的同时微信也会退出pFunc = FreeConsole;hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, NULL, 0, &dwID);if (hThread) {WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);}else {printf("FreeConsole调用失败!\n");return 1;}// 使目标进程调用FreeLibrary,卸载DLLpFunc = FreeLibrary;hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, (LPVOID)dwHandle, 0, &dwID);if (hThread) {WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);}else {printf("FreeLibrary调用失败!\n");return 1;}CloseHandle(hProcess);return 0;
}bool isFileExists_stat(string& name) {struct stat buffer;return (stat(name.c_str(), &buffer) == 0);
}string wstring2string(wstring wstr)
{std::string result;//获取缓冲区大小,并申请空间,缓冲区大小事按字节计算的  int len = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), NULL, 0, NULL, NULL);char* buffer = new char[len + 1];//宽字节编码转换成多字节编码  WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.size(), buffer, len, NULL, NULL);buffer[len] = '\0';//删除缓冲区并返回值  result.append(buffer);delete[] buffer;return result;
}int _tmain(int nargv,WCHAR* argvs[])
{wchar_t* wStr = (WCHAR*)L"";if (nargv == 1) {return 0;}else {wStr = argvs[1];}string name = wstring2string((wstring)wStr);DWORD dwId = 0;if (!isFileExists_stat(name)) {wstring info = L"注入失败!请检查DLL路径!";MessageBox(NULL, info.c_str(), _T("警告"), MB_ICONWARNING);return 0;}// 参数1:NULL// 参数2:目标窗口的标题// 返回值:目标窗口的句柄HWND hCalc = FindWindow(NULL, L"微信");printf("目标窗口的句柄为:%d\n", (int)hCalc);DWORD dwPid = 0;//参数1:目标进程的窗口句柄//参数2:把目标进程的PID存放进去DWORD dwRub = GetWindowThreadProcessId(hCalc, &dwPid);printf("目标窗口的进程PID为:%d\n", dwPid);//参数1:目标进程的PID//参数2:想要注入DLL的路径Inject(processID, wStr);return 0;
}

写在后面

感觉越来越有判头了。

PC微信逆向之发送消息相关推荐

  1. PC微信逆向:发送与接收消息的分析与代码实现

    文章目录 定位微信的消息接收函数 定位消息接收函数的相关思路 定位消息内容的地址 分析接收消息函数 好友消息 群消息 总结 代码实现 定位微信的消息发送函数 定位消息发送函数的相关思路 过滤当前聊天窗 ...

  2. python企业微信回调_python 微信企业号-回调模式接收微信端客户端发送消息并被动返回消息...

    说明:此代码用于接收手机微信端发送的消息 #-*- coding:utf-8 -*- from flask import Flask,request from WXBizMsgCrypt import ...

  3. MeterSphere实现“机器人定时在企业微信群中发送消息”功能

    背景 之前有过用PowerShell实现"机器人定时在企业微信群中发送消息"功能,并有输出相关教程. 但发现有一些问题:比如电脑关机了导致任务不能如期启动,于是在摸索中找到可替代P ...

  4. PowerShell实现“机器人定时在企业微信群中发送消息”功能(下)

    本章实现 由于篇幅较多,会拆分为上.下两部分来写. 本章实现: 06.机器人定时在企业微信群中发送消息功能 上章实现 01.如何在企业微信中添加群机器人 02.简单用机器人发送一条消息(postman ...

  5. PowerShell实现“机器人定时在企业微信群中发送消息”功能(上)

    本章实现 由于篇幅较多,会拆分为上.下两部分来写. 本章实现: 01.如何在企业微信中添加群机器人 02.简单用机器人发送一条消息(postman实现) 03.powershell打开 04.简单用机 ...

  6. python训练营微信广告发送机_python实现给微信公众号发送消息的方法

    本文实例讲述了python实现给微信公众号发送消息的方法.分享给大家供大家参考,具体如下: 现在通过发微信公众号信息来做消息通知和告警已经很普遍了.最常见的就是运维通过zabbix调用shell脚本给 ...

  7. 微信群发频繁发送消息,请稍后再试?

    微信群发消息是我们日常工作和生活中的常见操作,无论是为了推广产品,还是为了组织活动,或者是朋友之间的聊天,都需要经常发送消息.然而,当你频繁地群发消息时,你可能会收到微信提示"频繁发送消息, ...

  8. 微信公众号怎么推送消息_微信公众号发送消息

    A.模板消息发送 模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息. 备注 ...

  9. java实现如何定时给微信群中发送消息

    大家好,我是雄雄. 前言 前几天,发了一个系列这样的文章,如下所示: java实现每日给女友微信发送早安等微信信息 java实现给微信群中定时推送消息 如何将每日新闻添加到自己博客中,发送到微信群中 ...

  10. python3通过itchat登录微信给好友发送消息

    环境:windows7,python3.7 安装itchat,requests 金山词霸开放平台API:http://open.iciba.com/dsapi 如果遇到登录微信时出现错误告警,请参照: ...

最新文章

  1. Ensemble-BioMart:得到基因注释信息(有参考基因组的物种)
  2. python seaborn 热图 值对应颜色_Python数据分析之Seaborn(热图绘制)
  3. php artisan 更新,Laravel 5:PHP Artisan迁移:刷新
  4. restfull api 接口 规范
  5. java-不用辅助变量,两变量直接交换
  6. Linux性能监控与分析之--- CPU
  7. 使用Bigemap下载地图生成GST(Mapinfo格式)地图包
  8. 【八方位云台控制器】
  9. cisco1841(cisco1841路由器设置步骤)
  10. ionic刷新html页面,Ionic实现页面下拉刷新(ion-refresher)功能代码
  11. html5 音乐播放进度条,js实现音乐播放控制条
  12. winform直接控制云台_智云和快手发布重磅功能,手机云台升级,帮8成网民拍大片...
  13. poj3046 Ant Counting
  14. 【vue自定义指令】
  15. 代数方程模型——量纲分析
  16. 开源大型网游服务器架构项目
  17. 中国传媒大学计算机与网络安全,黄玮 - 中国传媒大学 - 计算机与网络空间安全学院...
  18. sap 无料号采购 直接物料组消耗到成本中心
  19. registration.setOrder
  20. 考完驾照-激动得不得了。

热门文章

  1. 2000元台式电脑组装配置单2021 2000元组装电脑配置清单
  2. 如何构建一个低成本、高效、准确的身份认证体系
  3. 【MySQL 数据库】聚合查询和联合查询操作
  4. 计算机网络之数据传输方式:电路交换、报文交换和分组交换
  5. 【HAVENT原创】使用 Spring Boot 的 AOP 全局记录执行时间日志
  6. 告别枯燥,ppt背景音乐怎么设置?
  7. 固态硬盘接口类型介绍
  8. 5.信道带宽、信道容量、香农公式
  9. OpenGL Assimp的骨骼动画
  10. linux两台设备网桥配置,Linux网桥配置