├── .gitignore ├── .idea ├── codeStyles │ └── Project.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── Plugin.md ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── net │ │ └── newlydev │ │ └── qqrobot │ │ ├── LoginActivity.java │ │ ├── MainActivity.java │ │ ├── MainService.java │ │ ├── PCTIM │ │ ├── Message │ │ │ ├── PictureKeyStore.java │ │ │ ├── PictureStore.java │ │ │ └── SendMessage.java │ │ ├── Package │ │ │ ├── ParseRecivePackage.java │ │ │ └── SendPackageFactory.java │ │ ├── QQGlobal.java │ │ ├── QQUser.java │ │ ├── Robot │ │ │ ├── QQRobot.java │ │ │ └── RobotApi.java │ │ ├── Socket │ │ │ ├── HeartBeat.java │ │ │ ├── LoginManager.java │ │ │ ├── MessageService.java │ │ │ └── Udpsocket.java │ │ ├── TLV │ │ │ ├── ParseTlvFactory.java │ │ │ ├── TLV.java │ │ │ └── TLVFactory.java │ │ ├── TXProtocol.java │ │ ├── Utils │ │ │ ├── ByteBuilder.java │ │ │ ├── ByteFactory.java │ │ │ ├── Crypter.java │ │ │ ├── CrypterUtil.java │ │ │ ├── Util.java │ │ │ └── ZLibUtils.java │ │ └── sdk │ │ │ ├── API.java │ │ │ ├── Plugin.java │ │ │ └── QQMessage.java │ │ └── mApplication.java │ └── res │ ├── drawable │ └── ic_launcher.png │ ├── layout │ └── main.xml │ └── values │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── net │ │ └── newlydev │ │ └── qqrobot │ │ └── demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── net │ │ │ └── newlydev │ │ │ └── qqrobot │ │ │ ├── PCTIM │ │ │ └── sdk │ │ │ │ ├── API.java │ │ │ │ ├── Plugin.java │ │ │ │ └── QQMessage.java │ │ │ └── demo │ │ │ └── Main.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── net │ └── newlydev │ └── qqrobot │ └── demo │ └── ExampleUnitTest.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | 45 | # Keystore files 46 | # Uncomment the following line if you do not want to check your keystore files in. 47 | #*.jks 48 | 49 | # External native build folder generated in Android Studio 2.2 and later 50 | .externalNativeBuild 51 | 52 | # Google Services (e.g. APIs or Firebase) 53 | google-services.json 54 | 55 | # Freeline 56 | freeline.py 57 | freeline/ 58 | freeline_project_description.json 59 | 60 | # fastlane 61 | fastlane/report.xml 62 | fastlane/Preview.html 63 | fastlane/screenshots 64 | fastlane/test_output 65 | fastlane/readme.md 66 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | xmlns:android 14 | 15 | ^$ 16 | 17 | 18 | 19 |
20 |
21 | 22 | 23 | 24 | xmlns:.* 25 | 26 | ^$ 27 | 28 | 29 | BY_NAME 30 | 31 |
32 |
33 | 34 | 35 | 36 | .*:id 37 | 38 | http://schemas.android.com/apk/res/android 39 | 40 | 41 | 42 |
43 |
44 | 45 | 46 | 47 | .*:name 48 | 49 | http://schemas.android.com/apk/res/android 50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | name 59 | 60 | ^$ 61 | 62 | 63 | 64 |
65 |
66 | 67 | 68 | 69 | style 70 | 71 | ^$ 72 | 73 | 74 | 75 |
76 |
77 | 78 | 79 | 80 | .* 81 | 82 | ^$ 83 | 84 | 85 | BY_NAME 86 | 87 |
88 |
89 | 90 | 91 | 92 | .* 93 | 94 | http://schemas.android.com/apk/res/android 95 | 96 | 97 | ANDROID_ATTRIBUTE_ORDER 98 | 99 |
100 |
101 | 102 | 103 | 104 | .* 105 | 106 | .* 107 | 108 | 109 | BY_NAME 110 | 111 |
112 |
113 |
114 |
115 |
116 |
-------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Plugin.md: -------------------------------------------------------------------------------- 1 | # 0.QQRobot插件规范 2 | ## 0.1维持QQRobot正常运作 3 | 作为开源应用,我不想限制插件的使用。但是,希望各位插件开发者能够合理,合法地使用QQRobot。不要利用插件过于频繁地发送消息(尤其是xml消息)。被TX封的可能不仅仅是你的账号,还可能是整个QQRobot框架。不要因为自己的行为影响更多的人。 4 | ## 0.2不要作恶 5 | 不要试图利用某种方式获取并存储非插件功能必须的信息。更不要试图破坏宿主的设备。建议将发布在公共场合的插件进行开源,以免引发纠纷。 6 | ## 0.3禁止商业化 7 | 不得利用插件获取利益(包括卖插件/部分功能,插件内嵌广告,群发广告等)。产生一切后果与QQRobot无关 8 | # 1.QQRobot插件开发快速入门 9 | ## 1.1.请求权限 10 | QQRobot插件与Xposed模块类似,也是一种安卓应用。任何QQRobot插件应当请求**net.newlydev.qqrobot.permission.plugin**权限 11 | 12 | `` 13 | ## 1.2安装SDK 14 | 将QQRobot源码目录中net.newlydev.qqrobot.PCTIM.sdk包下的三个文件复制到你的工程对应目录(/net/newlydev/qqrobot/PCTIM/sdk/)下即可,**不要改变包路径** 15 | ## 1.3实现Main类 16 | 应在你应用的包名下创建Main类,实现Plugin接口 17 | `public class Main implements Plugin{}` 18 | 19 | 并实现几个回调方法(不解释,基本上看方法名就知道是啥方法) 20 | ## 1.4持久化API 21 | onLoad方法会传入API,许多功能都需要它来实现 22 | ## 1.5编译运行 23 | 不解释 24 | # 2.疑难处理 25 | QQRobot不断在更新,如果你发现更新到某个版本时你的插件用不了了,请同步QQRobot源码重新安装SDK并更新你的代码。如果还有问题请发Issus -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 安卓上的qq机器人,使用pctim协议 2 | 本项目基于https://github.com/Saint-Theana/RingZux_QQ/ 3 | 4 | 插件开发帮助文档https://github.com/uebian/QQRobot/blob/master/Plugin.md 5 | 6 | |项目|支持情况| 7 | |:-|:-| 8 | |免扫码登录|√| 9 | |插件API|√| 10 | |发好友文本消息|√| 11 | |发好友XML消息|√| 12 | |发好友JSON消息|×| 13 | |发好友图片消息|×| 14 | |发好友语音消息|×| 15 | |发好友文件|×| 16 | |收好友文本消息|√| 17 | |收好友XML消息|√| 18 | |收好友JSON消息|×| 19 | |收好友图片消息|×| 20 | |收好友语音消息|×| 21 | |收好友文件|×| 22 | |发群文本消息|√| 23 | |发群XML消息|√| 24 | |发群JSON消息|×| 25 | |发群图片消息|√| 26 | |发群语音消息|×| 27 | |发群文件|×| 28 | |收群文本消息|√| 29 | |收群XML消息|√| 30 | |收群JSON消息|×| 31 | |收群图片消息|×| 32 | |收群语音消息|×| 33 | |收群文件|×| 34 | |群全体禁言|√| 35 | |群单独禁言|√| 36 | |群全体解除禁言|√| 37 | |群单独解除禁言|√| 38 | |群申请自动处理|×| 39 | |群踢人|×| 40 | |修改群名片|√| 41 | |获取群列表|×| 42 | |获取好友列表|×| 43 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion '29.0.3' 6 | 7 | defaultConfig { 8 | applicationId "net.newlydev.qqrobot" 9 | minSdkVersion 14 10 | targetSdkVersion 21 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | } 25 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\tools\adt-bundle-windows-x86_64-20131030\sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 12 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot; 2 | 3 | import android.app.*; 4 | import android.content.*; 5 | import android.graphics.*; 6 | import android.os.*; 7 | import android.view.*; 8 | import android.view.View.*; 9 | import android.widget.*; 10 | import java.io.*; 11 | import net.newlydev.qqrobot.PCTIM.Utils.*; 12 | 13 | public class LoginActivity extends Activity 14 | { 15 | ServiceConnection conn; 16 | IBinder ibinder; 17 | Messenger loginhandler; 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) 20 | { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.main); 23 | final ProgressDialog pb=new ProgressDialog(this); 24 | pb.setCancelable(false); 25 | pb.setMessage(getResources().getString(R.string.wait)); 26 | Intent i=new Intent(LoginActivity.this,MainService.class); 27 | startService(i); 28 | loginhandler=new Messenger(new Handler(){ 29 | @Override 30 | public void handleMessage(Message msg) 31 | { 32 | switch(msg.getData().getString("type")) 33 | { 34 | case "islogined": 35 | finish(); 36 | startActivity(new Intent(LoginActivity.this,MainActivity.class)); 37 | break; 38 | case "verifyCode": 39 | { 40 | AlertDialog.Builder ab=new AlertDialog.Builder(LoginActivity.this); 41 | LinearLayout ll=new LinearLayout(LoginActivity.this); 42 | ImageView iv=new ImageView(LoginActivity.this); 43 | InputStream verifyCodeStream = new ByteArrayInputStream(msg.getData().getByteArray("image")); 44 | iv.setImageBitmap( BitmapFactory.decodeStream(verifyCodeStream)); 45 | ll.addView(iv); 46 | ll.setOrientation(LinearLayout.VERTICAL); 47 | final EditText et_verifyCode=new EditText(LoginActivity.this); 48 | et_verifyCode.setHint("如显示不完全请直接点击确定按钮加载剩余图像"); 49 | ab.setView(ll); 50 | ll.addView(et_verifyCode); 51 | ab.setPositiveButton("确定", new DialogInterface.OnClickListener(){ 52 | 53 | @Override 54 | public void onClick(DialogInterface p1, int p2) 55 | { 56 | Message msg=new Message(); 57 | msg.replyTo=loginhandler; 58 | Bundle data=new Bundle(); 59 | data.putString("type","verifyCode"); 60 | data.putString("code",et_verifyCode.getText().toString()); 61 | msg.setData(data); 62 | try 63 | { 64 | new Messenger(ibinder).send(msg); 65 | } 66 | catch (RemoteException e) 67 | {} 68 | // TODO: Implement this method 69 | } 70 | }); 71 | ab.setCancelable(false); 72 | ab.show(); 73 | } 74 | break; 75 | case "error": 76 | pb.dismiss(); 77 | AlertDialog.Builder ab=new AlertDialog.Builder(LoginActivity.this); 78 | ab.setMessage(msg.getData().getString("info")); 79 | ab.show(); 80 | break; 81 | case "ok": 82 | Toast.makeText(LoginActivity.this,"Welcome:"+msg.getData().getString("name"),Toast.LENGTH_SHORT).show(); 83 | finish(); 84 | startActivity(new Intent(LoginActivity.this,MainActivity.class)); 85 | break; 86 | } 87 | } 88 | }); 89 | final EditText et_qq=findViewById(R.id.et_qq); 90 | final EditText et_pwd=findViewById(R.id.et_pwd); 91 | Button btn_login=findViewById(R.id.btn_login); 92 | conn=new ServiceConnection(){ 93 | 94 | @Override 95 | public void onServiceConnected(ComponentName p1, IBinder p2) 96 | { 97 | ibinder=p2; 98 | Message msg=new Message(); 99 | msg.replyTo=loginhandler; 100 | Bundle data=new Bundle(); 101 | data.putString("type","islogined"); 102 | msg.setData(data); 103 | try 104 | { 105 | new Messenger(ibinder).send(msg); 106 | } 107 | catch (RemoteException e) 108 | {} 109 | } 110 | 111 | @Override 112 | public void onServiceDisconnected(ComponentName p1) 113 | { 114 | // TODO: Implement this method 115 | } 116 | }; 117 | btn_login.setOnClickListener(new OnClickListener(){ 118 | 119 | @Override 120 | public void onClick(View p1) 121 | { 122 | pb.show(); 123 | Message msg=new Message(); 124 | msg.replyTo=loginhandler; 125 | Bundle data=new Bundle(); 126 | data.putString("type","login"); 127 | data.putLong("qq",Long.parseLong(et_qq.getText().toString())); 128 | data.putByteArray("pwd",Util.MD5(et_pwd.getText().toString())); 129 | msg.setData(data); 130 | try 131 | { 132 | new Messenger(ibinder).send(msg); 133 | } 134 | catch (RemoteException e) 135 | {} 136 | } 137 | }); 138 | bindService(i,conn, Context.BIND_AUTO_CREATE); 139 | } 140 | 141 | @Override 142 | protected void onDestroy() 143 | { 144 | super.onDestroy(); 145 | unbindService(conn); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/MainActivity.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot; 2 | import android.app.*; 3 | import android.content.*; 4 | import android.content.pm.*; 5 | import android.os.*; 6 | import android.widget.*; 7 | import dalvik.system.*; 8 | import java.io.*; 9 | import java.util.*; 10 | import net.newlydev.qqrobot.PCTIM.Utils.*; 11 | import net.newlydev.qqrobot.PCTIM.sdk.*; 12 | import android.widget.AdapterView.*; 13 | import android.view.*; 14 | 15 | public class MainActivity extends Activity 16 | { 17 | private ServiceConnection conn; 18 | private IBinder ibinder; 19 | private Messenger handler; 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) 22 | { 23 | // TODO: Implement this method 24 | super.onCreate(savedInstanceState); 25 | Intent i=new Intent(MainActivity.this,MainService.class); 26 | handler=new Messenger(new Handler(){}); 27 | conn=new ServiceConnection(){ 28 | @Override 29 | public void onServiceConnected(ComponentName p1, IBinder p2) 30 | { 31 | ibinder=p2; 32 | Message msg=new Message(); 33 | msg.replyTo=handler; 34 | Bundle data=new Bundle(); 35 | data.putString("type","islogined"); 36 | msg.setData(data); 37 | try 38 | { 39 | new Messenger(ibinder).send(msg); 40 | } 41 | catch (RemoteException e) 42 | {} 43 | } 44 | 45 | @Override 46 | public void onServiceDisconnected(ComponentName p1) 47 | { 48 | // TODO: Implement this method 49 | } 50 | }; 51 | bindService(i,conn,BIND_AUTO_CREATE); 52 | ListView lv=new ListView(this); 53 | setContentView(lv); 54 | final ArrayList pluginlist=new ArrayList(); 55 | Intent intent = new Intent(Intent.ACTION_MAIN, null); 56 | intent.addCategory(Intent.CATEGORY_LAUNCHER); 57 | final File pluginlistfile=new File(getDataDir(), "pluginlist.cfg"); 58 | ArrayList activiedplugin=new ArrayList(); 59 | try 60 | { 61 | if (!pluginlistfile.exists()) 62 | { 63 | pluginlistfile.createNewFile(); 64 | } 65 | DataInputStream fr=new DataInputStream(new FileInputStream(pluginlistfile)); 66 | String s; 67 | while ((s = fr.readLine()) != null) 68 | { 69 | activiedplugin.add(s); 70 | } 71 | fr.close(); 72 | } 73 | catch (Exception e) 74 | {} 75 | PackageManager packageManager= getPackageManager(); 76 | List list=packageManager.getInstalledPackages(0); 77 | for (PackageInfo p:list) 78 | { 79 | if (getPackageManager().checkPermission("net.newlydev.qqrobot.permission.plugin", p.applicationInfo.packageName) == PackageManager.PERMISSION_GRANTED) 80 | { 81 | boolean isenable=false; 82 | for (String eplugin:activiedplugin) 83 | { 84 | if (eplugin.equals(p.applicationInfo.packageName)) 85 | { 86 | isenable = true; 87 | break; 88 | } 89 | } 90 | pluginlist.add(p.applicationInfo.packageName + "(" + (isenable ?"enable": "disable") + ")"); 91 | } 92 | } 93 | final ArrayAdapter aa=new ArrayAdapter(this, android.R.layout.simple_list_item_1, pluginlist); 94 | lv.setAdapter(aa); 95 | lv.setOnItemClickListener(new OnItemClickListener(){ 96 | 97 | @Override 98 | public void onItemClick(AdapterView p1, View p2, int p3, long p4) 99 | { 100 | if (pluginlist.get(p3).endsWith("(disable)")) 101 | { 102 | pluginlist.set(p3, pluginlist.get(p3).substring(0, pluginlist.get(p3).length() - 9) + "(enable)"); 103 | } 104 | else 105 | { 106 | pluginlist.set(p3, pluginlist.get(p3).substring(0, pluginlist.get(p3).length() - 8) + "(disable)"); 107 | } 108 | try 109 | { 110 | FileWriter fw=new FileWriter(pluginlistfile); 111 | for (String tmp:pluginlist) 112 | { 113 | if (tmp.endsWith("(enable)")) 114 | { 115 | fw.write(pluginlist.get(p3).substring(0, pluginlist.get(p3).length() - 8) + "\n"); 116 | } 117 | } 118 | fw.close(); 119 | } 120 | catch (IOException e) 121 | {} 122 | aa.notifyDataSetChanged(); 123 | // TODO: Implement this method 124 | } 125 | }); 126 | } 127 | 128 | @Override 129 | public boolean onCreateOptionsMenu(Menu menu) 130 | { 131 | // TODO: Implement this method 132 | menu.add("Update Plugin").setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener(){ 133 | @Override 134 | public boolean onMenuItemClick(MenuItem p1) 135 | { 136 | Message msg=new Message(); 137 | Bundle data=new Bundle(); 138 | data.putString("type","updateRobot"); 139 | msg.setData(data); 140 | try 141 | { 142 | new Messenger(ibinder).send(msg); 143 | } 144 | catch (RemoteException e) 145 | {} 146 | // TODO: Implement this method 147 | return false; 148 | } 149 | }); 150 | return super.onCreateOptionsMenu(menu); 151 | } 152 | 153 | @Override 154 | protected void onDestroy() 155 | { 156 | // TODO: Implement this method 157 | super.onDestroy(); 158 | unbindService(conn); 159 | } 160 | 161 | 162 | } 163 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/MainService.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot; 2 | import android.app.*; 3 | import android.content.*; 4 | import android.os.*; 5 | import net.newlydev.qqrobot.PCTIM.*; 6 | import net.newlydev.qqrobot.PCTIM.Robot.*; 7 | import net.newlydev.qqrobot.PCTIM.Socket.*; 8 | 9 | public class MainService extends Service 10 | { 11 | LoginManager manager ; 12 | HeartBeat heartbeat; 13 | MessageService messageservice; 14 | QQUser user; 15 | Object lock=new Object(); 16 | Messenger currenthandler; 17 | final Messenger mMessenger = new Messenger(new Handler(){ 18 | @Override 19 | public void handleMessage(Message msg) 20 | { 21 | currenthandler = msg.replyTo; 22 | switch (msg.getData().getString("type")) 23 | { 24 | case "login": 25 | { 26 | user = new QQUser(msg.getData().getLong("qq", -1), msg.getData().getByteArray("pwd")); 27 | new Thread(){ 28 | @Override 29 | public void run() 30 | { 31 | manager = new LoginManager(user,lock); 32 | if(manager.login(currenthandler)) 33 | { 34 | Message msg=new Message(); 35 | Bundle data=new Bundle(); 36 | heartbeat=new HeartBeat(user,manager.socket); 37 | QQRobot robot=new QQRobot(manager.socket,user,getApplicationContext()); 38 | messageservice=new MessageService(user,manager.socket,robot); 39 | heartbeat.start(); 40 | messageservice.start(); 41 | data.putString("type","ok"); 42 | data.putString("name",user.NickName); 43 | msg.setData(data); 44 | try 45 | { 46 | currenthandler.send(msg); 47 | } 48 | catch (RemoteException e) 49 | {} 50 | }else{ 51 | Message msg=new Message(); 52 | Bundle data=new Bundle(); 53 | data.putString("type","error"); 54 | data.putString("info","Unknown error"); 55 | msg.setData(data); 56 | try 57 | { 58 | currenthandler.send(msg); 59 | } 60 | catch (RemoteException e) 61 | {} 62 | } 63 | } 64 | }.start(); 65 | } 66 | break; 67 | case "islogined": 68 | if (user != null && user.islogined) 69 | { 70 | Message msga=new Message(); 71 | Bundle data=new Bundle(); 72 | data.putString("type", "islogined"); 73 | msga.setData(data); 74 | try 75 | { 76 | currenthandler.send(msga); 77 | } 78 | catch (RemoteException e) 79 | {} 80 | 81 | } 82 | break; 83 | case "verifyCode": 84 | manager.setVerifyCode(msg.getData().getString("code")); 85 | break; 86 | case "updateRobot": 87 | QQRobot robot=new QQRobot(manager.socket,user,getApplicationContext()); 88 | messageservice.updateRobot(robot); 89 | break; 90 | } 91 | } 92 | }); 93 | 94 | @Override 95 | public int onStartCommand(Intent intent, int flags, int startId) 96 | { 97 | 98 | Notification.Builder builder = new Notification.Builder(this); 99 | builder.setContentText("QQRobot is running"); 100 | builder.setContentTitle("QQRobot"); 101 | builder.setSmallIcon(R.drawable.ic_launcher); 102 | Notification notification=builder.build(); 103 | notification.flags=Notification.FLAG_ONGOING_EVENT; 104 | startForeground(1,notification); 105 | return super.onStartCommand(intent, flags, startId); 106 | } 107 | 108 | 109 | @Override 110 | public IBinder onBind(Intent p1) 111 | { 112 | return mMessenger.getBinder(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Message/PictureKeyStore.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Message; 2 | 3 | public class PictureKeyStore 4 | { 5 | public String key30; 6 | public String key48; 7 | public byte[] ukey; 8 | public boolean uploaded; 9 | public PictureKeyStore(){ 10 | 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Message/PictureStore.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Message; 2 | 3 | import android.graphics.*; 4 | 5 | public class PictureStore 6 | { 7 | public long Group; 8 | public byte[] data; 9 | //public String File; 10 | public int pictureid; 11 | public PictureStore(){ 12 | 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Message/SendMessage.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Message; 2 | import java.io.*; 3 | import net.newlydev.qqrobot.PCTIM.*; 4 | import net.newlydev.qqrobot.PCTIM.Package.*; 5 | import net.newlydev.qqrobot.PCTIM.Socket.*; 6 | import net.newlydev.qqrobot.PCTIM.Utils.*; 7 | import net.newlydev.qqrobot.PCTIM.sdk.*; 8 | import android.graphics.*; 9 | 10 | public class SendMessage 11 | { 12 | public static void SendGroupMessage(QQUser user, Udpsocket socket, long gin, String msg, boolean isxml) 13 | { 14 | Util.log("[机器人] [群消息 发送] 到群 " + gin + " [消息] " + msg); 15 | byte[] data = SendPackageFactory.get0002(user, gin, msg, isxml); 16 | socket.sendMessage(data); 17 | } 18 | public static void SendGroupMessage(QQUser user, Udpsocket socket, long gin, Bitmap bitmap) 19 | { 20 | Util.log("[机器人] [群消息 发送] 到群 " + gin + " [图片消息]"); 21 | byte[] data = SendPackageFactory.get0388(user, gin, bitmap); 22 | socket.sendMessage(data); 23 | 24 | } 25 | public static void SendFriendMessage(QQUser user, Udpsocket socket, long uin, String msg, boolean isxml) 26 | { 27 | Util.log("[机器人] [好友消息 发送] 到 " + uin + " [消息] " + msg); 28 | byte[] data = SendPackageFactory.get00cd(user, uin,msg,isxml); 29 | socket.sendMessage(data); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Package/ParseRecivePackage.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Package; 2 | 3 | import java.util.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Message.*; 6 | import net.newlydev.qqrobot.PCTIM.TLV.*; 7 | import net.newlydev.qqrobot.PCTIM.Utils.*; 8 | import net.newlydev.qqrobot.PCTIM.sdk.*; 9 | 10 | public class ParseRecivePackage 11 | { 12 | public long Friend_Message_QQ; 13 | public byte[] Friend_Message_TIME; 14 | public byte[] Header = null; 15 | public int PackageLength = 0; 16 | public byte[] Version = null; 17 | public byte[] Command = null; 18 | public byte[] Sequence = null; 19 | public byte[] QQ = null; 20 | public byte[] empty = null; 21 | public byte[] body_encrypted = null; 22 | public byte[] body_decrypted = null; 23 | public byte[] tea_key=null; 24 | private byte[] _body = null; 25 | private QQUser _user = null; 26 | public byte[] Message_To_Respone = null; 27 | 28 | public byte Status=0x1; 29 | 30 | public byte VerifyCommand=0x1; 31 | 32 | public ParseRecivePackage(byte[] body, byte[] key, QQUser user) 33 | { 34 | this.PackageLength = body.length; 35 | this._body = body; 36 | ByteFactory bytefactory = new ByteFactory(body); 37 | this.tea_key = key; 38 | this._user = user; 39 | bytefactory.readBytes(1); 40 | this.Version = bytefactory.readBytes(2); 41 | this.Command = bytefactory.readBytes(2); 42 | this.Sequence = bytefactory.readBytes(2); 43 | this.QQ = bytefactory.readBytes(4); 44 | this.empty = bytefactory.readBytes(3); 45 | this.body_encrypted = bytefactory.readBytes(bytefactory.data.length - bytefactory.position - 1); 46 | } 47 | 48 | public void decrypt_body() 49 | { 50 | Crypter crypter = new Crypter(); 51 | this.body_decrypted = crypter.decrypt(this.body_encrypted, this.tea_key); 52 | this.Header = Util.subByte(this.body_decrypted, 0, 1); 53 | } 54 | 55 | public void parse_tlv() 56 | { 57 | Collection tlvs = TLV.ParseTlv(Util.subByte(this.body_decrypted, 1, this.body_decrypted.length - 1)); 58 | for (TLV tlv: tlvs) 59 | { 60 | ParseTlvFactory.parsetvl(tlv, _user); 61 | } 62 | } 63 | 64 | public QQMessage parse0017() 65 | { 66 | QQMessage qqmessage = new QQMessage(); 67 | try 68 | { 69 | this.decrypt_body(); 70 | ByteFactory bytefactory = new ByteFactory(this.body_decrypted); 71 | this.Message_To_Respone = Util.subByte(this.body_decrypted, 0, 16); 72 | long Target_uin = bytefactory.readlong(); 73 | qqmessage.Self_uin = bytefactory.readlong(); 74 | bytefactory.readBytes(10); 75 | int type = bytefactory.readint(); 76 | bytefactory.readBytes(2); 77 | bytefactory.readBytesbylength(); 78 | qqmessage.Group_uin = bytefactory.readlong(); 79 | byte[] flag = bytefactory.readBytes(1); 80 | ByteFactory message_datafactory = new ByteFactory(bytefactory.readrestBytes()); 81 | //Util.log(String.format("收到消息:type:%d;restbyte:",message_datafactory)+Util.byte2HexString(body_decrypted)); 82 | switch (type) 83 | { 84 | case 0x52 : // 群消息、被拉进/踢出群 85 | { 86 | if (flag[0] == 0x01) 87 | { 88 | qqmessage.Message_Type = 0;//群消息 89 | qqmessage.Sender_Uin = message_datafactory.readlong();//发消息人的QQ 90 | qqmessage.MessageIndex = message_datafactory.readBytes(4); //姑且叫消息索引吧 91 | qqmessage.Reciece_Message_Time = message_datafactory.readlong(); //接收时间 92 | message_datafactory.readBytes(24); 93 | qqmessage.Send_Message_Time = message_datafactory.readlong() * 1000; //发送时间 94 | qqmessage.Message_Id = message_datafactory.readlong(); //id 95 | message_datafactory.readBytes(8); 96 | qqmessage.Message_Font = message_datafactory.readStringbylength();//字体 97 | message_datafactory.readBytes(2); 98 | byte[] rich_data = message_datafactory.readrestBytes(); 99 | Util.parseRichText(qqmessage, rich_data); 100 | } 101 | break; 102 | } 103 | case 0x21: 104 | case 0x22: 105 | { 106 | message_datafactory.readBytes(5); 107 | qqmessage.Sender_Uin = message_datafactory.readlong(); 108 | Util.log("用户"+qqmessage.Sender_Uin+"变更:"+Util.byte2HexString(this._body)); 109 | break; 110 | } 111 | case 0x2C: 112 | { 113 | break; 114 | } 115 | case 732: 116 | { 117 | Util.log(qqmessage.Group_uin+"撤回消息"+Util.byte2HexString(message_datafactory.data)); 118 | break; 119 | } 120 | default: 121 | { 122 | break; 123 | } 124 | } 125 | return qqmessage; 126 | } 127 | catch (Exception e) 128 | { 129 | Util.log("解析0017错误,Exception: " + e.getMessage() + " 错误包: " + Util.byte2HexString(this._body)); 130 | return null; 131 | } 132 | } 133 | 134 | public void parse001d() 135 | { 136 | this.decrypt_body(); 137 | ByteFactory factory = new ByteFactory(this.body_decrypted); 138 | factory.readBytes(4); 139 | this._user.userskey = new String(factory.readBytes(10)); 140 | } 141 | 142 | public void parse00ba() 143 | { 144 | this.decrypt_body(); 145 | ByteFactory factory = new ByteFactory(this.body_decrypted); 146 | byte VerifyType = factory.readBytes(1)[0]; 147 | factory.readint(); 148 | this.Status = factory.readBytes(1)[0]; 149 | factory.readBytes(4); 150 | _user.TXProtocol.BufSigPic = factory.readBytesbylength(); 151 | if (VerifyType == 0x13) 152 | { 153 | byte[] VerifyCode = factory.readBytesbylength(); 154 | 155 | this.VerifyCommand = factory.readBytes(1)[0]; 156 | if (this.VerifyCommand == 0x00) 157 | { 158 | this.VerifyCommand = factory.readBytes(1)[0]; 159 | } 160 | else 161 | { 162 | factory.readBytes(1); 163 | } 164 | 165 | if (_user.QQPacket00BaVerifyCode.length == 0 || _user.QQPacket00BaVerifyCode == null) 166 | { 167 | _user.QQPacket00BaVerifyCode = VerifyCode; 168 | } 169 | else 170 | { 171 | ByteBuilder resultArr = new ByteBuilder(); 172 | resultArr.writebytes(_user.QQPacket00BaVerifyCode); 173 | resultArr.writebytes(VerifyCode); 174 | _user.QQPacket00BaVerifyCode = resultArr.getdata(); 175 | } 176 | 177 | _user.TXProtocol.PngToken = factory.readBytesbylength(); 178 | factory.readBytesbylength(); 179 | } 180 | else if (VerifyType == 0x14) 181 | { 182 | } 183 | } 184 | public QQMessage parse00ce() 185 | { 186 | QQMessage qqmessage = new QQMessage(); 187 | try 188 | { 189 | this.decrypt_body(); 190 | ByteFactory bytefactory = new ByteFactory(this.body_decrypted); 191 | this.Message_To_Respone = Util.subByte(this.body_decrypted, 0, 16); 192 | qqmessage.Message_Type = 1; 193 | qqmessage.Sender_Uin = bytefactory.readlong(); 194 | this.Friend_Message_QQ = qqmessage.Sender_Uin; 195 | qqmessage.Self_uin = bytefactory.readlong();//自己的QQ 196 | bytefactory.readBytes(10); 197 | bytefactory.readBytes(2); 198 | bytefactory.readint(); 199 | bytefactory.readBytesbylength(); 200 | bytefactory.readint(); //消息来源QQ的版本号 201 | bytefactory.readBytes(4); //FromQQ 202 | bytefactory.readBytes(4); //自己的QQ 203 | bytefactory.readBytes(20); 204 | qqmessage.Send_Message_Time = bytefactory.readlong() * 1000; 205 | bytefactory.readint(); //00 206 | this.Friend_Message_TIME = bytefactory.readBytes(4); //MessageDateTime 207 | bytefactory.readBytes(5); //00 208 | bytefactory.readBytes(3); 209 | bytefactory.readBytes(5); //00 210 | bytefactory.readBytes(4); //MessageDateTime 211 | qqmessage.Message_Id = bytefactory.readlong(); //id 212 | bytefactory.readBytes(8); 213 | qqmessage.Message_Font = bytefactory.readStringbylength(); 214 | bytefactory.readBytes(1); 215 | bytefactory.readBytes(1); 216 | Util.parseRichText(qqmessage, bytefactory.readrestBytes()); 217 | return qqmessage; 218 | } 219 | catch (Exception e) 220 | { 221 | Util.log("解析00ce错误,Exception: " + e.getMessage() + " 错误包: " + Util.byte2HexString(this._body)); 222 | return null; 223 | } 224 | } 225 | 226 | public PictureKeyStore parse0388() 227 | { 228 | PictureKeyStore keystore = new PictureKeyStore(); 229 | byte[] _ukey = null; 230 | this.decrypt_body(); 231 | if (this.PackageLength == 239) 232 | { 233 | ByteFactory factory = new ByteFactory(this.body_decrypted); 234 | byte last1 = 0; 235 | byte last2 = 0; 236 | byte last = 0; 237 | while (factory.position < factory.data.length) 238 | { 239 | last2 = last1; 240 | last1 = last; 241 | last = factory.readBytes(1)[0]; 242 | 243 | if (last == (byte)0x01 && last1 == (byte)0x80 && last2 == (byte) 0x42) 244 | { 245 | _ukey = factory.readBytes(128); 246 | break; 247 | } 248 | } 249 | keystore.uploaded = false; 250 | } 251 | else 252 | { 253 | keystore.uploaded = true; 254 | } 255 | keystore.ukey = _ukey; 256 | return keystore; 257 | } 258 | 259 | public void parse0836() 260 | { 261 | this.decrypt_body(); 262 | int result = this.Header[0]; 263 | if (result == -5 || result == 0 || result == 1 || result == 51 || 264 | result == 52 || result == 63 || result == 248 || 265 | result == 249 || result == 250 || result == 251 || 266 | result == 254 || result == 15 || result == 255) 267 | { 268 | this.parse_tlv(); 269 | } 270 | else 271 | { 272 | Crypter crypter = new Crypter(); 273 | this.body_decrypted = crypter.decrypt(this.body_decrypted, _user.TXProtocol.BufTgtgtKey); 274 | this.Header = Util.subByte(this.body_decrypted, 0, 1); 275 | this.parse_tlv(); 276 | } 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Package/SendPackageFactory.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Package; 2 | import android.graphics.*; 3 | import java.util.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Message.*; 6 | import net.newlydev.qqrobot.PCTIM.TLV.*; 7 | import net.newlydev.qqrobot.PCTIM.Utils.*; 8 | import net.newlydev.qqrobot.PCTIM.sdk.*; 9 | import java.io.*; 10 | 11 | public class SendPackageFactory 12 | { 13 | protected static int _seq = 0x3100; // (char)Util.Random.Next(); 14 | protected static byte[] body_end = {0x03}; 15 | public static byte[] get0002(QQUser user, long gin, String msg, boolean isxml) 16 | { 17 | 18 | ByteBuilder builder = new ByteBuilder(); 19 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 20 | builder.writebytes(user.TXProtocol.CMainVer); 21 | builder.writebytes(user.TXProtocol.CSubVer); 22 | builder.writebytes(new byte[]{0x00,0x02}); 23 | builder.writeint(GetNextSeq()); 24 | builder.writelong(user.QQ); 25 | builder.writebytes(user.TXProtocol.XxooA); 26 | builder.writebytes(user.TXProtocol.DwClientType); 27 | builder.writebytes(user.TXProtocol.DwPubNo); 28 | builder.writebytes(user.TXProtocol.XxooD); 29 | ByteBuilder body_builder=new ByteBuilder(); 30 | //long dateTime = Util.GetTimeSeconds(new Date()); 31 | long group = Util.ConvertQQGroupId(gin); 32 | if (!isxml) 33 | { 34 | byte[] message_to_send = Util.constructmessage(user, msg.getBytes()); 35 | body_builder.writebyte((byte)0x2A); 36 | body_builder.writelong(group); 37 | body_builder.writeint(message_to_send.length + 50); // 字符串长度 + 56,但是_data里面包含了和长度有关的额外六个byte 38 | body_builder.writebytes(new byte[] 39 | { 40 | 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x53, 41 | 0x47, 0x00, 42 | 0x00, 0x00, 0x00, 0x00 43 | }); 44 | body_builder.writelong(new Date().getTime() / 1000); 45 | body_builder.writebytes(Util.RandomKey(4)); 46 | body_builder.writebytes(Util.str_to_byte("0000000009008600")); 47 | body_builder.writebytes(new byte[] { 0x00, 0x0C }); 48 | body_builder.writebytes(Util.str_to_byte("E5BEAEE8BDAFE99B85E9BB91")); 49 | body_builder.writebytes(new byte[] { 0x00,0x00 }); 50 | body_builder.writebytes(message_to_send); 51 | 52 | } 53 | else 54 | { 55 | byte[] message_to_send =ZLibUtils.compress(msg.getBytes()); 56 | body_builder.writebyte((byte)0x2A); 57 | body_builder.writelong(group); 58 | body_builder.writeint(message_to_send.length + 64); 59 | body_builder.writebytes(new byte[] 60 | { 61 | 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x53, 62 | 0x47, 0x00, 63 | 0x00, 0x00, 0x00, 0x00 64 | }); 65 | body_builder.writelong(new Date().getTime() / 1000); 66 | body_builder.writebytes(Util.constructxmlmessage(user, message_to_send)); 67 | 68 | } 69 | Crypter crypter = new Crypter(); 70 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 71 | builder.writebytes(result); 72 | builder.writebytes(body_end); 73 | return builder.getdata(); 74 | } 75 | public static byte[] get001d(QQUser user) 76 | { 77 | //Util.log("[发送包] 命令: 00 1d"); 78 | ByteBuilder builder = new ByteBuilder(); 79 | 80 | 81 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 82 | builder.writebytes(user.TXProtocol.CMainVer); 83 | 84 | builder.writebytes(user.TXProtocol.CSubVer); 85 | builder.writebytes(Util.str_to_byte("00 1d")); 86 | builder.writeint(GetNextSeq()); 87 | 88 | builder.writelong(user.QQ); 89 | 90 | builder.writebytes(user.TXProtocol.XxooA); 91 | builder.writebytes(user.TXProtocol.DwClientType); 92 | builder.writebytes(user.TXProtocol.DwPubNo); 93 | builder.writebytes(user.TXProtocol.XxooD); 94 | 95 | 96 | 97 | ByteBuilder body_builder = new ByteBuilder() ; 98 | body_builder.writebytes(Util.str_to_byte(" 33 00 05 00 08 74 2E 71 71 2E 63 6F 6D 00 0A 71 75 6E 2E 71 71 2E 63 6F 6D 00 0C 71 7A 6F 6E 65 2E 71 71 2E 63 6F 6D 00 0C 6A 75 62 61 6F 2E 71 71 2E 63 6F 6D 00 09 6B 65 2E 71 71 2E 63 6F 6D ")); 99 | 100 | byte[] tlv_data = body_builder.getdata(); 101 | Crypter crypter = new Crypter(); 102 | byte[] result = crypter.encrypt(tlv_data, user.TXProtocol.SessionKey); 103 | builder.writebytes(result); 104 | builder.writebytes(body_end); 105 | return builder.getdata(); 106 | } 107 | public static byte[] get00ba(QQUser user, String code)// 108 | { 109 | ByteBuilder builder = new ByteBuilder(); 110 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 111 | builder.writebytes(user.TXProtocol.CMainVer); 112 | builder.writebytes(user.TXProtocol.CSubVer); 113 | builder.writebytes(Util.str_to_byte("00 ba")); 114 | builder.writeint(GetNextSeq()); 115 | builder.writelong(user.QQ); 116 | builder.writebytes(user.TXProtocol.XxooA); 117 | builder.writebytes(user.TXProtocol.DwClientType); 118 | builder.writebytes(user.TXProtocol.DwPubNo); 119 | builder.writebytes(user.TXProtocol.XxooD); 120 | builder.writebytes(user.QQPacket00BaKey); 121 | ByteBuilder body_builder = new ByteBuilder() ; 122 | body_builder.writebytes(Util.str_to_byte("00 02 00 00 08 04 01 E0")); 123 | body_builder.writebytes(user.TXProtocol.DwSsoVersion); 124 | body_builder.writebytes(user.TXProtocol.DwServiceId); 125 | body_builder.writebytes(user.TXProtocol.DwClientVer); 126 | body_builder.writebyte((byte) 0x00); 127 | body_builder.writebytesbylength(user.TXProtocol.BufSigClientAddr); 128 | body_builder.writebytes(new byte[] {0x01, 0x02}); 129 | body_builder.writebytesbylength(user.TXProtocol.BufDhPublicKey); 130 | if (code.equals("")) 131 | { 132 | body_builder.writebytes(new byte[] {0x13, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}); 133 | body_builder.writebyte(user.QQPacket00BaSequence); 134 | if (user.TXProtocol.PngToken == null || user.TXProtocol.PngToken.length == 0) 135 | { 136 | body_builder.writebyte((byte) 0x00); 137 | } 138 | else 139 | { 140 | body_builder.writebytesbylength(user.TXProtocol.PngToken); 141 | } 142 | } 143 | else 144 | { 145 | byte[] verifyCodeBytes = code.getBytes(); 146 | body_builder.writebytes(new byte[] {0x14, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00}); 147 | body_builder.writeint(verifyCodeBytes.length);///????? 148 | body_builder.writebytes(verifyCodeBytes); 149 | body_builder.writebytesbylength(user.TXProtocol.BufSigPic);//输入验证码后清空图片流 150 | user.QQPacket00BaVerifyCode = new byte[] { }; 151 | user.QQPacket00BaSequence = 1; 152 | } 153 | 154 | body_builder.writebytesbylength(user.QQPacket00BaFixKey); 155 | byte[] tlv_data = body_builder.getdata(); 156 | Crypter crypter = new Crypter(); 157 | byte[] result = crypter.encrypt(tlv_data, user.QQPacket00BaKey); 158 | builder.writebytes(result); 159 | builder.writebytes(body_end); 160 | user.QQPacket00BaSequence += 1; 161 | return builder.getdata(); 162 | } 163 | 164 | public static byte[] get0825(QQUser user) 165 | { 166 | ByteBuilder builder = new ByteBuilder(); 167 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 168 | builder.writebytes(user.TXProtocol.CMainVer); 169 | builder.writebytes(user.TXProtocol.CSubVer); 170 | builder.writebytes(new byte[]{0x08,0x25}); 171 | builder.writeint(GetNextSeq()); 172 | builder.writelong(user.QQ); 173 | builder.writebytes(user.TXProtocol.XxooA); 174 | builder.writebytes(user.TXProtocol.DwClientType); 175 | builder.writebytes(user.TXProtocol.DwPubNo); 176 | builder.writebytes(user.TXProtocol.XxooD); 177 | builder.writebytes(user.QQPacket0825Key); 178 | ByteBuilder tlv_builder = new ByteBuilder() ; 179 | byte[] tlv0018 = TLVFactory.tlv0018(user); 180 | byte[] tlv0039 = TLVFactory.tlv0309(user); 181 | byte[] tlv0036 = TLVFactory.tlv0036(2); 182 | byte[] tlv0114 = TLVFactory.tlv0114(user); 183 | tlv_builder.writebytes(tlv0018); 184 | tlv_builder.writebytes(tlv0039); 185 | tlv_builder.writebytes(tlv0036); 186 | tlv_builder.writebytes(tlv0114); 187 | byte[] tlv_data = tlv_builder.getdata(); 188 | Crypter crypter = new Crypter(); 189 | byte[] result = crypter.encrypt(tlv_data, user.QQPacket0825Key); 190 | builder.writebytes(result); 191 | builder.writebytes(body_end); 192 | return builder.getdata(); 193 | } 194 | 195 | public static byte[] get0836(QQUser user, boolean need_verifycode) 196 | { 197 | ByteBuilder builder = new ByteBuilder(); 198 | byte[] tlv0110 = null; 199 | byte[] tlv0032 = null; 200 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 201 | builder.writebytes(user.TXProtocol.CMainVer); 202 | builder.writebytes(user.TXProtocol.CSubVer); 203 | builder.writebytes(new byte[]{0x08,0x36}); 204 | builder.writeint(GetNextSeq()); 205 | builder.writelong(user.QQ); 206 | builder.writebytes(user.TXProtocol.XxooA); 207 | builder.writebytes(user.TXProtocol.DwClientType); 208 | builder.writebytes(user.TXProtocol.DwPubNo); 209 | builder.writebytes(user.TXProtocol.XxooD); 210 | builder.writebytes(user.TXProtocol.SubVer); 211 | builder.writebytes(user.TXProtocol.EcdhVer); 212 | builder.writebytesbylength(user.TXProtocol.BufDhPublicKey); 213 | builder.writebytes(new byte[] { 0x00, 0x00, 0x00, 0x10 }); 214 | builder.writebytes(user.QQPacket0836Key1); 215 | ByteBuilder tlv_builder = new ByteBuilder(); 216 | byte[] tlv0112 = TLVFactory.tlv0112(user); 217 | byte[] tlv030f = TLVFactory.tlv030f(user); 218 | byte[] tlv0005 = TLVFactory.tlv0005(user); 219 | byte[] tlv0006 = TLVFactory.tlv0006(user); 220 | byte[] tlv0015 = TLVFactory.tlv0015(user); 221 | byte[] tlv001a = TLVFactory.tlv001a(tlv0015, user); 222 | byte[] tlv0018 = TLVFactory.tlv0018(user); 223 | byte[] tlv0103 = TLVFactory.tlv0103(user); 224 | if (need_verifycode) 225 | { 226 | tlv0110 = TLVFactory.tlv0110(user); 227 | tlv0032 = TLVFactory.tlv0032(user); 228 | } 229 | byte[] tlv0312= TLVFactory.tlv0312(); 230 | byte[] tlv0508 = TLVFactory.tlv0508(); 231 | byte[] tlv0313= TLVFactory.tlv0313(user); 232 | byte[] tlv0102= TLVFactory.tlv0102(user); 233 | tlv_builder.writebytes(tlv0112); 234 | tlv_builder.writebytes(tlv030f); 235 | tlv_builder.writebytes(tlv0005); 236 | tlv_builder.writebytes(tlv0006); 237 | tlv_builder.writebytes(tlv0015); 238 | tlv_builder.writebytes(tlv001a); 239 | tlv_builder.writebytes(tlv0018); 240 | tlv_builder.writebytes(tlv0103); 241 | if (need_verifycode) 242 | { 243 | tlv_builder.writebytes(tlv0110); 244 | } 245 | tlv_builder.writebytes(tlv0312); 246 | tlv_builder.writebytes(tlv0508); 247 | tlv_builder.writebytes(tlv0313); 248 | tlv_builder.writebytes(tlv0102); 249 | byte[] tlv_data = tlv_builder.getdata(); 250 | Crypter crypter = new Crypter(); 251 | byte[] result = crypter.encrypt(tlv_data, user.TXProtocol.BufDhShareKey); 252 | builder.writebytes(result); 253 | builder.writebytes(body_end); 254 | return builder.getdata(); 255 | } 256 | 257 | public static byte[] get0828(QQUser user) 258 | { 259 | ByteBuilder builder = new ByteBuilder(); 260 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 261 | builder.writebytes(user.TXProtocol.CMainVer); 262 | builder.writebytes(user.TXProtocol.CSubVer); 263 | builder.writebytes(new byte[]{0x08,0x28}); 264 | builder.writeint(GetNextSeq()); 265 | builder.writelong(user.QQ); 266 | builder.writebytes(new byte[] { 0x02, 0x00, 0x00}); 267 | builder.writebytes(user.TXProtocol.DwClientType); 268 | builder.writebytes(user.TXProtocol.DwPubNo); 269 | builder.writebytes(new byte[] { 0x00, 0x30, 0x00, 0x3a }); 270 | builder.writebytesbylength(user.TXProtocol.BufSigSession); 271 | ByteBuilder tlv_builder = new ByteBuilder(); 272 | byte[] tlv0007 = TLVFactory.tlv0007(user); 273 | byte[] tlv000c = TLVFactory.tlv000c(user); 274 | byte[] tlv0015 = TLVFactory.tlv0015(user); 275 | byte[] tlv0036 = TLVFactory.tlv0036(2); 276 | byte[] tlv0018 = TLVFactory.tlv0018(user); 277 | byte[] tlv001f = TLVFactory.tlv001f(user); 278 | byte[] tlv0105 = TLVFactory.tlv0105(user); 279 | byte[] tlv010b = TLVFactory.tlv010b(user); 280 | byte[] tlv002d = TLVFactory.tlv002d(user); 281 | tlv_builder.writebytes(tlv0007); 282 | tlv_builder.writebytes(tlv000c); 283 | tlv_builder.writebytes(tlv0015); 284 | tlv_builder.writebytes(tlv0036); 285 | tlv_builder.writebytes(tlv0018); 286 | tlv_builder.writebytes(tlv001f); 287 | tlv_builder.writebytes(tlv0105); 288 | tlv_builder.writebytes(tlv010b); 289 | tlv_builder.writebytes(tlv002d); 290 | byte[] tlv_data = tlv_builder.getdata(); 291 | Crypter crypter = new Crypter(); 292 | byte[] result = crypter.encrypt(tlv_data, user.TXProtocol.BufSessionKey); 293 | builder.writebytes(result); 294 | builder.writebytes(body_end); 295 | return builder.getdata(); 296 | } 297 | 298 | public static byte[] get00ec(QQUser user, byte[] loginStatus) 299 | { 300 | ByteBuilder builder = new ByteBuilder(); 301 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 302 | builder.writebytes(user.TXProtocol.CMainVer); 303 | builder.writebytes(user.TXProtocol.CSubVer); 304 | builder.writebytes(Util.str_to_byte("00 ec")); 305 | builder.writeint(GetNextSeq()); 306 | builder.writelong(user.QQ); 307 | builder.writebytes(user.TXProtocol.XxooA); 308 | builder.writebytes(user.TXProtocol.DwClientType); 309 | builder.writebytes(user.TXProtocol.DwPubNo); 310 | builder.writebytes(user.TXProtocol.XxooD); 311 | 312 | ByteBuilder body_builder=new ByteBuilder(); 313 | 314 | body_builder.writebytes(new byte[] { 0x01, 0x00 }); 315 | body_builder.writebytes(loginStatus); 316 | body_builder.writebytes(new byte[] { 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00 }); 317 | 318 | Crypter crypter = new Crypter(); 319 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 320 | builder.writebytes(result); 321 | builder.writebytes(body_end); 322 | 323 | return builder.getdata(); 324 | 325 | } 326 | 327 | public static byte[] get0058(QQUser user) 328 | { 329 | ByteBuilder builder = new ByteBuilder(); 330 | 331 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 332 | builder.writebytes(user.TXProtocol.CMainVer); 333 | builder.writebytes(user.TXProtocol.CSubVer); 334 | builder.writebytes(new byte[]{0x00,0x58}); 335 | builder.writeint(GetNextSeq()); 336 | builder.writelong(user.QQ); 337 | builder.writebytes(user.TXProtocol.XxooA); 338 | builder.writebytes(user.TXProtocol.DwClientType); 339 | builder.writebytes(user.TXProtocol.DwPubNo); 340 | builder.writebytes(user.TXProtocol.XxooD); 341 | 342 | ByteBuilder body_builder=new ByteBuilder(); 343 | 344 | body_builder.writelong(user.QQ); 345 | 346 | Crypter crypter = new Crypter(); 347 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 348 | builder.writebytes(result); 349 | builder.writebytes(body_end); 350 | 351 | return builder.getdata(); 352 | 353 | } 354 | 355 | public static byte[] get0017(QQUser user, byte[] data_to_send, byte[] seq) 356 | { 357 | ByteBuilder builder = new ByteBuilder(); 358 | 359 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 360 | builder.writebytes(user.TXProtocol.CMainVer); 361 | builder.writebytes(user.TXProtocol.CSubVer); 362 | builder.writebytes(new byte[]{0x00,0x17}); 363 | builder.writebytes(seq); 364 | builder.writelong(user.QQ); 365 | builder.writebytes(user.TXProtocol.XxooA); 366 | builder.writebytes(user.TXProtocol.DwClientType); 367 | builder.writebytes(user.TXProtocol.DwPubNo); 368 | builder.writebytes(user.TXProtocol.XxooD); 369 | 370 | ByteBuilder body_builder=new ByteBuilder(); 371 | 372 | body_builder.writebytes(data_to_send); 373 | 374 | Crypter crypter = new Crypter(); 375 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 376 | builder.writebytes(result); 377 | builder.writebytes(body_end); 378 | 379 | return builder.getdata(); 380 | } 381 | 382 | public static byte[] get00ce(QQUser user, byte[] data_to_send, byte[] seq) 383 | { 384 | ByteBuilder builder = new ByteBuilder(); 385 | 386 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 387 | builder.writebytes(user.TXProtocol.CMainVer); 388 | builder.writebytes(user.TXProtocol.CSubVer); 389 | builder.writebytes(Util.str_to_byte("00ce")); 390 | builder.writebytes(seq); 391 | builder.writelong(user.QQ); 392 | builder.writebytes(user.TXProtocol.XxooA); 393 | builder.writebytes(user.TXProtocol.DwClientType); 394 | builder.writebytes(user.TXProtocol.DwPubNo); 395 | builder.writebytes(user.TXProtocol.XxooD); 396 | 397 | ByteBuilder body_builder=new ByteBuilder(); 398 | 399 | body_builder.writebytes(data_to_send); 400 | 401 | Crypter crypter = new Crypter(); 402 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 403 | builder.writebytes(result); 404 | builder.writebytes(body_end); 405 | 406 | return builder.getdata(); 407 | 408 | } 409 | 410 | public static byte[] get0319(QQUser user, long _recvQQ, byte[] MessageTime) 411 | { 412 | ByteBuilder builder = new ByteBuilder(); 413 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 414 | builder.writebytes(user.TXProtocol.CMainVer); 415 | builder.writebytes(user.TXProtocol.CSubVer); 416 | builder.writebytes(Util.str_to_byte("0319")); 417 | builder.writeint(GetNextSeq()); 418 | builder.writelong(user.QQ); 419 | builder.writebytes(new byte[] 420 | { 421 | 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x68, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 422 | 0x00, 0x00 423 | }); 424 | ByteBuilder body_builder=new ByteBuilder(); 425 | 426 | 427 | body_builder.writebytes(new byte[] { 0x00, 0x00, 0x00, 0x07 }); 428 | ByteBuilder data_builder=new ByteBuilder(); 429 | 430 | data_builder.writebytes(Util.str_to_byte("0A0C08")); 431 | data_builder.writebytes(Util.str_to_byte(Util.PB_toLength(_recvQQ))); 432 | data_builder.writebyte((byte) 0x10); 433 | data_builder.writebytes( 434 | Util.str_to_byte( 435 | Util.PB_toLength(Long.parseLong(Util.byte2HexString(MessageTime).replace(" ", ""), 16)))); 436 | data_builder.writebytes(new byte[] { 0x20, 0x00 }); 437 | //数据长度 438 | 439 | body_builder.writebytes(Util.subByte(Util.ToByte(data_builder.getdata().length), 0, 4)); 440 | body_builder.writebytes(Util.str_to_byte("08011203980100")); 441 | //数据 442 | body_builder.writebytes(data_builder.getdata()); 443 | 444 | Crypter crypter = new Crypter(); 445 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 446 | builder.writebytes(result); 447 | builder.writebytes(body_end); 448 | return builder.getdata(); 449 | } 450 | 451 | 452 | 453 | public static byte[] sendpic(QQUser user, long gin,byte[] bitmap) throws IOException 454 | { 455 | 456 | ByteBuilder builder = new ByteBuilder(); 457 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 458 | builder.writebytes(user.TXProtocol.CMainVer); 459 | builder.writebytes(user.TXProtocol.CSubVer); 460 | builder.writebytes(Util.str_to_byte("0002")); 461 | builder.writeint(GetNextSeq()); 462 | builder.writelong(user.QQ); 463 | builder.writebytes(Util.str_to_byte("02 00 00 00 01 01 01 00 00 68 20")); 464 | 465 | ByteBuilder body_builder=new ByteBuilder(); 466 | //long dateTime = Util.GetTimeSeconds(new Date()); 467 | long group = Util.ConvertQQGroupId(gin); 468 | //byte[] guid = ("{"+Util.GetMD5ToGuidHashFromFile(message.Message) + "}."+message.Message.split("[.]")[message.Message.split("[.]").length-1]).getBytes(); 469 | byte[] guid = ("{" + Util.GetMD5ToGuidHashFromBytes(bitmap) + "}.jpg").getBytes(); 470 | body_builder.writebyte((byte)0x2A); 471 | body_builder.writelong(group); 472 | body_builder.writebytes(Util.str_to_byte("01 00 00 02 01 00 00 00 00 00 00 00 4D 53 47 00 00 00 00 00")); 473 | body_builder.writelong(new Date().getTime() / 1000); 474 | body_builder.writebytes(Util.RandomKey(4)); 475 | body_builder.writebytes(Util.str_to_byte("00 00 00 00 09 00 86 00")); 476 | body_builder.writebytes(Util.str_to_byte("00 0C")); 477 | body_builder.writebytes(Util.str_to_byte("E5 BE AE E8 BD AF E9 9B 85 E9 BB 91")); 478 | body_builder.writebytes(Util.str_to_byte("00 00 03 00 CB 02")); 479 | body_builder.writebytes(Util.str_to_byte("00 2A")); 480 | body_builder.writebytes(guid); 481 | body_builder.writebytes(Util.str_to_byte("04 00 04")); 482 | body_builder.writebytes(Util.str_to_byte("9B 53 B0 08 05 00 04 D9 8A 5A 70 06 00 04 00 00 00 50 07 00 01 43 08 00 00 09 00 01 01 0B 00 00 14 00 04 11 00 00 00 15 00 04 00 00 02 BC 16 00 04 00 00 02 BC 18 00 04 00 00 7D 5E FF 00 5C 15 36 20 39 32 6B 41 31 43 39 62 35 33 62 30 30 38 64 39 38 61 35 61 37 30")); 483 | body_builder.writebytes(Util.str_to_byte("20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20")); 484 | body_builder.writebytes(guid); 485 | body_builder.writebyte((byte)0x41); 486 | 487 | 488 | Crypter crypter = new Crypter(); 489 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 490 | builder.writebytes(result); 491 | builder.writebytes(body_end); 492 | return builder.getdata(); 493 | } 494 | 495 | public static byte[] get00cd(QQUser user, long uin, String msg, boolean isxml) 496 | { 497 | 498 | ByteBuilder builder = new ByteBuilder(); 499 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 500 | builder.writebytes(user.TXProtocol.CMainVer); 501 | builder.writebytes(user.TXProtocol.CSubVer); 502 | builder.writebytes(Util.str_to_byte("00cd")); 503 | builder.writeint(GetNextSeq()); 504 | builder.writelong(user.QQ); 505 | builder.writebytes(user.TXProtocol.XxooA); 506 | builder.writebytes(user.TXProtocol.DwClientType); 507 | builder.writebytes(user.TXProtocol.DwPubNo); 508 | builder.writebytes(user.TXProtocol.XxooD); 509 | 510 | ByteBuilder body_builder=new ByteBuilder(); 511 | long dateTime = Util.GetTimeSeconds(new Date()); 512 | byte[] md5 = user.TXProtocol.SessionKey; 513 | if (!isxml) 514 | { 515 | 516 | byte[] message_to_send = Util.constructmessage(user, msg.getBytes()); 517 | body_builder.writelong(user.QQ); 518 | body_builder.writelong(uin); 519 | body_builder.writebytes(new byte[] 520 | { 521 | 0x00, 0x00, 0x00, 0x0D, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 522 | 0x01, 0x01 523 | }); 524 | body_builder.writebytes(user.TXProtocol.CMainVer); 525 | body_builder.writebytes(user.TXProtocol.CSubVer); 526 | body_builder.writelong(user.QQ); 527 | body_builder.writelong(uin); 528 | body_builder.writebytes(md5); 529 | body_builder.writebytes(new byte[] { 0x00, 0x0B }); 530 | body_builder.writebytes(Util.RandomKey(2)); 531 | body_builder.writelong(dateTime); 532 | body_builder.writebytes(new byte[] 533 | { 534 | 0x02, 0x34, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x4D, 535 | 0x53, 0x47, 536 | 0x00, 0x00, 0x00, 0x00, 0x00 537 | }); 538 | body_builder.writelong(dateTime); 539 | byte[] MessageId = Util.RandomKey(4); 540 | body_builder.writebytes(MessageId); 541 | body_builder.writebytes(Util.str_to_byte("0000000009008600")); 542 | body_builder.writebytes(new byte[] { 0x00, 0x06 }); 543 | body_builder.writebytes(Util.str_to_byte("E5AE8BE4BD93")); 544 | body_builder.writebytes(new byte[] { 0x00, 0x00 }); 545 | body_builder.writebytes(message_to_send); 546 | 547 | } 548 | else 549 | { 550 | byte[] message_to_send =ZLibUtils.compress(msg.getBytes()); 551 | body_builder.writelong(user.QQ); 552 | body_builder.writelong(uin); 553 | body_builder.writebytes(new byte[] { 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x00, 0x04 }); 554 | body_builder.writebytes(new byte[] { 0x00, 0x00, 0x00, 0x00 }); 555 | body_builder.writebytes(Util.str_to_byte("370F")); 556 | body_builder.writelong(user.QQ); 557 | body_builder.writelong(uin); 558 | body_builder.writebytes(md5); 559 | body_builder.writebytes(Util.str_to_byte("000B")); 560 | body_builder.writebytes(Util.RandomKey(2)); 561 | body_builder.writelong(dateTime); 562 | body_builder.writebytes(new byte[] 563 | { 564 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x4D, 565 | 0x53, 0x47, 566 | 0x00, 0x00, 0x00, 0x00, 0x00 567 | }); 568 | body_builder.writelong(dateTime); 569 | body_builder.writebytes(Util.constructxmlmessage(user, message_to_send)); 570 | } 571 | Crypter crypter = new Crypter(); 572 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 573 | builder.writebytes(result); 574 | builder.writebytes(body_end); 575 | return builder.getdata(); 576 | } 577 | 578 | public static byte[] get0388(QQUser user, long gin, Bitmap img) 579 | { 580 | ByteBuilder builder = new ByteBuilder(); 581 | builder.writebytes(QQGlobal.QQHeaderBasicFamily); 582 | builder.writebytes(user.TXProtocol.CMainVer); 583 | builder.writebytes(user.TXProtocol.CSubVer); 584 | builder.writebytes(Util.str_to_byte("0388")); 585 | int seq = GetNextSeq(); 586 | builder.writeint(seq); 587 | builder.writelong(user.QQ); 588 | builder.writebytes(Util.str_to_byte("04 00 00 00 01 01 01 00 00 68 20 00 00 00 00 00 00 00 00 ")); 589 | ByteBuilder body_builder=new ByteBuilder(); 590 | long width = img.getWidth(); // 图片的宽度 591 | long height = img.getHeight(); // 图片的高度 592 | byte[] img_byte = Util.Bufferedimg_tobytes(img); 593 | long img_length = img_byte.length; 594 | byte[] md5 = Util.str_to_byte(Util.getMD5(img_byte)); 595 | body_builder.writebytes(new byte[] {0x00,0x00,0x00,0x07}); 596 | 597 | ByteBuilder img_builder=new ByteBuilder(); 598 | img_builder.writebytes(Util.str_to_byte("5A")); 599 | img_builder.writebyte((byte)0x08); 600 | img_builder.writebytes(Util.str_to_byte(Util.PB_toLength(gin))); 601 | img_builder.writebyte((byte)0x10); 602 | img_builder.writebytes(Util.str_to_byte(Util.PB_toLength(user.QQ))); 603 | img_builder.writebytes(Util.str_to_byte("18 00")); 604 | img_builder.writebyte((byte)0x22); 605 | img_builder.writebyte((byte)md5.length); 606 | img_builder.writebytes(md5); 607 | img_builder.writebyte((byte)0x28); 608 | img_builder.writebytes(Util.str_to_byte(Util.PB_toLength(img_length))); 609 | img_builder.writebyte((byte)0x32); 610 | img_builder.writebytes(Util.str_to_byte("1A 37 00 4D 00 32 00 25 00 4C 00 31 00 56 00 32 00 7B 00 39 00 30 00 29 00 52 00 38 01 48 01")); 611 | img_builder.writebyte((byte)0x50); 612 | img_builder.writebytes(Util.str_to_byte(Util.PB_toLength(width))); 613 | img_builder.writebyte((byte)0x58); 614 | img_builder.writebytes(Util.str_to_byte(Util.PB_toLength(height))); 615 | img_builder.writebytes(Util.str_to_byte("60 04 6A 05 32 36 36 35 36 70 00 78 03 80 01")); 616 | img_builder.writebyte((byte)0x00); 617 | 618 | 619 | body_builder.writebytes(new byte[] { 0x00, 0x00 }); 620 | body_builder.writeint(img_builder.getdata().length); 621 | body_builder.writebytes(Util.str_to_byte("08 01 12 03 98 01 01 10 01 1A")); 622 | //数据 623 | body_builder.writebytes(img_builder.getdata()); 624 | Crypter crypter = new Crypter(); 625 | byte[] result = crypter.encrypt(body_builder.getdata(), user.TXProtocol.SessionKey); 626 | builder.writebytes(result); 627 | builder.writebytes(body_end); 628 | PictureStore store = new PictureStore(); 629 | store.pictureid = seq; 630 | store.data = img_byte; 631 | store.Group = gin; 632 | user.imgs.add(store); 633 | return builder.getdata(); 634 | } 635 | 636 | protected static int GetNextSeq() 637 | { 638 | _seq++; 639 | // 为了兼容iQQ 640 | // iQQ把序列号的高位都为0,如果为1,它可能会拒绝,wqfox称是因为TX是这样做的 641 | int i = 0x7FFF; 642 | _seq = _seq & i; 643 | if (_seq == 0) 644 | { 645 | _seq++; 646 | } 647 | return _seq; 648 | } 649 | } 650 | 651 | 652 | 653 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/QQGlobal.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM; 2 | import net.newlydev.qqrobot.PCTIM.Utils.*; 3 | 4 | public class QQGlobal 5 | { 6 | public static final byte[] QQHeaderBasicFamily = {0x02}; 7 | 8 | /// 9 | /// 包结尾标识 10 | /// 11 | public static final byte QQHeader03Family = 0x03; 12 | public static byte[] QqexeMD5 = Util.str_to_byte("facf7cc5ae02e6650c0107cdfe0e1b2c"); 13 | 14 | 15 | public static final byte[] Online = {0x0A}; 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/QQUser.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM; 2 | import java.util.*; 3 | import net.newlydev.qqrobot.PCTIM.Message.*; 4 | import net.newlydev.qqrobot.PCTIM.Utils.*; 5 | public class QQUser 6 | { 7 | 8 | public long QQ; 9 | public byte[] MD51; 10 | public byte[] MD52; 11 | public String NickName; 12 | public int Gender; 13 | public int Age; 14 | public long logintime; 15 | public boolean islogined = false; 16 | public boolean offline = false; 17 | 18 | public byte[] QQPacket00BaVerifyCode=new byte[0]; 19 | 20 | public byte Next; 21 | 22 | public String userskey; 23 | 24 | public String bkn; 25 | 26 | public String quncookie=""; 27 | 28 | public String pskey; 29 | 30 | public String qungtk; 31 | 32 | public byte[] QQPacket00BaKey = Util.RandomKey(); 33 | 34 | public byte QQPacket00BaSequence=0x01; 35 | 36 | public byte[] QQPacket00BaFixKey =Util.str_to_byte("69 20 D1 14 74 F5 B3 93 E4 D5 02 B3 71 1A CD 2A"); 37 | 38 | public QQUser(long qqNum,byte[] pwd) 39 | { 40 | 41 | QQ = qqNum; 42 | SetPassword(pwd); 43 | } 44 | 45 | 46 | public void SetPassword(byte[] pwd) 47 | { 48 | 49 | MD51 = pwd; 50 | MD52 = Util.MD5(Util.byteMerger(MD51,Util.ToByte(this.QQ))); 51 | } 52 | 53 | 54 | 55 | public List imgs = new ArrayList(); 56 | 57 | public TXProtocol TXProtocol = new TXProtocol(); 58 | 59 | public byte[] QQPacket0825Key = Util.RandomKey(); 60 | 61 | public boolean IsLoginRedirect; 62 | 63 | public byte[] QQPacket0836Key1 = Util.RandomKey(); 64 | 65 | public long lastMessage ; 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Robot/QQRobot.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Robot; 2 | import java.net.*; 3 | import java.util.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Socket.*; 6 | import net.newlydev.qqrobot.PCTIM.Utils.*; 7 | import net.newlydev.qqrobot.PCTIM.sdk.*; 8 | import java.io.*; 9 | import android.content.*; 10 | import dalvik.system.*; 11 | public class QQRobot 12 | { 13 | private Udpsocket socket = null; 14 | private QQUser user = null; 15 | private RobotApi api; 16 | List plugins =new ArrayList(); 17 | 18 | public QQRobot(Udpsocket _socket, QQUser _user,Context ctx) 19 | { 20 | this.socket = _socket; 21 | this.user = _user; 22 | this.api = new RobotApi(this.socket, this.user); 23 | ArrayList plugin_list=new ArrayList(); 24 | try 25 | { 26 | DataInputStream fr=new DataInputStream(new FileInputStream(new File(ctx.getDataDir(),"pluginlist.cfg"))); 27 | String s; 28 | while((s=fr.readLine())!=null) 29 | { 30 | String apkpath=ctx.getPackageManager().getApplicationInfo(s,0).sourceDir; 31 | plugin_list.add(apkpath); 32 | DexClassLoader dcl=new DexClassLoader(apkpath,ctx.getCacheDir().getPath(),null,ctx.getClassLoader()); 33 | Class pluginCls = dcl.loadClass(s+".Main"); 34 | final Plugin plugin = (Plugin)pluginCls.newInstance(); 35 | plugins.add(plugin); 36 | new Thread(){ 37 | @Override public void run() 38 | { 39 | plugin.onLoad(api); 40 | } 41 | }.start(); 42 | Util.log("[插件] 加载成功 [插件名]: " + plugin.name()); 43 | } 44 | fr.close(); 45 | 46 | } 47 | catch (Exception e) 48 | { 49 | e.printStackTrace(); 50 | } 51 | } 52 | 53 | public void call(final QQMessage qqmessage) 54 | { 55 | for (final Plugin plugin : this.plugins) 56 | { 57 | new Thread(){ 58 | public void run() 59 | { 60 | plugin.onMessageHandler(qqmessage); 61 | } 62 | }.start(); 63 | } 64 | } 65 | 66 | } 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Robot/RobotApi.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Robot; 2 | import java.io.*; 3 | import java.net.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Message.*; 6 | import net.newlydev.qqrobot.PCTIM.Socket.*; 7 | import net.newlydev.qqrobot.PCTIM.Utils.*; 8 | import net.newlydev.qqrobot.PCTIM.sdk.*; 9 | import org.json.*; 10 | import android.graphics.*; 11 | import android.content.Context; 12 | import net.newlydev.qqrobot.mApplication; 13 | 14 | public class RobotApi implements API 15 | { 16 | 17 | private Udpsocket socket = null; 18 | private QQUser user = null; 19 | 20 | @Override 21 | public Context getContext() 22 | { 23 | // TODO: Implement this method 24 | return mApplication.getContext(); 25 | } 26 | 27 | 28 | public RobotApi(Udpsocket _socket, QQUser _user) 29 | { 30 | this.user = _user; 31 | this.socket = _socket; 32 | } 33 | 34 | @Override 35 | public void SendGroupTextMessage(long gin, String msg) 36 | { 37 | SendMessage.SendGroupMessage(this.user, this.socket, gin,msg,false); 38 | // TODO: Implement this method 39 | } 40 | 41 | @Override 42 | public void SendGroupXMLMessage(long gin, String xml) 43 | { 44 | SendMessage.SendGroupMessage(this.user, this.socket, gin,xml,true); 45 | // TODO: Implement this method 46 | } 47 | 48 | @Override 49 | public void SendGroupBitmapMessage(long gin, Bitmap bitmap) 50 | { 51 | SendMessage.SendGroupMessage(this.user,this.socket,gin,bitmap); 52 | // TODO: Implement this method 53 | } 54 | 55 | @Override 56 | public void SendFriendTextMessage(long uin, String msg) 57 | { 58 | SendMessage.SendFriendMessage(this.user,this.socket,uin,msg,false); 59 | // TODO: Implement this method 60 | } 61 | 62 | @Override 63 | public void SendFriendXMLMessage(long uin, String xml) 64 | { 65 | SendMessage.SendFriendMessage(this.user,this.socket,uin,xml,true); 66 | // TODO: Implement this method 67 | } 68 | @Override 69 | public void GroupMemberShutUp(long gc, long uin, int time) 70 | { 71 | try 72 | { 73 | String urls="http://qinfo.clt.qq.com/cgi-bin/qun_info/set_group_shutup"; 74 | URL lll = new URL(urls); 75 | HttpURLConnection connection = (HttpURLConnection) lll.openConnection(); 76 | connection.setRequestMethod("POST"); 77 | connection.setRequestProperty("Cookie", user.quncookie); 78 | connection.setDoInput(true); 79 | connection.setDoOutput(true); 80 | String body=""; 81 | if (uin == 0) 82 | { 83 | if (time == 0) 84 | { 85 | body = "gc=" + gc + "&all_shutup=0&bkn=" + user.bkn + "&src=qinfo_v2"; 86 | } 87 | else 88 | { 89 | body = "gc=" + gc + "&all_shutup=1&bkn=" + user.bkn + "&src=qinfo_v2"; 90 | } 91 | } 92 | else 93 | { 94 | JSONArray data=new JSONArray(); 95 | data.put(new JSONObject().put("uin", uin).put("t", time)); 96 | body = "gc=" + gc + "&shutup_list=" +URLEncoder.encode(data.toString()) + "&bkn=" + user.bkn + "&src=qinfo_v2"; 97 | } 98 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8")); 99 | writer.write(body); 100 | writer.close(); 101 | connection.getResponseCode(); 102 | connection.disconnect(); 103 | } 104 | catch (Exception e) 105 | { 106 | e.printStackTrace(); 107 | } 108 | } 109 | 110 | @Override 111 | public void setGroupMemberCard(long gin, long uin, String card) 112 | { 113 | try 114 | { 115 | String urls="http://qinfo.clt.qq.com/cgi-bin/mem_card/set_group_mem_card"; 116 | URL lll = new URL(urls); 117 | HttpURLConnection connection = (HttpURLConnection) lll.openConnection(); 118 | connection.setRequestMethod("POST"); 119 | connection.setRequestProperty("Cookie", user.quncookie); 120 | connection.setDoInput(true); 121 | connection.setDoOutput(true); 122 | String body="gc="+gin+"&u="+uin+"&name="+URLEncoder.encode(card)+"&bkn="+user.bkn; 123 | BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(connection.getOutputStream(), "UTF-8")); 124 | writer.write(body); 125 | writer.close(); 126 | connection.getResponseCode(); 127 | connection.disconnect(); 128 | } 129 | catch (Exception e) 130 | { 131 | e.printStackTrace(); 132 | } 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Socket/HeartBeat.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Socket; 2 | import java.util.*; 3 | import net.newlydev.qqrobot.PCTIM.*; 4 | import net.newlydev.qqrobot.PCTIM.Package.*; 5 | 6 | public class HeartBeat 7 | { 8 | private QQUser user = null; 9 | private Thread thread = null; 10 | private long time_miles = 0; 11 | private Udpsocket socket = null; 12 | public HeartBeat(final QQUser _user,Udpsocket _socket){ 13 | 14 | this.user = _user; 15 | this.socket = _socket; 16 | this.thread = new Thread(){ 17 | public void run(){ 18 | while(true){ 19 | try 20 | { 21 | byte[] data = SendPackageFactory.get0058(_user); 22 | time_miles = new Date().getTime(); 23 | socket.sendMessage(data); 24 | this.sleep(10000); 25 | } 26 | catch (InterruptedException e) 27 | { 28 | System.out.println(e.getMessage()); 29 | } 30 | } 31 | 32 | } 33 | }; 34 | 35 | } 36 | 37 | 38 | public void start(){ 39 | this.thread.start(); 40 | 41 | } 42 | 43 | public void stop(){ 44 | this.thread.stop(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Socket/LoginManager.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Socket; 2 | import android.graphics.*; 3 | import android.os.*; 4 | import java.io.*; 5 | import java.util.*; 6 | import net.newlydev.qqrobot.PCTIM.*; 7 | import net.newlydev.qqrobot.PCTIM.Package.*; 8 | import net.newlydev.qqrobot.PCTIM.Utils.*; 9 | 10 | public class LoginManager 11 | { 12 | String verifyCode=""; 13 | private byte[] data = null; 14 | public Udpsocket socket = null; 15 | private QQUser _user; 16 | private Object lock; 17 | public LoginManager(QQUser user, Object lock) 18 | { 19 | this._user = user; 20 | this.lock = lock; 21 | user.TXProtocol.DwServerIP = "sz.tencent.com"; 22 | socket = new Udpsocket(user); 23 | 24 | } 25 | 26 | public void setVerifyCode(String verifyCode) 27 | { 28 | this.verifyCode = verifyCode; 29 | synchronized(this){ 30 | this.notify(); 31 | } 32 | } 33 | 34 | public void relogin() 35 | { 36 | data = SendPackageFactory.get0825(_user); 37 | socket.sendMessage(data); 38 | } 39 | 40 | 41 | public boolean login(Messenger messenger) 42 | { 43 | 44 | try 45 | { 46 | data = SendPackageFactory.get0825(_user); 47 | socket.sendMessage(data); 48 | byte[] result = socket.receiveMessage(); 49 | //System.out.println(Util.byte2HexString(result)); 50 | ParseRecivePackage parsereceive = new ParseRecivePackage(result, _user.QQPacket0825Key, _user); 51 | parsereceive.decrypt_body(); 52 | parsereceive.parse_tlv(); 53 | while (parsereceive.Header[0] == -2) 54 | { 55 | Util.log("重定向到:" + _user.TXProtocol.DwRedirectIP); 56 | _user.TXProtocol.WRedirectCount += 1; 57 | _user.IsLoginRedirect = true; 58 | socket = new Udpsocket(_user); 59 | 60 | data = SendPackageFactory.get0825(_user); 61 | 62 | socket.sendMessage(data); 63 | 64 | result = socket.receiveMessage(); 65 | parsereceive = new ParseRecivePackage(result, _user.QQPacket0825Key, _user); 66 | parsereceive.decrypt_body(); 67 | parsereceive.parse_tlv(); 68 | } 69 | 70 | Util.log("服务器连接成功,开始登陆"); 71 | data = SendPackageFactory.get0836(_user, false); 72 | socket.sendMessage(data); 73 | result = socket.receiveMessage(); 74 | parsereceive = new ParseRecivePackage(result, _user.TXProtocol.BufDhShareKey, _user); 75 | parsereceive.parse0836(); 76 | if (parsereceive.Header[0] == 52) 77 | { 78 | Util.log("密码错误"); 79 | Message msg=new Message(); 80 | Bundle mdata=new Bundle(); 81 | mdata.putString("type", "error"); 82 | mdata.putString("info", "password wrong"); 83 | msg.setData(mdata); 84 | messenger.send(msg); 85 | return false; 86 | } 87 | if (parsereceive.Header[0] == -5) 88 | { 89 | Util.log("需要验证码"); 90 | while (parsereceive.Status == 0x1) 91 | { 92 | while (verifyCode.equals("")) 93 | { 94 | data = SendPackageFactory.get00ba(_user, ""); 95 | socket.sendMessage(data); 96 | result = socket.receiveMessage(); 97 | parsereceive = new ParseRecivePackage(result, _user.QQPacket00BaKey, _user); 98 | parsereceive.parse00ba(); 99 | try 100 | { 101 | //InputStream verifyCodeStream = new ByteArrayInputStream(_user.QQPacket00BaVerifyCode); 102 | //BitmapFactory.decodeByteArray(_user.QQPacket00BaVerifyCode, 0, _user.QQPacket00BaVerifyCode.length).compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream("/sdcard/yzm.png")); 103 | Message msg=new Message(); 104 | Bundle mdata=new Bundle(); 105 | msg.setData(mdata); 106 | mdata.putString("type", "verifyCode"); 107 | mdata.putByteArray("image", _user.QQPacket00BaVerifyCode); 108 | messenger.send(msg); 109 | } 110 | catch (Exception e) 111 | { 112 | e.printStackTrace(); 113 | } 114 | synchronized (this) 115 | { 116 | wait(); 117 | } 118 | } 119 | data = SendPackageFactory.get00ba(_user, verifyCode); 120 | socket.sendMessage(data); 121 | result = socket.receiveMessage(); 122 | parsereceive = new ParseRecivePackage(result, _user.QQPacket00BaKey, _user); 123 | parsereceive.parse00ba(); 124 | if (parsereceive.Status != 0x0) 125 | { 126 | return false; 127 | } 128 | } 129 | } 130 | while (parsereceive.Header[0] != 0) 131 | { 132 | Util.log("二次登陆"); 133 | data = SendPackageFactory.get0836(_user, true); 134 | socket.sendMessage(data); 135 | result = socket.receiveMessage(); 136 | parsereceive = new ParseRecivePackage(result, _user.TXProtocol.BufDhShareKey, _user); 137 | parsereceive.parse0836(); 138 | Thread.sleep(1000); 139 | } 140 | if (parsereceive.Header[0] == 0) 141 | { 142 | _user.islogined = true; 143 | _user.logintime = new Date().getTime(); 144 | data = SendPackageFactory.get0828(_user); 145 | socket.sendMessage(data); 146 | result = socket.receiveMessage(); 147 | parsereceive = new ParseRecivePackage(result, _user.TXProtocol.BufTgtGtKey, _user); 148 | parsereceive.decrypt_body(); 149 | parsereceive.parse_tlv(); 150 | data = SendPackageFactory.get00ec(_user, QQGlobal.Online); 151 | socket.sendMessage(data); 152 | result = socket.receiveMessage(); 153 | parsereceive = new ParseRecivePackage(result, _user.TXProtocol.SessionKey, _user); 154 | parsereceive.decrypt_body(); 155 | data = SendPackageFactory.get001d(_user); 156 | socket.sendMessage(data); 157 | result = socket.receiveMessage(); 158 | parsereceive = new ParseRecivePackage(result, _user.TXProtocol.SessionKey, _user); 159 | parsereceive.parse001d(); 160 | Thread.sleep(500); 161 | Util.getquncookie(_user); 162 | Util.log("成功获取用户信息: Nick: " + _user.NickName + " Age: " + _user.Age + " Sex: " + _user.Gender+" bkn:"+_user.bkn+" Cookie:"+_user.quncookie); 163 | return true; 164 | } 165 | else 166 | { 167 | return false; 168 | } 169 | 170 | } 171 | catch (Exception e) 172 | { 173 | e.printStackTrace(); 174 | return false; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Socket/MessageService.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Socket; 2 | import android.graphics.*; 3 | import java.io.*; 4 | import java.util.*; 5 | import net.newlydev.qqrobot.PCTIM.*; 6 | import net.newlydev.qqrobot.PCTIM.Message.*; 7 | import net.newlydev.qqrobot.PCTIM.Package.*; 8 | import net.newlydev.qqrobot.PCTIM.Robot.*; 9 | import net.newlydev.qqrobot.PCTIM.Utils.*; 10 | import net.newlydev.qqrobot.PCTIM.sdk.*; 11 | 12 | public class MessageService 13 | { 14 | private Thread thread; 15 | private QQUser user; 16 | private Udpsocket socket; 17 | private QQRobot robot; 18 | 19 | public MessageService(QQUser _user, Udpsocket _socket, QQRobot _robot) 20 | { 21 | this.user = _user; 22 | this.socket = _socket; 23 | this.robot = _robot; 24 | this.thread = new Thread(){ 25 | @Override 26 | public void run() 27 | { 28 | while (true) 29 | { 30 | final byte[] data = socket.receiveMessage(); 31 | if (data != null) 32 | { 33 | user.lastMessage = new Date().getTime(); 34 | manage(data); 35 | } 36 | } 37 | } 38 | }; 39 | } 40 | public void manage(byte[] data) 41 | { 42 | 43 | final ParseRecivePackage parsereceive = new ParseRecivePackage(data, user.TXProtocol.SessionKey, user); 44 | //Util.log("[接收包] 命令: "+Util.byte2HexString(parsereceive.Command)); 45 | if (Util.GetInt(parsereceive.Command) == 23)//0017 46 | { 47 | QQMessage qqmessage = parsereceive.parse0017(); 48 | if (qqmessage != null) 49 | { 50 | byte[] data_to_send = SendPackageFactory.get0017(this.user, parsereceive.Message_To_Respone, parsereceive.Sequence); 51 | this.socket.sendMessage(data_to_send); 52 | if (qqmessage != null) 53 | { 54 | 55 | if (qqmessage.Sender_Uin != 0 && qqmessage.Sender_Uin != user.QQ) 56 | { 57 | if (user.logintime > qqmessage.Send_Message_Time) 58 | { 59 | 60 | //Util.log("[群消息(作废)] 来自群:" + qqmessage.Group_uin + " 的成员: " + qqmessage.SendName + " [消息] " + qqmessage.Message); 61 | } 62 | else 63 | { 64 | Util.log("[群消息] 索引:"+Util.byte2HexString(qqmessage.MessageIndex)+"来自群:" + qqmessage.Group_uin + " 的成员: " + qqmessage.SendName + " [消息] " + qqmessage.Message); 65 | this.robot.call(qqmessage); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | else if (Util.GetInt(parsereceive.Command) == 88)//0058 72 | { 73 | parsereceive.decrypt_body(); 74 | if (parsereceive.body_decrypted[0] != 0) 75 | { 76 | user.offline = true; 77 | user.islogined = false; 78 | } 79 | }else if (Util.GetInt(parsereceive.Command) == 206)//00ce 80 | { 81 | QQMessage qqmessage = parsereceive.parse00ce(); 82 | byte[] data_to_send = SendPackageFactory.get00ce(this.user, parsereceive.Message_To_Respone, parsereceive.Sequence); 83 | this.socket.sendMessage(data_to_send); 84 | data_to_send = SendPackageFactory.get0319(this.user, parsereceive.Friend_Message_QQ, parsereceive.Friend_Message_TIME); 85 | this.socket.sendMessage(data_to_send); 86 | if (qqmessage != null) 87 | { 88 | 89 | if (qqmessage.Sender_Uin != 0 && qqmessage.Sender_Uin != user.QQ) 90 | { 91 | if (user.logintime > qqmessage.Send_Message_Time) 92 | { 93 | 94 | //Util.log("[好友消息(作废)] 来自好友: " + qqmessage.Sender_Uin + " [消息] " + qqmessage.Message); 95 | } 96 | else 97 | { 98 | Util.log("[好友消息] 来自好友: " + qqmessage.Sender_Uin + " [消息] " + qqmessage.Message); 99 | this.robot.call(qqmessage); 100 | } 101 | } 102 | } 103 | } 104 | else if (Util.GetInt(parsereceive.Command) == 904)//0388 105 | { 106 | PictureStore store = null; 107 | final PictureKeyStore keystore = parsereceive.parse0388(); 108 | if (keystore.uploaded == false) 109 | { 110 | new Thread(){ 111 | public void run() 112 | { 113 | PictureStore new_store = Util.uploadimg(keystore, user, Util.GetInt(parsereceive.Sequence)); 114 | try 115 | { 116 | byte[] data_to_send = SendPackageFactory.sendpic(user,new_store.Group,new_store.data); 117 | socket.sendMessage(data_to_send); 118 | } 119 | catch (IOException e) 120 | { 121 | e.printStackTrace(); 122 | } 123 | } 124 | }.start(); 125 | } 126 | else 127 | { 128 | for (PictureStore onestore: user.imgs) 129 | { 130 | if (onestore.pictureid == Util.GetInt(parsereceive.Sequence)) 131 | { 132 | store = onestore; 133 | user.imgs.remove(onestore); 134 | break; 135 | } 136 | } 137 | try 138 | { 139 | byte[] data_to_send = SendPackageFactory.sendpic(this.user, store.Group,store.data); 140 | this.socket.sendMessage(data_to_send); 141 | } 142 | catch (IOException e) 143 | {} 144 | } 145 | } 146 | } 147 | public void updateRobot(QQRobot robot) 148 | { 149 | this.robot=robot; 150 | } 151 | public void start() 152 | { 153 | this.thread.start(); 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Socket/Udpsocket.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Socket; 2 | import java.io.*; 3 | import java.net.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Utils.*; 6 | 7 | 8 | public class Udpsocket 9 | { 10 | private DatagramSocket client_socket = null; 11 | 12 | private int server_port = 0; 13 | 14 | 15 | private InetAddress IPAddress = null; 16 | 17 | 18 | public Udpsocket(QQUser user){ 19 | this.server_port = user.TXProtocol.WServerPort; 20 | try 21 | { 22 | if (user.IsLoginRedirect){ 23 | IPAddress = InetAddress.getByName(user.TXProtocol.DwRedirectIP); 24 | 25 | }else{ 26 | IPAddress = InetAddress.getByName(user.TXProtocol.DwServerIP); 27 | } 28 | client_socket= new DatagramSocket(); 29 | } 30 | catch (Exception e) 31 | { 32 | System.out.println(e.getMessage()); 33 | } 34 | } 35 | 36 | public void sendMessage(byte[] send_data){ 37 | DatagramPacket send_packet = new DatagramPacket(send_data,send_data.length, 38 | IPAddress, server_port); 39 | try 40 | { 41 | client_socket.send(send_packet); 42 | } 43 | catch (IOException e) 44 | { 45 | System.out.println(e.getMessage()); 46 | } 47 | } 48 | 49 | public byte[] receiveMessage(){ 50 | byte[] arr = new byte[1024]; 51 | DatagramPacket packet = new DatagramPacket(arr, arr.length); 52 | 53 | try 54 | { 55 | 56 | client_socket.receive(packet); 57 | } 58 | catch (IOException e) 59 | { 60 | System.out.println(e.toString()); 61 | } 62 | 63 | 64 | byte[] recvdata = packet.getData();//得到 65 | 66 | return Util.subByte(recvdata,0,packet.getLength()); 67 | } 68 | 69 | 70 | 71 | 72 | public void shutdown(){ 73 | client_socket.close(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/TLV/ParseTlvFactory.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.TLV; 2 | 3 | import java.util.*; 4 | import net.newlydev.qqrobot.PCTIM.*; 5 | import net.newlydev.qqrobot.PCTIM.Utils.*; 6 | 7 | public class ParseTlvFactory 8 | { 9 | 10 | 11 | public static void parsetvl(TLV tlv, QQUser user) 12 | { 13 | 14 | int tlvtype = tlv.Tag; 15 | switch (tlvtype) 16 | { 17 | case 6: 18 | { 19 | user.TXProtocol.BufTgtgt = tlv.Value; 20 | break; 21 | } 22 | case 12: 23 | { 24 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 25 | bytefactory.readBytes(1); 26 | byte[] WSubVer = bytefactory.readBytes(1); 27 | if (WSubVer[0] == 2) 28 | { 29 | user.TXProtocol.DwIdc = bytefactory.readBytes(4); /*dwIDC =*/ 30 | 31 | user.TXProtocol.DwIsp = bytefactory.readBytes(4); /*dwISP =*/ 32 | bytefactory.readBytes(2); 33 | byte[] ip_byte = bytefactory.readBytes(4); 34 | user.TXProtocol.DwRedirectIP = Util.GetIpStringFromBytes(ip_byte); /*dwRedirectIP =*/ 35 | user.TXProtocol.WRedirectips.add(ip_byte); 36 | user.TXProtocol.WRedirectPort_Byte = bytefactory.readBytes(6); 37 | user.TXProtocol.WRedirectPort = Util.bytesToshort(user.TXProtocol.WRedirectPort_Byte); 38 | /*wRedirectPort =*/ 39 | } 40 | else 41 | { 42 | System.out.println("未知版本类型"); 43 | } 44 | break; 45 | } 46 | case 13: 47 | { 48 | byte[] WSubVer = Util.subByte(tlv.Value, 1, 1);//wSubVer 49 | if (WSubVer[0] == 1) 50 | { 51 | 52 | } 53 | else 54 | { 55 | System.out.println("未知版本类型"); 56 | } 57 | break; 58 | } 59 | case 20: 60 | { 61 | byte[] WSubVer = Util.subByte(tlv.Value, 1, 1); 62 | if (WSubVer[0] == 1) 63 | { 64 | 65 | } 66 | else 67 | { 68 | System.out.println("未知版本类型"); 69 | } 70 | break; 71 | } 72 | 73 | case 23: 74 | { 75 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 76 | bytefactory.readBytes(1); 77 | byte[] WSubVer = bytefactory.readBytes(1); 78 | if (WSubVer[0] == 1) 79 | { 80 | user.TXProtocol.DwServerTime_Byte = bytefactory.readBytes(4); 81 | long timeMillis = Util.GetLong(user.TXProtocol.DwServerTime_Byte); 82 | user.TXProtocol.DwServerTime = Util.GetDateTimeFromMillis(timeMillis); 83 | user.TXProtocol.TimeDifference = (4294967295l & timeMillis) - new Date().getTime(); 84 | user.TXProtocol.DwClientIP = Util.GetIpStringFromBytes(bytefactory.readBytes(4)); 85 | user.TXProtocol.DwClientIP_Byte = Util.IPStringToByteArray(user.TXProtocol.DwClientIP); 86 | user.TXProtocol.WClientPort = Util.bytesToshort(bytefactory.readrestBytes()); 87 | 88 | 89 | 90 | } 91 | break; 92 | } 93 | case 30: 94 | { 95 | user.TXProtocol.BufTgtgtKey = tlv.Value; 96 | break; 97 | } 98 | case 31: 99 | { 100 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 101 | bytefactory.readBytes(1); 102 | byte[] WSubVer = bytefactory.readBytes(1); 103 | user.TXProtocol.BufDeviceId = bytefactory.readrestBytes(); 104 | break; 105 | } 106 | case 47: 107 | { 108 | byte[] WSubVer = Util.subByte(tlv.Value, 1, 1); 109 | if (WSubVer[0] == 1) 110 | { 111 | 112 | } 113 | else 114 | { 115 | System.out.println("未知版本类型"); 116 | } 117 | break; 118 | } 119 | case 256: 120 | { 121 | System.out.println(Util.byte2HexString(tlv.Value)); 122 | break; 123 | } 124 | case 259: 125 | { 126 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 127 | bytefactory.readBytes(1); 128 | byte[] WSubVer = bytefactory.readBytes(1); 129 | if (WSubVer[0] == 1) 130 | { 131 | 132 | user.TXProtocol.BufSid = bytefactory.readBytesbylength(); 133 | } 134 | else 135 | { 136 | System.out.println("未知版本类型"); 137 | } 138 | break; 139 | } 140 | case 260: 141 | { 142 | ByteFactory factory = new ByteFactory(tlv.Value); 143 | 144 | int WSubVer = factory.readint(); //wSubVer 145 | if (WSubVer == 0x0001) 146 | { 147 | int wCsCmd = factory.readint(); 148 | long errorCode = factory.readlong(); 149 | 150 | factory.readBytes(1); //0x00 151 | factory.readBytes(1); //0x05 152 | byte PngData = factory.readBytes(1)[0]; //是否需要验证码:0不需要,1需要 153 | int len; 154 | if (PngData == 0x00) 155 | { 156 | len = factory.readBytes(1)[0]; 157 | while (len == 0) 158 | { 159 | len = factory.readBytes(1)[0]; 160 | } 161 | } 162 | else //ReplyCode != 0x01按下面走 兼容多版本 163 | { 164 | factory.readlong(); //需要验证码时为00 00 01 23,不需要时为全0 165 | len = factory.readint(); 166 | } 167 | 168 | byte[] buffer = factory.readBytes(len); 169 | user.TXProtocol.BufSigPic = buffer; 170 | if (PngData == 0x01) //有验证码数据 171 | { 172 | len = factory.readint(); 173 | buffer = factory.readBytes(len); 174 | user.QQPacket00BaVerifyCode = buffer; 175 | user.Next = factory.readBytes(1)[0]; 176 | factory.readBytes(1); 177 | //var directory = Util.MapPath("Verify"); 178 | //var filename = Path.Combine(directory, user.QQ + ".png"); 179 | //if (!Directory.Exists(directory)) 180 | //{ 181 | // Directory.CreateDirectory(directory); 182 | //} 183 | 184 | //var fs = Next == 0x00 185 | // ? new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Read) 186 | // : new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read); 187 | 188 | ////fs.Seek(0, SeekOrigin.End); 189 | //fs.Write(buffer, 0, buffer.Length); 190 | //fs.Close(); 191 | len = factory.readint(); 192 | buffer = factory.readBytes(len); 193 | user.TXProtocol.PngToken = buffer; 194 | if (factory.data.length > factory.position) 195 | { 196 | factory.readint(); 197 | len = factory.readint(); 198 | buffer = factory.readBytes(len); 199 | user.TXProtocol.PngKey = buffer; 200 | } 201 | } 202 | } 203 | break; 204 | } 205 | case 261: 206 | { 207 | byte[] WSubVer = Util.subByte(tlv.Value, 1, 1); 208 | if (WSubVer[0] == 1) 209 | { 210 | 211 | } 212 | else 213 | { 214 | System.out.println("未知版本类型"); 215 | } 216 | break; 217 | } 218 | case 263: 219 | { 220 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 221 | bytefactory.readBytes(1); 222 | byte[] WSubVer = bytefactory.readBytes(1); 223 | if (WSubVer[0] == 1) 224 | { 225 | bytefactory.readBytesbylength(); 226 | user.TXProtocol.BufTgtGtKey = bytefactory.readBytes(16); 227 | 228 | user.TXProtocol.BufTgt = bytefactory.readBytesbylength(); 229 | 230 | user.TXProtocol.Buf16BytesGtKeySt = bytefactory.readBytes(16); 231 | 232 | user.TXProtocol.BufServiceTicket = bytefactory.readBytesbylength(); 233 | 234 | byte[] http = bytefactory.readBytesbylength(); 235 | ByteFactory httpfactory = new ByteFactory(http); 236 | httpfactory.readBytes(1); 237 | user.TXProtocol.Buf16BytesGtKeyStHttp = httpfactory.readBytes(16); 238 | user.TXProtocol.BufServiceTicketHttp = httpfactory.readBytesbylength(); 239 | } 240 | else 241 | { 242 | System.out.println("未知版本类型"); 243 | } 244 | break; 245 | } 246 | case 264: 247 | { 248 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 249 | bytefactory.readBytes(1); 250 | byte[] WSubVer = bytefactory.readBytes(1); 251 | if (WSubVer[0] == 1) 252 | { 253 | byte[] data = bytefactory.readBytesbylength(); 254 | ByteFactory datafactory = new ByteFactory(new ByteFactory(data).readBytesbylength()); 255 | byte[] wSsoAccountWFaceIndex = datafactory.readBytes(2); 256 | int length = Util.GetInt(new byte[]{0x00,datafactory.readBytes(1)[0]}); 257 | if (length > 0) 258 | { 259 | user.NickName = new String(datafactory.readString(length)); 260 | } 261 | user.Gender = datafactory.readBytes(1)[0]; 262 | byte[] dwSsoAccountDwUinFlag = datafactory.readBytes(4); 263 | user.Age = datafactory.readBytes(1)[0]; 264 | } 265 | else 266 | { 267 | System.out.println("未知版本类型"); 268 | } 269 | break; 270 | } 271 | case 265: 272 | { 273 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 274 | bytefactory.readBytes(1); 275 | byte[] WSubVer = bytefactory.readBytes(1); 276 | if (WSubVer[0] == 1) 277 | { 278 | user.TXProtocol.BufSessionKey = bytefactory.readBytes(16); 279 | 280 | user.TXProtocol.BufSigSession = bytefactory.readBytesbylength(); 281 | 282 | user.TXProtocol.BufPwdForConn = bytefactory.readBytesbylength(); 283 | } 284 | else 285 | { 286 | System.out.println("未知版本类型"); 287 | } 288 | break; 289 | } 290 | case 268: 291 | { 292 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 293 | bytefactory.readBytes(1); 294 | byte[] WSubVer = bytefactory.readBytes(1); 295 | if (WSubVer[0] == 1) 296 | { 297 | 298 | user.TXProtocol.SessionKey = bytefactory.readBytes(16); 299 | byte[] dwUin = bytefactory.readBytes(4); 300 | String dwClientIP = Util.GetIpStringFromBytes(bytefactory.readBytes(4)); 301 | user.TXProtocol.WClientPort = Util.GetShort(bytefactory.readBytes(2)); 302 | //.... 303 | } 304 | else 305 | { 306 | System.out.println("未知版本类型"); 307 | } 308 | break; 309 | } 310 | case 269: 311 | { 312 | byte[] WSubVer = Util.subByte(tlv.Value, 1, 1); 313 | if (WSubVer[0] == 1) 314 | { 315 | 316 | } 317 | else 318 | { 319 | System.out.println("未知版本类型"); 320 | } 321 | break; 322 | } 323 | case 270: 324 | { 325 | ByteFactory bytefactory = new ByteFactory(tlv.Value); 326 | bytefactory.readBytes(1); 327 | byte[] WSubVer = bytefactory.readBytes(1); 328 | if (WSubVer[0] == 1) 329 | { 330 | 331 | 332 | ByteFactory sigfactory = new ByteFactory(bytefactory.readBytesbylength()); 333 | byte[] dwUinLevel =sigfactory.readBytes(3); 334 | byte[] dwUinLevelEx = sigfactory.readBytes(3); 335 | 336 | 337 | byte[] buf24ByteSignature = sigfactory.readBytesbylength(); 338 | 339 | 340 | 341 | byte[] buf32ByteValueAddedSignature = sigfactory.readBytesbylength(); 342 | 343 | 344 | 345 | byte[] buf12ByteUserBitmap = sigfactory.readBytesbylength(); 346 | 347 | user.TXProtocol.ClientKey = buf32ByteValueAddedSignature; 348 | } 349 | else 350 | { 351 | System.out.println("未知版本类型"); 352 | } 353 | break; 354 | } 355 | case 272: 356 | { 357 | if (Util.subByte(tlv.Value, 1, 1)[0] != 1) 358 | { 359 | System.out.println("未知版本"); 360 | } 361 | else 362 | { 363 | user.TXProtocol.BufSigPic = Util.subByte(tlv.Value, 2, tlv.Length - 2); 364 | } 365 | break; 366 | } 367 | case 274: 368 | { 369 | user.TXProtocol.BufSigClientAddr = tlv.Value; 370 | break; 371 | } 372 | case 277: 373 | { 374 | byte[] bufPacketMD5 = tlv.Value; 375 | break; 376 | } 377 | case 784: 378 | { 379 | user.TXProtocol.DwServerIP = Util.GetIpStringFromBytes(tlv.Value); 380 | break; 381 | } 382 | default: 383 | { 384 | System.out.println("未知tlv解析:" + tlv.Tag); 385 | } 386 | } 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/TLV/TLV.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.TLV; 2 | import java.util.*; 3 | import net.newlydev.qqrobot.PCTIM.Utils.*; 4 | 5 | public class TLV 6 | { 7 | private int _valueOffset; 8 | 9 | private TLV(int tag, int length, int valueOffset, byte[] data) 10 | { 11 | Tag = tag; 12 | Length = length; 13 | Data = data; 14 | Children = new ArrayList(); 15 | Value = new byte[Length]; 16 | _valueOffset = valueOffset; 17 | System.arraycopy(Data, _valueOffset, Value, 0, Length); 18 | 19 | HexValue = Util.byte2HexString(Value); 20 | HexData = Util.byte2HexString(Data); 21 | this.HexTag=Util.byte2HexString(Util.ToByte(tag)); 22 | 23 | 24 | } 25 | 26 | /// 27 | /// The raw TLV data. 28 | /// 29 | public byte[] Data; 30 | 31 | /// 32 | /// The raw TLV data. 33 | /// 34 | public String HexData = ""; 35 | 36 | /// 37 | /// The TLV tag. 38 | /// 39 | public int Tag ; 40 | 41 | /// 42 | /// The TLV tag. 43 | /// 44 | public String HexTag = ""; 45 | /// 46 | /// The length of the TLV value. 47 | /// 48 | public int Length =0 ; 49 | 50 | /// 51 | /// The length of the TLV value. 52 | /// 53 | public String HexLength = Util.NumToHexString(Length, 4); 54 | 55 | /// 56 | /// The TLV value. 57 | /// 58 | 59 | public byte[] Value =null; 60 | 61 | 62 | 63 | 64 | /// 65 | /// The TLV value. 66 | /// 67 | public String HexValue = ""; 68 | 69 | /// 70 | /// TLV children. 71 | /// 72 | public Collection Children ; 73 | 74 | /// 75 | /// Parse TLV data. 76 | /// 77 | /// The hex TLV blob. 78 | /// A collection of TLVs. 79 | public static Collection ParseTlv(String tlv) throws Exception 80 | { 81 | if ((tlv).isEmpty()) 82 | { 83 | throw new Exception("tlv"); 84 | } 85 | 86 | return ParseTlv(Util.str_to_byte(tlv)); 87 | } 88 | 89 | /// 90 | /// Parse TLV data. 91 | /// 92 | /// The byte array TLV blob. 93 | /// A collection of TLVs. 94 | public static Collection ParseTlv(byte[] tlv) 95 | { 96 | if (tlv == null || tlv.length == 0) 97 | { 98 | System.out.println("tlv parsfalse"); 99 | } 100 | 101 | List result = new ArrayList(); 102 | ParseTlv(tlv, result); 103 | 104 | return result; 105 | } 106 | 107 | private static void ParseTlv(byte[] rawTlv, Collection result) 108 | { 109 | 110 | for (int i = 0, start = 0; i < rawTlv.length; start = i) 111 | { 112 | 113 | // parse Tag 114 | 115 | //i++ 116 | i = i+ 2; 117 | 118 | int tag = Util.GetInt(rawTlv, start, i - start); 119 | 120 | //// parse Length 121 | //bool multiByteLength = (rawTlv[i] & 0x80) != 0; 122 | //int length = multiByteLength ? GetInt(rawTlv, i + 1, rawTlv[i] & 0x1F) : rawTlv[i]; 123 | //i = multiByteLength ? i + (rawTlv[i] & 0x1F) + 1 : i + 1; 124 | i = i +2; 125 | 126 | start = start + 2; 127 | int length = Util.GetInt(rawTlv, start, i - start); 128 | i = i+ length ; 129 | byte[] rawData = new byte[i - start]; 130 | 131 | System.arraycopy(rawTlv, start, rawData, 0, i - start); 132 | TLV tlv = new TLV(tag, length, rawData.length - length, rawData); 133 | result.add(tlv); 134 | 135 | 136 | } 137 | } 138 | 139 | 140 | 141 | 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/TLV/TLVFactory.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.TLV; 2 | 3 | import net.newlydev.qqrobot.PCTIM.*; 4 | import net.newlydev.qqrobot.PCTIM.Utils.*; 5 | 6 | public class TLVFactory 7 | { 8 | 9 | 10 | public static byte[] tlv0018(QQUser user){ 11 | ByteBuilder builder = new ByteBuilder(); 12 | byte[] WSubVer ={0x00,0x01}; 13 | builder.writebytes(WSubVer); //wSubVer 14 | builder.writebytes(user.TXProtocol.DwSsoVersion); //dwSSOVersion 15 | builder.writebytes(user.TXProtocol.DwServiceId); //dwServiceId 16 | builder.writebytes(user.TXProtocol.DwClientVer); //dwClientVer 17 | builder.writelong(user.QQ); 18 | builder.writeint(user.TXProtocol.WRedirectCount); //wRedirectCount 19 | builder.writebytes(new byte[]{00,00}); 20 | builder.rewriteint(builder.data.length); //长度 21 | builder.rewritebytes(new byte[]{0x00,0x18});//头部 22 | 23 | return builder.getdata(); 24 | } 25 | 26 | public static byte[] tlv0309(QQUser user){ 27 | ByteBuilder builder = new ByteBuilder(); 28 | byte[] WSubVer ={0x00,0x01}; 29 | builder.writebytes(WSubVer); //wSubVer 30 | builder.writebytes(Util.IPStringToByteArray(user.TXProtocol.DwServerIP)); //LastServerIP - 服务器最后的登录IP,可以为0 31 | builder.writebytes( Util.subByte(Util.ToByte(user.TXProtocol.WRedirectips.size()),3,1)); //cRedirectCount - 重定向的次数(IP的数量) 32 | for (byte[] ip : user.TXProtocol.WRedirectips) 33 | { 34 | builder.writebytes(ip); 35 | } 36 | 37 | builder.writebytes(user.TXProtocol.CPingType); //cPingType 38 | builder.rewriteint(builder.data.length); //长度 39 | builder.rewritebytes(new byte[]{0x03,0x09});//头部 40 | 41 | return builder.getdata(); 42 | } 43 | 44 | public static byte[] tlv0036(int ver){ 45 | ByteBuilder builder = new ByteBuilder(); 46 | 47 | if (ver ==2){ 48 | builder.writebytes(new byte[]{0x00,0x02,0x0,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}); //wSubVer 49 | builder.rewriteint(builder.data.length); //长度 50 | builder.rewritebytes(new byte[]{0x00,0x36});//头部 51 | }else if (ver ==1){ 52 | builder.rewritebytes(new byte[]{0x00,0x01,0x0,0x01,0x0,0x00,0x0,0x00}); //wSubVer 53 | builder.rewriteint(builder.data.length); //长度 54 | builder.rewritebytes(new byte[]{0x00,0x36});//头部 55 | } 56 | 57 | return builder.getdata(); 58 | } 59 | 60 | 61 | public static byte[] tlv0114(QQUser user){ 62 | ByteBuilder builder = new ByteBuilder(); 63 | byte[] WSubVer ={0x01,0x02}; 64 | builder.rewritebytes(WSubVer); //wDHVer 65 | builder.writebytesbylength(user.TXProtocol.BufDhPublicKey); //bufDHPublicKey长度 66 | builder.rewriteint(builder.data.length); //长度 67 | builder.rewritebytes(new byte[]{0x01,0x14});//头部 68 | 69 | return builder.getdata(); 70 | } 71 | 72 | 73 | 74 | public static byte[] tlv0112(QQUser user){ 75 | 76 | ByteBuilder builder = new ByteBuilder(); 77 | 78 | builder.writebytes(user.TXProtocol.BufSigClientAddr); 79 | builder.rewriteint(builder.data.length); //长度 80 | builder.rewritebytes(new byte[]{0x01,0x12});//头部 81 | 82 | return builder.getdata(); 83 | } 84 | 85 | public static byte[] tlv030f(QQUser user) 86 | { 87 | ByteBuilder builder = new ByteBuilder(); 88 | builder.writebytesbylength(user.TXProtocol.BufComputerName.getBytes()); 89 | builder.rewriteint(builder.data.length); //长度 90 | builder.rewritebytes(new byte[]{0x03,0x0f});//头部 91 | 92 | return builder.getdata(); 93 | } 94 | 95 | public static byte[] tlv0005(QQUser user) 96 | { 97 | ByteBuilder builder = new ByteBuilder(); 98 | byte[] WSubVer = {0x00,0x02}; 99 | builder.writebytes(WSubVer); 100 | builder.writelong(user.QQ); 101 | builder.rewriteint(builder.data.length); //长度 102 | builder.rewritebytes(new byte[]{0x00,0x05});//头部 103 | 104 | return builder.getdata(); 105 | } 106 | 107 | 108 | public static byte[] tlv0006(QQUser user) 109 | { 110 | ByteBuilder builder = new ByteBuilder(); 111 | if (user.TXProtocol.BufTgtgt == null){ 112 | 113 | byte[] WSubVer = {0x00,0x02}; 114 | 115 | byte[] random_byte = Util.random_byte(4); 116 | builder.writebytes(random_byte); 117 | builder.writebytes(WSubVer); 118 | builder.writelong(user.QQ); 119 | builder.writebytes(user.TXProtocol.DwSsoVersion); 120 | builder.writebytes(user.TXProtocol.DwServiceId); 121 | builder.writebytes(user.TXProtocol.DwClientVer); 122 | builder.writebytes(new byte[]{0x00,0x00}); 123 | builder.writebytes(user.TXProtocol.BRememberPwdLogin); 124 | builder.writebytes(user.MD51); //密码 125 | builder.writebytes(user.TXProtocol.DwServerTime_Byte); 126 | builder.writebytes(new byte[13]); 127 | builder.writebytes(user.TXProtocol.DwClientIP_Byte); 128 | builder.writebytes(user.TXProtocol.DwIsp); //dwISP 129 | builder.writebytes(user.TXProtocol.DwIdc); //dwIDC 130 | builder.writebytesbylength(user.TXProtocol.BufComputerIdEx); //机器码 131 | builder.writebytes(user.TXProtocol.BufTgtgtKey); //临时密匙 132 | Crypter crypter = new Crypter(); 133 | user.TXProtocol.BufTgtgt = crypter.encrypt(builder.getdata(), user.MD52); 134 | } 135 | builder.clean(); 136 | builder.writebytes(user.TXProtocol.BufTgtgt); 137 | 138 | builder.rewriteint(builder.data.length); //长度 139 | builder.rewritebytes(new byte[]{0x00,0x06});//头部 140 | 141 | return builder.getdata(); 142 | } 143 | 144 | 145 | public static byte[] tlv0015(QQUser user) 146 | { 147 | ByteBuilder builder = new ByteBuilder(); 148 | byte[] WSubVer = {0x00,0x01}; 149 | builder.writebytes(WSubVer); 150 | builder.writebyte((byte)0x01); 151 | builder.writebytes(user.TXProtocol.BufComputerId_crc32_reversed); 152 | builder.writebytesbylength(user.TXProtocol.BufComputerId); 153 | builder.writebyte((byte)0x02); 154 | builder.writebytes(user.TXProtocol.BufComputerIdEx_crc32_reversed); 155 | 156 | builder.writebytesbylength(user.TXProtocol.BufComputerIdEx); 157 | builder.rewriteint(builder.data.length); //长度 158 | builder.rewritebytes(new byte[]{0x00,0x15});//头部 159 | 160 | return builder.getdata(); 161 | } 162 | 163 | 164 | public static byte[] tlv001a(byte[] tlv0015, QQUser user) 165 | { 166 | ByteBuilder builder = new ByteBuilder(); 167 | 168 | Crypter crypter = new Crypter(); 169 | 170 | builder.writebytes(crypter.encrypt(tlv0015,user.TXProtocol.BufTgtgtKey)); 171 | 172 | builder.rewriteint(builder.data.length); //长度 173 | builder.rewritebytes(new byte[]{0x00,0x1a});//头部 174 | 175 | return builder.getdata(); 176 | } 177 | 178 | 179 | public static byte[] tlv0103(QQUser user) 180 | { 181 | ByteBuilder builder = new ByteBuilder(); 182 | byte[] WSubVer = {0x00,0x01}; 183 | builder.writebytes(WSubVer); 184 | builder.writebytesbylength(user.TXProtocol.BufSid); 185 | builder.rewriteint(builder.data.length); //长度 186 | builder.rewritebytes(new byte[]{0x01,0x03});//头部 187 | 188 | return builder.getdata(); 189 | 190 | } 191 | 192 | public static byte[] tlv0312() 193 | { 194 | ByteBuilder builder = new ByteBuilder(); 195 | 196 | builder.writebytes(new byte[]{0x01,0x00,0x00,0x00,0x01}); 197 | builder.rewriteint(builder.data.length); //长度 198 | builder.rewritebytes(new byte[]{0x03,0x12});//头部 199 | 200 | return builder.getdata(); 201 | } 202 | 203 | public static byte[] tlv0508() 204 | { 205 | ByteBuilder builder = new ByteBuilder(); 206 | 207 | builder.writebytes(new byte[]{0x01,0x00,0x00,0x00,0x00}); 208 | builder.rewriteint(builder.data.length); //长度 209 | builder.rewritebytes(new byte[]{0x05,0x08});//头部 210 | 211 | return builder.getdata(); 212 | } 213 | 214 | public static byte[] tlv0313(QQUser user) 215 | { 216 | ByteBuilder builder = new ByteBuilder(); 217 | 218 | builder.writebytes(new byte[]{0x01,0x01,0x02}); 219 | 220 | builder.writebytesbylength(user.TXProtocol.BufMacGuid); 221 | builder.writebytes(new byte[]{0x00,0x00,0x00,0x02}); 222 | builder.rewriteint(builder.data.length); //长度 223 | builder.rewritebytes(new byte[]{0x03,0x13});//头部 224 | 225 | return builder.getdata(); 226 | } 227 | 228 | public static byte[] tlv0102(QQUser user) 229 | { 230 | ByteBuilder builder = new ByteBuilder(); 231 | byte[] WSubVer = {0x00,0x01}; 232 | builder.writebytes(WSubVer); 233 | 234 | builder.writebytes(Util.str_to_byte("9e9b03236d7fa881a81072ec5097968e")); 235 | byte[] pic_byte = null; 236 | if (user.TXProtocol.BufSigPic == null){ 237 | pic_byte = Util.RandomKey(56); 238 | }else{ 239 | pic_byte = user.TXProtocol.BufSigPic; 240 | } 241 | builder.writebytesbylength(pic_byte); 242 | byte[] crckey = Util.RandomKey(16); 243 | byte[] crccode = Util.get_crc32(crckey); 244 | 245 | builder.writebytesbylength(Util.byteMerger(crckey,crccode)); 246 | 247 | builder.rewriteint(builder.data.length); //长度 248 | builder.rewritebytes(new byte[]{0x01,0x02});//头部 249 | 250 | return builder.getdata(); 251 | } 252 | 253 | public static byte[] tlv0110(QQUser user) 254 | { 255 | ByteBuilder builder = new ByteBuilder(); 256 | byte[] WSubVer = {0x00,0x01}; 257 | if (user.TXProtocol.BufSigPic == null) 258 | { 259 | return new byte[] { }; 260 | }else{ 261 | 262 | builder.writebytes(WSubVer); //wSubVer 263 | builder.writebytesbylength(user.TXProtocol.BufSigPic); 264 | builder.rewriteint(builder.data.length); //长度 265 | builder.rewritebytes(new byte[]{0x01,0x10});//头部 266 | return builder.getdata(); 267 | } 268 | 269 | } 270 | 271 | public static byte[] tlv0032(QQUser user) 272 | { 273 | ByteBuilder builder = new ByteBuilder(); 274 | builder.writebytes(Util.GetQdData(user)); 275 | builder.rewriteint(builder.data.length); //长度 276 | builder.rewritebytes(new byte[]{0x00,0x32});//头部 277 | 278 | return builder.getdata(); 279 | } 280 | 281 | public static byte[] tlv0007(QQUser user) 282 | { 283 | ByteBuilder builder = new ByteBuilder(); 284 | builder.writebytes(user.TXProtocol.BufTgt); 285 | builder.rewriteint(builder.data.length); //长度 286 | builder.rewritebytes(new byte[]{0x00,0x07});//头部 287 | 288 | return builder.getdata(); 289 | } 290 | 291 | public static byte[] tlv000c(QQUser user) 292 | { 293 | ByteBuilder builder = new ByteBuilder(); 294 | byte[] WSubVer = {0x00,0x02}; 295 | builder.writebytes(WSubVer); 296 | builder.writebytes(new byte[]{0x00,0x00}); 297 | builder.writebytes(user.TXProtocol.DwIdc); 298 | builder.writebytes(user.TXProtocol.DwIsp); 299 | if (user.TXProtocol.DwServerIP == null){ 300 | builder.writebytes(Util.IPStringToByteArray(user.TXProtocol.DwServerIP)); 301 | }else{ 302 | builder.writebytes(Util.IPStringToByteArray(user.TXProtocol.DwRedirectIP)); 303 | 304 | } 305 | builder.writeshort(user.TXProtocol.WServerPort); 306 | builder.writebytes(new byte[]{0x00,0x00}); 307 | builder.rewriteint(builder.data.length); //长度 308 | builder.rewritebytes(new byte[]{0x00,0x0c});//头部 309 | 310 | return builder.getdata(); 311 | } 312 | 313 | public static byte[] tlv001f(QQUser user) 314 | { 315 | ByteBuilder builder = new ByteBuilder(); 316 | byte[] WSubVer = {0x00,0x01}; 317 | builder.writebytes(WSubVer); 318 | builder.writebytes(user.TXProtocol.BufDeviceId); 319 | builder.rewriteint(builder.data.length); //长度 320 | builder.rewritebytes(new byte[]{0x00,0x1f});//头部 321 | 322 | return builder.getdata(); 323 | } 324 | 325 | public static byte[] tlv0105(QQUser user) 326 | { 327 | ByteBuilder builder = new ByteBuilder(); 328 | 329 | byte[] WSubVer = {0x00,0x01}; 330 | builder.writebytes(WSubVer); 331 | builder.writebytes(user.TXProtocol.XxooB); 332 | builder.writebytes(new byte[]{0x02,0x00,0x14,0x01,0x01,0x00,0x10}); 333 | builder.writebytes(Util.RandomKey()); 334 | builder.writebytes(new byte[]{0x00,0x14,0x01,0x02,0x00,0x10}); 335 | builder.writebytes(Util.RandomKey()); 336 | builder.rewriteint(builder.data.length); //长度 337 | builder.rewritebytes(new byte[]{0x01,0x05});//头部 338 | 339 | return builder.getdata(); 340 | } 341 | 342 | public static byte[] tlv010b(QQUser user) 343 | { 344 | ByteBuilder builder = new ByteBuilder(); 345 | 346 | byte[] WSubVer = {0x00,0x02}; 347 | builder.writebytes(WSubVer); 348 | byte[] newbyte = user.TXProtocol.BufTgt; 349 | byte flag = EncodeLoginFlag(newbyte, QQGlobal.QqexeMD5); 350 | builder.writebytes(user.MD51); 351 | builder.writebyte(flag); 352 | builder.writebyte((byte)0x10); 353 | builder.writebytes(new byte[]{0x00,0x00,0x00,0x00}); 354 | builder.writebytes(new byte[]{0x00,0x00,0x00,0x02}); 355 | byte[] qddata = Util.GetQdData(user); 356 | builder.writebytesbylength(qddata); 357 | builder.writebytes(new byte[]{0x00,0x00,0x00,0x00}); 358 | builder.rewriteint(builder.data.length); //长度 359 | builder.rewritebytes(new byte[]{0x01,0x0b});//头部 360 | 361 | return builder.getdata(); 362 | } 363 | 364 | public static byte[] tlv002d(QQUser user) 365 | { 366 | ByteBuilder builder = new ByteBuilder(); 367 | byte[] WSubVer = {0x00,0x01}; 368 | builder.writebytes(WSubVer); 369 | builder.writebytes(Util.IPStringToByteArray(Util.getHostIP())); 370 | builder.rewriteint(builder.data.length); //长度 371 | builder.rewritebytes(new byte[]{0x00,0x2d});//头部 372 | 373 | return builder.getdata(); 374 | } 375 | 376 | 377 | 378 | 379 | private static byte EncodeLoginFlag(byte[] bufTgt /*bufTGT*/, byte[] qqexeMD5 /*QQEXE_MD5*/) 380 | { 381 | byte flag = 1; 382 | byte rc = flag; 383 | for (byte t : bufTgt) 384 | { 385 | rc ^= t; 386 | } 387 | 388 | for (int i = 0; i < 4; i++) 389 | { 390 | int rcc = qqexeMD5[i * 4]&0x0ffffff; 391 | rcc ^= qqexeMD5[i * 4 + 1]; 392 | rcc ^= qqexeMD5[i * 4 + 3]; 393 | rcc ^= qqexeMD5[i * 4 + 2]; 394 | rc ^= rcc; 395 | } 396 | return rc; 397 | } 398 | 399 | 400 | } 401 | 402 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/TXProtocol.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM; 2 | import java.util.*; 3 | import net.newlydev.qqrobot.PCTIM.Utils.*; 4 | 5 | public class TXProtocol 6 | { 7 | public byte[] PngKey = new byte[0]; 8 | 9 | public byte[] PngToken =new byte[0]; 10 | public byte[] CMainVer = {0x37}; 11 | public byte[] CSubVer = {0x13}; 12 | 13 | public byte[] XxooA = { 0x03, 0x00, 0x00 }; 14 | public byte[] XxooD = { 0x00, 0x00, 0x00, 0x00 }; 15 | public byte[] XxooB = {0x01}; 16 | public byte[] SubVer = {0x00,0x01}; 17 | public byte[] EcdhVer = {0x01,0x03}; 18 | public byte[] DwClientType = { 0x00, 0x01, 0x2e, 0x01 }; 19 | 20 | 21 | public byte[] DwPubNo = { 0x00, 0x00, 0x68, 0x52 }; 22 | 23 | public byte[] DwSsoVersion = {0x00,0x00,0x04,0x53}; 24 | public byte[] DwServiceId = {0x00,0x00,0x00,0x01}; 25 | 26 | public byte[] DwClientVer = Util.str_to_byte("00 00 15 85"); 27 | 28 | 29 | public int WRedirectCount =0; 30 | 31 | public String DwServerIP = Util.http_dns("sz.tencent.com"); 32 | public short WServerPort = 8000; 33 | public List WRedirectips = new ArrayList(); 34 | 35 | public static byte[] CPingType ={0x02}; 36 | 37 | 38 | public byte[] BufDhPublicKey = Util.str_to_byte("02 6D 28 41 D2 A5 6F D2 FC 3E 2A 1F 03 75 DE 6E 28 8F A8 19 3E 5F 16 49 D3"); 39 | 40 | public String DwRedirectIP ; 41 | 42 | public byte[] BufSigClientAddr ; 43 | public Date DwServerTime ; 44 | public byte[] DwServerTime_Byte ; 45 | 46 | public long TimeDifference ; 47 | public String DwClientIP ; 48 | public byte[] DwClientIP_Byte ; 49 | 50 | public short WClientPort ; 51 | 52 | public byte[] DwIsp = new byte[] {0x00,0x00,0x00,0x00}; 53 | public byte[] DwIdc = new byte[] {0x00,0x00,0x12,0x00}; 54 | 55 | public short WRedirectPort ; 56 | public byte[] WRedirectPort_Byte ; 57 | 58 | public byte[] BufDhShareKey = Util.str_to_byte("1A E9 7F 7D C9 73 75 98 AC 02 E0 80 5F A9 C6 AF"); 59 | 60 | public String BufComputerName = "马化腾的灵魂战妓"; 61 | 62 | public byte[] BufTgtgt ; 63 | 64 | public byte[] BRememberPwdLogin = {0x00}; 65 | public byte[] BufComputerId_crc32_reversed = Util.str_to_byte("CCC2E96A"); 66 | //public byte[] bufComputerID = Util.RandomKey(); 67 | public byte[] BufComputerIdEx = Util.str_to_byte("7798000BAB5D4F3D3050652C4A2AF865"); 68 | 69 | public byte[] BufComputerIdEx_crc32_reversed = Util.str_to_byte("3CDE845F"); 70 | 71 | public byte[] BufDeviceId =Util.str_to_byte("0fabbe2104a72af1e19da1956a363df07b22ff2ec2cac92ba8d6da459d31a960"); 72 | 73 | public byte[] BufComputerId ={ 0x43, 0x04, 0x21, 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 74 | 75 | 76 | public byte[] BufTgtgtKey = Util.RandomKey(); 77 | 78 | public byte[] BufSid = Util.str_to_byte("1EC12571B24CEA919A6E8DE6954ECE06"); 79 | public byte[] BufMacGuid = Util.str_to_byte("214B1A0409ED1970987551BB2D3A7E0A"); 80 | public byte[] BufSigPic; 81 | 82 | public byte[] QdData ; 83 | public byte[] BufQdKey = 84 | { 0x77, 0x45, 0x37, 0x5e, 0x33, 0x69, 0x6d, 0x67, 0x23, 0x69, 0x29, 0x25, 0x68, 0x31, 0x32, 0x5d }; 85 | public byte[] DwQdVerion_Byte = {0x02,0x04,0x04,0x04}; 86 | public byte[] QdSufFix = { 0x68 }; 87 | public byte[] QdPreFix = { 0x3E }; 88 | public short CQdProtocolVer = 0x0063; 89 | public byte[] CQdProtocolVer_Byte = {0x00,0x63}; 90 | public long DwQdVerion = 0x02040404; 91 | public short WQdCsCmdNo = 0x0004; 92 | public byte[] WQdCsCmdNo_Byte = {0x00,0x04}; 93 | 94 | public byte[] CQdCcSubNo = {0x00}; 95 | 96 | 97 | public byte[] COsType = {0x03}; 98 | 99 | public byte[] BIsWow64 = {0x01}; 100 | 101 | 102 | public byte[] DwDrvVersionInfo = {0x01,0x02}; 103 | 104 | public byte[] BufVersionTsSafeEditDat = Util.str_to_byte("07df000a000c0001"); 105 | 106 | /// 107 | /// QScanEngine.dll的"文件版本" 108 | /// 109 | public byte[] BufVersionQScanEngineDll = { 0x00, 0x04, 0x00, 0x03, 0x00, 0x04, 0x20, 0x5c }; 110 | public byte[] BufTgtGtKey ; 111 | public byte[] BufTgt ; 112 | public byte[] Buf16BytesGtKeySt ; 113 | public byte[] BufServiceTicket ; 114 | public byte[] Buf16BytesGtKeyStHttp ; 115 | public byte[] BufServiceTicketHttp ; 116 | public byte[] BufGtKeyTgtPwd ; 117 | public byte[] BufSessionKey ; 118 | public byte[] BufSigSession ; 119 | public byte[] BufPwdForConn ; 120 | public byte[] ClientKey ; 121 | 122 | public byte[] SessionKey ; 123 | 124 | 125 | } 126 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Utils/ByteBuilder.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Utils; 2 | import net.newlydev.qqrobot.PCTIM.Utils.*; 3 | 4 | public class ByteBuilder 5 | { 6 | public byte[] data = new byte[]{}; 7 | 8 | public ByteBuilder(){ 9 | 10 | } 11 | 12 | public void clean() 13 | { 14 | this.data = new byte[]{}; 15 | } 16 | 17 | public void writebytesbylength(byte[] to_write) 18 | { 19 | this.writeint(to_write.length); 20 | this.writebytes(to_write); 21 | } 22 | 23 | 24 | public void writebytes(byte[] to_write){ 25 | this.data = Util.byteMerger(this.data,to_write); 26 | } 27 | 28 | public void rewritebytes(byte[] to_write){ 29 | this.data = Util.byteMerger(to_write,this.data); 30 | } 31 | 32 | public void writebyte(byte to_write){ 33 | this.data = Util.byteMerger(this.data,new byte[]{to_write}); 34 | } 35 | 36 | public void writeint(int to_write){ 37 | byte[] test = Util.subByte(Util.ToByte(to_write),2,2); 38 | 39 | this.data = Util.byteMerger(this.data,test); 40 | } 41 | public void rewriteint(int to_write) 42 | { 43 | byte[] test = Util.subByte(Util.ToByte(to_write),2,2); 44 | 45 | this.data = Util.byteMerger(test,this.data); 46 | } 47 | 48 | 49 | public void writelong(long to_write){ 50 | byte[] test = Util.subByte(Util.ToByte(to_write),4,4); 51 | 52 | this.data = Util.byteMerger(this.data,test); 53 | } 54 | public void writeshort(short to_write){ 55 | byte[] test = Util.subByte(Util.ToByte(to_write),0,4); 56 | 57 | this.data = Util.byteMerger(this.data,test); 58 | } 59 | 60 | public byte[] getdata(){ 61 | return this.data; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Utils/ByteFactory.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Utils; 2 | 3 | import net.newlydev.qqrobot.PCTIM.Utils.*; 4 | 5 | public class ByteFactory 6 | { 7 | public int position = 0; 8 | public byte[] data = null; 9 | public ByteFactory(byte[] _data){ 10 | this.data=_data; 11 | } 12 | 13 | public byte[] readrestBytes() 14 | { 15 | int length = this.data.length -this.position; 16 | byte[] test = Util.subByte(this.data,this.position,length); 17 | this.position +=length; 18 | return test; 19 | } 20 | 21 | 22 | 23 | public String readStringbylength(){ 24 | int length = Util.GetInt(Util.subByte(this.data,this.position,2)); 25 | this.position +=2; 26 | byte[] test = Util.subByte(this.data,position,length); 27 | this.position +=length; 28 | return new String(test); 29 | } 30 | 31 | public String readString(int length) 32 | { 33 | byte[] test = Util.subByte(this.data,position,length); 34 | this.position +=length; 35 | return new String(test); 36 | } 37 | 38 | 39 | public byte[] readBytesbylength(){ 40 | int length = Util.GetInt(Util.subByte(this.data,this.position,2)); 41 | this.position +=2; 42 | byte[] test = Util.subByte(this.data,position,length); 43 | this.position +=length; 44 | return test; 45 | } 46 | 47 | public byte[] readBytes(int length){ 48 | byte[] test = Util.subByte(this.data,this.position,length); 49 | this.position +=length; 50 | return test; 51 | } 52 | 53 | 54 | 55 | public int readint(){ 56 | int test = Util.GetInt(Util.subByte(this.data,this.position,2)); 57 | this.position +=2; 58 | return test; 59 | } 60 | 61 | 62 | 63 | public long readlong() 64 | { 65 | long test = Util.GetLong(Util.subByte(this.data,this.position,4)); 66 | this.position +=4; 67 | return test; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Utils/Crypter.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Utils; 2 | import java.io.*; 3 | import java.util.*; 4 | 5 | /** 6 | * 加密解密QQ消息的工具类. QQ消息的加密算法是一个16次的迭代过程,并且是反馈的,每一个加密单元是8字节,输出也是8字节,密钥是16字节 7 | * 我们以prePlain表示前一个明文块,plain表示当前明文块,crypt表示当前明文块加密得到的密文块,preCrypt表示前一个密文块 8 | * f表示加密算法,d表示解密算法 那么从plain得到crypt的过程是: crypt = f(plain ˆ preCrypt) ˆ 9 | * prePlain 所以,从crypt得到plain的过程自然是 plain = d(crypt ˆ prePlain) ˆ 10 | * preCrypt 此外,算法有它的填充机制,其会在明文前和明文后分别填充一定的字节数,以保证明文长度是8字节的倍数 11 | * 填充的字节数与原始明文长度有关,填充的方法是: 12 | * 13 | *
 14 |  *  * 
 15 |  *      ------- 消息填充算法 ----------- 
 16 |  *      a = (明文长度 + 10) mod 8
 17 |  *      if(a 不等于 0) a = 8 - a;
 18 |  *      b = 随机数 & 0xF8 | a;              这个的作用是把a的值保存了下来
 19 |  *      plain[0] = b;                     然后把b做为明文的第0个字节,这样第0个字节就保存了a的信息,这个信息在解密时就要用来找到真正明文的起始位置
 20 |  *      plain[1 至 a+2] = 随机数 & 0xFF;    这里用随机数填充明文的第1到第a+2个字节
 21 |  *      plain[a+3 至 a+3+明文长度-1] = 明文; 从a+3字节开始才是真正的明文
 22 |  *      plain[a+3+明文长度, 最后] = 0;       在最后,填充0,填充到总长度为8的整数为止。到此为止,结束了,这就是最后得到的要加密的明文内容
 23 |  *      ------- 消息填充算法 ------------ *   
 24 |  * 
 25 |  * 
