浅谈显 ipqq 发展历程 【附显 ipqq原理】

浅谈显 ipqq 发展历程 【附显 ipqq原理】 阅读详情

声明,本文章未经过疯狂绅士与runjin的同意不得转载。


浅谈IPQQ软件的技术发展轨迹

疯狂绅士、RunJin

摘要:本文先给出了IPQQ的界定,并介绍了不同历史时期具有里程碑意义的IPQQ。根据几个具有里程碑意义的IPQQ,作者简要的分析了其技术特点,并以此阐述IPQQ软件的发展轨迹。

关键字:   IPQQ、破解、木子QQ、外挂、珊瑚虫外挂

一、IPQQ历史回顾
QQ作为国内用户量最大的IM,专门针对其发布一些附加功能是一个古老的话题。在QQ的弹出窗口上显示发消息用户IP的变种QQ成为大家最常用的工具,这种工具我们称为IPQQ。
1999年,IPQQ的始祖出现了,是在QQ的前身OICQ时候出现的。邹丹制作的破解版,可以说形式上开创了一个时代。尽管时间过去了好几年,邹丹创立的模式几乎成为了一种标准——密界的标准,以后所有QQ显IP的方式都是继承邹丹显示IP的方式。
邹丹的这一举动,在当时引起了一大片争议声。邹丹也收到了腾讯的律师函,邹丹考虑再三后,退出了为制作IPQQ的行列。 
接下来又有好多个勇士站了出来,其中最有名的是木子。木子的软件非常流行,与邹丹的软件相比,它的框架更好,更稳定,同时支持不同版本的QQ。木子的这个框架影响了大量的刚出道的Cracker,包括本文的作者之一 ——RunJin,其发布的飘云版QQ在一段时期里面被人骂成了剽窃。期间有很多显IP的QQ,说白了都是参考木子的,其中绝大部分根本就是木子的版本,不过是改了几个字或很取巧的换上自己的标签而已。
珊瑚虫前身的出现又是一个里程碑,这东西由Cygwin捣腾出来的。先揭一下Cygwin的老底,此人与疯狂绅士同是水木清华黑客版版主。
珊瑚虫QQ外挂的原型是Cygwin大概用了两天写出来的。这东西的出现让腾讯大为挠头,因为这个软件本身并没有修改QQ的任何程序。它是一个单存的外挂形式,因此避免了相关的法律问题。要说明的是珊瑚虫的历史比木子更早。可以说,珊瑚虫就是紧接着邹丹的版本出现的。这个时期的珊瑚虫完全由soff制作,不过这个时候的珊瑚虫不是外挂形式,同时质量非常高,与一年后出现的木子版相比也毫不逊色。这也是后来Quaful和cygwin找到soff合作的原因。这个时期的珊瑚虫版本因为收到了腾讯的律师信而被迫中止,所以给了后来木子兴起的机会。
由于珊瑚虫外挂不涉及到版权问题,并且其性能优越,导致了珊瑚虫外挂的下载量急剧的增大,从一些网站的分析来看,它甚至超过了QQ本身的下载量。珊瑚虫的成功在于它不断的推出新版本,作者大致统计了一下,从珊瑚虫0.1版到现在的4.0beta版,珊瑚虫一共推出了61个版本(不包括4.0以下的测试版)。正是由于珊瑚虫不懈的努力,使得珊瑚虫外挂声名远扬。
也许是精力的问题,当珊瑚虫的第二任作者soff觉得天天追赶着QQ的新版本而被迫推出一个个新版本觉得实在是很无趣的时候,内心深处对珊瑚虫来一个大变化已经变得很迫切。在外挂思维以及GAIM与Luma QQ等思想的指引下,Soff考虑一种新的方式,这个方式的执行者是Qulful,Qulful卖命的写出了珊瑚虫QQ4.0。
珊瑚虫QQ4.0又是一个里程碑,简单的说,以前的版本只要耐心去检查QQ的内存,总能找到IP地址的入口。因此可以说在珊瑚虫4.0之前的显IP的版本大都是一个简单的Cracker过程,只要能看懂段刚(看雪)《加密与解密》一书,折腾出某个IPQQ不是一见很难的事情。就是做一个外挂形式的IPQQ也并非难事。而珊瑚虫4.0可以说从根本上解决这个问题,它直接截获的是QQ的网络包,从网络包中获取其IP地址的。
从上面的简单的回顾,我们可以看出IPQQ的几个里程碑。
1、邹丹,IPQQ的开山鼻祖,IPQQ形式的确立。
2、木子,IPQQ的中兴,确立其成为修改QQ函数的地址方式的IPQQ的霸主地位。
3、Cygwin,IPQQ的转折,以外挂的形式击败了修改函数地址方式,成为了近两年来事实上IPQQ的霸主。
4、Quaful,思路的拓展,IPQQ也许只是珊瑚虫外挂的一个简单运用而已。

上述四个里程碑中,由于邹丹的版本太久远,这里不作讨论,本文仅就后3个里程碑简单的来分析一下其技术思路,尤其是后两种。

二、木子显IPQQ版本原理简介
与所有的破解软件一样,都有一个原则——点穴。木子与其它所有的IPQQ一样,都是很偷懒的,都是试图只通过修改几个字节,就达到想要的效果。
对于IPQQ来说,能利用QQ原来的功能就利用原来的功能。这似乎是一条铁律。
木子IPQQ,在网上已经可以下载到它的代码。这里只讲解木子IPQQ的部分代码。
SetIP proc
          pushad
          ;=================取得IP地址==================;
          mov   eax,dword ptr [ebp-10h]
          push   offset dwIp
          push   offset szRecentip
          push   eax
          mov   ecx, [eax]
          call   dword ptr [ecx+34h]
          ;=================取得端口号==================;
……
          ;=================取得版本号==================;
……
not2003:           
下面我们再看飘云的IPQQ的代码。
pushad
          ; =================取得IP地址==================;
          mov   eax,dword ptr [ebp-14h]
          push   offset dwIp
          push   offset szRecentip
          push   eax
          mov   ecx,[eax]
          call   dword ptr [ecx+34h]
          cmp   dwIp,0 ;是否已经取得IP
          ……
上面给出了两个不同IPQQ的如何获得IP地址部分的,这两段代码是针对不同版本的QQ。黑体字表示上述的不同地方。
由此我们可以看到,要制作IPQQ核心是找到显示IP的那个函数的入口。这个函数的查找是个关键。而木子的出现其意义在于他给出了一个框架。因此很多后续的IPQQ就更偷懒,他们只是参考木子的版本,在取IP的原理上只要改动一点就可以了。
木子制作的IPQQ可以说是能利用QQ的功能就尽量利用QQ的功能。而很多后来者制作的IPQQ其原则是,能利用木子就尽量利用木子。上述给出的飘云的IPQQ代码就是一个例子。

