Troogle / OSUplayer

A Player 4 All Osuers!
GNU General Public License v3.0
71 stars 16 forks source link

辣鸡QQ的接口变了 #75

Closed UlyssesWu closed 8 years ago

UlyssesWu commented 8 years ago

以前PutRSInfo是可以接Unicode字符串的,然而在最新版的QQ(7.6 15742)中,它的行为方式变了,他会把输入的字符串都看做ANSI,然后转一个UTF8。结果自然是中文全部乱码,惨不忍睹。英文不受影响。

osup

为了绕过这个问题,我尝试使用这种方式:

 var ub = Encoding.UTF8.GetBytes("镇命歌");
 var gs = Encoding.Default.GetString(ub); //Unicode显示的GB2312强行编码的内容实际为UTF8的字符串
 var gb = Encoding.Default.GetBytes(gs);
 var us = Encoding.UTF8.GetString(gb);

ansi

结果自然是,前两个字正常显示成功,第三个字由于GB2312的汉字是2个字节,UTF8的汉字是三个字节,因此第9个字节被吃了,并且补了问号。看来是行不通的(虽说偶数个汉字是可以正常显示)。

不知道QQ这是故意为之,还是哪里出BUG。不知这个问题有没有解?

Fury94-Echo commented 8 years ago

辣鸡QQ音乐完全没乱码! 写个检测连续三字节字符数量 奇数的话补个汉字wwwwwwwwww 就补 辣鸡中的一个好了wwwwwwww

UlyssesWu commented 8 years ago

TO楼上…… 辣鸡QQ音乐是直接发信息给服务器,让服务器再通知辣鸡QQ改状态的。而且他发的时候都是通过ID表示的(我猜),除了他们辣鸡曲库内的歌之外显示不了别的,QZone也是同理(我再猜)

UlyssesWu commented 8 years ago

一个不通用的解决方案:

修改PutRSInfo函数(需要用到一些Patch手段),把原来针对GBK字符串的CTXStringA构造函数换成针对UTF8的,经过测试确实解决了问题。但是像这样Patch文件,一更新又没用了。

问题就出在我们传入的BSTR,被当做ANSI编码的字符串处理了。我一个渣渣,不懂C艹,也不太了解BSTR这种东西,但是我听说BSTR都是Unicode编码的。不知道是我太蠢,还是这COM库自己确实有问题。

putrs

先写到这,问题还是要继续关注的。先升级win10去了……

Fury94-Echo commented 8 years ago

每个字我都认识 组合在一起就不认识了orz 每次更新都打个补丁好了! win10大法好!

jixunmoe commented 8 years ago

@UlyssesWu IAT Hook :D

UlyssesWu commented 8 years ago

@JixunMoe 确实有在考虑,但是具体要怎么搞仍然一头雾水——说白了就是:我~不~会~啊~巨~巨~Q~A~Q~ (:зゝ∠)

jixunmoe commented 8 years ago

這個是 Inline Hook, 但是應該也夠用了 :D

可以往狀態信息添加一些識別內容,避免正常功能被過濾;也可以需要的時候 Hook 其他時候還原 :D

// HotPatch.cpp
#include "HotPatch.h"
#include "PatchUtil.h"

namespace Jixun {
    HotPatch::HotPatch(HMODULE hModule, LPCSTR lpProc, HookFunc myProc)
    {
        if (!hModule) return ;

        uAddr addr = this->fProc = uAddr(GetProcAddress(hModule, lpProc));
        if (!addr) return ;

        addr -= 5;

        // 005049E2 - E9 910CE411      jmp 12345678
        // 005049E7 >  ^ EB F9         jmp short e.005049E2
        DWORD oldProtect;
        VirtualProtect(LPVOID(addr), 7, PAGE_EXECUTE_READWRITE, &oldProtect);

        WriteUByteRaw(addr, 0xE9);
        WriteRelativeAddress(addr + 1, uAddr(myProc));
        WriteUShortRaw(addr + 5, 0xF9EB);

        VirtualProtect(LPVOID(addr), 7, oldProtect, &oldProtect);
    }

    bool HotPatch::isHooked()
    {
        return this->fProc != NULL;
    }

    void HotPatch::Pause()
    {
        WriteUShort(this->fProc, 0xFF8B);
    }

    void HotPatch::Resume()
    {
        WriteUShort(this->fProc, 0xF9EB);
    }

    uAddr HotPatch::GetAddress()
    {
        return this->fProc + 2;
    }
}
// HotPatch.h
#pragma once

#include "Common.h"

typedef void* HookFunc;

namespace Jixun {
    class HotPatch;

    class HotPatch
    {
    private:
        uAddr fProc;

    public:
        HotPatch(HMODULE hModule, LPCSTR lpProc, HookFunc myProc);
        bool isHooked();
        void Pause();
        void Resume();
        uAddr GetAddress();
    };
}

其它引入的文件可以在這裏找到:JixunMoe/dll-hijack

UlyssesWu commented 8 years ago

@JixunMoe 作为一个渣渣,我连应该hook哪里也不懂……CTXStringA的构造函数?还是PutRSInfo?然而PutRSInfo其实是一个内部函数,并没有导出,图里名字是我自己标注的。如果hook构造函数,怎么判断调用者?另外为了让PutRSInfo能调用另一个CTXStringA的构造函数,其实还需要把那个构造函数添加到导入表,因为原本它是不导入UTF8版函数的,只导入GBK版。 不过我已搞了另一种方案:在上述Patch的基础上,修改导出表,强行导出PutRSInfo函数,然后从我们的程序中调用。经测试似乎也可以用。这样不用考虑每次更新后Patch文件,而是一直调用已经Patch过的DLL就好。

jixunmoe commented 8 years ago

@UlyssesWu 這個思路也是可以噠 :D

我的想法是勾上轉碼函數.. 但是既然你能用別的方法搞定也行啦 :D

UlyssesWu commented 8 years ago

姑且能用……结贴。(总有种还会再开的感觉)

已在Sync2中使用此方案