26 | */ 27 | public class Crypter { 28 | // 指向当前的明文块 29 | private byte[] plain; 30 | // 这指向前面一个明文块 31 | private byte[] prePlain; 32 | // 输出的密文或者明文 33 | private byte[] out; 34 | // 当前加密的密文位置和上一次加密的密文块位置,他们相差8 35 | private int crypt, preCrypt; 36 | // 当前处理的加密解密块的位置 37 | private int pos; 38 | // 填充数 39 | private int padding; 40 | // 密钥 41 | private byte[] key; 42 | // 用于加密时,表示当前是否是第一个8字节块,因为加密算法是反馈的 43 | // 但是最开始的8个字节没有反馈可用,所有需要标明这种情况 44 | private boolean header = true; 45 | // 这个表示当前解密开始的位置,之所以要这么一个变量是为了避免当解密到最后时 46 | // 后面已经没有数据,这时候就会出错,这个变量就是用来判断这种情况免得出错 47 | private int contextStart; 48 | // 随机数对象 49 | private static Random random = CrypterUtil.random(); 50 | // 字节输出流 51 | private ByteArrayOutputStream baos; 52 | 53 | /** 54 | * 构造函数 55 | */ 56 | public Crypter() { 57 | baos = new ByteArrayOutputStream(8); 58 | } 59 | 60 | /** 61 | * 解密 62 | * @param in 密文 63 | * @param offset 密文开始的位置 64 | * @param len 密文长度 65 | * @param k 密钥 66 | * @return 明文 67 | */ 68 | public byte[] decrypt(byte[] in, int offset, int len, byte[] k) { 69 | // 检查密钥 70 | if(k == null) 71 | return null; 72 | 73 | crypt = preCrypt = 0; 74 | this.key = k; 75 | int count; 76 | byte[] m = new byte[offset + 8]; 77 | 78 | // 因为QQ消息加密之后至少是16字节,并且肯定是8的倍数,这里检查这种情况 79 | if((len % 8 != 0) || (len < 16)) return null; 80 | // 得到消息的头部,关键是得到真正明文开始的位置,这个信息存在第一个字节里面,所以其用解密得到的第一个字节与7做与 81 | prePlain = decipher(in, offset); 82 | pos = prePlain[0] & 0x7; 83 | // 得到真正明文的长度 84 | count = len - pos - 10; 85 | // 如果明文长度小于0,那肯定是出错了,比如传输错误之类的,返回 86 | if(count < 0) return null; 87 | 88 | // 这个是临时的preCrypt,和加密时第一个8字节块没有prePlain一样,解密时 89 | // 第一个8字节块也没有preCrypt,所有这里建一个全0的 90 | for(int i = offset; i < m.length; i++) 91 | m[i] = 0; 92 | // 通过了上面的代码,密文应该是没有问题了,我们分配输出缓冲区 93 | out = new byte[count]; 94 | // 设置preCrypt的位置等于0,注意目前的preCrypt位置是指向m的,因为java没有指针,所以我们在后面要控制当前密文buf的引用 95 | preCrypt = 0; 96 | // 当前的密文位置,为什么是8不是0呢?注意前面我们已经解密了头部信息了,现在当然该8了 97 | crypt = 8; 98 | // 自然这个也是8 99 | contextStart = 8; 100 | // 加1,和加密算法是对应的 101 | pos++; 102 | 103 | // 开始跳过头部,如果在这个过程中满了8字节,则解密下一块 104 | // 因为是解密下一块,所以我们有一个语句 m = in,下一块当然有preCrypt了,我们不再用m了 105 | // 但是如果不满8,这说明了什么?说明了头8个字节的密文是包含了明文信息的,当然还是要用m把明文弄出来 106 | // 所以,很显然,满了8的话,说明了头8个字节的密文除了一个长度信息有用之外,其他都是无用的填充 107 | padding = 1; 108 | while(padding <= 2) { 109 | if(pos < 8) { 110 | pos++; 111 | padding++; 112 | } 113 | if(pos == 8) { 114 | m = in; 115 | if(!decrypt8Bytes(in, offset, len)) return null; 116 | } 117 | } 118 | 119 | // 这里是解密的重要阶段,这个时候头部的填充都已经跳过了,开始解密 120 | // 注意如果上面一个while没有满8,这里第一个if里面用的就是原始的m,否则这个m就是in了 121 | int i = 0; 122 | while(count != 0) { 123 | if(pos < 8) { 124 | out[i] = (byte)(m[offset + preCrypt + pos] ^ prePlain[pos]); 125 | i++; 126 | count--; 127 | pos++; 128 | } 129 | if(pos == 8) { 130 | m = in; 131 | preCrypt = crypt - 8; 132 | if(!decrypt8Bytes(in, offset, len)) 133 | return null; 134 | } 135 | } 136 | 137 | // 最后的解密部分,上面一个while已经把明文都解出来了,就剩下尾部的填充了,应该全是0 138 | // 所以这里有检查是否解密了之后是不是0,如果不是的话那肯定出错了,返回null 139 | for(padding = 1; padding < 8; padding++) { 140 | if(pos < 8) { 141 | if((m[offset + preCrypt + pos] ^ prePlain[pos]) != 0) 142 | return null; 143 | pos++; 144 | } 145 | if(pos == 8) { 146 | m = in; 147 | preCrypt = crypt; 148 | if(!decrypt8Bytes(in, offset, len)) 149 | return null; 150 | } 151 | } 152 | return out; 153 | } 154 | 155 | /** 156 | * @param in 157 | * 需要被解密的密文 158 | * @param inLen 159 | * 密文长度 160 | * @param k 161 | * 密钥 162 | * @return Message 已解密的消息 163 | */ 164 | public byte[] decrypt(byte[] in, byte[] k) { 165 | return decrypt(in, 0, in.length, k); 166 | } 167 | 168 | /** 169 | * 加密 170 | * @param in 明文字节数组 171 | * @param offset 开始加密的偏移 172 | * @param len 加密长度 173 | * @param k 密钥 174 | * @return 密文字节数组 175 | */ 176 | public byte[] encrypt(byte[] in, int offset, int len, byte[] k) { 177 | // 检查密钥 178 | if(k == null) 179 | return in; 180 | 181 | plain = new byte[8]; 182 | prePlain = new byte[8]; 183 | pos = 1; 184 | padding = 0; 185 | crypt = preCrypt = 0; 186 | this.key = k; 187 | header = true; 188 | 189 | // 计算头部填充字节数 190 | pos = (len + 0x0A) % 8; 191 | if(pos != 0) 192 | pos = 8 - pos; 193 | // 计算输出的密文长度 194 | out = new byte[len + pos + 10]; 195 | // 这里的操作把pos存到了plain的第一个字节里面 196 | // 0xF8后面三位是空的,正好留给pos,因为pos是0到7的值,表示文本开始的字节位置 197 | plain[0] = (byte)((rand() & 0xF8) | pos); 198 | 199 | // 这里用随机产生的数填充plain[1]到plain[pos]之间的内容 200 | for(int i = 1; i <= pos; i++) 201 | plain[i] = (byte)(rand() & 0xFF); 202 | pos++; 203 | // 这个就是prePlain,第一个8字节块当然没有prePlain,所以我们做一个全0的给第一个8字节块 204 | for(int i = 0; i < 8; i++) 205 | prePlain[i] = 0x0; 206 | 207 | // 继续填充2个字节的随机数,这个过程中如果满了8字节就加密之 208 | padding = 1; 209 | while(padding <= 2) { 210 | if(pos < 8) { 211 | plain[pos++] = (byte)(rand() & 0xFF); 212 | padding++; 213 | } 214 | if(pos == 8) 215 | encrypt8Bytes(); 216 | } 217 | 218 | // 头部填充完了,这里开始填真正的明文了,也是满了8字节就加密,一直到明文读完 219 | int i = offset; 220 | while(len > 0) { 221 | if(pos < 8) { 222 | plain[pos++] = in[i++]; 223 | len--; 224 | } 225 | if(pos == 8) 226 | encrypt8Bytes(); 227 | } 228 | 229 | // 最后填上0,以保证是8字节的倍数 230 | padding = 1; 231 | while(padding <= 7) { 232 | if(pos < 8) { 233 | plain[pos++] = 0x0; 234 | padding++; 235 | } 236 | if(pos == 8) 237 | encrypt8Bytes(); 238 | } 239 | 240 | return out; 241 | } 242 | 243 | /** 244 | * @param in 245 | * 需要加密的明文 246 | * @param inLen 247 | * 明文长度 248 | * @param k 249 | * 密钥 250 | * @return Message 密文 251 | */ 252 | public byte[] encrypt(byte[] in, byte[] k) { 253 | return encrypt(in, 0, in.length, k); 254 | } 255 | 256 | /** 257 | * 加密一个8字节块 258 | * 259 | * @param in 260 | * 明文字节数组 261 | * @return 262 | * 密文字节数组 263 | */ 264 | private byte[] encipher(byte[] in) { 265 | // 迭代次数,16次 266 | int loop = 0x10; 267 | // 得到明文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数 268 | // 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的 269 | // 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与 270 | long y = CrypterUtil.getUnsignedInt(in, 0, 4); 271 | long z = CrypterUtil.getUnsignedInt(in, 4, 4); 272 | long a = CrypterUtil.getUnsignedInt(key, 0, 4); 273 | long b = CrypterUtil.getUnsignedInt(key, 4, 4); 274 | long c = CrypterUtil.getUnsignedInt(key, 8, 4); 275 | long d = CrypterUtil.getUnsignedInt(key, 12, 4); 276 | // 这是算法的一些控制变量,为什么delta是0x9E3779B9呢? 277 | // 这个数是TEA算法的delta,实际是就是(sqr(5) - 1) * 2^31 (根号5,减1,再乘2的31次方) 278 | long sum = 0; 279 | long delta = 0x9E3779B9; 280 | delta &= 0xFFFFFFFFL; 281 | 282 | // 开始迭代了,乱七八糟的,我也看不懂,反正和DES之类的差不多,都是这样倒来倒去 283 | while (loop-- > 0) { 284 | sum += delta; 285 | sum &= 0xFFFFFFFFL; 286 | y += ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); 287 | y &= 0xFFFFFFFFL; 288 | z += ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); 289 | z &= 0xFFFFFFFFL; 290 | } 291 | 292 | // 最后,我们输出密文,因为我用的long,所以需要强制转换一下变成int 293 | baos.reset(); 294 | writeInt((int)y); 295 | writeInt((int)z); 296 | return baos.toByteArray(); 297 | } 298 | 299 | /** 300 | * 解密从offset开始的8字节密文 301 | * 302 | * @param in 303 | * 密文字节数组 304 | * @param offset 305 | * 密文开始位置 306 | * @return 307 | * 明文 308 | */ 309 | private byte[] decipher(byte[] in, int offset) { 310 | // 迭代次数,16次 311 | int loop = 0x10; 312 | // 得到密文和密钥的各个部分,注意java没有无符号类型,所以为了表示一个无符号的整数 313 | // 我们用了long,这个long的前32位是全0的,我们通过这种方式模拟无符号整数,后面用到的long也都是一样的 314 | // 而且为了保证前32位为0,需要和0xFFFFFFFF做一下位与 315 | long y = CrypterUtil.getUnsignedInt(in, offset, 4); 316 | long z = CrypterUtil.getUnsignedInt(in, offset + 4, 4); 317 | long a = CrypterUtil.getUnsignedInt(key, 0, 4); 318 | long b = CrypterUtil.getUnsignedInt(key, 4, 4); 319 | long c = CrypterUtil.getUnsignedInt(key, 8, 4); 320 | long d = CrypterUtil.getUnsignedInt(key, 12, 4); 321 | // 算法的一些控制变量,sum在这里也有数了,这个sum和迭代次数有关系 322 | // 因为delta是这么多,所以sum如果是这么多的话,迭代的时候减减减,减16次,最后 323 | // 得到0。反正这就是为了得到和加密时相反顺序的控制变量,这样才能解密呀~~ 324 | long sum = 0xE3779B90; 325 | sum &= 0xFFFFFFFFL; 326 | long delta = 0x9E3779B9; 327 | delta &= 0xFFFFFFFFL; 328 | 329 | // 迭代开始了, @_@ 330 | while(loop-- > 0) { 331 | z -= ((y << 4) + c) ^ (y + sum) ^ ((y >>> 5) + d); 332 | z &= 0xFFFFFFFFL; 333 | y -= ((z << 4) + a) ^ (z + sum) ^ ((z >>> 5) + b); 334 | y &= 0xFFFFFFFFL; 335 | sum -= delta; 336 | sum &= 0xFFFFFFFFL; 337 | } 338 | 339 | baos.reset(); 340 | writeInt((int)y); 341 | writeInt((int)z); 342 | return baos.toByteArray(); 343 | } 344 | 345 | /** 346 | * 写入一个整型到输出流,高字节优先 347 | * 348 | * @param t 349 | */ 350 | private void writeInt(int t) { 351 | baos.write(t >>> 24); 352 | baos.write(t >>> 16); 353 | baos.write(t >>> 8); 354 | baos.write(t); 355 | } 356 | 357 | /** 358 | * 解密 359 | * 360 | * @param in 361 | * 密文 362 | * @return 363 | * 明文 364 | */ 365 | private byte[] decipher(byte[] in) { 366 | return decipher(in, 0); 367 | } 368 | 369 | /** 370 | * 加密8字节 371 | */ 372 | private void encrypt8Bytes() { 373 | // 这部分完成我上面所说的 plain ^ preCrypt,注意这里判断了是不是第一个8字节块,如果是的话,那个prePlain就当作preCrypt用 374 | for(pos = 0; pos < 8; pos++) { 375 | if(header) 376 | plain[pos] ^= prePlain[pos]; 377 | else 378 | plain[pos] ^= out[preCrypt + pos]; 379 | } 380 | // 这个完成我上面说的 f(plain ^ preCrypt) 381 | byte[] crypted = encipher(plain); 382 | // 这个没什么,就是拷贝一下,java不像c,所以我只好这么干,c就不用这一步了 383 | System.arraycopy(crypted, 0, out, crypt, 8); 384 | 385 | // 这个完成了 f(plain ^ preCrypt) ^ prePlain,ok,下面拷贝一下就行了 386 | for(pos = 0; pos < 8; pos++) 387 | out[crypt + pos] ^= prePlain[pos]; 388 | System.arraycopy(plain, 0, prePlain, 0, 8); 389 | 390 | // 完成了加密,现在是调整crypt,preCrypt等等东西的时候了 391 | preCrypt = crypt; 392 | crypt += 8; 393 | pos = 0; 394 | header = false; 395 | } 396 | 397 | /** 398 | * 解密8个字节 399 | * 400 | * @param in 401 | * 密文字节数组 402 | * @param offset 403 | * 从何处开始解密 404 | * @param len 405 | * 密文的长度 406 | * @return 407 | * true表示解密成功 408 | */ 409 | private boolean decrypt8Bytes(byte[] in , int offset, int len) { 410 | // 这里第一步就是判断后面还有没有数据,没有就返回,如果有,就执行 crypt ^ prePlain 411 | for(pos = 0; pos < 8; pos++) { 412 | if(contextStart + pos >= len) 413 | return true; 414 | prePlain[pos] ^= in[offset + crypt + pos]; 415 | } 416 | 417 | // 好,这里执行到了 d(crypt ^ prePlain) 418 | prePlain = decipher(prePlain); 419 | if(prePlain == null) 420 | return false; 421 | 422 | // 解密完成,最后一步好像没做? 423 | // 这里最后一步放到decrypt里面去做了,因为解密的步骤有点不太一样 424 | // 调整这些变量的值先 425 | contextStart += 8; 426 | crypt += 8; 427 | pos = 0; 428 | return true; 429 | } 430 | 431 | /** 432 | * 这是个随机因子产生器,用来填充头部的,如果为了调试,可以用一个固定值 433 | * 随机因子可以使相同的明文每次加密出来的密文都不一样 434 | * 435 | * @return 436 | * 随机因子 437 | */ 438 | private int rand() { 439 | return random.nextInt(); 440 | } 441 | 442 | } 443 | 444 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Utils/Util.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Utils; 2 | import android.graphics.*; 3 | import java.io.*; 4 | import java.net.*; 5 | import java.nio.*; 6 | import java.security.*; 7 | import java.security.cert.*; 8 | import java.text.*; 9 | import java.util.*; 10 | import java.util.zip.*; 11 | import javax.net.ssl.*; 12 | import net.newlydev.qqrobot.PCTIM.*; 13 | import net.newlydev.qqrobot.PCTIM.Message.*; 14 | import net.newlydev.qqrobot.PCTIM.sdk.*; 15 | 16 | public class Util 17 | { 18 | private static Date BaseDateTime = new Date(0); 19 | public static String ua = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36"; 20 | 21 | public static HostnameVerifier hv = new HostnameVerifier() { 22 | public boolean verify(String urlHostName, SSLSession session) 23 | { 24 | return true; 25 | } 26 | }; 27 | 28 | public static void getquncookie(QQUser user) 29 | { 30 | try 31 | { 32 | String urls="https://ssl.ptlogin2.qq.com/jump?pt_clientver=5509&pt_src=1&keyindex=9&clientuin=" + user.QQ + "&clientkey=" + Util.byte2HexString(user.TXProtocol.BufServiceTicketHttp).replaceAll(" ", "") + "&u1=http%3A%2F%2Fqun.qq.com%2Fmember.html%23gid%3D168209441"; 33 | URL lll = new URL(urls); 34 | HttpURLConnection connection = (HttpURLConnection) lll.openConnection();// 打开连接 35 | connection.setRequestMethod("GET"); 36 | connection.setRequestProperty("User-Agent", ua); 37 | connection.getResponseCode(); 38 | List cookies = connection.getHeaderFields().get("Set-Cookie"); 39 | user.userskey = ""; 40 | for (String cookie : cookies) 41 | { 42 | if (cookie.matches("skey=.*")) 43 | { 44 | user.userskey = cookie.replaceAll("skey=", "").replaceAll(";.*", ""); 45 | user.bkn = Util.GetBkn(user.userskey); 46 | } 47 | if (cookie.matches("p_skey=.*")) 48 | { 49 | user.pskey = cookie.replaceAll("p_skey=", "").replaceAll(";.*", ""); 50 | user.qungtk = Util.GET_GTK(user.pskey); 51 | } 52 | user.quncookie += cookie.replaceAll("Path=.*$", "").replaceAll("Expires=.*$", "") + " " ; 53 | } 54 | String url = connection.getHeaderField("Location"); 55 | fuck(url, user); 56 | connection.disconnect(); 57 | } 58 | catch (Exception e) 59 | { 60 | e.printStackTrace(); 61 | } 62 | } 63 | 64 | public static void fuck(String url, QQUser user) 65 | { 66 | try 67 | { 68 | URL lll = new URL(url); 69 | HttpURLConnection connection = (HttpURLConnection) lll.openConnection();// 打开连接 70 | connection.setRequestMethod("GET"); 71 | connection.setRequestProperty("User-Agent", ua); 72 | connection.setInstanceFollowRedirects(false); 73 | connection.getResponseCode(); 74 | List cookies = connection.getHeaderFields().get("Set-Cookie"); 75 | 76 | for (String cookie : cookies) 77 | { 78 | if (cookie.matches("skey=.*")) 79 | { 80 | user.userskey = cookie.replaceAll("skey=", "").replaceAll(";.*", ""); 81 | user.bkn = Util.GetBkn(user.userskey); 82 | } 83 | if (cookie.matches("p_skey=.*")) 84 | { 85 | user.pskey = cookie.replaceAll("p_skey=", "").replaceAll(";.*", ""); 86 | user.qungtk = Util.GET_GTK(user.pskey); 87 | } 88 | user.quncookie += cookie.replaceAll("Path=.*$", "").replaceAll("Expires=.*$", "") + " " ; 89 | } 90 | connection.disconnect(); 91 | } 92 | catch (Exception e) 93 | { 94 | e.printStackTrace(); 95 | } 96 | } 97 | 98 | public static void trustAllHttpsCertificates() throws Exception 99 | { 100 | TrustManager[] trustAllCerts = new TrustManager[1]; 101 | TrustManager tm = new miTM(); 102 | trustAllCerts[0] = tm; 103 | SSLContext sc = javax.net.ssl.SSLContext.getInstance("SSL"); 104 | sc.init(null, trustAllCerts, null); 105 | HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); 106 | } 107 | public static String getMD5(byte[] bytes) 108 | { 109 | ByteArrayInputStream fileInputStream=new ByteArrayInputStream(bytes); 110 | try 111 | { 112 | MessageDigest MD5 = MessageDigest.getInstance("MD5"); 113 | byte[] buffer = new byte[8192]; 114 | int length; 115 | while ((length = fileInputStream.read(buffer)) != -1) 116 | { 117 | MD5.update(buffer, 0, length); 118 | } 119 | return byte2HexString(MD5.digest()).replace(" ", ""); 120 | } 121 | catch (Exception e) 122 | { 123 | e.printStackTrace(); 124 | return null; 125 | } 126 | finally 127 | { 128 | try 129 | { 130 | if (fileInputStream != null) 131 | { 132 | fileInputStream.close(); 133 | } 134 | } 135 | catch (IOException e) 136 | { 137 | e.printStackTrace(); 138 | } 139 | } 140 | } 141 | 142 | public static void log(String string) 143 | { 144 | SimpleDateFormat format0 = new SimpleDateFormat("[HH:mm:ss]"); 145 | String log = format0.format(new Date().getTime()); 146 | try 147 | { 148 | FileWriter fw=new FileWriter("/sdcard/qqrobot.log", true); 149 | fw.write(log + " " + string + "\n"); 150 | fw.close(); 151 | } 152 | catch (IOException e) 153 | {} 154 | } 155 | 156 | public static byte[] reverse_byte(byte[] data) 157 | { 158 | byte[] Fuck = new byte[data.length]; 159 | for (int time = 0;time < data.length;time++) 160 | { 161 | Fuck[time] = data[data.length - time - 1]; 162 | 163 | } 164 | 165 | return Fuck; 166 | } 167 | 168 | public static long getfilelength(String file) 169 | { 170 | File f= new File(file); 171 | return f.length(); 172 | } 173 | 174 | public static byte[] get_crc32(byte[] data) 175 | { 176 | CRC32 crc32 = new CRC32(); 177 | crc32.update(data); 178 | return reverse_byte(str_to_byte(Long.toHexString(crc32.getValue()))); 179 | } 180 | public static String getMD5(File file) 181 | { 182 | FileInputStream fileInputStream = null; 183 | try 184 | { 185 | MessageDigest MD5 = MessageDigest.getInstance("MD5"); 186 | fileInputStream = new FileInputStream(file); 187 | byte[] buffer = new byte[8192]; 188 | int length; 189 | while ((length = fileInputStream.read(buffer)) != -1) 190 | { 191 | MD5.update(buffer, 0, length); 192 | } 193 | return byte2HexString(MD5.digest()).replace(" ", ""); 194 | } 195 | catch (Exception e) 196 | { 197 | e.printStackTrace(); 198 | return null; 199 | } 200 | finally 201 | { 202 | try 203 | { 204 | if (fileInputStream != null) 205 | { 206 | fileInputStream.close(); 207 | } 208 | } 209 | catch (IOException e) 210 | { 211 | e.printStackTrace(); 212 | } 213 | } 214 | } 215 | public static String getMD5(Bitmap bitmap) 216 | { 217 | return getMD5(Bufferedimg_tobytes(bitmap)); 218 | } 219 | public static String GetMD5HashFromFile(String fileName) 220 | { 221 | 222 | File file = new File(fileName); 223 | 224 | return getMD5(file); 225 | } 226 | 227 | 228 | public static String GetMD5ToGuidHashFromFile(String fileName) 229 | { 230 | String md5 = GetMD5HashFromFile(fileName); 231 | return md5.substring(0, 8) + "-" + md5.substring(8, 12) + "-" + md5.substring(12, 16) + "-" + md5.substring(16, 20) + "-" + md5.substring(20, md5.length()); 232 | } 233 | 234 | public static String GetMD5ToGuidHashFromBitmap(Bitmap bitmap) throws IOException 235 | { 236 | String md5 = getMD5(bitmap); 237 | return md5.substring(0, 8) + "-" + md5.substring(8, 12) + "-" + md5.substring(12, 16) + "-" + md5.substring(16, 20) + "-" + md5.substring(20, md5.length()); 238 | } 239 | public static String GetMD5ToGuidHashFromBytes(byte[] bytes) throws IOException 240 | { 241 | String md5 = getMD5(bytes); 242 | return md5.substring(0, 8) + "-" + md5.substring(8, 12) + "-" + md5.substring(12, 16) + "-" + md5.substring(16, 20) + "-" + md5.substring(20, md5.length()); 243 | } 244 | public static String http_dns(String host) 245 | { 246 | try 247 | { 248 | URL lll = new URL("http://119.29.29.29/d?dn=" + host); 249 | HttpURLConnection connection = (HttpURLConnection) lll.openConnection();// 打开连接 250 | connection.setRequestMethod("GET"); 251 | connection.getResponseCode(); 252 | BufferedReader br= new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); 253 | String line; 254 | StringBuilder sb = new StringBuilder(); 255 | while ((line = br.readLine()) != null) 256 | { 257 | sb.append(line); 258 | } 259 | br.close(); 260 | connection.disconnect(); 261 | String hosts = sb.toString(); 262 | if (hosts.contains(";")) 263 | { 264 | return hosts.split(";")[0]; 265 | } 266 | else 267 | { 268 | return hosts; 269 | } 270 | } 271 | catch (Exception e) 272 | { 273 | System.out.println(e.toString()); 274 | } 275 | return null; 276 | } 277 | 278 | public static PictureStore uploadimg(PictureKeyStore keystore, QQUser user, int pictureid) 279 | { 280 | PictureStore store = null; 281 | for (PictureStore onestore: user.imgs) 282 | { 283 | if (onestore.pictureid == pictureid) 284 | { 285 | store = onestore; 286 | user.imgs.remove(onestore); 287 | break; 288 | } 289 | } 290 | URL u = null; 291 | HttpURLConnection con = null; 292 | try 293 | { 294 | u = new URL("http://" + Util.http_dns("htdata2.qq.com") + "/cgi-bin/httpconn?htcmd=0x6ff0071&ver=5515&term=pc&ukey=" + Util.byte2HexString(keystore.ukey).replace(" ", "") + "&filesize=" + store.data.length + "&range=0&uin=" + user.QQ + "&groupcode=" + store.Group); 295 | con = (HttpURLConnection) u.openConnection(); 296 | con.setRequestMethod("POST"); 297 | con.setDoOutput(true); 298 | con.setDoInput(true); 299 | con.setUseCaches(false); 300 | con.setRequestProperty("Content-Type", "binary/octet-stream"); 301 | con.setRequestProperty("User-Agent", "QQClient"); 302 | OutputStream outStream = con.getOutputStream(); 303 | DataInputStream in = new DataInputStream(new ByteArrayInputStream(store.data)); 304 | byte[] bufferOut = new byte[1024]; 305 | int bytes = 0; 306 | while ((bytes = in.read(bufferOut)) != -1) 307 | { 308 | outStream.write(bufferOut, 0, bytes); 309 | } 310 | in.close(); 311 | outStream.flush(); 312 | outStream.close(); 313 | con.getResponseCode(); 314 | DataInputStream dis=new DataInputStream(con.getInputStream()); 315 | dis.readLine(); 316 | } 317 | catch (Exception e) 318 | { 319 | e.printStackTrace(); 320 | } 321 | return store; 322 | 323 | } 324 | 325 | public static String Length_toPB(String d) 326 | { 327 | String binary = Long.toString(GetQQNumRetUint(d), 2); 328 | String temp = ""; 329 | while (!(binary).isEmpty()) 330 | { 331 | temp = temp + binary.substring(binary.length() - 7, binary.length()); 332 | if (binary.length() >= 8) 333 | { 334 | binary = binary.substring(0, binary.length() - 8); 335 | } 336 | else 337 | { 338 | break; 339 | } 340 | } 341 | 342 | return Long.toHexString(Long.parseLong(temp, 2)); 343 | } 344 | 345 | public static long GetQQNumRetUint(String six) 346 | { 347 | return Long.parseLong(six.replace(" ", ""), 16); 348 | } 349 | 350 | public static String PB_toLength(long d) 351 | { 352 | String binary = Long.toString(d, 2); 353 | String temp = ""; 354 | while (!(binary.isEmpty() || binary == null)) 355 | { 356 | String binary1 = "0000000" + binary; 357 | temp = temp + "1" + binary1.substring(binary1.length() - 7, binary1.length()); 358 | if (binary.length() >= 7) 359 | { 360 | binary = binary.substring(0, (binary.length() - 7)); 361 | } 362 | else 363 | { 364 | break; 365 | } 366 | } 367 | String temp1 = temp.substring(temp.length() - 7, temp.length()); 368 | temp = temp.substring(0, temp.length() - 8) + "0" + temp1; 369 | return Long.toHexString(Long.parseLong(temp, 2)); 370 | } 371 | 372 | public static byte[] Bufferedimg_tobytes(Bitmap img) 373 | { 374 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 375 | img.compress(Bitmap.CompressFormat.JPEG, 100, baos); 376 | return baos.toByteArray(); 377 | } 378 | 379 | public static byte[] constructxmlmessage(QQUser user, byte[] data) 380 | { 381 | 382 | ByteBuilder builder = new ByteBuilder(); 383 | builder.writebytes(Util.RandomKey(4)); 384 | builder.writebytes(Util.str_to_byte("0000000009008600")); 385 | builder.writebytes(new byte[]{0x00,0x0c}); 386 | builder.writebytes(Util.str_to_byte("E5BEAEE8BDAFE99B85E9BB91")); 387 | builder.writebytes(new byte[] { 0x00, 0x00, 0x14 }); 388 | builder.writeint(data.length + 11); 389 | builder.writebyte((byte) 0x01); 390 | builder.writeint(data.length + 1); 391 | builder.writebyte((byte) 0x01); 392 | builder.writebytes(data); 393 | builder.writebytes(new byte[] { 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x4D }); 394 | return builder.getdata(); 395 | } 396 | 397 | public static byte[] constructmessage(QQUser user, byte[] data) 398 | { 399 | ByteBuilder builder = new ByteBuilder(); 400 | builder.writebyte((byte)0x01); 401 | builder.writeint((data.length + 3)); 402 | builder.writebyte((byte)0x01); 403 | builder.writeint(data.length); 404 | builder.writebytes(data); 405 | 406 | return builder.getdata(); 407 | } 408 | public static long ConvertQQGroupId(long code) 409 | { 410 | String group = String.valueOf(code); 411 | long left = Long.parseLong(group.substring(0, group.length() - 6)); 412 | 413 | String right = "", gid = ""; 414 | if (left >= 1 && left <= 10) 415 | { 416 | right = group.substring(group.length() - 6, group.length()); 417 | gid = String.valueOf(left + 202) + right; 418 | } 419 | else if (left >= 11 && left <= 19) 420 | { 421 | right = group.substring(group.length() - 6, group.length()); 422 | gid = String.valueOf(left + 469) + right; 423 | } 424 | else if (left >= 20 && left <= 66) 425 | { 426 | left = Long.parseLong(String.valueOf(left).substring(0, 1)); 427 | right = group.substring(group.length() - 7, group.length()); 428 | gid = String.valueOf(left + 208) + right; 429 | } 430 | else if (left >= 67 && left <= 156) 431 | { 432 | right = group.substring(group.length() - 6, group.length()); 433 | gid = String.valueOf(left + 1943) + right; 434 | } 435 | else if (left >= 157 && left <= 209) 436 | { 437 | left = Long.parseLong(String.valueOf(left).substring(0, 2)); 438 | right = group.substring(group.length() - 7, group.length()); 439 | gid = String.valueOf(left + 199) + right; 440 | } 441 | else if (left >= 210 && left <= 309) 442 | { 443 | left = Long.parseLong(String.valueOf(left).substring(0, 2)); 444 | right = group.substring(group.length() - 7, group.length()); 445 | gid = String.valueOf(left + 389) + right; 446 | } 447 | else if (left >= 310 && left <= 499) 448 | { 449 | left = Long.parseLong(String.valueOf(left).substring(0, 2)); 450 | right = group.substring(group.length() - 7, group.length()); 451 | gid = String.valueOf(left + 349) + right; 452 | } 453 | else 454 | { 455 | return code; 456 | } 457 | return Long.parseLong(gid); 458 | } 459 | 460 | 461 | public static void parseRichText(QQMessage qqmessage, byte[] rich_data) 462 | { 463 | ByteFactory bytefactory = new ByteFactory(rich_data); 464 | int messagetype = bytefactory.readBytes(1)[0]; 465 | int messagelength = bytefactory.readint(); 466 | int position = bytefactory.position; 467 | while (position + messagelength <= bytefactory.data.length) 468 | { 469 | bytefactory.readBytes(1); 470 | switch (messagetype) 471 | { 472 | case 0x01: // 纯文本消息、@ 473 | { 474 | qqmessage.contain_type = 1; 475 | String messageStr = bytefactory.readStringbylength(); 476 | if (messageStr.startsWith("@") && position + messagelength - bytefactory.position == 16) 477 | { 478 | if (qqmessage.Isat == false) 479 | { 480 | qqmessage.Isat = true; 481 | } 482 | bytefactory.readBytes(10); 483 | qqmessage.Atlist.add(messageStr + " Target: " + bytefactory.readlong()); 484 | } 485 | else 486 | { 487 | qqmessage.Message += messageStr; 488 | } 489 | break; 490 | } 491 | case 0x03: // 图片 492 | { 493 | qqmessage.contain_type = 2; 494 | qqmessage.Message += bytefactory.readStringbylength(); 495 | //log(Util.byte2HexString(rich_data)); 496 | //log(new String(rich_data)); 497 | break; 498 | } 499 | case 0x0A: // 音频 500 | { 501 | qqmessage.contain_type = 3; 502 | qqmessage.Message += bytefactory.readBytesbylength(); 503 | break; 504 | } 505 | case 0x0E: // 未知 506 | { 507 | break; 508 | } 509 | case 0x12: // 群名片 510 | { 511 | ByteFactory cardfactory = new ByteFactory(Util.subByte(rich_data, position, rich_data.length - position)); 512 | int cardtype = cardfactory.readBytes(1)[0]; 513 | 514 | int cardlength = cardfactory.readint(); 515 | int cardposition = cardfactory.position; 516 | 517 | while (cardposition + cardlength <= cardfactory.data.length) 518 | { 519 | 520 | 521 | if (cardtype == 0x01 || cardtype == 0x02) 522 | { 523 | qqmessage.SendName = cardfactory.readString(cardlength); 524 | } 525 | 526 | if (cardposition + cardlength == cardfactory.data.length) 527 | { 528 | break; 529 | } 530 | cardfactory.readBytes((cardposition + cardlength - cardfactory.position)); 531 | 532 | cardtype = cardfactory.readBytes(1)[0]; 533 | cardlength = cardfactory.readint(); 534 | cardposition = cardfactory.position; 535 | } 536 | 537 | 538 | break; 539 | } 540 | case 0x14: // xml 541 | { 542 | qqmessage.contain_type = 1; 543 | ByteFactory xmlfactory = new ByteFactory(Util.subByte(rich_data, position, rich_data.length - position)); 544 | xmlfactory.readBytes(1); 545 | int length = xmlfactory.readint(); 546 | xmlfactory.readBytes(1); 547 | byte[] xml = xmlfactory.readBytes(length); 548 | qqmessage.Message = new String(ZLibUtils.decompress(xml)); 549 | 550 | break; 551 | 552 | } 553 | case 0x18: // 群文件 554 | { 555 | System.out.println("Fuck"); 556 | break; 557 | } 558 | case 0x19: // 红包 559 | { 560 | break; 561 | 562 | } 563 | default: 564 | { 565 | break; 566 | } 567 | } 568 | if (position + messagelength == bytefactory.data.length) 569 | { 570 | break; 571 | } 572 | bytefactory.readBytes((position + messagelength - bytefactory.position)); 573 | messagetype = bytefactory.readBytes(1)[0]; 574 | messagelength = bytefactory.readint(); 575 | position = bytefactory.position; 576 | } 577 | } 578 | 579 | 580 | 581 | public static String getHostIP() 582 | { 583 | 584 | String hostIp = null; 585 | try 586 | { 587 | Enumeration nis = NetworkInterface.getNetworkInterfaces(); 588 | InetAddress ia = null; 589 | while (nis.hasMoreElements()) 590 | { 591 | NetworkInterface ni = (NetworkInterface) nis.nextElement(); 592 | Enumeration ias = ni.getInetAddresses(); 593 | while (ias.hasMoreElements()) 594 | { 595 | ia = ias.nextElement(); 596 | if (ia instanceof Inet6Address) 597 | { 598 | continue;// skip ipv6 599 | } 600 | String ip = ia.getHostAddress(); 601 | if (!"127.0.0.1".equals(ip)) 602 | { 603 | hostIp = ia.getHostAddress(); 604 | break; 605 | } 606 | } 607 | } 608 | } 609 | catch (SocketException e) 610 | { 611 | 612 | e.printStackTrace(); 613 | } 614 | return hostIp; 615 | } 616 | public static long GetTimeSeconds(Date dateTime) 617 | { 618 | return (long) dateTime.getTime() - BaseDateTime.getTime() / 1000; 619 | } 620 | 621 | 622 | public static byte[] GetQdData(QQUser user) 623 | { 624 | byte[] data = new byte[]{}; 625 | data = Util.byteMerger(data, Util.IPStringToByteArray(user.TXProtocol.DwServerIP)); 626 | byte[] qddata = new byte[]{}; 627 | qddata = Util.byteMerger(qddata, user.TXProtocol.DwQdVerion_Byte); 628 | qddata = Util.byteMerger(qddata, new byte[]{0x00,0x00,0x00,0x00}); 629 | qddata = Util.byteMerger(qddata, user.TXProtocol.DwPubNo); 630 | qddata = Util.byteMerger(qddata, Util.subByte(Util.ToByte(user.QQ), 4, 4)); 631 | qddata = Util.byteMerger(qddata, Util.subByte(Util.ToByte(data.length), 2, 2)); 632 | 633 | data = new byte[]{}; 634 | data = Util.byteMerger(data, user.TXProtocol.QdPreFix); 635 | data = Util.byteMerger(data, user.TXProtocol.CQdProtocolVer_Byte); 636 | data = Util.byteMerger(data, user.TXProtocol.DwQdVerion_Byte); 637 | data = Util.byteMerger(data, new byte[]{0x00}); 638 | data = Util.byteMerger(data, user.TXProtocol.WQdCsCmdNo_Byte); 639 | data = Util.byteMerger(data, user.TXProtocol.CQdCcSubNo); 640 | data = Util.byteMerger(data, Util.str_to_byte("0E88")); 641 | data = Util.byteMerger(data, new byte[]{0x00,0x00,0x00,0x00}); 642 | data = Util.byteMerger(data, user.TXProtocol.BufComputerIdEx); 643 | data = Util.byteMerger(data, user.TXProtocol.COsType); 644 | data = Util.byteMerger(data, user.TXProtocol.BIsWow64); 645 | data = Util.byteMerger(data, user.TXProtocol.DwPubNo); 646 | data = Util.byteMerger(data, Util.subByte(user.TXProtocol.DwClientVer, 2, 2)); 647 | data = Util.byteMerger(data, new byte[]{0x00,0x00}); 648 | data = Util.byteMerger(data, user.TXProtocol.DwDrvVersionInfo); 649 | data = Util.byteMerger(data, new byte[]{0x00,0x00,0x00,0x00}); 650 | data = Util.byteMerger(data, user.TXProtocol.BufVersionTsSafeEditDat); 651 | data = Util.byteMerger(data, user.TXProtocol.BufVersionQScanEngineDll); 652 | data = Util.byteMerger(data, new byte[]{0x00}); 653 | Crypter crypter = new Crypter(); 654 | data = Util.byteMerger(data, crypter.encrypt(qddata, user.TXProtocol.BufQdKey)); 655 | data = Util.byteMerger(data, user.TXProtocol.QdSufFix); 656 | 657 | int size = data.length + 3; 658 | qddata = new byte[]{}; 659 | qddata = Util.byteMerger(qddata, user.TXProtocol.QdPreFix); 660 | qddata = Util.byteMerger(qddata, Util.subByte(Util.ToByte(size), 2, 6)); 661 | qddata = Util.byteMerger(qddata, new byte[]{0x00,0x00}); 662 | qddata = Util.byteMerger(qddata, Util.subByte(Util.ToByte(data.length), 2, 6)); 663 | qddata = Util.byteMerger(qddata, new byte[]{0x00,0x00}); 664 | 665 | user.TXProtocol.QdData = data; 666 | return data; 667 | } 668 | 669 | 670 | public static byte[] random_byte(int size) 671 | { 672 | byte [] b=new byte[size]; 673 | Random random=new Random(); 674 | random.nextBytes(b); 675 | 676 | return b; 677 | } 678 | 679 | 680 | public static short bytesToshort(byte[] input) 681 | { 682 | byte high = input[0]; 683 | byte low = input[1]; 684 | short z = (short)(((high & 0x00FF) << 8) | (0x00FF & low)); 685 | return z; 686 | 687 | 688 | // TODO: Implement this metho 689 | } 690 | 691 | public static String GetIpStringFromBytes(byte[] input) 692 | { 693 | String u1 = String.valueOf((int)input[0] & 0xff); 694 | String u2 = String.valueOf((int)input[1] & 0xff); 695 | String u3 = String.valueOf((int)input[2] & 0xff); 696 | String u4 = String.valueOf((int)input[3] & 0xff); 697 | return u1 + "." + u2 + "." + u3 + "." + u4; 698 | } 699 | 700 | public static long bytesToLong(byte[] input, int offset, boolean littleEndian) 701 | { 702 | // 将byte[] 封装为 ByteBuffer 703 | 704 | ByteBuffer buffer = ByteBuffer.wrap(Util.byteMerger(new byte[8 - input.length], input)); 705 | 706 | if (littleEndian) 707 | { 708 | // ByteBuffer.order(ByteOrder) 方法指定字节序,即大小端模式(BIG_ENDIAN/LITTLE_ENDIAN) 709 | // ByteBuffer 默认为大端(BIG_ENDIAN)模式 710 | buffer.order(ByteOrder.LITTLE_ENDIAN); 711 | } 712 | 713 | return buffer.getLong(); 714 | } 715 | 716 | public static int GetInt(byte[] data, int offset, int length) 717 | { 718 | byte[] test = new byte[]{0x00,0x00,data[offset],data[offset + 1],0x00,0x00,0x00,0x00}; 719 | ByteBuffer u = ByteBuffer.wrap(test); 720 | 721 | return u.getInt(); 722 | } 723 | 724 | public static int GetInt(byte[] data) 725 | { 726 | byte[] test = new byte[]{0x00,0x00,data[0],data[1],0x00,0x00,0x00,0x00}; 727 | 728 | ByteBuffer u = ByteBuffer.wrap(test); 729 | 730 | return u.getInt(); 731 | } 732 | 733 | public static long GetLong(byte[] data) 734 | { 735 | byte[] test = new byte[]{0x00,0x00,0x00,0x00,data[0],data[1],data[2],data[3]}; 736 | 737 | ByteBuffer u = ByteBuffer.wrap(test); 738 | 739 | return u.getLong(); 740 | } 741 | 742 | public static short GetShort(byte[] data) 743 | { 744 | byte[] test = new byte[]{data[0],data[1],0x00,0x00,0x00,0x00,0x00,0x00}; 745 | 746 | ByteBuffer u = ByteBuffer.wrap(test); 747 | 748 | return u.getShort(); 749 | } 750 | 751 | public static Date GetDateTimeFromMillis(long timeMillis) 752 | { 753 | Date date = new Date(timeMillis); 754 | return date; 755 | } 756 | 757 | public static byte[] GetBytes(String string) 758 | { 759 | 760 | 761 | // TODO: Implement this method 762 | return string.getBytes(); 763 | } 764 | 765 | 766 | public static String GET_GTK(String skey) 767 | { 768 | String arg = "tencentQQVIP123443safde&!%^%1282"; 769 | List list = new ArrayList(); 770 | int num = 5381; 771 | list.add(172192); 772 | int i = 0; 773 | for (int length = skey.length(); i < length; i++) 774 | { 775 | int num2 = (skey).charAt(i); 776 | list.add((num << 5) + num2); 777 | num = num2; 778 | } 779 | 780 | StringBuilder stringBuilder = new StringBuilder(); 781 | for (i = 0; i < list.size(); i++) 782 | { 783 | stringBuilder.append(list.get(i).toString()); 784 | } 785 | 786 | return Md5(stringBuilder + arg); 787 | } 788 | public static String Md5(String text) 789 | { 790 | 791 | try 792 | { 793 | MessageDigest md = MessageDigest.getInstance("MD5"); 794 | 795 | byte[] bytes = md.digest(text.getBytes()); 796 | String result = ""; 797 | for (byte b : bytes) 798 | { 799 | result += String.format("%02x", b); 800 | } 801 | 802 | return result; 803 | } 804 | catch (NoSuchAlgorithmException e) 805 | {} 806 | return null; 807 | } 808 | 809 | public static byte[] MD5(String arg) 810 | { 811 | try 812 | { 813 | MessageDigest md = MessageDigest.getInstance("MD5"); 814 | 815 | 816 | byte[] bytes = md.digest(arg.getBytes()); 817 | return bytes; 818 | } 819 | catch (NoSuchAlgorithmException e) 820 | {} 821 | return null; 822 | } 823 | public static byte[] MD5(byte[] arg) 824 | { 825 | try 826 | { 827 | MessageDigest md = MessageDigest.getInstance("MD5"); 828 | 829 | 830 | byte[] bytes = md.digest(arg); 831 | return bytes; 832 | } 833 | catch (NoSuchAlgorithmException e) 834 | {} 835 | return null; 836 | } 837 | public static String GetBkn(String skey) 838 | { 839 | int hash = 5381; 840 | 841 | for (int i = 0, len = skey.length(); i < len; ++i) 842 | 843 | hash += (hash << 5) + skey.charAt(i); 844 | 845 | int gtkOrbkn = hash & 2147483647; 846 | 847 | return String.valueOf(gtkOrbkn); 848 | } 849 | 850 | public static String ToHex(byte[] data, String p1, String p2) 851 | { 852 | String hex= ""; 853 | if (data != null) 854 | { 855 | for (Byte b : data) 856 | { 857 | hex += String.format("%02X", b.intValue() & 0xFF); 858 | } 859 | } 860 | return hex; 861 | } 862 | 863 | 864 | public static String NumToHexString(int qq, int Length) 865 | { 866 | 867 | String text = String.valueOf(qq); 868 | if (text.length() == Length) 869 | { 870 | return text; 871 | } 872 | 873 | if (text.length() > Length) 874 | { 875 | return null; 876 | } 877 | return null; 878 | } 879 | 880 | public static byte[] byteMerger(List bytes) 881 | { 882 | int total_length = 0; 883 | int offset= 0; 884 | for (byte[] one : bytes) 885 | { 886 | total_length = total_length + one.length; 887 | } 888 | byte[] byte_3 = new byte[total_length]; 889 | 890 | for (byte[] one_byte : bytes) 891 | { 892 | System.arraycopy(one_byte, 0, byte_3, offset, one_byte.length); 893 | offset = offset + one_byte.length; 894 | } 895 | return byte_3; 896 | } 897 | 898 | 899 | public static byte[] IPStringToByteArray(String ip) 900 | { 901 | byte[] array = new byte[4]; 902 | String[] array2 = ip.split("[.]"); 903 | if (array2.length == 4) 904 | { 905 | for (int i = 0; i < 4; i++) 906 | { 907 | array[i] = (byte) Integer.parseInt(array2[i]); 908 | } 909 | } 910 | 911 | return array; 912 | } 913 | 914 | 915 | 916 | public static byte[] RandomKey() 917 | { 918 | byte[] key = new byte[16]; 919 | new Random().nextBytes(key); 920 | return key; 921 | } 922 | public static byte[] RandomKey(int size) 923 | { 924 | byte[] key = new byte[size]; 925 | new Random().nextBytes(key); 926 | return key; 927 | } 928 | 929 | public static byte[] str_to_byte(String str) 930 | { 931 | String replaceAll = str.replaceAll(" ", ""); 932 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(replaceAll.length() >> 1); 933 | for (int i = 0; i <= replaceAll.length() - 2; i += 2) 934 | { 935 | byteArrayOutputStream.write(Integer.parseInt(replaceAll.substring(i, i + 2), 16) & 255); 936 | } 937 | return byteArrayOutputStream.toByteArray(); 938 | } 939 | 940 | public static byte[] byteMerger(byte[] byte_1, byte[] byte_2) 941 | { 942 | byte[] byte_3 = new byte[byte_1.length + byte_2.length]; 943 | System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length); 944 | System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length); 945 | return byte_3; 946 | } 947 | 948 | public static byte[] ToByte(long x) 949 | { 950 | ByteBuffer buffer = ByteBuffer.allocate(8); 951 | 952 | buffer.putLong(x); 953 | return buffer.array(); 954 | } 955 | public static byte[] ToByte(int x) 956 | { 957 | ByteBuffer buffer = ByteBuffer.allocate(8); 958 | 959 | buffer.putInt(x); 960 | return buffer.array(); 961 | } 962 | public static byte[] ToByte(short x) 963 | { 964 | ByteBuffer buffer = ByteBuffer.allocate(8); 965 | 966 | buffer.putShort(x); 967 | return buffer.array(); 968 | } 969 | public static String byte2HexString(byte[] bytes) 970 | { 971 | String hex= ""; 972 | if (bytes != null) 973 | { 974 | for (Byte b : bytes) 975 | { 976 | hex += String.format("%02X", b.intValue() & 0xFF) + " "; 977 | } 978 | } 979 | return hex; 980 | 981 | } 982 | 983 | public static byte[] subByte(byte[] b, int off, int length) 984 | { 985 | 986 | if (b.length != 0 && b != null) 987 | { 988 | byte[] b1 = new byte[length]; 989 | System.arraycopy(b, off, b1, 0, length); 990 | return b1; 991 | } 992 | return new byte[1]; 993 | } 994 | 995 | public static class miTM implements TrustManager,X509TrustManager 996 | { 997 | 998 | @Override 999 | public void checkServerTrusted(X509Certificate[] p1, String p2) throws CertificateException 1000 | { 1001 | } 1002 | 1003 | @Override 1004 | public X509Certificate[] getAcceptedIssuers() 1005 | { 1006 | return null; 1007 | } 1008 | 1009 | public static boolean isServerTrusted(X509Certificate[] certs) 1010 | { 1011 | return true; 1012 | } 1013 | 1014 | public static boolean isClientTrusted(X509Certificate[] certs) 1015 | { 1016 | return true; 1017 | } 1018 | 1019 | @Override 1020 | public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException 1021 | { 1022 | } 1023 | } 1024 | } 1025 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/Utils/ZLibUtils.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.Utils; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.util.zip.Deflater; 8 | import java.util.zip.DeflaterOutputStream; 9 | import java.util.zip.Inflater; 10 | import java.util.zip.InflaterInputStream; 11 | 12 | /** 13 | * ZLib压缩工具 14 | * 15 | * @author 梁栋 16 | * @version 1.0 17 | * @since 1.0 18 | */ 19 | public class ZLibUtils { 20 | 21 | /** 22 | * 压缩 23 | * 24 | * @param data 25 | * 待压缩数据 26 | * @return byte[] 压缩后的数据 27 | */ 28 | public static byte[] compress(byte[] data) { 29 | byte[] output = new byte[0]; 30 | 31 | Deflater compresser = new Deflater(); 32 | 33 | compresser.reset(); 34 | compresser.setInput(data); 35 | compresser.finish(); 36 | ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length); 37 | try { 38 | byte[] buf = new byte[1024]; 39 | while (!compresser.finished()) { 40 | int i = compresser.deflate(buf); 41 | bos.write(buf, 0, i); 42 | } 43 | output = bos.toByteArray(); 44 | } catch (Exception e) { 45 | output = data; 46 | e.printStackTrace(); 47 | } finally { 48 | try { 49 | bos.close(); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | compresser.end(); 55 | return output; 56 | } 57 | 58 | /** 59 | * 压缩 60 | * 61 | * @param data 62 | * 待压缩数据 63 | * 64 | * @param os 65 | * 输出流 66 | */ 67 | public static void compress(byte[] data, OutputStream os) { 68 | DeflaterOutputStream dos = new DeflaterOutputStream(os); 69 | 70 | try { 71 | dos.write(data, 0, data.length); 72 | 73 | dos.finish(); 74 | 75 | dos.flush(); 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | /** 82 | * 解压缩 83 | * 84 | * @param data 85 | * 待压缩的数据 86 | * @return byte[] 解压缩后的数据 87 | */ 88 | public static byte[] decompress(byte[] data) { 89 | byte[] output = new byte[0]; 90 | 91 | Inflater decompresser = new Inflater(); 92 | decompresser.reset(); 93 | decompresser.setInput(data); 94 | 95 | ByteArrayOutputStream o = new ByteArrayOutputStream(data.length); 96 | try { 97 | byte[] buf = new byte[1024]; 98 | while (!decompresser.finished()) { 99 | int i = decompresser.inflate(buf); 100 | o.write(buf, 0, i); 101 | } 102 | output = o.toByteArray(); 103 | } catch (Exception e) { 104 | output = data; 105 | e.printStackTrace(); 106 | } finally { 107 | try { 108 | o.close(); 109 | } catch (IOException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | decompresser.end(); 115 | return output; 116 | } 117 | 118 | /** 119 | * 解压缩 120 | * 121 | * @param is 122 | * 输入流 123 | * @return byte[] 解压缩后的数据 124 | */ 125 | public static byte[] decompress(InputStream is) { 126 | InflaterInputStream iis = new InflaterInputStream(is); 127 | ByteArrayOutputStream o = new ByteArrayOutputStream(1024); 128 | try { 129 | int i = 1024; 130 | byte[] buf = new byte[i]; 131 | 132 | while ((i = iis.read(buf, 0, i)) > 0) { 133 | o.write(buf, 0, i); 134 | } 135 | 136 | } catch (IOException e) { 137 | e.printStackTrace(); 138 | } 139 | return o.toByteArray(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/sdk/API.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.sdk; 2 | import android.graphics.*; 3 | import android.content.Context; 4 | 5 | public interface API 6 | { 7 | public void SendGroupTextMessage(long gin,String msg); 8 | public void SendGroupXMLMessage(long gin,String xml); 9 | public void SendGroupBitmapMessage(long gin,Bitmap bitmap); 10 | public void SendFriendTextMessage(long uin,String msg); 11 | public void SendFriendXMLMessage(long uin,String xml); 12 | public void GroupMemberShutUp(long gin,long uin,int time); 13 | public void setGroupMemberCard(long gin,long uin,String card); 14 | public Context getContext(); 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/sdk/Plugin.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.sdk; 2 | 3 | public interface Plugin 4 | { 5 | String author(); 6 | String Version(); 7 | String name(); 8 | 9 | void onLoad(API api); 10 | 11 | void onMessageHandler(QQMessage message); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/PCTIM/sdk/QQMessage.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot.PCTIM.sdk; 2 | import java.util.*; 3 | 4 | public class QQMessage 5 | { 6 | public int Message_Type = 0; 7 | public long Sender_Uin = 0; 8 | public long Message_Time = 0; 9 | public long Message_Id =0; 10 | 11 | public byte[] MessageIndex; 12 | 13 | public long Reciece_Message_Time; 14 | 15 | public long Send_Message_Time; 16 | 17 | public String Message_Font; 18 | 19 | public String Message = ""; 20 | 21 | public String Message_Text; 22 | 23 | public boolean Isat; 24 | 25 | public List Atlist = new ArrayList(); 26 | 27 | public String SendName; 28 | 29 | public long Group_uin; 30 | 31 | public long Self_uin; 32 | 33 | public int contain_type =0; 34 | 35 | public QQMessage(){ 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/net/newlydev/qqrobot/mApplication.java: -------------------------------------------------------------------------------- 1 | package net.newlydev.qqrobot; 2 | import android.app.*; 3 | import net.newlydev.qqrobot.PCTIM.Utils.*; 4 | import android.content.Context; 5 | 6 | public class mApplication extends Application 7 | { 8 | private static mApplication mApp; 9 | 10 | public static Context getContext() 11 | { 12 | return mApp.getApplicationContext(); 13 | } 14 | @Override 15 | public void onCreate() 16 | { 17 | // TODO: Implement this method 18 | super.onCreate(); 19 | mApp=this; 20 | try 21 | { 22 | Util.trustAllHttpsCertificates(); 23 | } 24 | catch (Exception e) 25 | {} 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uebian/QQRobot/5f38aba8224f2f521f62198b70b354544a8172eb/app/src/main/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 23 | 24 | 29 | 30 |