三、珊瑚虫4.0以下版本原理。
“珊瑚虫最初版很简单的,也参考了之前别人修改的方法,比如版本库就是直接盗用木子的,不过这个版本库盗用不是技术盗用,不被人介意的。”这段话是珊瑚虫的作者Cygwin所说的。
珊瑚虫外挂版本与木子版本比起来最大的不同在于——其利用了钩子的技术,不知道其是否用到了win2k的SDK包,否则这段代码都可以直接参考。是在QQ运行的时候获得了其IP地址的。而不是象木子一样直接给出一个显IP的破解版本。然而,在取得IP的原理上,珊瑚虫和木子是基本相同和飘云的更是非常相象。
下面是珊瑚虫3.16的取IP部分反汇编代码:
mov eax,dword ptr ss:[esp+10]
lea ecx,dword ptr ss:[esp+1C]
push ecx
push DUMPED.01B8D9EC     ; ASCII对应的字符为"dwRecentIP"
mov edx,dword ptr ds:[eax]
push eax
call dword ptr ds:[edx+34]
………………………………………..
mov eax,dword ptr ss:[esp+10]
lea ecx,dword ptr ss:[esp+1C]
push ecx
push DUMPED.01B8D9D8     ; ASCII对应的字符为"dwC2CIP"
mov edx,dword ptr ds:[eax]
push eax
call dword ptr ds:[edx+34]
…………………………………..
mov eax,dword ptr ss:[esp+10]
lea ecx,dword ptr ss:[esp+1C]
push ecx
push DUMPED.01B8D9C8       ; ASCII对应的字符为 "dwIP"
mov edx,dword ptr ds:[eax]
push eax
call dword ptr ds:[edx+34]

虽然寄存器的具体使用不同,但动态调试的时候就会发现,其调用的函数和飘云的是一样的。
珊瑚虫的原理可以用下面的话简要来表达。
1、     绕开原来的流程
2、     获取IP
3、     把自己的窗体挂上QQ的窗体
4、     把获取的IP显示在自己的窗体上
RunJin用softice 、OllyDbg在: win 2000 sp4 , win 98操作系统对 QQ2005贺岁版珊瑚虫外挂进行了分析。
由于win nt 平台和win9x 的差异,珊瑚虫外挂因系统的不同而分开两种工作原理。珊瑚外挂是给QQ的主程序QQ.exe外挂了一个Coralqq.dll,而完成这个工作的就是coralqq.exe .要加载一个dll文件必须是qq.exe。
在win2000/xp下coralqq.exe先创建QQ的进程,同时也就创建了QQ的暂停的主线程,接着往QQ进程的内存写入代码,修改Ntdll.NtTestAlert的代码让程序跳到附加代码处执行,在执行的过程中恢复Ntdll.NtTestAlert处被改了的代码,同时加载Coralqq.dll。
在win9x下,coralqq.exe先创建QQ的进程,同时也就创建了QQ的暂停的主线程,接着在QQ.exe的内存写数据,改写QQ.exe的oep从而达到改变程序流程的目的,让其先执行附加代码,加载Coralqq.dll,加载完后再跳到原来的QQ.exe的oep的下一条指令继续执行。详见RunJin的<<珊瑚虫外挂原理分析>>一文。
从RunJin的分析可以看出,珊瑚虫外挂想得很周到的,它绕开原来QQ的流程也是非常巧妙。当然如何高效的利用钩子大家可以去看相关的文章,这里不再详述。
可以这么说,珊瑚虫最大的特色是,用C++代替原来木子的部分代码。而这段代码的前面部分又是那么的巧妙的绕开了原来QQ的流程。
正如木子版本一样,越来越多的人利用上了一个原则——能用珊瑚虫的功能就尽量利用珊瑚虫的功能。
QQ狂人DIY版是把珊瑚虫外挂注入到QQ.exe里面,以实现直接点QQ.exe就可以直接启动珊瑚虫外挂的目的。
这类把外挂形式又变成了非外挂形式。它们所用的工具:LordPE,详细的教程在网上有下载。最终出来的IPQQ部分机器可能不兼容,但是大部分是可以的。

四、珊瑚虫4.0 IPQQ原理简介
珊瑚虫4.0可以说是IPQQ有史以来一个最重大的突破。在介绍这个版本必须提到KQL库(Kwafu QQ Library)。
KQL是怎么出来的呢?全是LumaQQ给逼的。那个如来神掌的影响力实在太大了,人人都问珊瑚虫:“珊瑚虫为什么不做如来神掌啊?”当然在3.x的架构下是不可能的事情。后来Quaful就说,干脆把LumaQQ移植过来,这样就能实现了。后来Quaful用了2个月的时间完成了这个工作。Kwafu是Quaful的一个马甲的名字,所以叫做Kwafu QQ Library。至于说KQL带来的QQ版本无关的其他好处,都是附带的好处了。
Windows平台下的KQL库重点是最大限度的保持和官方QQ客户端的兼容性,所以,KQL库是做为一个“外挂”来开发,它的表面功能全都还是依附于官方QQ客户端来实现的。对于用户来说,就是不会影响传送文件、视频聊天等功能,珊瑚虫新增加的功能对原来的影响都尽可能的小。这个库很多内容是参考LumaQQ。因此Quaful把Luma等人也列为作者。
在跟Quaful通过几次邮件后,作者个人的看法是珊瑚虫4.0最重大的变化在于,原来主要是截获内存里面的东西,现在是直接截获一个个网络包。
原来的方法基本上不用了解QQ的通讯协议,背后的方法是要深入到QQ的协议里面去。
原来的方法如果QQ重新写过一个客户端,尤其是更改一下获得IP地址的函数名字等,那么IPQQ基本上要重新来过一次。而采用4.0的方式基本上没有这个后顾之忧。它所需要做的是重新钩一个窗体而已,因为腾讯不会不顾通讯协议的不兼容而抛弃大量的用户的。
从技术思想上来说,原来的方法是紧密依靠QQ,现在呢是我只是依靠QQ一下而已。
对于这款外挂我曾经建议过Quaful用驱动的方式来写,也就是我能知道QQ的任意一个包,发给谁,是什么IP与端口。因为用驱动写是直接到网络的低层,占用的系统资源更少,但开发的工作量要增加非常的多。从工程的角度上来讲并不划算。
我把这个想法告诉Quaful后,他一直打击我的看法。Quaful声称:“用驱动层拦截QQ包并不会比现在更省资源。无论是TDI还是NDIS,对系统的影响都是全局的,不仅会影响QQ,还会影响所有的应用程序。这是更加消耗系统资源的做法。”
作者曾试图在Win2k的DDK包中根据相关的例子来分析,但发现工作巨大,因此放弃。但从相关的效果上看,Quaful是对的。从驱动层来写,的确对系统的影响是全局的。但我认为这种写法更有扩充性。
五、总结
在不同时期的IPQQ中我们看到了他们之间的继承关系。如果深入到最核心的技术中去考察这个问题,我们可以看到,四个IPQQ可以用如下的话来概括。
1、邹丹,我第一个改QQ。
2、木子,我改QQ改出套路来了。
3、Cygwin,我在运行的时候改QQ。
4、Quaful,QQ你改吧,你怎么改我都能逮住你。
此外,我们也可以看到其它一些作者的思路。
1、     木子、珊瑚虫你能改QQ我能改你!
2、     木子、珊瑚虫你先改吧,我学你的样改。

