(1条消息)解决:Uncaught TypeError: $ is not a function_JavaScript_weixin_41551266的博客-CSDN博客

mikel阅读(627)

来源: (1条消息)解决:Uncaught TypeError: $ is not a function_JavaScript_weixin_41551266的博客-CSDN博客

本来好好的,突然就出现的错误,不过这并不是什么难解决的错误;

我的问题是:在js文件里我定义了一个 var $;变量,只要把这个去掉就没问题了。

顺便总结一下:出现这种错误的解决方法:

1,先看看你的jq文件是否已经先导入了

一般来说,你的js文件需要位于最后

2.就是在js文件里找一下有没有 var $; 【呜呜我傻】

看完一些博客,大概明白,WordPress会先运行自己的脚本,然后释放$,这样就不会与其他库冲突。

所以一旦你定义了$;他就会默认使用你对$的定义,而不会管JQ的定义;

3. 下面是一些防止冲突的方法

JQuery(function ($) {
直接使用JQuery就不会又冲突问题,再在JQuery使用$,利用闭包函数的作用域规避冲突

 

参考链接:https://crunchify.com/how-to-fix-wordpress-uncaught-typeerror-is-not-a-function-jQuery-error/

https://stackoverflow.com/questions/12343714/typeerror-is-not-a-function-when-calling-jQuery-function
————————————————
版权声明:本文为CSDN博主「weixin_41551266」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41551266/article/details/90577776

Redis缓存设计与性能优化 - 白露非霜 - 博客园

mikel阅读(943)

来源: Redis缓存设计与性能优化 – 白露非霜 – 博客园

Redis我们一般是用作缓存,扛并发;或者用于某些特定的业务场景,比如前面说到redis各种数据类型的使用场景以及redis的哨兵和集群模式。

这里主要整理了下redis用作缓存,存在的一些问题,以及改善方案。

 

简单的流程就像这个样子,一般请先到缓存区获取,如果缓存没有再到后端的数据库去查询。

1.缓存穿透

缓存穿透是指,是指查询一个根本不存在数据,这样缓存层里面没有,就会去访问后面的存储层了。如果有大量的这种恶意请求过来,都打向后面的存储层。显然我们的存储层是扛不住这样的压力。这样缓存就失去了保护后面存储的意义了。

解决方案:

1.缓存空对象

对于缓存穿透,可以采用缓存空对象,第一次进来缓存和DB都没有,就存个空对象到缓存里面。但是如果大批量的恶意请求过来,这样做就会导致缓存的key暴增,显然不是一个很好的方案。

2.布隆过滤器

对于不存在的数据布隆过滤器一般都能够过滤掉,不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;但是它说不存在时,那就肯定不存在。布隆过滤器是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是能够把元素的hash值算得比较均匀。向布隆过滤器中添加 key 时,会使用多个hash 函数对key进行hash分别算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个hash函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就 完成了 add 操作。

向布隆过滤器询问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都为1,只要有一个位为0,那么说明布隆过滤器中这个key肯定不存在。但是都是 1,这并不能说明这个key就一定存在,只是极有可能存在,因为这些位被置为1可能是因为其它的key存在所致。

 

guvua包布隆过滤器的使用,导包

 

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
        </dependency>

 

伪代码:

复制代码
    public void bloomFilterTest() {
        BloomFilter<CharSequence> bloomFilter = BloomFilter.create(
                Funnels.stringFunnel(Charset.forName("UTF-8")),
                1000, //期望存入的数据个数
                0.001);//误差率
        //添加到布隆过滤器
        String[] keys = new String[1000];
        for (String key: keys) {
            bloomFilter.put(key);
        }

        String key = "key";
        boolean exist = bloomFilter.mightContain(key);
        if (!exist) {
            return;
        }
        //todo 存在才去缓存获取
    }
复制代码

可以看到这个类里面有很多的hash算法:com.google.common.hash.Hashing

redisson也有布隆过滤器的实现。

 

 

2.缓存失效

由于大批量的key同时失效,导致,大量的请求同时打向数据库,造成数据库压力过大,甚至直接挂掉。我们在批量写入缓存的时候,设置超时时间,可以是一个固定时间+随机时间方式来生成,这样就可以错开失效时间。

3.缓存雪崩

缓存雪崩是指缓存层挂掉之后,所有请求都打向数据库,数据库扛不住,也可能挂掉,就导致对应的服务也挂掉,也会影响上游的调用服务。这样的级联问题。就像雪崩最开始一小片,然后越来越大,导致整个服务崩溃。

解决方案:

1.保证缓存层的高可用性,比如redis哨兵或者redis集群。

2.各依赖服务之间做限流,熔断,降级等,比如Hystri,阿里的sentinel

4.缓存一致性

引入缓存之后,随之而来的问题就是当DB数据更新时,缓存中的数据就会与db数据不一致。所以数据修改时是先更新缓存还是先更新DB?

如果先更新缓存,然后更新DB失败,那么下一个请求过来读取的缓存数据不是最新的。而我们实际上最终数据肯定都是以DB为准的。

先更新db 在更新缓存,这是在更新DB的时候来的请求读取的数据也是不是最新的

淘汰缓存——更新DB——重新刷进缓存,在更新db是来的请求在缓存没有数据,就会去请求DB,如果并发 可能操作多各请求去写DB,那么就需要加锁了

加锁——淘汰缓存——更新DB——重新刷进缓存,这样相对而言就比较保险了

5.bigkey问题

Bigkey是什么?在redis中,一个字符串最大512MB;hash,list,set,zset可以存储2^31 – 1 个元素。

一般来说字符串超过10kb,其他的几种元素个数不要超过5000个。

可以使用src/redis-cli –bigkeys 来查看bigkey,我这里设置了一个30多K的字符串,看下扫描结果,扫除了一个字符串类型的bigkey,4084字节。

 

 

Bigkey有哪些危害。一是删除时阻塞其他请求,比如一个bigkey,平时都没什么,但是设置了过期时间,到期了删除时,可能就会阻塞其他请求,4.0之后可以开启lazyfree-lazy- expire yes来异步删除;二是造成网络拥堵,比如一个key数据量达到1MB,假设并发量1000,这个时候获取它就会产生1000MB的流量,千兆网卡,峰值的速率也才128MB/S,并不是扛不住并发,而是会占用大量网络带宽。

对于很大list,set这些,我们可以将数据拆分,生成一个系列的的key去存放数据。如果是redis集群这些key自然就可以分到不同的小主从上面去,如果是单机,那么可以自己实现一个路由算法,来如何获取这一系列key中的某一个。

6. 客户端使用

1.避免多个服务使用一个redis实例,如果实在有,可以看下将业务拆分,把这些公共数据服务化。

2.使用连接池,控制有效连接,同时也提高效率。连接池重要参数设置:

1 maxActive 资源池中最大连接数 默认值8

2 maxIdle 资源池允许最大空闲 的连接数 默认值8

3 minIdle 资源池确保最少空闲 的连接数 默认值0

4 blockWhenExhausted 当资源池用尽后,调用者是否要等待。只有当为true时,下面的maxWaitMillis才会生效,默认值true 建议使用默认值

5 maxWaitMillis 当资源池连接用尽后,调用者的最大等待时间(单位为毫秒) -1:表示永不超时 不建议使用默认值

6 testOnBorrow 向资源池借用连接时是否做连接有效性检测(ping),无效连接会被移除 默认值false 业务量很大时候建议 设置为false(多一次 ping的开销)。

7 testOnReturn 向资源池归还连接时是否做连接有效性检测(ping),无效连接会被移除 默认值false 业务量很大时候建议 设置为false(多一次 ping的开销)。

8 jmxEnabled 是否开启jmx监控,可用于监控 默认值true 建议开启,但应用本身也要开启

前面三个参数相对而言更重要,单独拎出来再说下:

最大连接数maxActive:

可以从业务希望的并发量,客户端执行时间,redis资源设置(应用个数(集群部署多少个实例) * maxActive <= maxclients(redis最大连接数,redis配置中设置的)),等因素考虑。

比如一次客户端执行时间2ms,那么一个连接的QPS就是500,业务期望的QPS是3000,那么理论上连接池大小3000/500=60个,实际上考虑其他影响,一般设置比理论值稍微大点。但这个值不是越大越好,一方面连接太多占用客户端和服务端资源,另一方面对    于Redis这种高 QPS的服务器,一个大命令的阻塞即使设置再大资源池仍然会无济于事。

最大空闲连接数maxIdle:

maxIdle实际上才是业务需要的最大连接数,空闲的连接造好放在那儿,进来一个请求就可以直接拿来用了。maxActive是为了给出总量,所以maxIdle不要设置过小,否则会有当空闲连接不够,就会创建新的连接,又会有新的开销,最佳就是maxActive =       maxIdle。这样就避免连接池伸缩带来的性能干扰。但是如果并发量不大或者maxActive设置过高,会导致不必要的连接资源浪费。一般推荐maxIdle可以设置为按上面的业务期望QPS计算出来的理论连接数,maxActive可以再放大一些。

最小空闲连接数minIdle:

至少保持多少空闲连接,在使用连接的过程中,如果连接数超过了minIdle,那么继续建立连接,如果超过了 maxIdle,当超过的连接执行完业务后会慢慢被移出连接池释放掉。

3.缓存预热

比如说上线一个抢购活动,肯定到点开始就会有很多人来请求了,这个时候就可以提前做数据的预热,既可以把连接池初始化好,也可以把数据放好。

SQLSERVER中的timestamp 和 C#中的byte[] 转换 - iDEAAM - 博客园

mikel阅读(1304)

来源: SQLSERVER中的timestamp 和 C#中的byte[] 转换 – iDEAAM – 博客园

项目中由于需求设计,数据库中需要一个timestamp时间戳类型的字段来作为区别数据添加和修改的标识。由于timestamp在SQL SERVER 2005数据库中,不可为空的timestamp类型在语义上等同于binary(8)类型,可为空的 timestamp类型在语义上等同于varbinary(8)类型,这将导致在C#程序中获取到的timestamp类型则变成了byte[]类型。所以如果我们需要从数据库中获取并使用这个时间戳的话就必需经过转换。

我们先建立一张测试表,语句如下:

CREATE TABLE [dbo].[tb_Ts](
 [id] [int] IDENTITY(1,1) NOT NULL,
 [TS] [timestamp] NULL,
  [nvarchar](50) NULL
) ON [PRIMARY]

表名为tb_Ts,只有三个字段id,TS和text。其中TS字段就是我们需要的时间戳字段

SQL Server 中timestamp类型的定义

首先看下timestamp在SQL Server 2005中的定义,该定义摘抄自SQL Server 2005联机丛书(具体详情点击此链接):

timestamp 公开数据库中自动生成的唯一二进制数字的数据类型。timestamp 通常用作给表行加版本戳的机制。 存储大小为 8 个字节。 timestamp 数据类型只是递增的数字,不保留日期或时间。 若要记录日期或时间,请使用 datetime 数据类型。

备注:

每个数据库都有一个计数器,当对数据库中包含 timestamp 列的表执行插入或更新操作时,该计数器值就会增加。 该计数器是数据库时间戳。 这可以跟踪数据库内的相对时间,而不是时钟相关联的实际时间。

一个表只能有一个 timestamp 列, 每次修改或插入包含 timestamp 列的行时,就会在 timestamp 列中插入增量数据库时间戳值。 这一属性使 timestamp 列不适合作为键使用,尤其是不能作为主键使用。

对数据行(row)的任何更新都会更改 timestamp 值,从而更改键值。 如果该列属于主键,那么旧的键值将无效,进而引用该旧值的外键也将不再有效。 如果该表在动态游标中引用,则所有更新均会更改游标中行的位置。 如果该列属于索引键,则对数据行的所有更新还将导致索引更新。

使用某一行中的 timestamp 列可以很容易地确定该行中的任何值自上次读取以后是否发生了更改。 如果对行进行了更改,就会更新该时间戳值。 如果没有对行进行更改,则该时间戳值将与以前读取该行时的时间戳值一致。 若要返回数据库的当前时间戳值,请使用 @@DBTS

Transact-SQL timestamp 数据类型不同于在 SQL-2003 标准中定义的 timestamp 数据类型。 SQL-2003 timestamp 数据类型等同于 Transact-SQL datetime 数据类型。

rowversion 的数据类型为 timestamp 数据类型的同义词,并具有数据类型同义词的行为。 在 DDL 语句,请尽量使用 rowversion 而不是 timestamp。 有关详细信息,请参阅 数据类型同义词 (Transact-SQL)

程序中获取出的timestamp

如何使用SQL语句插入timestamp字段值?

我们从上面的timestamp定义中知道了timestamp这个值一般都是数据库自动添加和修改的,相当于自动增长标识一样(而且执行update修改语句这个字段也会自动更新),所以一般这个字段我们只做查询操作。如果要更新这个字段则会提示这个错误信息:不能更新时间戳列。但是这个字段是可以手动添加的,不过也只能使用DEFALUT字段(default字段为SQL Service数据库的一个默认值),如果传入其他值则会提示错误信息:不能将显式值插入时间戳列。请对列列表使用 INSERT 来排除时间戳列,或将 DEFAULT 插入时间戳列。下面是添加timestamp的SQL语句:

INSERT INTO [tb_Ts]([TS]) VALUES(DEFAULT)

解决数据库中timestamp类型和C#中byte[]类型转换问题

在程序中我们发现,通过ADO.NET获取数据库中timestamp字段值到程序中,结果类型为byte[]。假设在数据库中timestamp的值为0x00000000000007D6,那么获取到.net程序中的值就不是这样了,一把来说会变成byte[]的数组类型。那么我们的解决方式有两种,第一种方式是直接在数据库中将timestamp进行转换,可以转换成十六进制字符串类型或者BIGINT的长整形,这也是我推荐的方法。还有一种是在.NET 程序中使用BitConverter方法进行转换。以下是两种方式的代码:

方法一(在SQL中转换):

SELECT TS
,CAST(TS AS VARBINARY(8)) AS 'timestamp转十六进制字符串' 
,CONVERT(BIGINT,TS) AS 'timestamp转bigint类型'
FROM tb_Ts

这样一来我们就可以获取到timestamp的十六进制字符串或者bigint,最终查询出来的结果如下图:timestamp转换成十六进制字符串或者bigint类型的结果

另外要说明的一点是,VARBINARY(8)对应的C# 类型是byte[],所以建议直接转换成bigint类型,否则在C#中还要调用下面的方法


方法二(在程序中转换,调用下面的方法即可):

/// <summary>
/// 将数据库中timespan转换成十六进制字符串
/// </summary>
/// <param name="objTs">从数据库中获取的timespan值</param>      
/// <returns>timespan十六进制字符串</returns>
public  string ConvertToTimeSpanString(object objTs)
{
byte[] btTsArray=objTs as byte[];
string strTimeSpan = "0x"+ BitConverter.ToString(btTsArray).Replace("-","");
return strTimeSpan;
}

PC微信逆向:使用HOOK拦截二维码_Python_鬼手的博客-CSDN博客

mikel阅读(2634)

源: PC微信逆向:使用HOOK拦截二维码_Python_鬼手的博客-CSDN博客

文章目录
微信版本
寻找微信二维码基址
PNG文件格式
使用CE过滤基址
使用OD确定二维码基址
验证二维码基址
寻找微信二维码内容的基址
微信二维码的存储内容
使用CE寻找二维码内容的基址
验证基址
定制微信登录二维码的可能性
使用hook截取二维码
最终效果
微信版本

寻找微信二维码基址
PNG文件格式
微信二维码在内存中存放形式是png格式的二进制数据,所以我们需要眼熟一下png的文件格式,如图

前32个字节是固定的,分别是btPngSignature和struct PNG_CHUNK chunk结构,其中保存有png图片的标识。

其中NG和IHDR是每个PNG文件都会有的标识,眼熟一下就好。微信的二维码图片就是通过这种格式在内存中存放

使用CE过滤基址
首先在微信未登录状态下附加微信,此时二维码还未加载

然后选择未知的数值,点击首次扫描

出现三百万个结果

此时我们再次点击切换账号,出现二维码,让保存二维码的地址被赋值

然后选择变动的数值 再次扫描

此时还剩下七万个结果

然后用手机扫描二维码 不要点击登录,再次扫描变动的数值,此时还剩三万多个结果

接着随意移动微信框,点击未变动的数值,还剩一万多个结果。返回二维码登录重复以上操作,直到地址栏还剩下两个绿色的基址,这两个绿色的基址就是我们要的。

因为随机基址的存在,这个地址在各位的电脑上是不一样的。但是低四位是一致的,这两个地址应该是xxxx9194和xxxx919C。

使用OD确定二维码基址
然后重启一次微信,再用CE附加,回到这个状态

用OD附加微信,在找到的第一个地址xxxx9194下内存写入断点

点击切换账号,在二维码未加载时程序会断下。注意,这个地方会断下来两次,第二次才是我们要的结果。

因为二维码是存放在微信的核心模块WeChatWin中的,所以我们在堆栈中找到所有的WeChatWin中的函数

像这种API的调用就可以直接排除掉,然后在每一个疑似函数上下断点。找的时候堆栈尽量往下拉,这个函数会比较靠后。

因为我已经找过一遍了,所以直接告诉你们是这一个。特征是有一个ecx传参。

接着在这个函数上下断点,删除内存访问断点,F9运行

然后扫一下二维码,点击返回二维码登录,程序断下

此时观察ecx指针的内容,明显是一个结构体,结构体的第一个是地址,第二个好像是大小。然后在这个地址上数据窗口跟随

里面是PNG文件的二进制数据,这个就是我们要找的微信二维码的基址

验证二维码基址
打开PCHunter,选择微信进程,查看->查看进程内存,输入地址和大小,然后将内存dump下来

打开图片

现在已经确定就是我们需要的二维码。然后我们将这个call的地址减去模块基址,记录下偏移。待会需要HOOK这个call

寻找微信二维码内容的基址
微信二维码的存储内容
二维码其实是一种开放性的信息存储器,它将固定的信息存储在自己的黑白小方块之间。大部分的二维码都有一个特点,就是里面存放的其实是一段文本。我们可以利用这个文本来寻找突破口

将微信的二维码截图保存,然后用在线的二维码解码器解析微信的二维码

可以看到解码之后的结果是一段网址

使用CE寻找二维码内容的基址

如果直接搜索这段网址是找不到任何结果的,原因是因为微信在保存这段位置的时候,实际上是将它分为了两部分存储

第一部分:http://weixin.qq.com/x
第二部分:/I-yOUnFpRaZOwZyVPC0H
1
2
第一部分的固定不变的,第二部分被当作一个参数传入,客户端从服务器获取的只是第二部分的内容。所以我们去搜索第二部分。

另外,微信的二维码会定时刷新,刷新的时候会改变第二部分的内容。如果你搜不到的话可能是因为之前的文本已经失效了。从解析文本到搜索文本最好在一分钟之内完成

此时 我们直接搜索第二部分的文本

搜索完成之后,等待二维码自动刷新,然后找到那个变化之后的地址,用截图上传的方式确保找到的是正确的地址

然后用OD附加微信,在找到的地址上下内存写入断点

等待二维码自动刷新,二维码刷新时会往原来存放二维码的地址写入新的二维码数据,程序就会断下

此时eax指向二维码的文本内容,我们找到堆栈中的第一个地址,在数据窗口显示,此时就能找到存放微信二维码数据的基址了

然后我们在CE中添加WeChatWin.dll模块,找到模块基址,算出偏移(用0x104CF618-0xF250000=127F618)。然后将这个地址换成模块基址+偏移的方式添加到CE地址栏。

验证基址
重新打开微信,用CE载入

保留当前列表,然后将二维码内容指针的值添加到列表

点击确定。此时二维码的内容和解析出来的内容一致,说明基址有效

定制微信登录二维码的可能性
那么我们拿到这个二维码的内容有什么作用呢?我们可以将这个获取到的二维码内容调用二维码生成器的API接口进行再次编码,然后生成一个更加漂亮好看专属二维码,效果如图:

使用hook截取二维码
接着我们编写一个dll,将这个dll注入到微信进程中,利用IAT Hook截取微信的二维码。部分关键代码如下:

开启HOOK

void StartHook(DWORD dwHookOffset,LPVOID pFunAddr, HWND hWnd)
{
hDlg = hWnd;
//拿到模块基址
DWORD dwWeChatWinAddr = GetWeChatWinAddr();
//需要HOOK的地址
DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;

//填充数据
jmpCode[0] = 0xE9;
//计算偏移
*(DWORD*)(&jmpCode[1]) = (DWORD)pFunAddr – dwHookAddr-5;

// 保存以前的属性用于还原
DWORD OldProtext = 0;

// 因为要往代码段写入数据,又因为代码段是不可写的,所以需要修改属性
VirtualProtect((LPVOID)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

//保存原有的指令
memcpy(backCode, (void*)dwHookAddr, 5);

//写入自己的代码
memcpy((void*)dwHookAddr, jmpCode, 5);

// 执行完了操作之后需要进行还原
VirtualProtect((LPVOID)dwHookAddr, 5, OldProtext, &OldProtext);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
卸载HOOK

void UnHook(DWORD dwHookOffset)
{
DWORD dwWeChatWinAddr = GetWeChatWinAddr();
DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;

// 保存以前的属性用于还原
DWORD OldProtext = 0;

// 因为要往代码段写入数据,又因为代码段是不可写的,所以需要修改属性
VirtualProtect((LPVOID*)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

// Hook 就是向其中写入自己的代码
memcpy((LPVOID*)dwHookAddr, backCode, 5);

// 执行完了操作之后需要进行还原
VirtualProtect((LPVOID*)dwHookAddr, 5, OldProtext, &OldProtext);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
保存图片

void SaveImg(DWORD qrcode)
{
//获取图片长度
DWORD dwPicLen = qrcode + 0x4;
size_t cpyLen = (size_t)*((LPVOID*)dwPicLen);
//拷贝图片的数据
char PicData[0xFFF] = { 0 };
memcpy(PicData, *((LPVOID*)qrcode), cpyLen);

//将文件写到本地
HANDLE hFile = CreateFileA(“E:\\qrcode.png”,GENERIC_ALL,0,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
if (hFile==NULL)
{
MessageBox(NULL, “创建图片文件失败”, “错误”, 0);
return;
}

DWORD dwRead = 0;
if (WriteFile(hFile, PicData, cpyLen, &dwRead, NULL) == 0)
{
MessageBox(NULL, “写入图片文件失败”, “错误”, 0);
return;
}

CloseHandle(hFile);
//显示图片
CImage img;
CRect rect;
//拿到控件的句柄
HWND hPic = GetDlgItem(hDlg, IDC_QRPIC);
GetClientRect(hPic, &rect);
//载入图片
img.Load(“E:\\qrcode.png”);
img.Draw(GetDC(hPic), rect);

//显示二维码内容
ShowQrCodeContent(hDlg);

//完成之后卸载HOOK
UnHook(QrCodeOffset);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
最终效果
最终效果如图:

最后附上工程和成品DLL

目前微信机器人的成品已经发布,需要代码请移步Github。还请亲们帮忙点个star

https://github.com/TonyChen56/WeChatRobot
————————————————
版权声明:本文为CSDN博主「鬼手56」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38474570/article/details/92798577

【PC微信探秘】使用C#读取PC微信内存数据_C#_赵庆明老师-CSDN博客

mikel阅读(1087)

来源: 【PC微信探秘】使用C#读取PC微信内存数据_C#_赵庆明老师-CSDN博客

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace L014ReadWeChatMemory
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Button1_Click(object sender, EventArgs e)
{
this.textBox1.Clear();

//微信进程
Process WxProcess = null;
//WeChatWin.dll基址
IntPtr WeChatWinBaseAddress = IntPtr.Zero;
//微信版本
String WeChatVersion = “”;
Process[] processes = Process.GetProcesses();
foreach (Process process in processes)
{
if (process.ProcessName == “WeChat”)
{
WxProcess = process;
this.textBox1.AppendText(“微信已找到!” + Environment.NewLine);
this.textBox1.AppendText(“微信句柄:\t” + “0x” + ((int)(process.Handle)).ToString(“X8”) + Environment.NewLine);
foreach (ProcessModule processModule in process.Modules)
{
if (processModule.ModuleName == “WeChatWin.dll”)
{
WeChatWinBaseAddress = processModule.BaseAddress;
this.textBox1.AppendText(“微信基址:\t” + “0x” + ((int)(processModule.BaseAddress)).ToString(“X8”) + Environment.NewLine);

WeChatVersion = processModule.FileVersionInfo.FileVersion;
this.textBox1.AppendText(“微信版本:\t” + processModule.FileVersionInfo.FileVersion + Environment.NewLine);
break;
}
}
break;
}
}

if (WxProcess == null)
{
this.textBox1.AppendText(“微信没有找到!”);
return;
}

//微信号
int WxNameAddress = (int)WeChatWinBaseAddress + 0x1131B90;
this.textBox1.AppendText(“微信号地址:\t” + “0x” + ((int)(WxNameAddress)).ToString(“X8”) + Environment.NewLine);
this.textBox1.AppendText(“微信号:\t” + GetString(WxProcess.Handle, (IntPtr)WxNameAddress) + Environment.NewLine);

//微信昵称
int WxNickNameAddress = (int)WeChatWinBaseAddress + 0x1131C64;
this.textBox1.AppendText(“微信昵称地址:\t” + “0x” + ((int)(WxNickNameAddress)).ToString(“X8”) + Environment.NewLine);
this.textBox1.AppendText(“微信昵称:\t” + GetString(WxProcess.Handle, (IntPtr)WxNickNameAddress) + Environment.NewLine);

}

String GetString(IntPtr hProcess, IntPtr lpBaseAddress, int nSize = 100)
{
byte[] data = new byte[nSize];
if (ReadProcessMemory(hProcess, lpBaseAddress, data, nSize, 0) == 0)
{
//读取内存失败!
return “”;
}
String result = “”;
String TempString = Encoding.ASCII.GetString(data);
// \0
foreach (char item in TempString)
{
if (item == ‘\0’)
{
break;
}
result += item.ToString();
}
return result;
}

[DllImport(“Kernel32.dll”)]
//BOOL ReadProcessMemory(
// HANDLE hProcess,
// LPCVOID lpBaseAddress,
// LPVOID lpBuffer,
// SIZE_T nSize,
// SIZE_T* lpNumberOfBytesRead
//);
public static extern int ReadProcessMemory(
IntPtr hProcess, //正在读取内存的进程句柄。句柄必须具有PROCESS_VM_READ访问权限。
IntPtr lpBaseAddress, //指向要从中读取的指定进程中的基址的指针。在发生任何数据传输之前,系统会验证基本地址和指定大小的内存中的所有数据是否都可以进行读访问,如果无法访问,则该函数将失败。
byte[] lpBuffer, //指向缓冲区的指针,该缓冲区从指定进程的地址空间接收内容。
int nSize, //要从指定进程读取的字节数。
int lpNumberOfBytesRead //指向变量的指针,该变量接收传输到指定缓冲区的字节数。如果lpNumberOfBytesRead为NULL,则忽略该参数。
);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
示例来源:
网易云课堂《2019 PC 微信探秘》

交流QQ群:
456197310
————————————————
版权声明:本文为CSDN博主「赵庆明老师」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013667796/article/details/90754802

PC微信hook学习笔记(一)—— 获取个人信息_数据库_qq_32886245的博客-CSDN博客

mikel阅读(2670)

来源: (1条消息)PC微信hook学习笔记(一)—— 获取个人信息_数据库_qq_32886245的博客-CSDN博客

PC微信hook学习笔记(一)—— 获取微信个人信息
1 起步
2. 获取基址
2.1 用CE查看个人信息
2.1.1 获取昵称基址
2.1.2 dll模块基址
2.2 用OD查看个人信息
2.3 内存查看命令
2.4 头像地址
3 地址偏移
3.1 用CE查看地址
3.2 指针
3.3 计算地址偏移量
3.3.1 其他信息地址
3.4 登录信息地址
这次跟着2019PC微信hook逆向分析课程学习下如何发送微信消息。
教程示例代码github地址:https://github.com/hedada-hc/pc_wechat_hook

本篇笔记学习教程:找微信个人数据基址
本篇笔记参考博客:PC微信逆向:使用CE+OD查找个人数据
博客大佬地址:请访问

1 起步
CE
CE修改器(Cheat Engine)是一款内存修改编辑工具,它允许你修改你的游戏,所以你将总是赢.它包括16进制编辑,反汇编程序,内存查找工具。与同类修改工具相比,它具有强大的反汇编功能,且自身附带了辅助工具制作工具,可以用它直接生成辅助工具。

OD
OD,是一个反汇编工具,又叫OllyDebug,一个新的动态追踪工具。OllyDbg是一种具有可视化界面的32位汇编分析调试器,是一个新的动态追踪工具,将IDA与SoftICE结合起来的思想,Ring3级调试器,非常容易上手,己代替SoftICE成为当今最为流行的调试解密工具了。同时还支持插件扩展功能,是目前最强大的调试工具。

下载
在吾爱破解的吾爱中下载OD(吾爱破解专用版Ollydbg.rar)
在西西软件园下载Cheat Engine 7.0.0中文版 绿色汉化版
安装
(1)解压缩吾爱破解专用版Ollydbg.rar,按照使用说明 .txt来操作即可。
(2)解压缩CE的zip文件
2. 获取基址
2.1 用CE查看个人信息
2.1.1 获取昵称基址
打开微信并登陆

打开Cheat Engine.exe

在CE中,点击【小电脑快捷图标】→【微信进程】,并在右侧扫描区输入登录的昵称并扫描数据。
(1)用昵称扫描一次,最好找到绿色的地址。绿色的地址一般是基址,红色是动态地址。
(2)找到后,双击绿色的地址,添加到下方的监控区域中。
(3)这一步没有显示绿色地址的话,用登录账号的省份试试,如:Jiangsu。

(4)右击添加好的地址条,选择【浏览相关内存区域】
(5)在弹框【内存浏览器】下方的地址信息里,可以上下滚动,能查看到登录微信的地址、手机号等信息。

双击监控区域的地址数据,并找个txt文本之类的记录绿色地址和弹框里的地址。如:65F472DC、WeChatWin.dll+13972DC——这两个就是昵称地址。

2.1.2 dll模块基址
在CE中添加刚才复制的地址:WeChatWin.dll,双击【描述】,并修改描述为:WeChatWin.dll基址;记录下WeChatWin.dll基址的地址:66BB0000。

WeChatWin.dll基址的地址:十六进制66BB0000, 被称为dll模块基址
微信昵称找到了,那么微信号、手机号等也不远了。但在CE用同样的方法扫描数据,却无法获取,我这里只扫描到个人地址信息:

Jiangsu:65F473C8,WeChatWin.dll+13973C8
Taizhou:65F473E0,WeChatWin.dll+13973E0
这时我们需要用到另一个工具:OD

2.2 用OD查看个人信息
OD基础操作的可以参考这个文章:OD 简介
大家也可以将鼠标放在字母快捷键上查看说明:

2.3 内存查看命令
-内存查看命令统一为d*格式: d[类型] [地址范围]

d代表Display,类型包括:字符、字符串、双字等。具体来说,d*命令共有这几种:d、 da、db、dc、dd、dD、df、dp、dq、du、dw、dW、dyb、dyd、ds、dS。

命令 格式
基本类型
dw 双字节WORD格式
dd 4字节DWORD格式
dq 8字节格式
df 4字节单精度浮点数格式
dD 8字节双精度浮点数格式
dw 指针大小格式,32位系统下4字节,64位系统下为8字节
dp 指针大小格式,32位系统下4字节,64位系统下为8字节
基本字符串
da ASCII字符串格式
du UNICODE字符串格式
db 字节 + ASCII字符串
dW 双字节WORD + ASCII字符串
dc 4字节DWORD + ASCII字符串
高级字符串
ds ANSI_STRING类型字符串格式
dS UNICODE_STRING类型字符串格式
2.4 头像地址
先来查找头像地址,打开吾爱破解[LCG].exe
附加微信进程
(1)【菜单】→【文件】→【附加】;
(2)点击【附加弹框】的标题部分,并输入WeChat;选中WeChat.exe,并点击【附加】。

(4)附加完之后,一定要点击下三角形的运行小按钮,否则微信无法显示窗口。

(5)在OD下方的命令输入框内输入:dc 昵称基址,上下滚动来查看相关信息。

(6)在OD下方的命令输入框内输入:dd 昵称基址,并往下翻一翻,找到头像url。单击头像url,右击→【复制】→【到剪贴板】,并在浏览器中打开头像地址。复制其中一个就够了。

3 地址偏移
3.1 用CE查看地址
我们在上一步复制的内容如下:65F475A4 0F590D18 ASCII “http://wx.qlogo.cn/mmhead/ver_1/BdevmwKtgCib16q8RKJLgJXnz8qla3nNg2T48yP1MexU2ibAJILSQSQAErXaNiaGKMnU”;

将头像url放到浏览器中查看,但现在看不了头像了。

首先,我们在CE里扫描头像url:http://wx.qlogo.cn/mmhead/ver_1/BdevmwKtgCib16q8RKJLgJXnz8qla3nNg2T48yP1MexU2ibAJILSQSQAErXaNiaGKMnU,但没有出现绿色的基址;
接着,点击【添加地址】,试一下刚才复制到的头像地址信息,如第一个:65F475A4和0F590D18;
结果,65F475A4显示乱码;而0F590D18是红色的,不是我们想要的绿色基址;

3.2 指针
刚才试一试的结果告诉我们:65F475A4和0F590D18都是地址。就是说,65F475A4这个地址里,放的是另一个地址;而另一个地址0F590D18里放的才是具体数据。这种地址里装了另一个地址的,就叫指针。如下表所示。

地址 数据
65F473C8 Jiangsu
65F473E0 Taizhou
65F472DC 你看花开啦
65F475A4 0F590D18
0F590D18 http://wx.qlogo.cn/mmhead/ver_1/BdevmwKtgCib16q8RKJLgJXnz8qla3nNg2T48yP1MexU2ibAJILSQSQAErXaNiaGKMnU
3.3 计算地址偏移量
利用模块基址+偏移量的方式,可以获取到数据基址。 我们可以用微信头像的指针地址减去模块基址就能得到微信的头像偏移,即微信头像的指针地址 – WeChatWin.dll模块基址 = 偏移量。
(原理待更新·····我现在还是有点不懂)

WeChatWin.dll基址的地址:64BB0000
打开【计算器】,选择【程序员】→【HEX】。
输入:65F475A4 – 64BB0000 ,得到 139 75A4,那么微信头像地址是:WeChatWin.dll+13975A4。

此时,我们可以用CE扫描到的城市地址( Taizhou:65F473E0,WeChatWin.dll+13973E0)来验证下:65F473E0 – 64BB0000 = 13973E0,证明计算方法是正确的。
3.3.1 其他信息地址
获取微信号地址:65F47264-64BB0000 = 1397264

获取手机号地址:65F47310 – 64BB0000 = 1397310
3.4 登录信息地址
将前面几个小节扫描到的和计算得到的地址都加到CE中,得到如下地址列表。
指针地址很明显,其数值里是另一个地址,而另一个地址里,装着我们要的数据。

————————————————
版权声明:本文为CSDN博主「qq_32886245」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_32886245/article/details/103711543

(1条消息)微信Hook实战记录2:动手实现恶意dll内存插入器_C/C++_github_39463644的博客-CSDN博客

mikel阅读(933)

来源: (1条消息)微信Hook实战记录2:动手实现恶意dll内存插入器_C/C++_github_39463644的博客-CSDN博客

Hook任何软件,整体思路都是通过内存调试软件对软件运行时内存进行断点调试,找到想要hook的内存地址,转为可以通过程序主dll可以获得的相对地址,然后再此处插入自己的恶意汇编代码,如果代码比较复杂,还需要编写寄存器保护逻辑,避免自己的恶意代码修改了寄存器中的值后,程序正常的逻辑无法运行。

前言
在上一篇文章「微信Hook实战记录 1:找到个人信息」中,介绍了如何使用OD与CE来找到微信中存放个人信息的内存位置,本篇文章尝试通过C++编写一个简单的内存注入工具,将我们自己的恶意dll注入到微信的内存中。

本文记录了大量细节,完全可以模仿复现相同的效果。

内存注入工具的编写
打开 Visual Studio 2019(下载的时候勾选 C++ 桌面支持,6.9G左右),选择创建新项目

选择创建 Windows 桌面向导

然后创建就可以了,这里创面项目名为 Project1

创建完后,VS 会为我们创建一个默认的界面,我们在 解决方案 -> 源文件 中找到 Project1.cpp,这个文件就是我们要写逻辑的主要界面

将其中多余的代码删除,就留下如下代码

// Project1.cpp : 定义应用程序的入口点。
//

#include “framework.h”
#include “Project1.h”

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
wWinMain 是界面程序的入口,所有不必删除

接着将 解决方案 –> 资源文件 中的内容删除,这是 VS 默认创建好的,删除后,自己新建一个资源文件

选择 Dialog,即弹出框

调整一下创建出的按钮位置以及整体大小

要设置按钮与整体的属性,如按钮要设置其内容与ID,而整体Dialog也要设置一下其ID,可以右键点击 –> 属性

其他控制以相同的方式去设置,设置后后,就可以写代码了

首先修改一下 Project1.h 中的内容,如下

#pragma once

#include “resource1.h”
1
2
3
具体内容要看你VS为你生成了什么,前面我们通过图像界面操作后,VS会生成的相应的头文件,这里我生成了 resource1.h 的头文件,其内容如下:

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 Project1.rc 使用
//
#define ID_MAIN 101
#define UN_DLL 1001
#define INJECT_DLL 1002

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1003
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
可以看出,其实就是我们设置的ID等内容

接着来修改一下 Project1.cpp, 其 wWinMain 方法修改如下:

#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>

// 微信进程名
#define WECHAT_PROCESS_NAME “WeChat.exe”

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{

// hInstance 将句柄传递给弹窗
// 传入 Dialgo 主体ID
// DialogProc 回调函数,是一个指针,即该函数的地址
DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
因为创建的资源是 Dialog ,所以这里通过 DialogBox 来使用它,它在 Windows.h 的头文件中,如果你使用的 VS 2019,其 framework.h 文件就包含Windows.h了。

DialogBox的参数可以通过微软的文档来查看

void DialogBoxA(
hInstance,
lpTemplate,
hWndParent,
lpDialogFunc
);
1
2
3
4
5
6
hInstance,类型:HINSTANCE,包含对话框模板的模块句柄。如果此参数为 NULL,则使用当前可执行文件。
lpTemplate,类型:LPCTSTR,对话框模板。可以使用MAKEINTRESOURCE宏来创建此值。
hWndParent,类型:HWND,拥有该对话框的窗口的句柄。
lpDialogFunc,类型:DLGPROC,指向对话框过程的指针。

根据文档提示,完成代码

#include “framework.h”
#include “Project1.h”
#include <TlHelp32.h>
#include <stdio.h>

#define WECHAT_PROCESS_NAME “WeChat.exe”

// 注册函数,方便其他模块使用
INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam);
VOID InjectDll();

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{

DialogBox(hInstance, MAKEINTRESOURCE(ID_MAIN), NULL, &DialogProc);
return 0;
}

INT_PTR CALLBACK DialogProc(_In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
if (uMsg == WM_INITDIALOG) {
MessageBox(NULL, “首次加载”, “标题”, 0);
}
// 关闭界面操作
if (uMsg == WM_CLOSE) {
EndDialog(hwndDlg, 0);
}

// 所有的界面上的按钮事件都会走这个宏
if (uMsg == WM_COMMAND) {
if (wParam == INJECT_DLL) {
InjectDll(); //注入 Dll
}

if (wParam == UN_DLL) {

}
}

return FALSE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
接着来实现 InjectDll() 方法,完成注入 DLL 的功能,整体分 3 步走。

1.获取微信的PID,通过PID获得微信进程的句柄,从而拥有了权限
2.申请内存,内存大小为 DLL 路径大小
3.将 DLL 路径注入到微信进程中

DWORD ProcessNameFindPID(LPCSTR ProcessName)
{
// #include <TlHelp32.h>
// 获取进程快照
HANDLE ProcessAll = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);// 获取整个系统的进程快照
// 在快照中对比过 processname 进程名称
PROCESSENTRY32 processInfo = { 0 };
processInfo.dwSize = sizeof(PROCESSENTRY32);
do
{
// WeChat.exe
// th32ProcessID 进程ID, szExeFile进程名
if (strcmp(ProcessName, (char*)processInfo.szExeFile) == 0) {
return processInfo.th32ProcessID;
}
} while (Process32Next(ProcessAll, &processInfo));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上述方法,需要 windows 平台 C++ 基础。其实主要就是对 Windows.h 中方法的调用,接着完成如下效果

VOID InjectDll()
{
// 自己恶意dll的路径
CHAR pathStr[0x100] = { “D://GetWeChatInfo.dll” };
// 获取微信PID
DWORD PID = ProcessNameFindPID(WECHAT_PROCESS_NAME);
if (PID == 0) {
MessageBox(NULL, “没有找到微信进程或微信没有启动”, “错误”, 0);
return;
}
// 通过PID获取句柄 C++中句柄类型为 HANDLE
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (NULL == hProcess) {
MessageBox(NULL, “进程打开失败,可能权限不足或关闭了微信应用”, “错误”, 0);
return;
}

//申请内存,内存大小为我们要注入 DLL 的大小
LPVOID dllAdd = VirtualAllocEx(hProcess, NULL, sizeof(pathStr), MEM_COMMIT, PAGE_READWRITE);
if (NULL == dllAdd) {
MessageBox(NULL, “内存分配失败”, “错误”, 0);
return;
}

// 将 DLL 写入到对应的内存地址中
if(WriteProcessMemory(hProcess, dllAdd, pathStr, strlen(pathStr), NULL) == 0) {
MessageBox(NULL, “路径写入失败”, “错误”, 0);
return;
}

// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
完成后,直接编译运行,如果编译后的效果依旧是之前代码的效果,你可以 重新生成解决方案,然后再编译。

点击注入,获得注入的位置,通过 Cheat Engine 来查看该位置的内容

注入dll成功

注入成功后,还需要调用 Kernel32.dll 中的方法,调用该路径下的 dll 就完成 dll的注入了,代码如下:

// 省略显示前面的代码

// 创建一块内存用来打印一段文本,方便看效果
CHAR test[0x100] = { 0 };
sprintf_s(test, “写入的地址为:%p”, dllAdd);
OutputDebugString(test);

//将地址写入后,通过 Kernel32.dll 调用该地址的 DLL 就完成了
HMODULE k32 = GetModuleHandle(“Kernel32.dll”);
LPVOID loadAdd = GetProcAddress(k32, “LoadLibraryA”);
// 远程执行我们的dll,通过注入的dll地址以及CreateRemoteThread方法让微信进程调用起我们的进程
HANDLE exec = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadAdd, dllAdd, 0, NULL);
if (exec == NULL) {
MessageBox(NULL, “远程注入失败”, “错误”, 0);
return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
此时 DLL 就相当于在微信进程中了。

DLL中的函数就可以操作微信进程中的内存了,从而实现获取微信信息的目的。

如果没有调用其DLL,则需要注意一下 LoadLibraryA 方法,该方法会调用 ASCII的路径地址,如果你是unicode的话,则需要调用 LoadLibraryW 方法,如果不成功,可以尝试替换一下。

没有Windows平台开发C++的朋友看完上面代码可能会感到疑惑,为什么将恶意dll路径插入内存后,可以让微信进程自己去载入这个恶意dll呢?

简单解释一下,在windows中,每个进程在启动时都会载入Kernel32.dll,微信进程会载入Kernel32.dll,浏览器进程也会载入Kernel32.dll,而我们可以通过Kernel32.dll中的LoadLibraryA()方法或LoadLibraryW()方法去动态载入新的dll,需要解释一下,我们在自己的进程中获取LoadLibraryA()方法的内存位置,这个内存位置对windows中其他进程而言是相同的。

但关键点是通过微信进程去使用Kernel32.dll中的LoadLibraryA()方法载入新的dll,为了实现这个目的,就需要使用CreateRemoteThread()方法,该方法可以远程调用其他进程的中的方法,前提是要有该进程的句柄(即执行权限),而我们在一开始就获得了微信进程的执行权限。

总结一下整个流程:

1.将恶意dll路径插入到微信进程运行内存中
2.获取当前进程(我们自己的进程,而非微信进程)Kernel32.dll中LoadLibraryA()方法或LoadLibraryW()方法,因为微信进程也载入了Kernel32.dll,所以微信进程可以在相同的内存地址处找到LoadLibraryA()方法或LoadLibraryW()方法
3.通过CreateRemoteThread()方法,让微信进程去调用LoadLibraryA()方法载入恶意dll
结尾
本文并没有给出恶意dll的具体实现逻辑,后面将实现一个简单的dll,来获取微信的个人信息,其实基于上一篇文章,找到了个人信息的具体微信红藕,该dll要做的只是从中读取信息,仅此而已。

但恶意dll还可以实现很多复杂的功能,比如监控聊天信息、自动收红包、自动踢人等各种功能,这些功能在后面有机会再分享给大家。

如果本文对你有帮助,点击「在看」支持二两,下篇文章见。
————————————————
版权声明:本文为CSDN博主「二两hackpython」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/github_39463644/article/details/103451010

API Hook PC微信防多开 - Crack_Me - 博客园

mikel阅读(1621)

来源: API Hook PC微信防多开 – Crack_Me – 博客园

1.首先确定微信放多开是如何确定的,大多数程序的防多开都是使用互斥体来完成,所以就从创建互斥体的API开始下手(CreateMutexA/CreateMutexW)

image

可以看到创建互斥体的名称

image

一般程序在防多开zhong 都是判断判断互斥体的名称是否相同,所以我们做个实验来验证我们猜想的是否正确

(使用x32dbg直接修改内存效果一样)

image

修改值直接运行,并接着在打开一个微信,结果如下

image

,双开成果,说明我们的猜测是正确的,所以直接用apiHook,Hook CreateMutexW函数,修改chau纳入的第三个参数,就可以过掉微信防多开,dll代码如下

void HookApi()
{
//获取CreateMutexW函数地址
Addr = GetProcAddress(LoadLibraryA(“Kernel32.dll”), “CreateMutexW”);
//保存原始的指令
memcpy(OldOpcode, Addr, 5);
//计算跳转偏移
DWORD Offset = (DWORD)HookCreateMutexA – (DWORD)Addr – 5;

//组合新的Opcode
*(DWORD*)&JmpOpcode[1] = Offset;

}

HANDLE WINAPI HookCreateMutexA(
LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL                  bInitialOwner,
LPCSTR                lpName
)
{
HANDLE Handle = 0;
//调用CreateMutexW创建不同名称的互斥体
Handle = CreateMutexW(lpMutexAttributes, bInitialOwner, L”bbbbbbbb23333333″);
return Handle;

}

void EnableHook(BOOL Enable = TRUE)
{
DWORD OldProtect = 0;
//修改内存页属性,可读写
VirtualProtect(Addr, 5, PAGE_EXECUTE_READWRITE, &OldProtect);
//填充新的Opcode
memcpy(Addr,Enable ? JmpOpcode:OldOpcode, 5);
//还原目标地址所在分页的属性
VirtualProtect(Addr, 5, OldProtect, &OldProtect);
}

通过对微信pc hook实现微信助手 - 腾格里

mikel阅读(1395)

来源: 通过对微信pc hook实现微信助手 – 腾格里

本软件主要通过对pc端微信hook来实现的,微信版本2.6.8.52。
软件下载地址:
链接: https://pan.baidu.com/s/1hre17eW3vOO3O8FcnMulaQ
提取码: 2qih

软件实现功能:

  1. 支持爆粉
  2. 支持文本消息群发
  3. 支持自动收款
  4. 支持自动加好友
  5. 支持消息防撤回
  6. 支持消息回复接口

操作视频

软件图片:
baofeng.PNG

qunfa.PNG

autoreply.PNG

set.PNG

微信自动回复接口api:http://blog.yshizi.cn/115.html

联系方式
微信:znana2019
邮件:xuhang1108@126.com

C# PC版微信消息监听自动回复 - 小赫赫加油 - 博客园

mikel阅读(3371)

来源: C# PC版微信消息监听自动回复 – 小赫赫加油 – 博客园

最近有个微商客户需要搞个 个人微信监听群消息关键字并实现自动回复功能, 因为他有很多群  很多买家咨询的话 一个个回复太麻烦, 客户要求 比如群里有人发 关键字 产品1  则自动回复产品1的相关描述

 

首先设置关键字,将关键字和回复内容存到一个txt就行

 

然后就是微信消息监听(windows的消息  句柄不懂的自己 google):

上监听的关键代码 :

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/// 消息主要接受程序
       /// </summary>
       /// <param name="m"></param>
       protected override void DefWndProc(ref Message m)
       {
           switch (m.Msg)
           {
               case 74:    //74代表WM_COPYDATA                   
                   string message = "";
                   int f = (int)m.WParam;
                   try
                   {
                       COPYDATASTRUCT2 cds2 = (COPYDATASTRUCT2)m.GetLParam(typeof(COPYDATASTRUCT2));
                       message = Marshal.PtrToStringAnsi(cds2.lpData);
                   }
                   catch (Exception e)
                   {
                       LogHelper.WriteLog("解析微信消息报错", e);  //发文字带空格报错
                   }
                   switch (f)
                   {
                       //登录时解析用户信息
                       case 10003:
                           try
                           {
                               if (!string.IsNullOrEmpty(message))
                               {
                                   MessageHandler.Parse10003(message);
                               }
                               this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText("【" + MessageHandler.myUserInfo.UserName + "】登录成功!\r\n"); }));
                           }
                           catch (Exception e1)
                           {<br>                                LogHelper.WriteLog("解析微信登录用户信息错误", e1);
                           }
                           return;<br>
                       //文字,图片,视频,文件 解析
                       case 10012:
                           try
                           {
                               if (message.Contains("msgtyp:"))
                               {
                                   string[] datas = Regex.Split(message, "msgtyp:", RegexOptions.IgnoreCase);
                                   if (datas.Count() >= 2)
                                   {
                                       string msgtyp = datas[1].Split(',')[0];
                                       switch (msgtyp.Replace(" """))
                                       {
                                           case "1":  //文字
                                               if (message.Contains("[群-->]") && message.Contains("[消息内容-->]"))
                                               {
                                                   string word = "";      //发送内容
                                                   //取消息内容
                                                   string[] wordArr = Regex.Split(message, @"消息内容-->]", RegexOptions.IgnoreCase);
                                                   string[] aa = wordArr[1].Split(new string[] { "||" }, StringSplitOptions.RemoveEmptyEntries);
                                                   if (aa[0].StartsWith(" "))
                                                   {
                                                       word = aa[0].Substring(1);
                                                   }
                                                   else
                                                   {
                                                       word = aa[0];
                                                   }
                                                   string groupId = WxMessageHelper.GetChatRoom(message);
                                                   string groupName = WxMessageHelper.GetGroupName(message);
                                                   string nickName = WxMessageHelper.GetNickName(message);
                                                   string memberpuid = WxMessageHelper.GetWxId(message);
                                                   List<string> data = ReadFile();
                                                   foreach(var str in data)
                                                   {
                                                       string key = str.Split(';')[0];
                                                       string value = str.Split(';')[1];
                                                       if (word.Contains(key))
                                                       {
                                                           string[] sarr = Regex.Split(word, str, RegexOptions.IgnoreCase);
                                                           this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText("【" + nickName + "】在【" + groupName + "】发送了关键词消息:"+ sarr[0]);
                                                               richTextBox1.AppendTextColorful(key, Color.Red, false);
                                                               richTextBox1.AppendText(key + "\r\n");
                                                           }));
                                                           //调用回复
                                                           SendMessage(groupId + "||" + value, 20001);
                                                       }
                                                   }
                                               }
                                               break;
                                       }
                                   }
                               }
                           }
                           catch (Exception e2)
                           {
                               this.Invoke(new MethodInvoker(() => { richTextBox1.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "10012 : 解析微信群信息错误\r\n\r\n"); }));
                           }
                           break;
                       default:
                           break;
                   }
                   //消息内容  cds.lpData
                   break;
               default:
                   base.DefWndProc(ref m);
                   break;
           }
       }

 

 

1
WParam=10003 时,监听的是微信登录的操作, 这时给前台窗体一个提示, 提示登录成功 如下图

 

登录时message 消息体的值  ||分割  第一个就是微信昵称,  wxid_ 开头的就是微信唯一标识 , 最后是登录用户的头像

 

 

 

然后用 另一个号往群里发消息 (当前登录用户和那个号必须在一个群 )

发送  : 产品1  这时观察message 的值

 

@chatroom 结尾的是 群的唯一标识id   群聊就是群名称,   发送人昵称也有 发送的消息内容也有, 拿正则取一下就完事。

 

1
word就是提取的发送内容, 然后判断如果发送的内容包含关键字 则调用SendMessage方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// <summary>
       /// 发送消息
       /// </summary>
       /// <param name="message">消息内容</param>
       /// <param name="wParam">通讯号</param>
       /// <returns></returns>
       private int SendMessage(string message, int wParam)
       {
           byte[] sarr = System.Text.Encoding.Default.GetBytes(message);
           int len = sarr.Length;
           COPYDATASTRUCT cds;
           cds.dwData = (IntPtr)Convert.ToInt16(1);    //可以是任意值
           cds.cbData = len + 1;   //指定lpData内存区域的字节数
           cds.lpData = message;  //发送给目标窗口所在进程的数据
           return myapi.SendMessage(common.微信句柄, 74, wParam, ref cds);  //74代表WM_COPYDATA
       }

 

 

1
2
3
4
5
6
7
8
9
10
[StructLayout(LayoutKind.Sequential, Size=1)]
public struct myapi
{
    [DllImport("user32", EntryPoint="SetWindowLong")]
    public static extern int SetWindowLongA(int hwnd, int nIndex, int dwNewLong);
    [DllImport("User32.dll")]
    public static extern int SendMessage(int hwnd, int msg, int wParam, ref COPYDATASTRUCT IParam);
    [DllImport("User32.dll")]
    public static extern int FindWindow(string lpClassName, string lpWindowName);
}

 

来2个效果图

 

 

 

 

这是用客户微信号  在群里 发 产品1   则当前微信立马回复  前面设置的回复内容

 

博客经个人辛苦努力所得,如有转载会特别申明,博客所有权归本人和博客园所有,如有转载请在显著位置给出博文链接和作者姓名,否则本人将付诸法律

原文地址: https://www.cnblogs.com/alonglonga/p/11876300.html    小赫赫首发

有问题联系Q: 591811930