吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 9305|回复: 11
收起左侧

[其他原创] 让代码飞出一段钢琴曲(freepiano小助手)(全局键盘钩子+dll劫持)+有码

  [复制链接]
anhkgg 发表于 2017-5-1 18:55
本帖最后由 anhkgg 于 2017-5-1 19:13 编辑

概述

突然想玩一下键盘弹曲子,就找到了freepiano,专业的东西不懂,就找了写简谱来玩玩,感觉挺不错的,哈哈~~
玩疯了之后,突然想到,我平时写代码,是不是可以弹出一段曲子呢,是不是心情会变得非常好,代码也写的更有节奏呢~~


说不定还搞出来一个什么《代码之歌》的钢琴曲~~ 嘎嘎


突然被自己这个想法吸引住了,不管咋样,每敲下代码的一个字符,后面想起了背景音乐,真是不错的,程序员也可以是“钢琴师”啊~~


有了想法,就开整!!!


有下面几点问题:
  • freepiano必须是激活窗口下,才能接受键盘输入
  • 我要在写代码时,让freepiano响应按键,就需要全局劫持键盘输入了
  • 怎么给freepiano通知,我按下了什么呢?

忘了说,freepiano长这样:
  

开搞
先简单整理下思路:
  • 首先肯定是弄个键盘钩子难道全局的所有键盘输入,暂定WH_KEYBOARD
  • 怎么让钩子执行?弄个exe,把freepiano再启动起来,感觉麻烦,然后就想让freepiano加载我的模块吧,简单确认了一下,可行(后面具体描述)
  • 劫持到键盘输入之后,通过PostMessage给freepiano发送键盘消息,模拟WM_KEYDOWN/WM_KEYUP

1. 加载我的模块


首先想到的就是DLL劫持和修改freepiano的导入表,后者不够优雅,果断要选择dll劫持。
然后就用depends看了下freepiano的导入信息,发现几个可以劫持的(dsound.dll,d3d9.dll等),简单代码确认了一下,freepiano可以劫持这两个模块,选择了d3d9.dll(函数少)。



然后偷懒用了aheadlib导出了d3d9.dll的导出函数信息,简单方便,飞快得就搞定了劫持。代码很简单,就贴一点(都不需要手写):

[C++] 纯文本查看 复制代码
// 导出函数
#pragma comment(linker, "/EXPORT:Direct3DShaderValidatorCreate9=_AheadLib_Direct3DShaderValidatorCreate9,@1")
#pragma comment(linker, "/EXPORT:PSGPError=_AheadLib_PSGPError,@2")
#pragma comment(linker, "/EXPORT:PSGPSampleTexture=_AheadLib_PSGPSampleTexture,@3")...
// 导出函数ALCDECL AheadLib_Direct3DShaderValidatorCreate9(void){ 
       // 保存返回地址      
  __asm POP m_dwReturn[0 * TYPE long];    
    // 调用原始函数      
  GetAddress("Direct3DShaderValidatorCreate9")();     
   // 转跳到返回地址       
 __asm JMP m_dwReturn[0 * TYPE long];
}


一试,OK,模块起来了,freepiano正常工作。



2. 安装钩子选择了安装全局WH_KEYBOARD钩子,这个代码网上也太多了,就不细说了,看看就行
[C++] 纯文本查看 复制代码
//安装钩子

BOOL Hook(HMODULE hMod){      
  g_Hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hMod, 0);                
return g_Hook?TRUE:FALSE;}
//卸载钩子
VOID Unhook(){        
if(g_Hook)        {                
UnhookWindowsHookEx(g_Hook);        
}}

//钩子函数,劫持键盘消息

LRESULT CALLBACK KeyboardProc(  int code,       // hook code                                                        
  WPARAM wParam,  // virtual-key code                                                         
 LPARAM lParam   // keystroke-message information                                                          
)                                                       
   {              
  if(code == HC_ACTION)        {           
     SendKeyMsg(wParam, lParam);       
 }        
return CallNextHookEx(g_Hook, code, wParam, lParam);

}



3. 发送按键信息给freepiano

首先想到的就是在钩子函数里给freepiano发送WM_KEYDOWN/WM_KEYUP消息就行了。
先找到freepiano的窗口,spy++上,找到窗口标题和类型信息,然后代码:

[C++] 纯文本查看 复制代码
HWND hwnd = FindWindow("FreePianoMainWindow", "Wispow Freepiano 2");
if(hwnd == NULL){       
 hwnd =  FindWindow("FreePianoMainWindow", NULL);        i
f(hwnd == NULL)        {              
  hwnd =  FindWindow(NULL, "Wispow Freepiano 2");      
  }}


然后就是发消息:

[C++] 纯文本查看 复制代码
if(hwnd){        
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);      
  if(keydown)    
    {               
 keydown = false;             
   PostMessage(hwnd, WM_KEYDOWN, wParam, lParam);        
}else        
{ 
               keydown = true;              
  PostMessage(hwnd, WM_KEYUP, wParam, lParam);     
   }}



测试,失败了,没有预想的效果。分析原因:

  • Post发送消息失败。但是通过spy++抓消息看到freepiano是收到了消息的。那就不是这个原因。
  • freepiano校验了窗口是否激活?然后就用上面每次置顶试了一下,依然不行。
  • freepiano使用了GetKeyState之类的函数检查按键状态,通过ida简单看了一下导入表,没有相关函数(没有深究是否显示导入了)。
  • 用ida看了下freepiano的窗口消息处理,看是否有什么过滤

[C++] 纯文本查看 复制代码
v7.lpfnWndProc = (WNDPROC)xxx_main_wndproc_41D070; //窗口响应函数
v7.cbClsExtra = 0;
v7.cbWndExtra = 0;
v7.hInstance = GetModuleHandleA(0);
v7.hIcon = LoadIconA(v1, (LPCSTR)0xA);
v7.hCursor = 0;
v7.hbrBackground = 0;
v7.lpszMenuName = 0;
v7.lpszClassName = "FreePianoMainWindow";



然后发现居然没有对WM_KEYDOWN/WM_KEYUP/WM_CHAR之类的消息进行处理,那是怎么接受的按键信息

  • 继续用ida看是否有钩子之类的处理,果然,导入表中明晃晃的SetWindowsHookEx,进入一看,一个WM_KEYBOARD_LL局部钩子





进钩子函数一下,各种按键状态记录的处理,不深究了。基本确认他使用这种方式来接受按键信息。

[C++] 纯文本查看 复制代码
v6 = (unsigned __int8)byte_4F6DC8[scanCode];  
if ( (unsigned int)(v6 - 1) > 0x6A )    
goto LABEL_23;
  if ( (unsigned __int8)byte_4F6ED0[v6] != pressed_0 ) 
 {    byte_4F6ED0[v6] = pressed_0;    sub_449B20(v6, pressed_0 != 0);  } 
 if ( (_BYTE)dword_4F6DC0    || BYTE1(dword_4F6DC0) && (v6 == 'D' || v6 == 'H') 
     || BYTE2(dword_4F6DC0) && (byte_4F6F15 || byte_4F6F17) && v6 == 28 ) 
   result = 1;  else


4. 改变策略

那就不能直接PostMessage发送消息了。
  • 修改我的钩子为WM_KEYBOARD_LL全局键盘钩子,消息和freepiano完全一样了

[C++] 纯文本查看 复制代码
g_Hook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hMod, 0);



2. 钩子函数通过WM_COPYDATA打包数据,发送给freepiano


[C++] 纯文本查看 复制代码
LRESULT CALLBACK LowLevelKeyboardProc( 
 int nCode,     // hook code                                 
                                         WPARAM wParam, // message identifier                
                                                            LPARAM lParam  // message data              
                                                            )
{     
   COPYDATASTRUCT CopyData = {0};       
 KeyboardLL_Msg Msg = {0};        
Msg.nCode = nCode;       
 Msg.wParam = wParam;      
  memcpy(&Msg.lParam, (char*)lParam, sizeof(KBDLLHOOKSTRUCT));     
   CopyData.cbData = sizeof(KeyboardLL_Msg);   
     CopyData.dwData = 0;       
 CopyData.lpData = &Msg;     
   HWND hwnd = FindFreepiano();       

 if(hwnd)     
   {             
   BOOL ret = SendMessage(hwnd, WM_COPYDATA, (WPARAM)hwnd, (LPARAM)&CopyData);        
}       

 return CallNextHookEx(g_Hook, nCode, wParam, lParam);

}

typedef struct _KeyboardLL_Msg{        
int nCode;        
WPARAM wParam;        
KBDLLHOOKSTRUCT lParam;
}KeyboardLL_Msg, *PKeyboardLL_Msg;



  3. 通过SetWindowLong挂钩freepiano的窗口响应函数,增加处理WM_COPYDATA,来接受全局键盘信息,找到freepiano的钩子函数地址A,然后接受到WM_COPYDATA之后,直接调用A,把键盘信息给freepiano