在上述四个IPQQ中作者最欣赏珊瑚虫4.0,个人认为其发展的方向非常多。当然目前的版本是通过sokect来分析的。这样做相对来说比较占内存,速度也不够快。 

文中涉及到如下人物:
作者: 疯狂绅士   原水木清华黑客版版主 

作者:RunJin   飘云的老大 现读本科二年级

邹丹,78年出生
木子,姓名保密
Cygwin,姓名 保密 原水木清华黑客版版主 
Soff   姓名 保密 北京理工大学当老师
Quaful,姓名 保密   清华大学在读博士 
 

 

编写QQ外挂插件的原理和方法

 

 

前一段时间研究了下QQ目前各种外挂的机理,包括著名的coralQQ。鉴于目前网上关于这方面的文章少之又少,一般能找到的应该就这下面3篇(由于可能涉及版权问题,我链接就不给出了:

  a.木子版显IPQQ的制作教程

  b.关于QQ外挂DLL的加载原理的分析

  c.明日帝国(sunwangme)写的我是这样来做破解qq,做QQ外挂的系列

  在开始我的分析前我简要对上面这些资料作下评价,首先我觉得如果你也想写个类似的外挂插件,他们的文章你是必看的,而且特别是你想真的写出什么有用的东西的话,明日帝国得文章一定要看,而且必须看懂。对于木子版的教程应该说是最早“公开”的资料了,很多人都是看了这个教程开始写自己的外挂的。但是他通过直接修改QQ来做显IP补丁,可能引起的法律问题不说(如果你只是自娱自乐的话),他不能适应不同版本的QQ,而且用户也不太能接受直接的修改,而且教程已经不能直接用于目前版本的QQ了。

  第二个教程是做外挂DLL插件必看的,但是他丝毫没涉及显示IP的问题,只是简单介绍了DLL注入的问题,并对win9x环境下手动加载dll到进程空间作了分析。但是目前win9x已逐渐退出舞台,所以一般只要使用CreateRemoteThread即可。

  第一部分:

  1.1 主流的外挂插件如何获取IP和其他信息的?

  也许你会认为他拦截了底层的Socket通讯?当然不至于,但这样肯定是最有效的办法。

  让我们换个思路:如果你现在需要和一个QQ好友传输文件或者进行语音聊天或者发送了图片或自定义表情。那么QQ必须知道对方的IP地址和端口信息,这样才能把数据传给对方。

  所以,很有可能QQ内部已经实现了获取IP地址和其他信息的相关函数了。的确如此。这也是木子版QQ教程里面提到的办法,调用QQ内部的函数。

  下面是截至QQ组件之一的CQQApplication.dll中的汇编代码:(建议先浏览木子版QQ的教程)

  027832C7 8B45 F0 mov eax,dword ptr ss:[ebp-10]
  027832CA 53 push ebx
  027832CB 68 38558302 push CQQAppli.02835538 ; ASCII dwIP
  027832D0 50 push eax
  027832D1 8B08 mov ecx,dword ptr ds:[eax]
  027832D3 FF51 18 call dword ptr ds:[ecx+18]
  027832D6 8B45 F0 mov eax,dword ptr ss:[ebp-10]
  027832D9 53 push ebx
  027832DA 68 40558302 push CQQAppli.02835540 ; ASCII wPort
  027832DF 50 push eax
  027832E0 8B08 mov ecx,dword ptr ds:[eax]
  027832E2 FF51 14 call dword ptr ds:[ecx+14]

   CQQApplication.dll通俗来说就是负责显示和实现QQ聊天窗口的模块。就是那些和xxx聊天中的窗口,所以这就是为什么要在其中寻找这样的代码的依据。

  从上面的汇编来看,显然是调用了2个thiscall规范的函数,也就是我们所说的C++类成员函数。

  2个成员函数的的大致形式是this->Func(void *ptr1,char *cmd,DWORD *ptr2);其中cmd就是上面dwIP、wPort这些字符串,而ptr2也很容易知道是函数返回值得存储指针。现在关键是要获取this指针,也就是ecx寄存器的数据和ptr1这个神秘指针的数据。

  如果你有兴趣反汇编CoralQQ中相关的代码,也会发现与上面类似的调用部分。

  这里暂时不深入这些函数的作用和那个cmd指针的细节,我们先来研究如何获取this指针和ptr2吧。

  注意

  027832DF 50 push eax

  027832E0 8B08 mov ecx,dword ptr ds:[eax]

  这2段代码,也就是说ptr2获取了,那么this指针也可以得到。所以现在一切的关键就是找出ptr2的来历。这样我们就能很轻松的实现显示ip了。

   1.2 神秘的ptr2指针

  为了能更快的说明问题,这里就不厚道的引用CoralQQ.dll的汇编了~

  0056D97F 51 push ecx
  0056D980 52 push edx
  0056D981 50 push eax
  0056D982 FF15 38AB5A00 call dword ptr ds:[5AAB38] ; BasicCtr.GetFriendQQData
  0056D988 8B4424 14 mov eax,dword ptr ss:[esp+14]
  0056D98C 83C4 0C add esp,0C
  0056D98F 3BC3 cmp eax,ebx
  0056D991 0F84 03020000 je CoralQQ.0056DB9A
  0056D997 57 push edi
  0056D998 895C24 14 mov dword ptr ss:[esp+14],ebx
  0056D99C 8D5424 14 lea edx,dword ptr ss:[esp+14]
  0056D9A0 52 push edx
  0056D9A1 68 50BB5900 push CoralQQ.0059BB50
  0056D9A6 C64424 30 01 mov byte ptr ss:[esp+30],1
  0056D9AB 8B08 mov ecx,dword ptr ds:[eax]
  0056D9AD 68 C8BA5900 push CoralQQ.0059BAC8 ; ASCII QQUSER_DYNAMIC_DATA
  0056D9B2 50 push eax
  0056D9B3 8B41 54 mov eax,dword ptr ds:[ecx+54]
  0056D9B6 FFD0 call eax
  0056D9B8 8B4424 14 mov eax,dword ptr ss:[esp+14]
  0056D9BC 3BC3 cmp eax,ebx
  0056D9BE 0F84 F9000000 je CoralQQ.0056DABD
  0056D9C4 8B08 mov ecx,dword ptr ds:[eax]
  0056D9C6 8D5424 1C lea edx,dword ptr ss:[esp+1C]
  0056D9CA 52 push edx
  0056D9CB 68 ACA15900 push CoralQQ.0059A1AC ; ASCII wProcotol
  0056D9D0 50 push eax
  0056D9D1 8B41 30 mov eax,dword ptr ds:[ecx+30]
  0056D9D4 FFD0 call eax
  0056D9D6 8B4424 14 mov eax,dword ptr ss:[esp+14]
  0056D9DA 8B08 mov ecx,dword ptr ds:[eax]
  0056D9DC 8D5424 10 lea edx,dword ptr ss:[esp+10]
  0056D9E0 52 push edx
  0056D9E1 68 94A15900 push CoralQQ.0059A194 ; ASCII dwRecentIP
  0056D9E6 50 push eax
  0056D9E7 8B41 34 mov eax,dword ptr ds:[ecx+34]
  0056D9EA FFD0 call eax

  以上代码正式coralQQ 4.5版获取IP信息的片断。我们只需要关注上面的0056D982和0056D9B6地址的2个调用函数。

  为什么这样说了,先看下面获取dwRecentIP数据的代码,它和上面提到的那个成员函数是属于一个类的(这里没提供出完整代码,你可以自己验证下:-P)。那么这里的this指针从哪里来呢?

  0056D9DA 8B08 mov ecx,dword ptr ds:[eax]

  0056D9D6 8B4424 14 mov eax,dword ptr ss:[esp+14]

  按照thiscall规范,ecx就保存了this指针,上面代码说明ecx是来自[esp+14]的,我们再往上看:

  0056D99C 8D5424 14 lea edx,dword ptr ss:[esp+14]
  0056D9A0 52 push edx
  0056D9A1 68 50BB5900 push CoralQQ.0059BB50
  0056D9A6 C64424 30 01 mov byte ptr ss:[esp+30],1
  0056D9AB 8B08 mov ecx,dword ptr ds:[eax]
  0056D9AD 68 C8BA5900 push CoralQQ.0059BAC8 ; ASCII QQUSER_DYNAMIC_DATA
  0056D9B2 50 push eax
  0056D9B3 8B41 54 mov eax,dword ptr ds:[ecx+54]
  0056D9B6 FFD0 call eax

  看到么 0056D99C lea edx,dword ptr ss:[esp+14]!!

  也就是说this指针和ptr2时由这个函数获得的,我们暂时以它的一个参数命名:QQUSER_DYNAMIC_DATA。

  但不幸的是,这个函数同样也是thiscall调用规范的,也就说也是需要得到this指针……不过不慌:

  0056D97F 51 push ecx
  0056D980 52 push edx
  0056D981 50 push eax
  0056D982 FF15 38AB5A00 call dword ptr ds:[5AAB38] ; BasicCtr.GetFriendQQData
  0056D988 8B4424 14 mov eax,dword ptr ss:[esp+14]

  0056D9AB 8B08 mov ecx,dword ptr ds:[eax]

  注意上面2段代码,QQUSER_DYNAMIC_DATA函数的this指针最终是[esp+14],而esp+14的数据是

  0056D97F 51 push ecx

  这段代码压入的。所幸的是GetFriendQQData是个导出函数(位于BasicCtrDll.dll),我们看看他的申明:

  int GetFriendQQData(struct IQQCore *,unsigned long,struct IQQData * *);

  上面这个push ecx实际上是压入了参数struct IQQData * *。

  所以现在的只要获得struct IQQCore *,和第二个神秘参数的含义就能实现显示IP的功能了。

1.3 IQQCore和Uin

  int GetFriendQQData(struct IQQCore *,unsigned long,struct IQQData * *);

  这个函数,从而获得那个struct IQQData *指针。但问题就是要调用这个函数必须要提供2个参数:IQQCore *和一个unsigned long(DWORD)的神秘数据。

  在开始正式分析前请各位思考下,如果要你编写一个能显示好友IP的函数,你需要先知道什么呢?

  至少需要知道要去获取哪个QQ好友吧。这个肯定是必须的,否则函数就没有执行的意义了

  现在我们跟踪下上面这个GetFriendQQData函数。在其入口点下断点。然后小心的把鼠标移动到QQ好友列表中某个头像上(需要使用CoralQQ……有点不厚道)。这时候应该程序就会被断下。

  因为正常理鼠标移至好友头像会显示信息卡片,CoralQQ会在下面显示IP信息,所以按照上一篇文章反汇编的代码,GetFriendQQData必然会调用。我们从堆栈里面找到这个unsigned long对应的数据:

  0x1A53836

  因为是DWORD数据,把它转化为10进制看看:27605046

  这不是我的QQ号码么……的确,先前鼠标是移动在我的头像上了。

  所以可以猜测这个unsigned long就是好友的QQ号码。

  经过多次验证,的确如此。

  所以,这个神秘的unsigned long明确:他是要获取好友信息的号码,顺便补充下,这个unsigned long在QQ中可是有专门名字的:Uin

  接下来就是struct IQQCore*,我想他的作用从名字中应该就能猜出大概来。虽然具体他的结构我还没弄清,但可以肯定他好比是QQ程序内核信息的指针。而现在最关键的问题是如何去获得它。

  如果你研究过BasicCtrlDll.dll中导出的函数,你会发现几乎一半的函数的参数都由这个IQQCore,比如:

  int GetCurrentStatus(struct IQQCore *,int *)
  int GetCurrentUin(struct IQQCore *,unsigned long *)
  int GetFriendStat(struct IQQCore *,unsigned long)

  那么我们就拦截其中的一部分函数,看看提供给他们的这个struct IQQCore *参数具体是什么。

  如果你的确这样做了,那么会发现所有的函数,无论在什么时候,这个struct IQQCore *的值是确定的唯一的。如果你这个IQQCore *指针的地址区域下内存写入断点的话会发现struct IQQCore *实际上在QQ进行登录初始化时就创建了,以后就不再被修改。

  所以现在的问题就很简单了,我们可以暂时不用理会struct IQQCore *到底是什么,只要能获取到他就ok

   1.4 如何获取struct IQQCore *?

  如果只是调试个QQ,得到struct IQQCore *是非常容易的,但记住我们是要编写外挂。所以就是说我们要问:外挂如果通过程序来获取这个指针呢?

  最野蛮的办法:把外挂写成一个调试器,模拟手工调试的过程,获得这个IQQCore *。ok,我很佩服你这样做,这也是我原先的想法。虽然写这个一个调试器是很简单的,但是他基本上没有实际意义,因为不能应对各种版本的QQ程序,而且也就无法再使用OD这些调试器来调试你的程序了

  下面的方法要感谢明日帝国(sunwangme)写的教程了,可能一开始你看他教程会不知所云,但相信你现在去看他的文章就会很有感触。

  上面说过这个IQQCore 是不会改变的,而且BasicCtrlDll.dll导出的那么多函数又偏偏要用到他,为什么不去拦截一个有IQQCore *参数的函数来获取这个IQQCore *呢?

  具体的做法我会在编写插件时说明。可能你会想拦截导出函数(也就是API)不也是调试器作的事么?其实也有别的办法,这里就是用API Hook技术来实现的(建议先了解下win32的hook技术)。

  问题是API SetWindowsHookEx是不可能hook一个API的。所以这里要用比较“底层”的办法:

  一个导出函数的入口内存地址可以用GetProcAddress API获取,我们只要修改程序的代码,使得他在原先函数入口点执行时跳入我们的函数取执行,然后再跳转回来即可。(实际做法不是这样,在说明如何编写插件时我会说到)

  现在问题就是要在BasicCtrlDll.dll导出函数中选取一个比较理想的函数取拦截,得到IQQCore

  要拦截的函数应该具有如下特点:

  函数形参简单,最好只有IQQCore *一个参数

  函数能尽早被调用,这样能及早的获取IQQCore *

  函数不能是thiscall规范的,也就是说函数必须是全局函数,不是一个成员函数

  为何要这些特点应该都能理解,我对最后一个做下说明,thiscall规范的函数还需要一个类的this指针地址,这会给拦截造成一定麻烦(今后就会遇到这样的情况,以后再讨论)。

  最终我们选取的函数是QQHelperDll.dll中的一个导出函数:

  int IsLogin(struct IQQCore *);

  很满足我们的要求,而且用OD跟踪发现,他在QQ登录后就不停的调用,太爽了……

  总结一下:要获取struct IQQCore *通过拦截API获取参数实现,我们拦截的函数选用了IsLogin()。

   1.5 Uin和struct IQQData *

  现在还有2个问题要去研究,第一,在调用GetFriendQQData时候,Uin(就是那个unsigned long参数)如何确定呢?

  这个问题要等到我介绍插件编写时在讨论,但可以先做下暗示,我们编写的插件是需要在打开和好友聊天的对话框以及将鼠标移动到好友头像上时,显示对方的IP信息,就第一个情况:QQ的聊天对话框里面不就有好友的QQ号码(Uin)么?第二个情况:虽然探出的信息卡片没有号码,但可以猜测QQ也是先需要获取相关信息的。

  现在我们再说说这个IQQData *的作用,看看BasicCtrlDll.dll的一些导出函数:

  long GetQQDataBuf(struct IQQData *,char const *,class CString &)
  long GetQQDataStr(struct IQQData *,char const *,class CString &)

  其中需要IQQData *参数,那么我们看看这些函数有什么作用呢?以GetQQDataStr在CoralQQ中的使用情况为例:

  0056DAC4 57 push edi
  0056DAC5 68 DCBA5900 push CoralQQ.0059BADC ; ASCII NAME
  0056DACA 52 push edx
  0056DACB FF15 44AB5A00 call dword ptr ds:[5AAB44] ; BasicCtr.GetQQDataStr
  0056DAE5 8B4424 24 mov eax,dword ptr ss:[esp+24]
  0056DAE9 8D56 34 lea edx,dword ptr ds:[esi+34]
  0056DAEC 52 push edx
  0056DAED 68 ECBA5900 push CoralQQ.0059BAEC ; ASCII REMARK_REALNAME
  0056DAF2 50 push eax
  0056DAF3 FF15 44AB5A00 call dword ptr ds:[5AAB44] ; BasicCtr.GetQQDataStr
  0056DAF9 8B5424 30 mov edx,dword ptr ss:[esp+30]
  0056DAFD 8D4E 38 lea ecx,dword ptr ds:[esi+38]
  0056DB00 51 push ecx
  0056DB01 68 FCBA5900 push CoralQQ.0059BAFC ; ASCII COUNTRY
  0056DB06 52 push edx
  0056DB07 FF15 44AB5A00 call dword ptr ds:[5AAB44] ; BasicCtr.GetQQDataStr
  0056DB0D 8B4C24 3C mov ecx,dword ptr ss:[esp+3C]
  0056DB11 8D46 3C lea eax,dword ptr ds:[esi+3C]
  0056DB14 50 push eax
  0056DB15 68 04BB5900 push CoralQQ.0059BB04 ; ASCII PROVINCE
  0056DB1A 51 push ecx
  0056DB1B FF15 44AB5A00 call dword ptr ds:[5AAB44] ; BasicCtr.GetQQDataStr

  这些代码是在获取IP信息后出现的,猜猜在做什么呢?NAME,COUNTRY,PROVINCE这些词汇来看,应该是在获取当前要显示ip好友的名字、国籍、省份。而具体的执行函数就是GetQQDataStr。

  再结合这个函数的参数来看,IQQData *,很有可能就是存放着一个用户相关信息的结构

  事实也是如此的,到目前为止编写QQ外挂插件的条件已经具备。

 

2.编写QQ显IP插件

  2.1 编写加载外挂dll的程序

  这里采用VC作为开发环境,我使用的是VS2005

  首先说下大体的思路:

  这里仿造coralQQ一样,将真正的外挂代码写入dll,然后编写一个exe文件去加载qq.exe,并把我们写得dll注入。

  这一步在前一篇文章中已经给出了具体代码。

  2.2 编写插件主体

  感觉自己如果一涉及具体代码就回说不来话,所以这次就先谈思路,然后给出部分代码。

  dll要做的事:

  将我们前面分析过的关键API挂钩,以便得到IQQCore *,Uin,以及在qq聊天窗口弹出时进行捕获以便显示IP信息。

  要做到上面的要求,首先就是要在dll加载后开始做API hook的工作。

  不过这件事情不能在DllMain里面写,推荐的做法是在DllMain里面创建一个新的线程,线程的执行函数我们设为:WorkerProc(VC编写的话推荐用_beginthread创建这个线程,否则无法使用CRT函数),为什么不能直接在DllMain里面写我稍候分析

  然后就是在WorkerProc里面写入具体的hook代码。

  1.拦截QQHelperDll.dll中的IsLogin函数

  前面分析过了,IQQCore *指针可以通过拦截这个API来获取。

  这里我们采用的是《windows核心编程》中推荐的拦截API hook的办法(修改入口地址跳转相对他烦了些,而且我们这里只是想做hook):通过动态修改API调用者模块的IAT表,将原先API的入口地址替换成我们函数的。具体的替换代码我们就采用书中提供的了的ReplaceIATEntryInOneMod(见前一篇)。

  现在要注意一个问题,我们用来替换原先API的函数必须和原函数采用同样的调用规范,这个IsLogin本身就是cdecl得,所以只要用个形参和返回值一样的函数取替换即可:

  给出实现本功能的代码:

  typedef int (*OrgIsLogin)(DWORD ptrIQQCore);
  int PokeIsLogin(DWORD ptrIQQCore)
  {
   global_ptrIQQCore = ptrIQQCore;
   return ((OrgIsLogin)(PROC)OrgIsLoginProc)(ptrIQQCore);
  }

  ////下面代码在WorkerProc函数中///

  OrgIsLoginProc = GetProcAddress( GetModuleHandleA(QQHelperDll.dll),?IsLogin@@YAHPAUIQQCore@@@Z);
  if (ReplaceIATEntryInOneMod(QQHelperDll.dll,OrgIsLoginProc,(PROC)&PokeIsLogin,GetModuleHandleA(qq.exe)))
  {
   //替换成功
  }
  //

  今后,只要QQ.exe调用IsLogin,我们的PokeIsLogin函数就会调用,并把IQQCore纪录下来,注意替换的函数要最后去调用原函数,否则就会让程序崩溃。

  2.拦截SetForegroundWindow API

  拦截该API是因为每次弹出新的QQ聊天窗口时,CQQApplication.dll都会去调用它,以便把聊天窗口至于前景显示。所以拦截CQQApplication.dll对他的调用就能在由新的聊天窗口出现时调用我们插件的代码去显示IP信息,而不必傻傻的去写循环等待了

  这里就要注意SetForegroundWindow 是采用stdall调用规范的,所以别忘在替换函数名前加上__stdcall(或WINAPI宏)标记。

  该部分的替换代码如下:

  typedef BOOL ( __stdcall *OrgSetForegrandWindow)(HWND hWnd);
  extern C BOOL APIENTRY OnQQWndShow(HWND hWnd)
  {
   bool trueResult = ((OrgSetForegrandWindow)(PROC)OrgFuncProc)(hWnd);
   return trueResult;
  }
  //下面代码在WorkerProc函数或由其调用的函数中
  OrgFuncProc = GetProcAddress( GetModuleHandleA(user32),SetForegroundWindow);
  if (ReplaceIATEntryInOneMod(user32.dll,OrgFuncProc,(PROC)&OnQQWndShow,global_hCQQAppModule))
  {
   //替换函数入口成功
  }

  其中global_hCQQAppModule是CQQApplication.dll在qq.exe中的模块句柄,可以调用GetModuleHandleA(CQQApplication.dll)来实现。不过现在存在一个问题

  用od加载qq.exe就会发现实际上CQQApplication.dll并不是在qq.exe启动后加载的,而是在登录以后。所以如果我们的dll插件在被加载之后立刻调用GetModuleHandleA的话就会返回NULL,那么之后的函数替换就不可能实现了。

  这里我用了个比较笨的办法,在替换SetForegroundWindow前编写循环不断的去掉用GetModuleHandleA,直到返回非null,而为了防止频率过快可以在循环中加上Sleep函数。(之前也尝试loadlibrary提前加载,但一直失败。还有中办法就是从前面的IsLogin来判断是否登录了QQ。但没有试验过。总之如果你有比较好的办法也希望告诉我)

  这部分的代码如下:

  bool WaitforLogon()
  {
   while(!bIsDllUnload)
   {
    global_hCQQAppModule = GetModuleHandleA(CQQApplication.dll);
    if (global_hCQQAppModule) return true;
    Sleep(300);
   }
   return false;
  }


  这就是为什么前面说过不能再dllmain里面作函数替换的工作了,否则会导致dllmain无法退出,从而会锁死整个qq

 

3.获取Uin

  现在IQQCore *已经获得了,同时只要在OnQQWndShow中写入得到用户IP信息的代码,再使用FindWindow的方法把原先的广告去除,再创建个Edit或者Static来显示我们的数据即可。

  但之前还要得到Uin,也就是对方好友的QQ号。

  其实这里有个很笨的办法:QQ聊天对话框中有对方的号码的:比如“&heaven(27605046)(后面是个性签名)。的确可以用FindWindow+GetWindowText获取,然后得到括号里面的数据就好了,但是是否有更简单办法呢?有

  这里要感谢明日帝国(sunwangme)的教程,这里就是用他的方法了,具体原理还是大家去他blog看吧(google一下)

  大体的方法是:在QQ准备显示聊天对话框时,上面“&heaven(27605046)(后面是个性签名)这段文字会调用位于QQBaseClassInDll.dll中的CAllInOneStatusBar::SetUin(unsigned long);这个函数,其中参数就是我们要的QQ号了。而且可以保证CQQApplication.dll仅仅在要显示对话框前才会调用它。(再次感谢明日帝国)

  所以和上面一样,这次拦截CAllInOneStatusBar::SetUin(unsigned long);

  OrgSetUINProc =
  GetProcAddress(GetModuleHandleA(QQBaseClassInDll.dll),?SetUin@CAllInOneStatusBar@@QAEX_J@Z);
  if (ReplaceIATEntryInOneMod(QQBaseClassInDll.dll,OrgSetUINProc,(PROC)&Poke_GETUIDA,global_hCQQAllInOne)){
  }

  今后就会先调用我们的OrgSetUINProc,其中获取参数即可。

  不过要注意2个问题,第一这个QQBaseClassInDll.dll也不是一开始加载,更不是在登录后加载,而是在第一次要显示QQ聊天窗口才加载,不过比较幸运的是可以直接LoadLibrary把它先载过来。

  还有一个问题就是SetUin是thiscall规范的……哎,自己对thiscall还不是很了解,所以怕出错,那个OrgSetUINProc只能用naked调用规范了,同时要自己写汇编去完成细节的事:

  __declspec( naked ) int Poke_GETUIDA()
  {
   _asm
   {
    push ecx
    mov ecx,[esp+8]
    mov dwRecentUINA,ecx
    pop ecx
    jmp OrgSetUINProc
   }
  }

  ok,这样一切都完成了

  3.编写获取IP信息的代码

  先整理下上面我们3个替换函数的执行顺序:

  PokeIsLogin:最好执行,且今后不断被调用

  Poke_GETUIDA:在将要显示QQ聊天窗口时执行

  OnQQWndShow:在Poke_GETUIDA之后运行。

  所以可以保证在OnQQWndShow中可以得到我们获取IP信息所有必要的参数了。

  那么就开始编写获取IP的代码:

  按照前面分析的,先用BasicCtr.GetFriendQQData得到当前好友的IQQData *:

  DWORD MainHandle;
  if (ptrBasicCtr_GetFriendQQData==NULL || global_ptrIQQCore==NULL) return 0;
  _asm
  {
   mov edx,dwID
   mov eax,global_ptrIQQCore
   lea ecx,MainHandle
   push ecx
   push edx
   push eax
   call ptrBasicCtr_GetFriendQQData ; BasicCtr.GetFriendQQData
   add esp,0xC
  }

  其中dwID是Uin,即用DWORD保存的QQ号码

  global_ptrIQQCore就是前面获得的IQQCore *

  ptrBasicCtr_GetFriendQQData 是GetFriendQQData的入口地址,用GetProcAddress得到

  最终MainHandle将保存IQQData *,如果运行失败,会返回null

  接下来就是通过QQUSER_DYNAMIC_DATA(暂时命名)函数来得到动态信息类的指针:

  char *szQQUSER_DYNAMIC_DATA = QQUSER_DYNAMIC_DATA;
  DWORD tmpInfo;
  DWORD returnVal;
  tmpInfo = 0xba863a1e;

  if (mainHandle == NULL)
  {
   return NULL;
  }

  _asm
  {
   mov eax,mainHandle
   lea edx,returnVal
   push edx
   lea edx,tmpInfo
   push edx
   mov ecx,[eax]
   push szQQUSER_DYNAMIC_DATA
   push eax
   call [ecx+54h]
  }

  结果将保存在returnVal中,同样,如果执行错误returnVal=NULL

  最后就是去获取IP地址了

  char *szwProcotol = wProcotol;
  char *szRecentip=dwRecentIP;
  char *szwRecentPort=wRecentPort;
  char *szdwC2CIP=dwC2CIP;
  char *szwC2CPort=wC2CPort;
  char *szdwIP=dwIP;
  char *szwPort=wPort;
  bool GetDestIPInfo(DWORD ptrClassHandle,DWORD *ptrDestIp,DWORD *ptrDestPort)
  {
   if (ptrClassHandle==NULL || ptrDestIp==NULL || ptrDestPort==NULL) return false;
   //Using Std Info Buffer
   _asm
   {
    pushad
    pushf
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestIp
    push edx
    push szRecentip
    push eax
    mov eax,[ecx+34h]
    call eax
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestPort
    push edx
    push szwRecentPort
    push eax
    mov eax,[ecx+30h]
    call eax
    popf
    popad
   }
   (*ptrDestPort) &= 0xFFFF;
   if ((*ptrDestIp) != NULL && (*ptrDestPort) != NULL) return true;
   _asm
   {
    pushad
    pushf
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestIp
    push edx
    push szdwC2CIP
    push eax
    mov eax,[ecx+34h]
    call eax
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestPort
    push edx
    push szwC2CPort
    push eax
    mov eax,[ecx+30h]
    call eax
    popf
    popad
   }
   (*ptrDestPort) &= 0xFFFF;
   if ((*ptrDestIp) != NULL && (*ptrDestPort) != NULL) return true;
   _asm
   {
    pushad
    pushf
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestIp
    push edx
    push szdwIP
    push eax
    mov eax,[ecx+34h]
    call eax
    mov eax,ptrClassHandle
    mov ecx,[eax]
    mov edx,ptrDestPort
    push edx
    push szwPort
    push eax
    mov eax,[ecx+30h]
    call eax
    popf
    popad
   }
   (*ptrDestPort) &= 0xFFFF;
   return ((*ptrDestIp) != NULL && (*ptrDestPort) != NULL);
  }

  ptrClassHandle是前面得到的returnVal,IP地址的DWORD数据和WORD的port信息将保存在后2个参数指针的地址中。(代码借鉴了CoralQQ)

  到目前为止已大功告成!其他的细节代码就请各位发挥吧

   4.扩展

  上面并没有介绍如何得到对方QQ版本的信息,其实和获取IP一样,你在CQQApplication.dll中找到wProcotol的字符串参考,就会发现它和获取IP的代码很像,直接抄下来用就是了。

珊瑚虫QQ显IP源代码 珊瑚虫QQ显示IP的源代码,非常难得的资料!要就赶快下载 立即下载

相关推荐

IPQQ V130106--QQ显IP--bug已修复

IPQQ V130106--QQ显IP--bug已修复

腾讯QQ珊瑚虫外挂原理分析

由于win nt 平台和win9x 的差异,珊瑚虫外挂因系统的不同而分开两种工作原理 分析工具: softice , OD 我的操作系统: win 2000 sp4 , win 98 分析对象: QQ2005贺岁版 珊瑚虫外挂  为了方便分析,我先把CoralQQ.dll和coralqq.exe先脱壳,这两个文件加的都是超弱壳,脱壳过程不在这里多说.  由于win

问道-正确认识人生的规律,只有从人生的规律中才能求得实实在在的名与利 2799

显示ip地址版QQ

可以显示ip地址的qq版本. -2009版本.

QQ显IP显隐身制作原理介绍转)

        到QQ2008为至,可爱的珊瑚版本显IP显隐身的QQ已经不能用了,前几天下载了比特版本,传美版本,极速版本用了一下,感觉没有珊瑚虫的版本好用,总之用习惯了一种版本,不乐意改变太多.于是抱着这种心态,我于前天下午认真研究了QQ显IP显隐身的原理,自己成功编译出了一款比较满意的显IP显隐身的QQ2008贺岁版本,以满足自己工作和学习的需要.下面我来谈一下显IP显隐身的基本原理.

