吾爱汇编

 找回密码
 立即注册

QQ登录

绑定QQ避免忘记帐号

查看: 5839|回复: 16

[原创逆向图文] 懒人逆向豪迪QQ群发的方法

  [复制链接]
李沉舟 发表于 2017-3-24 17:26 | 显示全部楼层 |阅读模式

本帖最后由 镜中神无 于 2017-3-24 20:54 编辑

QQ豪迪群发这玩意确实是被玩烂了,我汗……
听说每月教程前3有大大的奖励,我这个菜鸡也就厚着脸皮来凑个热闹。
我个人的思路是:
1.找到全局变量
2.写个DLL,导入进去,在该全局变量上写下内存写入断点
3.中断的时候就修改全局变量的值
4.HOOK CreateFileA,解决掉自校验
(这样搞能不能发我也不知道,我用的是QQ国际版,无法测试)
按传统套路来说是手动去自校验,逆向。但我是懒人,听说懒是聪明的表现,所以能懒我就尽量懒。其实也可以直接dll劫持,这样就不用考虑去自校验了,但是一个一个添加导出函数原型好累的说。

本人学艺不精,标准菜鸡一只,如有错误,还望指正,本人不胜感激。

豪迪软件官网:8222.com
下载地址:http://115.28.32.153/d/QQSend1.zip

解压,基本套路就不详细讲了。

直接OD载入,搜索UNICODE字符串引用,找到“已注册”字样,进入,发现如下代码。

005549B8  |.  803D DC405900>cmp     byte ptr [5940DC], 0       ; 验证BYTE PTR[5940DC]的值是否为0
005549BF  |.  74 10         je      short 005549D1                            ; 为0则JE跳,未注册
005549C1  |.  BA 8C4A5500   mov     edx, 00554A8C                    ;  已注册版本
005549C6  |.  8B83 FC020000 mov     eax, dword ptr [ebx+2FC]
005549CC  |.  E8 BF82FAFF   call    004FCC90
005549D1  |>  A1 D0405900   mov     eax, dword ptr [5940D0]


我们得到了全局变量的地址,5940DC(当然你也可以写个特征码搜索,再来个DLL劫持,直接通杀各版本豪迪)

个人去自校验的思路是,备份一份未修改的程序,HOOK CreateFileA,如果程序想要打开自身数据进行校验,我就修改参数,让它打开未修改的程序。
注意请把未修改的qqqf.exe改名为qqqf.exe.bak,放于同一目录下,否则代码无法正常工作!
请在XP及以上系统运行,否则代码无法正常工作!

下面贴上完整代码(Delphi)
实际用内存断点的时候需要考虑跨断点长度,此处为1字节长度,故无需考虑断点长度,跨分页等问题,实际使用的时候需要注意。
(想想自己能记得清的算法已经不多了,链表就是其中的一个,老师教的东西都还回去了,唉……)
[Delphi] 纯文本查看 复制代码
library Patch;

uses
  Windows,
  SysUtils,
  Classes;

type
  EXCEPTION_POINTERS = record
    ExceptionRecord: ^_EXCEPTION_RECORD;
    ContextRecord: ^_CONTEXT;
  end;
  PEXCEPTION_POINTERS = ^EXCEPTION_POINTERS;

  PLink = ^Link; //线程单向链表结构,触发单步异常需要使用
  Link = record
    dwThreadId: Cardinal; //线程ID
    bStates: Boolean; //True需要写入全局变量,False不需要
    pNext: PLink;
  end;

const
  pVar: Pointer = Pointer($5940DC); //全局变量地址
  EXCEPTION_CONTINUE_EXECUTION = -1;
  EXCEPTION_CONTINUE_SEARCH = 0;

var
  bCode: array[0..4] of Byte;
  pApiFunc: Pointer;

  iBegin: Cardinal; //内存分页开始地址
  iEnd: Cardinal; //内存分页结束地址

  pHead: PLink = nil; //链表头部

  iProtect: Cardinal;

  bTrue: Boolean = True;
{$R *.res}
{$J-}

procedure GetJmpCode(pCode, pJmp: Cardinal; var bMachine: array of Byte); //计算jmp xxxxxxxx机器码,第一个参数是源地址,第二个是目标地址,第三个是储存机器码的数组变量
var
  iRVA: Cardinal;
begin
  iRVA := pJmp - pCode - 5;
  bMachine[0] := $E9;
  CopyMemory(@bMachine[1], @iRVA, 4);
end;

function MyCreateFileA(szFileName: PAnsiChar; dwDesiredAccess: Cardinal; dwShareMode: Cardinal; lpSecurityAttributes: PSECURITY_DESCRIPTOR; dwCreationDisposition: Cardinal; dwFlagsAndAttributes: Cardinal; hTemplateFile: Cardinal): Cardinal; stdcall
var
  sFile: string;
  wsFile: PWideChar;