通过一个线程,循环查找freepianp窗口(可能还没起来),然后hook窗口响应函数

[C++] 纯文本查看 复制代码
void HookWinProc(){            
   while(1)        {                           
     HWND hwnd = FindFreepiano();           
     if(hwnd)                {                   
     g_WndProc = (pfn_WindProc)GetWindowLong(hwnd, GWL_WNDPROC);                 
       if(g_WndProc)                    
    {                             
   SetWindowLong(hwnd, GWL_WNDPROC, (LONG)fakeWindowProc);                      
          break;                        
} 
               }                
Sleep(10);      
  }}


自己的函数中加入对WM_COPYDATA的消息处理,调用freepiano的钩子函数g_LowLevelKeyboardProc发键盘消息过去。

[C++] 纯文本查看 复制代码
LRESULT WINAPI fakeWindowProc(                                          
 HWND hWnd,              // handle to window                                 
          UINT Msg,               // message                                       
    WPARAM wParam,          // first message parameter                                           
LPARAM lParam           // second message parameter   
                                        ){  
      if(Msg == WM_COPYDATA)        {        
        COPYDATASTRUCT* CopyData = (COPYDATASTRUCT*)lParam;               
         //if(CopyData->cbData == sizeof(KeyboardLL_Msg))                {            
            KeyboardLL_Msg* Msg = (KeyboardLL_Msg*)CopyData->lpData;          
              g_LowLevelKeyboardProc(Msg->nCode, Msg->wParam, (LPARAM)&Msg->lParam);             
   } 
       }
                return g_WndProc(hWnd, Msg, wParam, lParam);
}


g_LowLevelKeyboardProc地址这里使用硬编码,图方便

[C++] 纯文本查看 复制代码
HMODULE hExe = GetModuleHandle(NULL);
g_LowLevelKeyboardProc = (pfn_LowLevelKeyboardProc)((DWORD)hExe + (DWORD)g_LowLevelKeyboardProc);


功能到这里基本搞定。

总结

测试通过。

手指立马不受控制的在编辑器里、浏览器、文件浏览器里各种按键,然后耳边响起了悠扬(忽略乱打的节奏的话)的钢琴声~~

然后录了一段程序员启蒙歌-《hello world》!你们感受一下:

在线试听:https://pan.baidu.com/s/1cIlPOM

[C++] 纯文本查看 复制代码
#include <stdio.h>
int main()
{ 
             printf("hello world!");      
        return 0;
}

由于手抽,打错了几个字,所以可以听到多次短暂的退格琴声,表介意~~~可能的优化:
  • 加入进程名单控制,不想在某些进程中听到琴声
  • 代码优化~~
有兴趣的同学可以去折腾,我这里就不继续了~~

源码+工具:https://github.com/anhkgg/coding_piano
已经编译了,直接下载就可以试用,src目录是源码

有兴趣的可以去看看我得博客:http://anhkgg.github.io/coding-piano-hook/

免费评分

参与人数 9吾爱币 +7 热心值 +9 收起 理由
随意点 + 1 + 1 谢谢@Thanks!
samkong + 1 + 1 谢谢@Thanks!
xiaoding + 1 + 1 已答复!
sunchuyang + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
pqzhen9999 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lingchaoss + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
紫色の风铃 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
榻榻米 + 1 大神真会玩~
钢铁侠_123 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

可缓缓归矣 发表于 2017-5-1 21:22
楼主,我看你骨骼精奇,是万中无一的音乐奇才,发扬中华乐曲就靠你了,我这有本秘籍--->《高山流水》,见与你有缘,就免费给你了!
头像被屏蔽
wanghai123 发表于 2017-5-1 19:17
Gilgamesh 发表于 2017-5-1 19:19
lingchaoss 发表于 2017-5-1 20:12
谢谢分享
威武霸气潇洒涛 发表于 2017-5-1 20:20
支持下!谢谢分享!!
凹凸獒 发表于 2017-5-1 20:40
思路不错
17605202756 发表于 2017-5-1 20:40
厉害厉害厉害默默支持中....
1425803695 发表于 2017-5-1 21:05
一看就是音乐大神
头像被屏蔽
wzhJava1 发表于 2017-5-1 22:24 来自手机
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-14 00:32

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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