happy5856的专栏 2315

QQ显ip显隐身原理分析(转)

qq彩虹显ip显隐身原理分析,与大家分享

qq显示对方ip

qq显示对方ip,只需发送截图即可。无毒高效,

QQ显好友IP地址插件2018版:支持最新版的QQ

NtrQQ是一款功能强大的QQ辅助增强插件,主要功能为QQ显IP和QQ去广告,为了应大家的需求,增加了一系列强悍的功能,但是从本质上,NtrQQ就是一款传统的QQ显IP插件。 此QQ显示ip插件特点: 1、图标全亮 本功能可本地开启所有业务图标(别人不可见),开启此功能后还可享受绿钻的特权:比如在群内可分享歌曲给你的群友。 2、本地会员 不需要缴费,能点亮超级会员图标(别人不可见),会员涂鸦、会员...

qq_40907977的博客 4549

IP解析(基于珊瑚虫QQ数据)

基于珊瑚虫QQ数据,把IP地址解析成一个地址信息。

木子版QQ作者专访

  《家用电脑》:您为什么对腾讯QQ这么深恶痛绝?  向右走:首先,我要更正一点,我并不是对QQ深恶痛绝,而是对腾讯的不负责任,甚至是违法的行为深  恶痛绝。腾讯在近几年的所作所为,实在是有悖于商业道德和社会责任。我想,对这些错误的做法,任  何人都有权利,也有义务提出批评。  《家用电脑》:当年的官司是不是让您放弃木子版QQ制作的主要原因?您觉得重新编译QQ的程序,会像  腾讯说的那样是违法行为