begin
  //这里不考虑还原API头部5字节,直接调用W版本的API,XP及以上是A版转为W版,XP以下是W版转A版
  //故本代码无法运行在XP以下
  //而且本代码使用的VEH也是XP才支持的
  SetLength(sFile, 260);
  GetModuleFileName(0, PAnsiChar(sFile), 260);
  sFile := StrPas(PAnsiChar(sFile));
  if UpperCase(szFileName) = UpperCase(sFile) then
  begin
    sFile := ExtractFilePath(StrPas(szFileName));
    sFile := sFile + 'qqqf.exe.bak';
    GetMem(wsFile, (Length(sFile) + 1)  * 2);
    StringToWideChar(sFile, wsFile, Length(sFile) + 1);
    Result := CreateFileW(wsFile, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    FreeMem(wsFile);
  end
  else
  begin
    GetMem(wsFile, (Length(szFileName) + 1)  * 2);
    StringToWideChar(StrPas(szFileName), wsFile, Length(szFileName) + 1);
    Result := CreateFileW(wsFile, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
    FreeMem(wsFile);
  end;
end;

function VectoredHandler(ExceptionInfo: PEXCEPTION_POINTERS): Integer; stdcall
var
  pTmpLink: PLink;
  pJLink: PLink;
begin
  case ExceptionInfo.ExceptionRecord.ExceptionCode of
    EXCEPTION_ACCESS_VIOLATION: //越权访问异常
    begin
      if ExceptionInfo.ExceptionRecord.ExceptionInformation[0] = 1 then //写操作
      begin
        if (ExceptionInfo.ExceptionRecord.ExceptionInformation[1] >= iBegin) and (ExceptionInfo.ExceptionRecord.ExceptionInformation[1] <= iEnd) then
        begin
          if ExceptionInfo.ExceptionRecord.ExceptionInformation[1] = Cardinal(pVar) then
          begin
            if pHead = nil then //如果链表头部为空指针
            begin
              New(pHead);
              pHead.dwThreadId := GetCurrentThreadId();
              pHead.bStates := True;
              pHead.pNext := nil;
            end
            else
            begin
              New(pTmpLink);
              pJLink := pHead;
              while pJLink.pNext <> nil do //添加到链表末尾
              begin
                pJLink := pJLink.pNext;
              end;
              pJLink.pNext := pTmpLink;
              pTmpLink.bStates := True;
              pTmpLink.pNext := nil;
            end;
          end
          else
          begin
            if pHead = nil then //如果链表头部为空指针
            begin
              New(pHead);
              pHead.dwThreadId := GetCurrentThreadId();
              pHead.bStates := False;
              pHead.pNext := nil;
            end
            else
            begin
              New(pTmpLink);
              pJLink := pHead;
              while pJLink.pNext <> nil do //添加到链表末尾
              begin
                pJLink := pJLink.pNext;
              end;
              pJLink.pNext := pTmpLink;
              pTmpLink.bStates := False;
              pTmpLink.pNext := nil;
            end;          
          end;
          VirtualProtect(Pointer(iBegin), iEnd - iBegin + 1, 4, iProtect);
          ExceptionInfo.ContextRecord.EFlags := ExceptionInfo.ContextRecord.EFlags or $100; //设置线程TF位
          Result := EXCEPTION_CONTINUE_EXECUTION; //处理异常
        end
        else
          Result := EXCEPTION_CONTINUE_SEARCH;
      end
      else
        Result := EXCEPTION_CONTINUE_SEARCH;
    end;
    EXCEPTION_SINGLE_STEP:
    begin
      if pHead = nil then
        Result := EXCEPTION_CONTINUE_SEARCH
      else
      begin
        if pHead.pNext = nil then
        begin
          if pHead.dwThreadId = GetCurrentThreadId() then
          begin
            if pHead.bStates then
            begin
              CopyMemory(pVar, @bTrue, 1);
            end;
            VirtualProtect(pVar, 1, PAGE_READONLY, iProtect);
            Dispose(pHead);
            pHead := nil;
            Result := EXCEPTION_CONTINUE_EXECUTION;
          end
          else
            Result := EXCEPTION_CONTINUE_SEARCH;
        end
        else
        begin
          pTmpLink := pHead;
          pJLink := pHead;
          while pTmpLink <> nil do
          begin
            if pTmpLink.dwThreadId = GetCurrentThreadId() then
            begin
              pJLink.pNext := pTmpLink.pNext;
              if pTmpLink.bStates then
              begin
                CopyMemory(pVar, @bTrue, 1);
              end;
              VirtualProtect(pVar, 1, PAGE_READONLY, iProtect);
              Dispose(pTmpLink);
              Result := EXCEPTION_CONTINUE_EXECUTION;
              Exit;
            end;
            pJLink := pTmpLink;
            pTmpLink := pTmpLink.pNext;
          end;
        end;
      end;
    end;
  
    else
      Result := EXCEPTION_CONTINUE_SEARCH; //不处理异常
  end;
end;

procedure Main();
type
  TAddVectoredExceptionHandler = procedure (First: Cardinal; pFunc: Pointer); stdcall;
var
  hModule: Cardinal;
  iProtect: Cardinal;
  pFunc: TAddVectoredExceptionHandler;
  stInfo: SYSTEM_INFO;
begin
  hModule := GetModuleHandle('kernel32.dll');
  if hModule = 0 then
    hModule := LoadLibrary('kernel32.dll');
  pApiFunc := GetProcAddress(hModule, 'CreateFileA');
  pFunc := GetProcAddress(hModule, 'AddVectoredExceptionHandler');
  pFunc(1, @VectoredHandler);
  VirtualProtect(pApiFunc, 5, PAGE_EXECUTE_READWRITE, iProtect);
  GetJmpCode(Cardinal(pApiFunc), Cardinal(@MyCreateFileA), bCode); //计算JMP机器码
  CopyMemory(pApiFunc, @bCode, 5); //写入API头部,开始HOOK
  GetSystemInfo(stInfo);
  iBegin := (Cardinal(pVar) div stInfo.dwPageSize) * stInfo.dwPageSize;
  iEnd := (Cardinal(pVar) div stInfo.dwPageSize + 1) * stInfo.dwPageSize;
  VirtualProtect(pVar, 1, PAGE_READONLY, iProtect); //修改全局变量内存属性为只读
end;

exports
  main;

begin
  Main(); //安装APIHOOK,以及设置内存断点
end.


编译以后,把编译出来的Patch.dll放到群发器目录下,打开Lord_PE。
PE编辑器——目录——输入表后面的第一个按钮。
1.png
选中一项,右键——Add Import

2.png
点击确定,然后一路保存回去,打开就会发现有惊喜。
虽然弹出了网络验证失败的框框,但是我们还是注册成功了。
因为所有写入这个全局变量的代码都被我们用内存断点拦截下来了。
3.png

源码下载: Patch QQSend1.rar (46.91 KB, 下载次数: 65)

评分

参与人数 14威望 +1 HB +37 THX +7 收起 理由
lies + 1
消逝的过去 + 2
禽大师 + 1
sjtkxy + 1 + 1
bing_mao + 1
zxjzzh + 1 [吾爱汇编论坛52HB.COM]-学破解防破解,知进攻懂防守!
一路走来不容易 + 1
叶落花开 + 2
hnymsh + 2
丄床 + 2 + 1 [快捷评语] - 2017,让我们17学破解!
小莫同学 + 1 [快捷评语] - 吃水不忘打井人,给个评分懂感恩!
小小橙 + 1 [快捷评语] - 分享精神,是最值得尊敬的!
81NewArk + 4 + 1 [快捷评语] - 吃水不忘打井人,给个评分懂感恩!
Shark恒 + 1 + 20 + 1 [快捷评语] - 吃水不忘打井人,给个评分懂感恩!

查看全部评分

吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
Shark恒 发表于 2017-3-24 22:48 | 显示全部楼层

其实要是有时间的话,从头分析到结尾,给个精华也不为过。不过确实需要很多时间。。懒癌怎么搞。。
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
小莫同学 发表于 2017-3-27 18:09 | 显示全部楼层

支持表哥
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
灰太狼大王 发表于 2017-3-27 18:54 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
炮炮君 发表于 2017-3-27 20:32 | 显示全部楼层

学习了思路  很感谢楼主
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
风云神龙 发表于 2017-3-27 22:58 | 显示全部楼层
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
头像被屏蔽
冷丝 发表于 2017-3-29 09:38 来自手机端 | 显示全部楼层

提示: 作者被禁止或删除 内容自动屏蔽
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
ryanshum 发表于 2017-4-3 00:47 | 显示全部楼层

小白表示不懂
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
care 发表于 2017-4-3 09:22 | 显示全部楼层

学习一点是一点
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
泳嘉丨Raii 发表于 2017-4-3 10:12 | 显示全部楼层

这东西其实挺好用的!
吾爱汇编论坛-学破解,防破解!知进攻,懂防守!逆向分析,软件安全!52HB.COM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

警告:本站严惩灌水回复,尊重自己从尊重他人开始!

1层
2层
3层
4层
5层
6层
7层
8层
9层
10层

免责声明

吾爱汇编(www.52hb.com)所讨论的技术及相关工具仅限用于研究学习,皆在提高软件产品的安全性,严禁用于不良动机。任何个人、团体、组织不得将其用于非法目的,否则,一切后果自行承担。吾爱汇编不承担任何因为技术滥用所产生的连带责任。吾爱汇编内容源于网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除。如有侵权请邮件或微信与我们联系处理。

站长邮箱:SharkHeng@sina.com
站长QQ:1140549900


QQ|RSS|手机版|小黑屋|帮助|吾爱汇编 ( 京公网安备11011502005403号 , 京ICP备20003498号-6 )|网站地图

Powered by Discuz!

吾爱汇编 www.52hb.com

快速回复 返回顶部 返回列表