李沉舟 发表于 2017-3-24 17:26

懒人破解豪迪QQ群发的方法

本帖最后由 镜中神无 于 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 , 0       ; 验证BYTE PTR的值是否为0
005549BF|.74 10         je      short 005549D1                            ; 为0则JE跳,未注册
005549C1|.BA 8C4A5500   mov   edx, 00554A8C                  ;已注册版本
005549C6|.8B83 FC020000 mov   eax, dword ptr
005549CC|.E8 BF82FAFF   call    004FCC90
005549D1|>A1 D0405900   mov   eax, dword ptr


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

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

下面贴上完整代码(Delphi)
实际用内存断点的时候需要考虑跨断点长度,此处为1字节长度,故无需考虑断点长度,跨分页等问题,实际使用的时候需要注意。
(想想自己能记得清的算法已经不多了,链表就是其中的一个,老师教的东西都还回去了,唉……)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 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 := $E9;
CopyMemory(@bMachine, @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 = 1 then //写操作
      begin
      if (ExceptionInfo.ExceptionRecord.ExceptionInformation >= iBegin) and (ExceptionInfo.ExceptionRecord.ExceptionInformation <= iEnd) then
      begin
          if ExceptionInfo.ExceptionRecord.ExceptionInformation = 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编辑器——目录——输入表后面的第一个按钮。

选中一项,右键——Add Import


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


源码下载:

Shark恒 发表于 2017-3-24 22:48

其实要是有时间的话,从头分析到结尾,给个精华也不为过。不过确实需要很多时间。。懒癌怎么搞。。{:6_217:}

小莫同学 发表于 2017-3-27 18:09

支持表哥{:5_188:}

灰太狼大王 发表于 2017-3-27 18:54

厉害,不过看不懂啊

炮炮君 发表于 2017-3-27 20:32

学习了思路很感谢楼主

风云神龙 发表于 2017-3-27 22:58

支持楼主了

冷丝 发表于 2017-3-29 09:38

ryanshum 发表于 2017-4-3 00:47

小白表示不懂{:5_187:}

care 发表于 2017-4-3 09:22

学习一点是一点

泳嘉丨Raii 发表于 2017-4-3 10:12

这东西其实挺好用的!
页: [1] 2
查看完整版本: 懒人逆向豪迪QQ群发的方法