新人老周的专栏 3949

二阶高通滤波器硬件设计:5种RC到有源方案对比与选型

在传感器前端和音频信号调理中,二阶高通滤波器是抑制直流偏移与低频干扰的关键模块。其设计核心围绕截止频率、品质因数Q、增益与噪声四个指标展开,其中Q值决定滤波器在转折频率附近的响应形状,如Butterworth最大平坦响应需Q=0.707。从纯无源RC电路到引入运放的有源拓扑,演变逻辑在于解决负载效应、提供增益并借助反馈提升Q值。实际工程中,RC级联、VCVS(Sallen-Key)、状态变量及回转器模拟电感等方案各有适用场景:RC电路简单但Q值受限,Sallen-Key单运放即可实现高Q,状态变量适合Q值独

weixin_32770423的博客 97

基于ESP32-P4的低成本旋转式激光雷达系统设计

激光雷达(LiDAR)作为机器人环境感知的核心传感器,其本质是融合光学测距、机电控制与实时数据处理的嵌入式系统。理解时间飞行(ToF)原理与机械旋转耦合机制,是实现高精度点云采集的基础;而系统级误差源——如计时抖动、电机惯性、信号串扰和温漂——直接决定实际建图鲁棒性。在资源受限场景下,选用RISC-V双核MCU(如ESP32-P4)可兼顾实时控制与网络服务能力,通过硬件时间戳捕获、S型加减速轨迹规划、旋转变压器供电及双Bank OTA等工程手段,有效平衡分辨率、帧率与稳定性。本文围绕360°旋转式激光雷达的

