据统计90%查看本帖的人,都已经注册本站了哦
您需要 登录 才可以下载或查看,没有账号?立即注册
×
某网游登录的时候添加了验证码验证(如图所示),如果要实现验证码的识别,通常方式是截图识别或者直接提取验证码图片
但是这游戏比较蛋疼,图片框的大小和验证码图片的大小有误差,导致部分验证码图片会在窗口中显示不完全
为了提高识别率,采用内存中读取验证码图片数据的方法会比较好
既然游戏需要获取验证码,那肯定要先和服务器做通讯,下断send WSASend
当按下登录按钮的时候,WSASend被触发,且只触发一次,说明这个包很可能就是请求验证码图片的
alt+k查看栈内的数据,有一个函数名为GetCaptchaForGame,如图所示
根据函数名我们可以得出这个函数为游戏获取验证码图片的函数。着重分析这个函数。
在函数尾部下断,游戏在弹出验证码之前就已经触发断点,说明该函数仅作为获取验证码数据的功能。
观察函数参数,该函数为5个参数,如图所示
我们经过观察,逐一分析,可以发现参数5为验证码图片的数据大小,参数4位验证码图片的数据
因为验证码无论获取失败还是错误都会经过这里,我们可以找到更合适的地方去HOOK。
既然这里是仅仅作为验证码的获取的函数,那我们看一下参数4 和参数5 会发现都是来自[ebp-0x40]这个架构体
这个结构体指针由ecx传递,如图所示,在调用处传过来
那我们返回上一层分析,ecx来自[ebp-0x3c]
让函数执行到返回,发现od一直不会把进程挂起了,而且游戏的验证码识别框出来了,说明这个函数内已经执行了创建窗口的代码
那我们分析这个函数内和[ebp-0x3c]的地方。
在靠后一点就可以看到很有用的东西了
我们一直步过过去,可以发现到0071FCC1 FF15 4433E300 call dword ptr ds:[0xE33344]执行之后我们的图片框有了数据,那我们在这里HOOK就可以了
下面是HOOK代码的编写,我们可以采取这样一个流程
挂全局钩子,dll注入后判断进程是否为游戏进程,如果是则挂WH_GETMESSAGE类型的钩子,当窗体的类名为SpLoginDialog时安装钩子
EXE部分,我们同样挂WH_GETMESSAGE类型的钩子,用于接收dll的数据
下面代码编写
注入DLL
.版本 2
.子程序 injectDll, 整数型, 公开
.参数 dllPath, 文本型
hHook = SetWindowsHookEx (#WH_GETMESSAGE, 到整数 (&myFun), LoadLibrary (dllPath), 0)
返回 (hHook)
dll的钩子回调
.版本 2
.子程序 myFun, 整数型
.参数 nCode, 整数型
.参数 wParam, 整数型
.参数 lParam, 整数型
.局部变量 字节集, 字节集
.局部变量 hWnd, 整数型
.局部变量 className, 文本型
字节集 = 指针到字节集 (lParam, 24)
hWnd = 取字节集数据 (字节集, #整数型, 1)
.判断开始 (hHook = 0)
.判断开始 (-1 ≠ 寻找文本 (取执行文件名 (), “kartrider”, , 真))
className = 取空白文本 (255)
GetClassNameA (hWnd, className, 254)
.判断开始 (-1 ≠ 寻找文本 (className, “SpLoginDialog”, , 真))
_setAllHook ()
.默认
.判断结束
.默认
.判断结束
.默认
.判断结束
返回 (CallNextHookEx (hHook, nCode, wParam, lParam))
卸载钩子
.版本 2
.子程序 unHook, , 公开
UnhookWindowsHookEx (hHook)
下面是HOOK代码的编写,代码执行到我们的HOOK的地方之后,执行postData()函数
.版本 2
.子程序 setHookCapData
.局部变量 hookAddr, 整数型
.局部变量 oldFlag, 整数型
.局部变量 lenth, 整数型
.局部变量 newCode, 字节集
hookAddr = 7470237
lenth = _取子程序地址 (&myShellCode) - hookAddr + 3 - 5
newCode = { 233 } + 到字节集 (lenth)
VirtualProtect (hookAddr, 5, #PAGE_EXECUTE_READWRITE, oldFlag)
写到内存 (newCode, hookAddr, )
VirtualProtect (hookAddr, 5, oldFlag, oldFlag)
.子程序 myShellCode
' _asm:mov ecx,dword [ebp-0x3C]
' _asm:mov dword [ecx+0x8],eax
' _asm:pushad
' _asm:pushfd
' _asm:add ecx,0x40
' _asm:push ecx
' _asm:push eax
_取子程序地址 (&postData)
' _asm:call eax
' _asm:popfd
' _asm:popad
' _asm:push 0x71FCA3
' _asm:retn
postData函数负责把对应信息投递给EXE
.版本 2
.子程序 postData, 整数型
.参数 hWnd, 整数型
.参数 capMsg, 整数型
.局部变量 hWnd_App, 整数型
hWnd_app = FindWindow (“daskjdsa”, 字符 (0))
.如果真 (hWnd_app > 0)
PostMessage (hWnd_app, 621, hWnd, capMsg)
.如果真结束
g_hWnd = hWnd
返回 (0)
EXE运行后,先安装全局钩子,然后再安装自己的消息钩子
.版本 2
.子程序 __启动窗口_创建完毕
' _验证码初始化 ()
SetWindowsHook (3, &钩子回调) ' 安装消息钩子,用于启动线程
injectDll (“tools.dll”)
EXE的钩子回调
.版本 2
.子程序 钩子回调, 整数型
.参数 nCode, 整数型
.参数 wParam, 整数型
.参数 lParam, 整数型
.局部变量 MSG, MSG
.局部变量 字节集, 字节集
.局部变量 myMsg, myMsg
.局部变量 data, 整数型
字节集 = 指针到字节集 (lParam, 24)
MSG.hwnd = 取字节集数据 (字节集, #整数型, 1)
MSG.message = 取字节集数据 (字节集, #整数型, 5)
MSG.wParam = 取字节集数据 (字节集, #整数型, 9)
MSG.lParam = 取字节集数据 (字节集, #整数型, 13)
.如果真 (MSG.message = 621)
_getCapData (MSG.wParam, MSG.lParam)
.如果真结束
返回 (CallNextHookEx (0, nCode, wParam, lParam))
图片框显示完整的验证码图片
.版本 2
.支持库 const
.子程序 _getCapData, 逻辑型
.参数 hWnd, 整数型
.参数 capMsg, 整数型
.局部变量 lenth, 整数型
.局部变量 processId, 整数型
.局部变量 hProcess, 整数型
.局部变量 capData, 字节集
.局部变量 dataPoint, 整数型
.局部变量 resultData, 字节集
.局部变量 ret, 逻辑型
.局部变量 data, 字节集
processId = 0
hProcess = 0
lenth = 0
GetWindowThreadProcessId (hWnd, processId)
.判断开始 (processId = 0)
返回 (假)
.默认
hProcess = OpenProcess (#PROCESS_ALL_ACCESS, 0, processId)
.判断开始 (hProcess = 0)
返回 (假)
.默认
ReadProcessMemory (hProcess, capMsg, lenth, 4, 0)
.判断开始 (lenth = 0)
CloseHandle (hProcess)
返回 (假)
.默认
ReadProcessMemory (hProcess, capMsg - 4, dataPoint, 4, 0)
.判断开始 (dataPoint = 0)
CloseHandle (hProcess)
返回 (假)
.默认
capData = 取空白字节集 (lenth)
ReadProcessMemory_ (hProcess, dataPoint, capData, lenth, 0)
.判断开始 (取字节集长度 (capData) = 0)
CloseHandle (hProcess)
返回 (假)
.默认
图片框1.图片 = capData
返回 (ret)
.判断结束
.判断结束
.判断结束
.判断结束
|