weixin_36231030的博客 430

浅谈显 ipqq 发展历程

 浅谈显 ipqq 发展历程 【附显 ipqq原理】声明,本文章未经过疯狂绅士与runjin的同意不得转载。浅谈IPQQ软件的技术发展轨迹疯狂绅士、RunJin摘要:本文先给出了IPQQ的界定,并介绍了不同历史时期具有里程碑意义的IPQQ。根据几个具有里程碑意义的IPQQ,作者简要的分析了其技术特点,并以此阐述IPQQ软件的发展轨迹。关键字:   IPQQ、破解、木子QQ、外挂、珊瑚虫外挂

成功者找方法,失败者找借口! 1865

ios生成海报使用dom-to-image中toPng报错isTrusted:true

主要是ios中safari浏览器解析错误,引起的报错,主要引起原因toPng会调用toSvg,toSvg直接将html标签内容通过svg中foreignObject标签直接包裹,然后转base64, 通过data:image/svg+xml;解决办法:二次生成(调用两次),判断ios or 安卓,生成第一次生成,不用参数,例如:scale: 3,在第二次生成再进行清晰度操作,避免多次生成,引起性能消耗。以上种种很多案例,使用canvase进行转换的,标签丢失,直接toPng版本的,兼容性问题。

weixin_44601570的博客 2105

如何查看对方的QQ登录是IP地址的方法

与好友在网络上相互传输资料时,有时先要知道对方计算机的IP地址,才能与对方建立信息传输通道;那么对方的 IP地址该如何搜查得到呢?这样的问题你也许会嗤之以鼻,的确,查询对方计算机的IP地址,实在简单得不值得一提;可是,要让你列举出多种IP地址搜查方法时,你可能就感到勉为其难了。下面,本文就对如何快速、准确地搜查出对方好友的计算机IP地址,提出如下几种方法,相信能对大家有所帮助!   1、邮件查询法...

tingguan的博客 3万+

BLHeli_S电调刷Bluejay固件实战:解锁双向DShot与RPM滤波全攻略

本文详细介绍了如何为BLHeli_S电调刷写开源Bluejay固件,以解锁双向DShot协议与RPM滤波等关键功能。内容涵盖硬件兼容性检查、固件刷写全流程、双向DShot启用与OSD报错解决方案,并对48kHz与96kHz PWM频率的选择提供了实战建议,帮助穿越机玩家在不更换硬件的前提下显著提升飞行性能与操控体验。

weixin_29188997的博客 511

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的远距离停车位检测系统(深度学习代码+UI界面+训练数据集)

本文详细介绍了基于YOLOv5/YOLOv6/YOLOv7/YOLOv8深度学习模型的远距离停车位检测系统的完整实现方案。该系统能够准确识别200米范围内的停车位状态,为智能停车场管理系统、自动驾驶车辆提供关键的视觉感知能力。文章涵盖算法原理、数据集构建、模型训练、系统实现及优化策略,并提供完整的代码实现和UI界面设计。关键词:YOLO系列,深度学习,停车位检测,计算机视觉,智能交通系统pythonimport cv2import os"""创建数据存储目录""""""定时采集图像"""

YOLO 185

显IPQQ教父"木子"怒斥珊瑚虫QQ 赚取非法巨额暴利 欺骗善良网民

 曾经的木子版 vs 现在的某些修改版  总有些人把木子版和现在的某些什么虫版本的修改版QQ联系到一起去谈,这确实让我非常无奈。因为这两个东西之间根本就没有任何可比性,道不通不相为谋。  曾经的木子版,秉承了严谨的软件开发及测试流程,对这个东西的品质有严格的控制。我们仅仅去除了腾讯QQ软件中,影响用户使用体验的过多广告显示。并且,我们遵循着国家相关法规,及知识产权保护的相关国际准则

FreeXploiT 5510

【25届数字IC秋招总结】面试经验10——紫光同芯

25届数字ic面试记录——紫光同芯

lalala0635的博客 862

论珊瑚虫并不光彩的历史

皮卡丘投递:提及珊瑚虫很多人应该立马就想到QQ,是的,珊瑚虫作为一款经典的QQ显IP程序已经在QQ界称霸了好多年了,论及腾讯和珊瑚虫的一系列纠纷,更多的网民自然会站在珊瑚虫这边,因为腾讯的确做了太多利欲熏心的事情,但是他们的选择真的是对的吗?首先虽然珊瑚虫去掉了腾讯QQ的广告方便了广大QQ用户免除该死的广告的骚扰,但是这种做法的确侵犯了腾讯的权益,我想这点大家也不会否认.显IPQQ发展至今已经司空

5206

告别JavaScript!C#通过WebAssembly在浏览器中实现“原生级“性能

C# + WebAssembly:前端开发的革命性替代方案 摘要:本文探讨了C#通过WebAssembly取代JavaScript开发前端应用的新趋势。文章分析了JavaScript的局限性(弱类型、回调地狱等),并详细介绍了C#+WebAssembly组合的优势:强类型系统、高性能、多线程支持和代码重用。技术实现方面,阐述了C#通过Mono/.NET编译为WebAssembly字节码的原理,重点介绍了Blazor框架的两种模式。文章还提供了实战指南,包括项目创建、多线程配置,以及一个与ChatGPT AP

墨夶的博客 945

UWB 多传感器通信的纳秒无线位置和时钟同步方法

利用UWB高精度的特点,实现多节点下的无线时钟同步和定位。通过优化硬件架构,减少不确定延迟的影响,系统可以提供纳秒级时钟同步消息

DT程序员的博客 1325

木子专访:腾讯七宗罪

腾讯,作为中国最大的即时消息软件运营商,拥有同时在线用户高达3000万,其主力产品腾讯QQ甚至已经影响到了很多年轻一代的生活交流方式。这样一个企业,在业界被千万光环所笼罩,但是它真的值得用户信赖和尊敬吗?在这些年,腾讯到底是如何对待自己的产 品,如何对待自己超过3亿的庞大用户呢?让我们一起来看一个真实的腾讯。  抄袭,永无止境       之所以把“抄袭”作为腾讯的第一个关键词,原因很简单,回顾一

软体疯子专栏 3971

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

珊瑚虫外挂原理分析[zt]

作者: RunJin runjin000@yahoo.com.cn分析工具: softice , OD我的操作系统: win 2000 sp4 , win 98分析对象: QQ2005贺岁版 珊瑚虫外挂为了方便分析,我先把CoralQQ.dll和coralqq.exe先脱壳,这两个文件加的都是超弱壳,脱壳过程不在这里多说. 由于win nt 平

wensir的老窝 3105

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

没了珊瑚虫你用谁?八大QQ主流修改版大比拼

北京市法院依法认定“珊瑚虫版QQ”侵权,责令珊瑚虫版QQ作者向腾讯致歉并赔偿多达10万元人民币经济损失。珊瑚虫工作室网站上已不再提供珊瑚虫版QQ的下载,而只有“珊瑚虫增强包”、“珊瑚虫工具栏”等插件下载。从此以后珊瑚虫版QQ将离开我们的视线,而只保留“珊瑚虫增强包”等不更改QQ软件的插件,以规避法律风险。   显IP、无广告、可定制的QQ一直是用户们的至爱,没有了珊瑚虫,...

weixin_33924220的博客 577

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

上一篇: 想加薪,你是否太难为老板了
下一篇: 评“最牛B”程序员招聘广告与“大学生搓澡工”两事件 【 金旭亮 】
freexploit
博客等级 码龄22年 315粉丝 778原创
评论 7
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值