├── README.md
├── apiServer.py
├── config
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── enumExec.cpython-38.pyc
│ ├── logger.cpython-38.pyc
│ └── manager.cpython-38.pyc
├── base
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── config.cpython-38.pyc
│ │ ├── serialize.cpython-38.pyc
│ │ └── transform.cpython-38.pyc
│ ├── config.py
│ ├── serialize.py
│ └── transform.py
├── config.py
├── enumExec.py
├── logger.py
├── manager.py
├── transforms
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── config_dict.cpython-38.pyc
│ │ ├── dict_json.cpython-38.pyc
│ │ ├── dict_keyval.cpython-38.pyc
│ │ ├── filter.cpython-38.pyc
│ │ ├── map.cpython-38.pyc
│ │ ├── meta.cpython-38.pyc
│ │ └── props.cpython-38.pyc
│ ├── config_dict.py
│ ├── dict_json.py
│ ├── dict_keyval.py
│ ├── filter.py
│ ├── map.py
│ ├── meta.py
│ ├── props.py
│ └── serialize.py
└── v1
│ ├── __init__.py
│ ├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── config.cpython-38.pyc
│ └── serialize.cpython-38.pyc
│ ├── config.py
│ └── serialize.py
├── env
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── env.cpython-38.pyc
│ ├── env_app.cpython-38.pyc
│ ├── env_config.cpython-38.pyc
│ ├── env_config_id.cpython-38.pyc
│ ├── env_config_kh.cpython-38.pyc
│ ├── env_config_kz.cpython-38.pyc
│ ├── env_config_ph.cpython-38.pyc
│ ├── env_config_ru.cpython-38.pyc
│ └── env_ios.cpython-38.pyc
├── env.py
├── env_android.py
├── env_app.py
├── env_config.py
├── env_config_hk.py
├── env_config_id.py
├── env_config_kh.py
├── env_config_kz.py
├── env_config_ph.py
├── env_config_ru.py
├── env_config_us.py
└── env_config_vn.py
├── examples
├── walogin_handshake_ik.py
├── walogin_handshake_xx.py
└── walogin_handshake_xxfallback.py
├── login.png
├── messagestack
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── group_info.cpython-38.pyc
│ ├── play_group.cpython-38.pyc
│ └── register.cpython-38.pyc
├── group_info.py
├── group_msg_catch.py
├── play_group.py
└── register.py
├── registration
├── __init__.py
├── __pycache__
│ ├── __init__.cpython-38.pyc
│ ├── coderequest.cpython-38.pyc
│ ├── existsrequest.cpython-38.pyc
│ └── regrequest.cpython-38.pyc
├── clientLogRequest.py
├── coderequest.py
├── existsrequest.py
└── regrequest.py
├── resource
└── WA_Security_WhitePaper.pdf
└── versions
└── whatsapp_2.21.110.zip
/README.md:
--------------------------------------------------------------------------------
1 | # whatsapp api Share about yowsup telegram:@tgforme2
2 |
3 | **基于WhatsApp iOS协议逆向的共享资源**
4 |
5 | ** WhatsApp api 全量协议(可实现注册登录,发消息,群组, 炒群, 拉群,头像功能,有需要技术支持及源码可联系 telegram:@tgforme2) **
6 |
7 | ** WhatsApp api Full protocol (can use registration and login, sending messages, group, avatar functions, technical support and source code can be contacted telegram:@tgforme2) **
8 |
9 | ****
10 | !!!WhatsApp ws 新号 协议号 拉群号 美国出售 量大价低
11 |
12 | ****
13 | ## 0x01. WhatsApp ipa 脱壳 历史版本
14 |
15 |
16 | - 最近版本: [whatsapp_2.21.110.zip](/versions/)
17 |
18 |
19 |
20 | ****
21 | ## 0x02. WhatsApp iOS协议部分
22 | 
23 |
24 |
25 | # **0x01 工具**
26 |
27 | 首先是要用到的工具,中间主要用了ida,hopper和lldb
28 |
29 | - dumpdecrypted: 将苹果加密过的app砸壳
30 | - class-dump: 导出MachO文件里ObjC类及方法定义
31 | - CydiaSubstrate: 将第三方动态库注入进程
32 | - Cycript: 用js语法写ObjC方法
33 | - Theos: 越狱插件开发工具
34 | - IDA: 反汇编、反编译工具
35 | - Hopper: OSX反汇编、反编译工具
36 | - Debugserver + LLDB: 动态调试器
37 |
38 | # **0x02 ARM指令**
39 |
40 | ```
41 | arm是RISC结构,数据从内存到CPU之间移动只能通过L/S指令来完成,就是ldr/str指令
42 | ldr 把数据从内存移到cpu
43 | str 把cpu的数据数据转移到内存
44 | lldb读取内存的数据,memory read
45 | ldr r0, 0x12345678 //把0x12345678这个地址中的值存放到r0中
46 | ldr r0, =0x12345678 //把0x12345678这个地址写到r0中
47 | 例子:
48 | COUNT EQU 0x40003100 //定义一个COUNT变量,地址是0x40003100
49 | ...
50 | LDR R1,=COUNT //将COUNT这个变量的地址,也就是0x40003100放到R1中
51 | MOV R0,#0 //将立即数0放到R0中
52 | STR R0,[R1] //将R0中的值放到以R1中的值为地址的存储单元去
53 |
54 | B 跳转指令
55 | BL 带返回的跳转指令
56 | BLX 带返回和状态切换的跳转指令
57 | BX 带状态切换的跳转指令
58 |
59 | BLX指令从ARM指令集跳转到指令中所指定的目标地址,并将处理器的工作状态从ARM切换到Thumb状态,该指令同时将PC的当前内容保存到寄存器R14,因此,当子程序使用Thumb指令集,而调用者者使用ARM指令集,可以通过BLX指令实现子程序的调用和处理器工作状态切换,同时,子程序的返回可以通过将寄存器R14的值复制到PC中来完成。
60 | R0-R3: 用于函数参数及返回值的传递,超过4个参数,其它参数存在栈中,在ARM中栈是向下生长的,R0还可以作为返回值。
61 | R4-R6, R8, R10-R11: 没有特殊规定,就是普通的通用寄存器
62 | R7: 栈帧指针,指向母函数与被调用子函数在栈中的交界。
63 | R9: 在iOS3.0被操作系统保留
64 | R12: 内部过程调用寄存器,动态链接时会用到,不必深究
65 | R13: SP(stack pointer),是栈顶指针
66 | R14: LR(link register),存放函数的返回地址。
67 | R15: PC(program counter),指向当前指令地址。
68 | ADC 带进位的加法
69 | ADD 加法
70 | AND 逻辑与
71 | B 分支跳转,很少单独使用
72 | BL 分支跳转,跳转后返回地址存入r14
73 | BX 分支跳转,并切换指令模式(Thumb/ARM)
74 | CMP 比较值,结果存在程序状态寄存器,一般用于分支判断
75 | BEQ 结果为0则跳转
76 | BNE 结果不为0跳转
77 | LDR 加载寄存器,从内存加载到寄存器
78 | LDRB 装载字节到寄存器
79 | LDRH 装载半字到寄存器(一个字是32位)
80 | LSL 逻辑左移 这是一个选项,不是指令
81 | LSR 逻辑右移 这是一个选项,不是指令
82 | MOV 传送值/寄存器到一个寄存器
83 | STR 存储一个寄存器,寄存器值存到内存
84 | STRB 存储一个字节
85 | STRH 存储一个半字
86 | SUB 减法
87 | PUSH POP 堆栈操作
88 |
89 | 有时候需要
90 |
91 | db ;定义字节类型变量,一个字节数据占一个字节单元,读完一个偏移量加1
92 | dw ;定义字类型变量,一个字数据占2个字节单元,读完一个,偏移量加2
93 | dd ;定义双字类型变量,一个双字数据占4个字节单元,读完一个,偏移量加4
94 |
95 | IDA给某个位置命名的时,它会使用该位置的虚拟地址和表示一个该地址的类型的前缀进行命名:
96 | sub_xxx ;地址xxx处的子例程
97 | loc_xxx ;地址xxx处的一个指令
98 | byte_xxx ;位置xxx处的8位数据
99 | word_xxx ;位置xxx处的16位数据
100 | dword_xxx ;位置xxx处的32位数据
101 | unk_xxx ;位置xxx处大小未知的数据
102 | ```
103 |
104 | ```
105 | 关于sp,bp等栈寄存器的解释:
106 | SP is stack pointer. The stack is generally used to hold "automatic" variables and context/parameters across function calls. Conceptually you can think of the "stack" as a place where you "pile" your data. You keep "stacking" one piece of data over the other and the stack pointer tells you how "high" your "stack" of data is. You can remove data from the "top" of the "stack" and make it shorter.
107 |
108 |
109 |
110 |
111 | 虽然是英文,但是看起来要比中文易懂
112 | ```
113 |
114 | ida里面有三种颜色的箭头:
115 |
116 | 1. 蓝色,顺序执行
117 | 2. 绿色,条件为(YES)
118 | 3. 红色,条件为(NO)
119 |
120 | # **0x03 lldb使用方法**
121 |
122 | ```
123 | lldb操作相关指令
124 | image list -o -f 查看进程在虚拟内存中相对模块基地址
125 | br s -a [addr] 打断点
126 | breakpoint delete 删除断点
127 | s/n 是针对源代码
128 | br list 列出所有断点
129 | br dis 1 禁用序号为1的断点
130 | jump 跳转到新地址
131 |
132 | ni 断点的单步之行, netxi(next instruction简写:ni)
133 | si stepi(step instruction 简写:si)
134 | display /10i $pc-16 显示当前PC附近的10条指令
135 |
136 | si会进入函数之行,ni执行完但是不会进入函数内,执行过程中可以利用display /i $pc来看下一个执行的instruction是什么
137 |
138 | c 放开执行该断点
139 | p 输出某个寄存器的值
140 | p $r0 输出寄存器的内容
141 | 也可以将一个地址所存放的值进行打印
142 | p/x $sp 就是输出$sp指针所指的地址处存放的值,以16进制表示
143 | po (char *)$r2 po打印Object-C对象
144 | register read --all 读取所有的寄存器内容
145 | thread list //打印所有线程
146 | thread select //跳到某个线程
147 | thread info //输出当前线程信息
148 | frame variable //打印当前栈所有变量
149 | frame variable '变量名' //打印某个变量
150 | frame info 查看当前帧栈信息
151 | frame select 跳到指定帧栈
152 |
153 | ```
154 |
155 | 
156 |
157 | 
158 |
159 | frida的常见用法:
160 |
161 | - hook函数(IOS中theos具备的功能)
162 | - 记录函数执行日至(IOS中theos具备的功能)
163 | - 调用函数(IOS中cycript具备的功能)
164 | - 读写内存(类似调试器的功能)
165 |
166 | lldb:
167 |
168 | - lldb在object-c类对象所有函数设置断点: `breakpoint set -r '\[ClassName .*\]$'`
169 |
170 | 常用:
171 |
172 | ```
173 | breakpoint set --name
174 | "set a breakpoint on a given function name, globally. eg.
175 | breakpoint set --name viewDidLoad
176 | or
177 | breakpoint set --name "-[UIView setFrame:]"
178 |
179 | break set --selector
180 | "set a breakpoint on a selector, globally. e.g.,
181 | breakpoint set --selector dealloc
182 | bt //查看堆栈
183 | frame select
184 | thread list
185 | expression $r6 //查看r6寄存器的值
186 | 1. 加参数可以更改显示方式,如/x十六进制打印
187 | 2. po一般用作查看对象信息
188 | 3. po的命令是“expression -O -"命令的别名
189 |
190 | 第一次使用malloc_info需要在lld里面导入lldb.macosx.heap
191 | malloc_info -s
192 | memory read 读取内存的值
193 | ```
194 |
195 | ### **0x04 Hopper基本使用**
196 |
197 | hopper和LLDB所选择的ARM架构位数得一致,要么是32位,要么都是64位,计算公式:hopper里面显示的都是"模块偏移前基地址",而lldb要操作的都是"模块偏移后的基地址",所以从hopper到lldb要做一个地址偏移量的转换。
198 |
199 | ```
200 | 偏移后模块基地址 = 偏移前模块地址 + ALSR
201 | ```
202 |
203 | 偏移前地址从Hopper看:
204 |
205 | 
206 |
207 | ALSR偏移地址从LLDB看:
208 |
209 | 
210 |
211 | 由上图可知ASLR偏移:30000偏移后基地址为:34000
212 |
213 | (从hopper的login搜索找到方法[WCAccountPhoneLoginControlLogic initWithData:]:查看偏移基地址:
214 |
215 | 
216 |
217 | 则偏移后的地址: 14B6A66 + 30000 = 14E6A66设置断点动态调试,使用:
218 |
219 | ```
220 | br s -a 0x 14E6A66
221 | ```
222 |
223 | ### **0x05 Cycript**
224 |
225 | 安装Cycript
226 |
227 | ```
228 | dpkg -i cycript_0.9.461_iphoneos-arm.deb
229 | dpkg -i libffi_1-3.0.10-5_iphoneos-arm.deb
230 |
231 | cycript -p
232 | ```
233 |
234 | 步骤:
235 |
236 | 安装cydia之后的ssh,然后mac本机:
237 |
238 | ```
239 | brew install usbmuxd
240 | iproxy 2222 22 //iphone的22端口转发到本机的2222
241 | ssh root@localhost -p 2222 //默认密码 alpine
242 | ```
243 |
244 | ```
245 | cycript:
246 | [UIApp description]
247 | [[UIApp keyWindow] recursiveDescription].toString() //输出如下
248 | >
249 | | | | | | | | | | >
250 | | | | | | | | | | >
251 |
252 | //查看某个UI:
253 | [#0x18b1c070 _ivarDescription].toString()
254 | [#0x15baf520 nextResponder] 某个地址的调用方法
255 |
256 | [[UIApp keyWindow] _autolayoutTrace].toString()
257 | //choose传递一个类,可以在内存中找出属于这个类的对象
258 | //输出对象的属性:
259 | 方法1: 简单基本获取方法。
260 | *controller(直接在对象前面加个*)
261 |
262 | 方法2:方法一无法获取,就使用方法2
263 | [i for (i in *UIApp)]
264 |
265 | 方法3:建议方法三,方法三能获取到更多
266 | function tryPrintIvars(a){ var x={}; for(i in *a){ try{ x[i] = (*a)[i]; } catch(e){} } return x; }
267 |
268 | function printMethods(className, isa) {
269 | var count = new new Type("I");
270 | var classObj = (isa != undefined) ? objc_getClass(className)->isa : objc_getClass(className);
271 | var methods = class_copyMethodList(classObj, count);
272 | var methodsArray = [];
273 | for(var i = 0; i < *count; i++) {
274 | var method = methods[i];
275 | methodsArray.push({selector:method_getName(method), implementation:method_getImplementation(method)});
276 | }
277 | free(methods);
278 | return methodsArray;
279 | }
280 | ```
281 |
282 | ```
283 | cycript -p Springboard 或 cycript -p pid
284 |
285 | #在内存中找一个MD5Signater类的实例对象
286 | choose(MD5Signater)
287 |
288 | #调用0x166b4fb0处的对象的show函数
289 | [#0x166b4fb0 show]
290 |
291 | #对show函数传入参数3344
292 | [#0x166b4fb0 show:3344]
293 |
294 | #新建一个MD5Signater类的实例,并调用它的setSecret函数,传入参数1
295 | obj = [MD5Signater alloc]
296 | [#0x146f1a30 setSecret:1]
297 |
298 | 在Objective-C中,[someObject somemethod]的底层实现,实际上是objc_msgSend(someObject,someMethod),其中前一个是Objective-C对象,后者则可以强制转换成一个字符串。
299 | ```
300 |
301 | 在Objective-C里面,成员方法与类方法的区别:
302 |
303 | - 成员方法是以减号 "-" 开头 //成员方法必须使用对象调用
304 | - 类方法是以加号开头 "+" //类方法可以直接使用类名调用
305 |
306 | ### **Objective-C方法名的问题**
307 |
308 | ```
309 | - (double)pi;
310 |
311 | 方法名就是pi
312 |
313 | - (int)square:(int)num;
314 |
315 | 带参数的方法名有点特殊,冒号后面一定是参数,可以理解为,有几个冒号就有几个参数,把空格后面到参数前面的内容拼起来就是方法名。所以这个方法名是square:(注意冒号)
316 |
317 | - (int)addNum1:(int)num1 addNum2:(int)num2;
318 |
319 | 根据上面的方法,这个方法名是addNum1:addNum2:
320 | ```
321 |
322 | 所以根据上面方法名的问题,在cycript里面调用的时候,是这样:
323 |
324 | ```
325 | cy# choose(PARSEPedometerInfo)
326 | [#"PARSPedometerInfo<0x12f22cd60>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000",#"PARSPedometerInfo<0x12f406c90>: \n integration=1541 \n iPhone=1541 \n watch=0 \n heartRat=0\n at:2017-12-26 16:00:00 +0000"]
327 | 也即找到两个PARSPedometerInfo类的对象,随便用其中一个即可
328 | [#0x12f22cd60 setIntegratedSteps:66666]
329 |
330 | setIntegratedSteps是减号开头的函数,如果是+号开头的函数用法则[className funcName:6666],如下面的函数是+号开头的函数,可以直接调用这个类中的函数,而不用创建这个类的实例:
331 | cy# [PARSCryptDataUtils encryptWithServerTimestamp:"18013790233"]
332 |
333 | 带减号的函数,要实例化之后才可以调用
334 | 带加号的函数,可以直接调用
335 | ```
336 |
337 | 这一部分主要参考[http://3xp10it.cc/%E4%BA%8C%E8%BF%9B%E5%88%B6/2017/12/25/ida%E9%80%9A%E8%BF%87usb%E8%B0%83%E8%AF%95ios%E4%B8%8B%E7%9A%84app/](http://3xp10it.cc/%E4%BA%8C%E8%BF%9B%E5%88%B6/2017/12/25/ida%E9%80%9A%E8%BF%87usb%E8%B0%83%E8%AF%95ios%E4%B8%8B%E7%9A%84app/)文章
338 |
339 | ### **0x06 调试流程**
340 |
341 | 如果要使用lldb调试越狱设备上的进程,需要先将connect的端口映射到本地,以1234端口为例:
342 |
343 | ```
344 | iproxy 1234 1234
345 | 然后打开lldb,输入以下命令:
346 | process connect connect://localhost:1234
347 | 连接越狱设备,输入:
348 | debugserver *:1234 -a
349 | 只要越狱设备上的debugserver(重签名过的)正常运行,就可以通过lldb进行远程调试
350 | ```
351 |
352 | 越狱设备第一次连接xcode的时候会在/Developer/usr/bin目录下生成一个debugserver,这个debugserver在ios里面运行会失败需要使用ldid签名,需要两个东西:
353 |
354 | - ldid [http://7xibfi.com1.z0.glb.clouddn.com/uploads/default/668/c134605bb19a433f.xml](http://7xibfi.com1.z0.glb.clouddn.com/uploads/default/668/c134605bb19a433f.xml)
355 | - xml(文件) [http://joedj.net/ldid](http://joedj.net/ldid)
356 |
357 | xml文件保存为`ent.xml`,然后签名:
358 |
359 | ```
360 | ldid -Sent.xml debugserver
361 | ```
362 |
363 | 然后回传到ios上面即可,使用wget或者scp(scp失败,这里是用的是wget)
364 |
365 | ```
366 | debugserver 0.0.0.0:1234 "SpringBoard"
367 | (lldb)process connect connect://:
368 | ```
369 |
370 |
371 | ### **Object-C 的一些基础知识**
372 |
373 | 在Objective-C中的“方法调用”其实应该叫做消息传递。以objc_msgSend函数为例子,
374 |
375 | ```
376 | [person sayHello]
377 | 可以解释为调用person对象的sayHello方法,但是如果从Object-C的Runtime角度来说,这个代码世纪是在发送一个消息,这个代码,编译器时机会将它转换成这样一个函数调用:
378 | objc_msgSend(person,@selector(sayHello))
379 | ```
380 |
381 | 第一个参数是要发送消息的实例,也就是person对象。objc_msgSend会先查询它的methodList方法列表,使用第二个参数sayHello
382 |
383 | ```
384 | 苹果文档这样写的
385 | id objc_msgSend(id self, SEL _cmd, ...)
386 | ```
387 |
388 | 将一个消息发送给一个对象,并且返回一个值。其中,self是消息的接受者,_cmd是selector,... 是可变参数列表。
389 |
390 | 在现代操作系统中,一个线程会被分配一个stack,当一个函数被调用,一个stack frame(帧栈)就会被压到stack里,里面包含这个函数设计的参数,局部变量,返回地址等相关信息。当函数返回这个帧栈之后,这个帧栈就会被销毁。
391 |
392 | ```
393 | _text:0001D76A MOV R0, #(selRefHTTPMethod - 0x1C776) ; selRef_HTTPMethod
394 | _text:0001D772 ADD R0, PC ; selRefHTTPMethod
395 | __text:0001D774 LDR R1, [R0] ; "HTTPMethod"
396 | __text:0001D776 MOV R0, R10
397 | _text:0001D778 STR R1, [SP,#0xAC+varA0]
398 | _text:0001D77A BLX _objcmsgSend
399 | __text:0001D77E MOV R7, R7
400 | _text:0001D780 BLX _objcretainAutoreleasedReturnValue
401 | __text:0001D784 MOV R4, R0
402 | _text:0001D786 MOV R0, #(selRefsetRequestMethod_ - 0x1C794) ; selRef_setRequestMethod_
403 | __text:0001D78E MOV R2, R4
404 | ```
405 |
406 | 0001D77A处的selector为HTTPMethod,在functions windows里可以搜到这个函数,函数在执行前把调用的对象存储在R0中。
407 |
408 | ```
409 | __text:0001D774 LDR R1, [R0] ; "HTTPMethod" //把方法名放到R1中
410 | __text:0001D776 MOV R0, R10 //R0赋值为R10所在的值,此处R10位HTTPMethod这个方法归属的类的指针之类。
411 | 上面两条指令确定了调用的函数,调用完方法,如果一个方法有返回值,会更新在R0,大于一个返回值,就会通过栈来返回值。(意思是如果函数不止一个返回值,就会通过栈来返回)
412 | ```
413 |
414 | ```
415 | NSString *string1 = @"test 1";
416 | NSString *string2 = @"test 2";
417 | (lldb) po string1
418 | test 1
419 | (lldb) p string1
420 | (NSString *) $2 = 0x0000000100003af0 @"test 1"
421 | (lldb) p string2
422 | (NSString *) $3 = 0x0000000100003b10 @"test 2"
423 | ```
424 |
425 | 
426 |
427 | 
428 |
--------------------------------------------------------------------------------
/apiServer.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | # import sys
3 | # sys.path.append("/Users/ethan/whatsapp-py")
4 |
5 | import tornado.web
6 | import tornado.ioloop
7 | import tornado.httpserver
8 | import tornado.options
9 | from tornado.options import options, define
10 | from tornado.web import url, RequestHandler
11 | import re
12 | from tornado import gen
13 | from tornado.concurrent import run_on_executor
14 | from concurrent.futures import ThreadPoolExecutor
15 | import time
16 | import datetime
17 | from login.cli import CliStack
18 |
19 | from login.register import *
20 | from config.v1.config import Config
21 | import os
22 |
23 | define("port", default=8001, type=int, help="run server on the given port.")
24 |
25 | class mobileField():
26 | REGULAR = "^([1-9]+\\d*)"
27 |
28 | def __init__(self, error_dict=None, required=True):
29 | self.error_dict = {}
30 | if error_dict:
31 | self.error_dict.update(error_dict)
32 | self.required = required
33 | self.error = None
34 | self.is_valid = False
35 | self.value = None
36 |
37 | def validate(self, name, input_value):
38 | if not self.required:
39 | # 如果输入的值可以为空,
40 | self.is_valid = True
41 | self.value = input_value
42 |
43 | else:
44 | # 如果要求输入的值不能为空
45 | if not input_value.strip():
46 | # 如果我输入的值是空值,然后我就去取error_dict里面required对应的值
47 | if self.error_dict.get("required", None):
48 | self.error = self.error_dict["required"]
49 | else:
50 | # 否则,默认把required赋值到self.error
51 | self.error = "%s is required" % name
52 | else:
53 | # 如果我输入的值不是空值,那么我就要和正则表达式进行比较
54 | ret = re.match(mobileField.REGULAR, input_value)
55 | if ret:
56 | self.is_valid = True
57 | # self.value = ret.group()
58 | self.value = input_value
59 | else:
60 | if self.error_dict.get("valid", None):
61 | self.error = self.error_dict["valid"]
62 | else:
63 | self.error = "%s is invalid" % name
64 |
65 | class BaseForm(object):
66 | def check_valid(self, handle):
67 | flag = True
68 | error_message_dict = {}
69 | success_value_dict = {}
70 |
71 | for key, regular in self.__dict__.items():
72 | input_value = handle.get_argument(key, default="")
73 | regular.validate(key, input_value)
74 | if regular.is_valid:
75 | success_value_dict[key] = regular.value
76 | else:
77 | error_message_dict[key] = regular.error
78 | flag = False
79 | return flag, success_value_dict, error_message_dict
80 |
81 | class MainForm(BaseForm):
82 | def __init__(self):
83 | self.mobile = mobileField(required=False, error_dict={"required": "手机号", "valid": "格式错误"})
84 | self.countryCode = mobileField(required=False, error_dict={"required": "国家码", "valid": "格式错误"})
85 | self.ipAgent = mobileField(required=False, error_dict={"valid": "格式错误"})
86 | self.ipAgentName = mobileField(required=False, error_dict={"valid": "格式错误"})
87 | self.ipAgentPassword = mobileField(required=False, error_dict={"valid": "格式错误"})
88 |
89 | self.SMSCode = mobileField(required=False, error_dict={"valid": "格式错误"})
90 |
91 | self.toMobile = mobileField(required=False, error_dict={"valid": "格式错误"})
92 | self.toText = mobileField(required=False, error_dict={"valid": "格式错误"})
93 |
94 | self.groupUrl = mobileField(required=False, error_dict={"valid": "格式错误"})
95 |
96 |
97 | class IndexHandler(RequestHandler):
98 | def get(self):
99 | self.render("index.html")
100 |
101 |
102 | #获取验证码
103 | class getCodeHandler(RequestHandler):
104 | def initialize(self, subject):
105 | self.subject = subject
106 |
107 | def get(self):
108 | obj = MainForm()
109 | is_valid, success_dict, error_dict = obj.check_valid(self)
110 | if is_valid:
111 | print("success", success_dict)
112 | resu = {'code': 200, 'message': '成功'}
113 |
114 | config = Config()
115 |
116 | config.phone = success_dict['mobile']
117 | config.cc = success_dict['countryCode']
118 | config.sim_mcc = search_mcc(config.cc)
119 | config.sim_mnc = "01"
120 | p_in = str(config.phone)[len(str(config.cc)):]
121 |
122 | profile = Profile(config.phone)
123 | if profile.config is None:
124 | config.pushname = "steatt"
125 |
126 | if not profile.config or not profile.config.edge_routing_info:
127 | r = register(config)
128 | result = r.handleRequestCode(method="sms", config=r._config)
129 | self.write(result)
130 | else:
131 | self.write({'code': 1001, 'message':'本地已有登录数据可直接登录'})
132 |
133 | else:
134 | print("error", error_dict)
135 | self.render("index.html", error_dict=error_dict)
136 |
137 | class registerHandler(RequestHandler):
138 | executor = ThreadPoolExecutor(4)
139 |
140 | def initialize(self, subject):
141 | self.subject = subject
142 |
143 | @gen.coroutine
144 | def get(self):
145 | obj = MainForm()
146 | is_valid, success_dict, error_dict = obj.check_valid(self)
147 | if is_valid:
148 | print("success", success_dict)
149 |
150 | config = Config()
151 |
152 | config.phone = success_dict['mobile']
153 | config.cc = success_dict['countryCode']
154 | config.sim_mcc = search_mcc(config.cc)
155 | config.sim_mnc = "01"
156 | smsCode = success_dict['SMSCode']
157 |
158 | profile = Profile(config.phone)
159 | if profile.config is None:
160 | config.pushname = "stedgg"
161 |
162 | if not profile.config or not profile.config.edge_routing_info:
163 | r = register(profile.config)
164 | result = r.handleRegister(code=smsCode, config=r._config)
165 | self.write(result)
166 | #注册成功后登录
167 | try:
168 | start = time.time()
169 | # 并行执行
170 | result1, result2 = yield gen.with_timeout(datetime.timedelta(seconds=300),
171 | [self.login(config.phone), self.doing()],
172 | quiet_exceptions=tornado.gen.TimeoutError)
173 | print(result1, result2)
174 | print(time.time() - start)
175 | except gen.TimeoutError:
176 | print("Timeout")
177 |
178 | else:
179 | self.write({'code': 1001, 'message': '本地已有登录数据可直接登录'})
180 | #注册成功后登录
181 | try:
182 | start = time.time()
183 | # 并行执行
184 | result1, result2 = yield gen.with_timeout(datetime.timedelta(seconds=300),
185 | [self.login(config.phone), self.doing()],
186 | quiet_exceptions=tornado.gen.TimeoutError)
187 | print(result1, result2)
188 | print(time.time() - start)
189 | except gen.TimeoutError:
190 | print("Timeout")
191 |
192 | else:
193 | print("error", error_dict)
194 | self.render("index.html", error_dict=error_dict)
195 |
196 | @run_on_executor
197 | def login(self, mobile):
198 | profile = Profile(mobile)
199 | stack = CliStack(profile)
200 | stack.connect()
201 | return {'code': 0, 'message': '完成注册登录'}
202 |
203 | @run_on_executor
204 | def doing(self):
205 | return "2"
206 |
207 | class messageHandler(RequestHandler):
208 | executor = ThreadPoolExecutor(4)
209 |
210 | @gen.coroutine
211 | def get(self):
212 | obj = MainForm()
213 | is_valid, success_dict, error_dict = obj.check_valid(self)
214 | if is_valid:
215 | profile = Profile(success_dict['mobile'])
216 | if profile.config and profile.config.edge_routing_info:
217 | try:
218 | start = time.time()
219 | result = yield gen.with_timeout(datetime.timedelta(seconds=300),
220 | [self.sendMessage(profile.config.phone, success_dict['toMobile'], success_dict['toText'])],
221 | quiet_exceptions=tornado.gen.TimeoutError)
222 | self.write(result[0])
223 | print(time.time() - start)
224 | except gen.TimeoutError:
225 | print("Timeout")
226 |
227 | else:
228 | self.write({'code': 1002, 'message': '登录数据已失效请重新登录'})
229 |
230 | else:
231 | print("error", error_dict)
232 | self.render("index.html", error_dict=error_dict)
233 |
234 | @run_on_executor
235 | def sendMessage(self, mobile, to, text):
236 | profile = Profile(mobile)
237 | stack = CliStack(profile)
238 | stack.sendMessages(to, text)
239 | return {'code': 0, 'message': '发送完成'}
240 |
241 | class GroupJoin(RequestHandler):
242 | executor = ThreadPoolExecutor(4)
243 |
244 | @gen.coroutine
245 | def get(self):
246 | obj = MainForm()
247 | is_valid, success_dict, error_dict = obj.check_valid(self)
248 | if is_valid:
249 | groupUrl = success_dict['groupUrl']
250 | profile = Profile('6281995412652')
251 |
252 | if profile.config and profile.config.edge_routing_info:
253 | try:
254 | start = time.time()
255 | result = yield gen.with_timeout(datetime.timedelta(seconds=300),
256 | [self.groupJoinUrl(profile.config.phone, groupUrl)],
257 | quiet_exceptions=tornado.gen.TimeoutError)
258 | self.write(result[0])
259 | print(time.time() - start)
260 | except gen.TimeoutError:
261 | print("Timeout")
262 |
263 | else:
264 | self.write({'code': 1002, 'message': '登录数据已失效请重新登录'})
265 |
266 | else:
267 | print("error", error_dict)
268 | self.render("index.html", error_dict=error_dict)
269 |
270 | @run_on_executor
271 | def groupJoinUrl(self, mobile, groupUrl):
272 | profile = Profile(mobile)
273 | stack = CliStack(profile)
274 | stack.groupInfoList(groupUrl)
275 |
276 | return {'code': 0, 'message': '完成注册登录'}
277 |
278 | import pandas as pd
279 | def search_mcc(countryCode='86'):
280 | df = pd.read_csv(os.path.abspath('.') + '/countries.tsv', header=None, encoding='utf-8', delimiter='\t', skiprows=1)
281 | line = df[df[2] == int(countryCode)]
282 | countryCode = line[3]
283 | mcc = "460"
284 | for i, v in countryCode.items():
285 | mcc = v
286 | break
287 | print ("mcc = " + mcc)
288 | return mcc.split(",")[0]
289 |
290 | class Test(RequestHandler):
291 |
292 | @gen.coroutine
293 | def get(self):
294 | print(self.get_argument("tag"), self.get_argument("time"))
295 | self.finish({"tag":self.get_argument("tag"),'time': self.get_argument("time")})
296 |
297 |
298 | # /api/WhatsAppProtocol/SendSMSCode
299 | if __name__ == "__main__":
300 | tornado.options.parse_command_line()
301 | app = tornado.web.Application([
302 | (r"/", IndexHandler),
303 | (r"/api/WhatsAppProtocol/SendSMSCode", getCodeHandler, {"subject": "success"}),
304 | (r"/api/WhatsAppProtocol/Register", registerHandler, {"subject": "success"}),
305 | (r"/api/WhatsAppProtocol/SendText", messageHandler),
306 | (r"/api/WhatsAppProtocol/GroupJoin", GroupJoin),
307 | (r"/api/test", Test),
308 |
309 | ],
310 | debug=False)
311 |
312 | http_server = tornado.httpserver.HTTPServer(app)
313 | http_server.listen(options.port)
314 | tornado.ioloop.IOLoop.current().start()
315 |
--------------------------------------------------------------------------------
/config/__init__.py:
--------------------------------------------------------------------------------
1 | import logging
2 |
3 | logger = logging.getLogger(__name__)
4 | ch = logging.StreamHandler()
5 | # ch.setLevel(logging.DEBUG)
6 |
7 | formatter = logging.Formatter('%(levelname).1s %(asctime)s %(name)s - %(message)s')
8 |
9 | ch.setFormatter(formatter)
10 |
11 | logger.addHandler(ch)
--------------------------------------------------------------------------------
/config/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/config/__pycache__/enumExec.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/__pycache__/enumExec.cpython-38.pyc
--------------------------------------------------------------------------------
/config/__pycache__/logger.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/__pycache__/logger.cpython-38.pyc
--------------------------------------------------------------------------------
/config/__pycache__/manager.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/__pycache__/manager.cpython-38.pyc
--------------------------------------------------------------------------------
/config/base/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/base/__init__.py
--------------------------------------------------------------------------------
/config/base/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/base/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/config/base/__pycache__/config.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/base/__pycache__/config.cpython-38.pyc
--------------------------------------------------------------------------------
/config/base/__pycache__/serialize.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/base/__pycache__/serialize.cpython-38.pyc
--------------------------------------------------------------------------------
/config/base/__pycache__/transform.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/base/__pycache__/transform.cpython-38.pyc
--------------------------------------------------------------------------------
/config/base/config.py:
--------------------------------------------------------------------------------
1 |
2 | class Config(object):
3 | def __init__(self, version):
4 | self._version = version
5 |
6 | def __contains__(self, item):
7 | return self[item] is not None
8 |
9 | def __getitem__(self, item):
10 | return getattr(self, "_%s" % item)
11 |
12 | def __setitem__(self, key, value):
13 | setattr(self, key, value)
14 |
15 | def keys(self):
16 | return [var[1:] for var in vars(self)]
17 |
18 | @property
19 | def version(self):
20 | return self._version
21 |
--------------------------------------------------------------------------------
/config/base/serialize.py:
--------------------------------------------------------------------------------
1 | class ConfigSerialize(object):
2 |
3 | def __init__(self, transforms):
4 | self._transforms = transforms
5 |
6 | def serialize(self, config):
7 | """
8 | :param config:
9 | :type config: Config
10 | :return:
11 | :rtype: bytes
12 | """
13 | for transform in self._transforms:
14 | config = transform.transform(config)
15 | return config
16 |
17 | def deserialize(self, data):
18 | """
19 | :type cls: type
20 | :param data:
21 | :type data: bytes
22 | :return:
23 | :rtype: Config
24 | """
25 | for transform in self._transforms[::-1]:
26 | data = transform.reverse(data)
27 | return data
28 |
--------------------------------------------------------------------------------
/config/base/transform.py:
--------------------------------------------------------------------------------
1 | class ConfigTransform(object):
2 | def transform(self, config):
3 | """
4 | :param config:
5 | :type config: Config
6 | :return: dict
7 | :rtype:
8 | """
9 |
10 | def reverse(self, data):
11 | """
12 | :param data:
13 | :type data:
14 | :return:
15 | :rtype: Config
16 | """
17 |
--------------------------------------------------------------------------------
/config/config.py:
--------------------------------------------------------------------------------
1 | from config.base import config
2 | import logging
3 |
4 | logger = logging.getLogger(__name__)
5 | class Config(object):
6 | def __init__(self, version):
7 | self._version = version
8 |
9 | def __contains__(self, item):
10 | return self[item] is not None
11 |
12 | def __getitem__(self, item):
13 | return getattr(self, "_%s" % item)
14 |
15 | def __setitem__(self, key, value):
16 | setattr(self, key, value)
17 |
18 | def keys(self):
19 | return [var[1:] for var in vars(self)]
20 |
21 | @property
22 | def version(self):
23 | return self._version
24 |
25 |
26 | class Config(config.Config):
27 | def __init__(
28 | self,
29 | phone=None,
30 | cc=None,
31 | password=None,
32 | pushname=None,
33 | id=None,
34 | mcc=None,
35 | mnc=None,
36 | client_static_keypair=None,
37 | server_static_public=None,
38 | expid=None,
39 | fdid=None,
40 | edge_routing_info=None,
41 | chat_dns_domain=None
42 | ):
43 | super(Config, self).__init__(1)
44 |
45 | self._phone = str(phone) if phone is not None else None # type: str
46 | self._cc = cc # type: int
47 | self._password = password # type: str
48 | self._pushname = pushname # type: str
49 | self._id = id
50 | self._client_static_keypair = client_static_keypair
51 | self._server_static_public = server_static_public
52 | self._expid = expid
53 | self._fdid = fdid
54 | self._mcc = mcc
55 | self._mnc = mnc
56 | self._edge_routing_info = edge_routing_info
57 | self._chat_dns_domain = chat_dns_domain
58 |
59 | if self._password is not None:
60 | logger.warn("Setting a password in Config is deprecated and not used anymore. "
61 | "client_static_keypair is used instead")
62 |
63 | def __str__(self):
64 | from config.v1.serialize import ConfigSerialize
65 | from config.transforms.dict_json import DictJsonTransform
66 | return DictJsonTransform().transform(ConfigSerialize(self.__class__).serialize(self))
67 |
68 | @property
69 | def phone(self):
70 | return self._phone
71 |
72 | @phone.setter
73 | def phone(self, value):
74 | self._phone = str(value) if value is not None else None
75 |
76 | @property
77 | def cc(self):
78 | return self._cc
79 |
80 | @cc.setter
81 | def cc(self, value):
82 | self._cc = value
83 |
84 | @property
85 | def password(self):
86 | return self._password
87 |
88 | @password.setter
89 | def password(self, value):
90 | self._password = value
91 | if value is not None:
92 | logger.warn("Setting a password in Config is deprecated and not used anymore. "
93 | "client_static_keypair is used instead")
94 |
95 | @property
96 | def pushname(self):
97 | return self._pushname
98 |
99 | @pushname.setter
100 | def pushname(self, value):
101 | self._pushname = value
102 |
103 | @property
104 | def mcc(self):
105 | return self._mcc
106 |
107 | @mcc.setter
108 | def mcc(self, value):
109 | self._mcc = value
110 |
111 | @property
112 | def mnc(self):
113 | return self._mnc
114 |
115 | @mnc.setter
116 | def mnc(self, value):
117 | self._mnc = value
118 |
119 | @property
120 | def id(self):
121 | return self._id
122 |
123 | @id.setter
124 | def id(self, value):
125 | self._id = value
126 |
127 | @property
128 | def client_static_keypair(self):
129 | return self._client_static_keypair
130 |
131 | @client_static_keypair.setter
132 | def client_static_keypair(self, value):
133 | self._client_static_keypair = value
134 |
135 | @property
136 | def server_static_public(self):
137 | return self._server_static_public
138 |
139 | @server_static_public.setter
140 | def server_static_public(self, value):
141 | self._server_static_public = value
142 |
143 | @property
144 | def expid(self):
145 | return self._expid
146 |
147 | @expid.setter
148 | def expid(self, value):
149 | self._expid = value
150 |
151 | @property
152 | def fdid(self):
153 | return self._fdid.upper()
154 |
155 | @fdid.setter
156 | def fdid(self, value):
157 | self._fdid = value
158 |
159 | @property
160 | def edge_routing_info(self):
161 | return self._edge_routing_info
162 |
163 | @edge_routing_info.setter
164 | def edge_routing_info(self, value):
165 | self._edge_routing_info = value
166 |
167 | @property
168 | def chat_dns_domain(self):
169 | return self._chat_dns_domain
170 |
171 | @chat_dns_domain.setter
172 | def chat_dns_domain(self, value):
173 | self._chat_dns_domain = value
174 |
--------------------------------------------------------------------------------
/config/enumExec.py:
--------------------------------------------------------------------------------
1 |
2 | from enum import Enum
3 | # 需要执行的功能
4 | class ExecTypes(Enum):
5 | login = 0b1
6 | group_join = 0b1 << 1
7 | group_info = 0b1 << 2
8 | group_kick = 0b1 << 3
9 |
10 | send_text = 0b1 << 4
11 | receive_text = 0b1 << 5
12 | others = 0b1 << 6
13 |
--------------------------------------------------------------------------------
/config/logger.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import os
4 | import re, time
5 | from logging import Logger
6 | from logging.handlers import TimedRotatingFileHandler
7 |
8 | from env.env_app import app_env
9 |
10 | currentPath = os.path.dirname(__file__) + '/logs/'
11 | # currentPath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '/logs'
12 |
13 | class FinalLogger():
14 |
15 | def init_logger(self, logger_name):
16 |
17 | if logger_name not in Logger.manager.loggerDict:
18 | logger = logging.getLogger(logger_name)
19 | logger.setLevel(logging.DEBUG)
20 | log_path = os.path.join(currentPath, logger_name)
21 | msg = {
22 | "app": os.getenv('APP'),
23 | "@timestamp": "%(asctime)s",
24 | "date": "%(asctime)s",
25 | "logLevel": "%(levelname)s",
26 | "file": "%(pathname)s:%(lineno)d",
27 | "msg": "%(message)s"
28 | }
29 | format_str = json.dumps(msg)
30 | formatter = logging.Formatter(format_str, '%Y-%m-%dT%H:%M:%SZ')
31 |
32 | fileHandler = TimedRotatingFileHandler(filename=log_path, when="midnight", backupCount=7)
33 | fileHandler.suffix = "%Y-%m-%d.log"
34 | fileHandler.extMatch = re.compile(r"^\d{4}-\d{2}-\d{2}.log$")
35 | fileHandler.setFormatter(formatter)
36 | logger.addHandler(fileHandler)
37 |
38 | logger = logging.getLogger(logger_name)
39 | return logger
40 |
41 | class FinalLogger2():
42 |
43 | def init_logger(self, logger_name1=None):
44 |
45 | num = app_env()
46 | if num == 1:
47 | logger_name = "1546944914"
48 | elif num == 2:
49 | logger_name = "4615761317"
50 | elif num == 3:
51 | logger_name = "6412722277"
52 | else:
53 | logger_name = "8728356502"
54 |
55 | logger_name = time.strftime('%Y-%m-%d', time.localtime()) + "--" + logger_name + "-" + "Info" + '.log'
56 |
57 | if logger_name not in Logger.manager.loggerDict:
58 | logger = logging.getLogger(logger_name)
59 | logger.setLevel(logging.DEBUG)
60 |
61 | # allfilename = time.strftime('%Y-%m-%d', time.localtime()) + "--" + logger_name + "-" + "Info" + '.log'
62 | all_log_file = os.path.join(currentPath, logger_name)
63 | msg = {
64 | "app": os.getenv('APP'),
65 | "@timestamp": "%(asctime)s",
66 | "date": "%(asctime)s",
67 | "logLevel": "%(levelname)s",
68 | "file": "%(pathname)s:%(lineno)d",
69 | "msg": "%(message)s",
70 | }
71 | format_str = json.dumps(msg)
72 |
73 | formatter = logging.Formatter(format_str, '%Y-%m-%dT%H:%M:%SZ')
74 | fileHandler = logging.FileHandler(all_log_file)
75 | fileHandler.setFormatter(formatter)
76 | logger.addHandler(fileHandler)
77 |
78 | logger = logging.getLogger(logger_name)
79 | return logger
80 |
81 | class FinalLogger1():
82 |
83 | def init_logger(self, logger_name):
84 |
85 | num = app_env()
86 | if num == 1:
87 | logger_name = "1546944914"
88 | elif num == 2:
89 | logger_name = "4615761317"
90 | elif num == 3:
91 | logger_name = "6412722277"
92 | else:
93 | logger_name = "8728356502"
94 |
95 | if logger_name not in Logger.manager.loggerDict:
96 |
97 | logger = logging.getLogger(logger_name)
98 | logger.setLevel(logging.DEBUG)
99 |
100 | allfilename = time.strftime('%Y-%m-%d', time.localtime()) + "--" + logger_name + "-" + "Info" + '.log'
101 | all_log_file = os.path.join(currentPath, allfilename)
102 |
103 | msg = {
104 | "app": os.getenv('APP'),
105 | "@timestamp": "%(asctime)s",
106 | "date": "%(asctime)s",
107 | "logLevel": "%(levelname)s",
108 | "file": "%(pathname)s:%(lineno)d",
109 | "msg": "%(message)s",
110 | }
111 | format_str = json.dumps(msg)
112 | formatter = logging.Formatter(format_str, '%Y-%m-%dT%H:%M:%SZ')
113 |
114 | all_log_handler = TimedRotatingFileHandler(all_log_file, when='midnight', backupCount=7)
115 | all_log_handler.setFormatter(formatter)
116 | all_log_handler.setLevel(logging.INFO)
117 | logger.addHandler(all_log_handler)
118 |
119 | logger = logging.getLogger(logger_name)
120 | return logger
121 |
--------------------------------------------------------------------------------
/config/manager.py:
--------------------------------------------------------------------------------
1 | from config.v1.config import Config
2 | from config.transforms.dict_keyval import DictKeyValTransform
3 | from config.transforms.dict_json import DictJsonTransform
4 | from config.v1.serialize import ConfigSerialize
5 | from common.tools import StorageTools
6 | import logging
7 | import os
8 | from mysql.execSql import *
9 | import base64
10 |
11 | logger = logging.getLogger(__name__)
12 |
13 |
14 | class ConfigManager(object):
15 | NAME_FILE_CONFIG = "config"
16 |
17 | TYPE_KEYVAL = 1
18 | TYPE_JSON = 2
19 |
20 | TYPE_NAMES = {
21 | TYPE_KEYVAL: "keyval",
22 | TYPE_JSON: "json"
23 | }
24 |
25 | MAP_EXT = {
26 | "yo": TYPE_KEYVAL,
27 | "json": TYPE_JSON,
28 | }
29 |
30 | TYPES = {
31 | TYPE_KEYVAL: DictKeyValTransform,
32 | TYPE_JSON: DictJsonTransform
33 | }
34 |
35 | def load(self, path_or_profile_name, profile_only=False, fromNet=False):
36 | # type: (str, bool) -> Config
37 | """
38 | Will first try to interpret path_or_profile_name as direct path to a config file and load from there. If
39 | this fails will interpret it as profile name and load from profile dir.
40 | :param path_or_profile_name:
41 | :param profile_only
42 | :return Config instance, or None if no config could be found
43 | """
44 | logger.debug("load(path_or_profile_name=%s, profile_only=%s)" % (path_or_profile_name, profile_only))
45 |
46 | exhausted = []
47 | if not profile_only:
48 | if fromNet:
49 | mobile = path_or_profile_name
50 | sql = "SELECT config FROM phone_conf WHERE id={m_value}".format(m_value=mobile)
51 | execSql = ExecuteSQL(MYSQL_NAME)
52 | results = execSql.fetch_sql(sql)
53 | execSql.db_close()
54 |
55 | if len(results):
56 | r = results[0]
57 | config = str(r[0])
58 | data = str(base64.b64decode(config), "utf-8")
59 | datadict = self.TYPES[2]().reverse(data)
60 |
61 | config = self.load_data(datadict)
62 | else:
63 | print(mobile + ":mysql config为空!")
64 | config = self._load_path(path_or_profile_name)
65 |
66 | else:
67 | config = self._load_path(path_or_profile_name)
68 | else:
69 | config = None
70 | if config is not None:
71 | return config
72 | else:
73 | logger.debug("path_or_profile_name is not a path, using it as profile name")
74 | if not profile_only:
75 | exhausted.append(path_or_profile_name)
76 | profile_name = path_or_profile_name
77 | config_dir = StorageTools.getStorageForProfile(profile_name)
78 | logger.debug("Detecting config for profile=%s, dir=%s" % (profile_name, config_dir))
79 | for ftype in self.MAP_EXT:
80 | if len(ftype):
81 | fname = (self.NAME_FILE_CONFIG + "." + ftype)
82 | else:
83 | fname = self.NAME_FILE_CONFIG
84 |
85 | fpath = os.path.join(config_dir, fname)
86 | logger.debug("Trying %s" % fpath)
87 | if os.path.isfile(fpath):
88 | return self._load_path(fpath)
89 |
90 | exhausted.append(fpath)
91 |
92 | logger.error("Could not find a config for profile=%s, paths checked: %s" % (profile_name, ":".join(exhausted)))
93 |
94 | def _type_to_str(self, type):
95 | """
96 | :param type:
97 | :type type: int
98 | :return:
99 | :rtype:
100 | """
101 | for key, val in self.TYPE_NAMES.items():
102 | if key == type:
103 | return val
104 |
105 | def _load_path(self, path):
106 | """
107 | :param path:
108 | :type path:
109 | :return:
110 | :rtype:
111 | """
112 | logger.debug("_load_path(path=%s)" % path)
113 | if os.path.isfile(path):
114 | configtype = self.guess_type(path)
115 | logger.debug("Detected config type: %s" % self._type_to_str(configtype))
116 | if configtype in self.TYPES:
117 | logger.debug("Opening config for reading")
118 | with open(path, 'r') as f:
119 | data = f.read()
120 | datadict = self.TYPES[configtype]().reverse(data)
121 | return self.load_data(datadict)
122 | else:
123 | raise ValueError("Unsupported config type")
124 | else:
125 | logger.debug("_load_path couldn't find the path: %s" % path)
126 |
127 |
128 | def load_data(self, datadict):
129 | logger.debug("Loading config")
130 | return ConfigSerialize(Config).deserialize(datadict)
131 |
132 | def guess_type(self, config_path):
133 | dissected = os.path.splitext(config_path)
134 | if len(dissected) > 1:
135 | ext = dissected[1][1:].lower()
136 | config_type = self.MAP_EXT[ext] if ext in self.MAP_EXT else None
137 | else:
138 | config_type = None
139 |
140 | if config_type is not None:
141 | return config_type
142 | else:
143 | logger.debug("Trying auto detect config type by parsing")
144 | with open(config_path, 'r') as f:
145 | data = f.read()
146 | for config_type, transform in self.TYPES.items():
147 | config_type_str = self.TYPE_NAMES[config_type]
148 | try:
149 | logger.debug("Trying to parse as %s" % config_type_str)
150 | if transform().reverse(data):
151 | logger.debug("Successfully detected %s as config type for %s" % (config_type_str, config_path))
152 | return config_type
153 | except Exception as ex:
154 | logger.debug("%s was not parseable as %s, reason: %s" % (config_path, config_type_str, ex))
155 |
156 | def get_str_transform(self, serialize_type):
157 | if serialize_type in self.TYPES:
158 | return self.TYPES[serialize_type]()
159 |
160 | def config_to_str(self, config, serialize_type=TYPE_JSON):
161 | transform = self.get_str_transform(serialize_type)
162 | if transform is not None:
163 | return transform.transform(ConfigSerialize(config.__class__).serialize(config))
164 |
165 | raise ValueError("unrecognized serialize_type=%d" % serialize_type)
166 |
167 | def save(self, profile_name, config, serialize_type=TYPE_JSON, dest=None):
168 | outputdata = self.config_to_str(config, serialize_type)
169 |
170 | if dest is None:
171 | StorageTools.writeProfileConfig(profile_name, outputdata)
172 | else:
173 | with open(dest, 'wb') as outputfile:
174 | outputfile.write(outputdata)
175 |
176 | def uploadData(self, phone):
177 | config = self.load(phone)
178 | outputdata = self.config_to_str(config)
179 |
180 | configData = str(base64.b64encode(outputdata.encode("utf-8")), "utf-8")
181 | import time
182 | localTime = time.localtime(time.time())
183 | strTime = time.strftime("%Y-%m-%d %H:%M:%S", localTime)
184 | execSql = ExecuteSQL()
185 | actionValue = (phone, strTime, configData)
186 | sqlInsert = "INSERT INTO wa_users_data (phone, time, config) VALUES {values} ON DUPLICATE KEY UPDATE config='{c_value}'".format(
187 | values=actionValue, c_value=configData)
188 | execSql.execute_sql(sqlInsert)
189 | execSql.db_close()
190 |
191 | def downloadData(self, phone):
192 | sql = "SELECT config FROM wa_users_data WHERE phone={m_value}".format(m_value=phone)
193 | execSql = ExecuteSQL()
194 | results = execSql.fetch_sql(sql)
195 | execSql.db_close()
196 |
197 | if len(results):
198 | r = results[0]
199 | config = str(r[0])
200 | data = str(base64.b64decode(config), "utf-8")
201 | datadict = self.TYPES[2]().reverse(data)
202 |
203 | config = self.load_data(datadict)
204 | self.save(phone, config)
205 | return config
206 | else:
207 | return None
208 | # {
209 | # "__version__": 1,
210 | # "cc": "62",
211 | # "client_static_keypair": "qFrn5lw9208ixtrqeH4Ll91UBeuyk/1wJjc8nFHR+ExDqy/1UsUUUAaEO0c4B86Vr68uFjbTO/enrJyWPIl3Bw==",
212 | # "country": "id",
213 | # "edge_routing_info": "CAIIDQ==",
214 | # "expid": "nYEJmaIQSmyg7A/hnDlvmw==",
215 | # "fdid": "a5bc7fbc-4f55-4c1e-b95b-c7b8ffee9893",
216 | # "id": "b2O9CbIxNK9cSEXU0js/gVXvnlc=",
217 | # "login": "6283182456634",
218 | # "phone": "6283182456634",
219 | # "pushname": "Fterri Mremington",
220 | # "server_static_public": "xDn6MqBPn3O6ptDhPQt/tqcXrv2dK7aR//NQLFIVal0=",
221 | # "sim_mcc": "510",
222 | # "sim_mnc": "001"
223 | # }
224 | def saveLoginData(self, phone, datadict, loginData):
225 | config = self.load_data(datadict)
226 | outputdata = self.config_to_str(config)
227 |
228 | configData = str(base64.b64encode(outputdata.encode("utf-8")), "utf-8")
229 | import time
230 | localTime = time.localtime(time.time())
231 | strTime = time.strftime("%Y-%m-%d %H:%M:%S", localTime)
232 | import json
233 | jsonData=json.dumps(loginData)
234 | actionValue = (phone, strTime, configData, datadict['env_name'], jsonData)
235 |
236 |
237 | execSql = ExecuteSQL()
238 | sqlInsert = "INSERT INTO wa_users_data (phone, time, config, env, data) VALUES {values} ON DUPLICATE KEY UPDATE config='{c_value}', time='{time}', data='{data}'".format(
239 | values=actionValue, c_value=configData, time=strTime, data=jsonData)
240 | execSql.execute_sql(sqlInsert)
241 |
242 | deviceInfo = SingleonConfig()
243 |
244 | values = (deviceInfo.deviceId, deviceInfo.versionId, loginData['phone'], loginData['nickName'], loginData['cc'], loginData['country'], strTime, strTime, "")
245 | sql = "INSERT INTO wa_users (device_id, version_id, phone, name, cc, country, rtime, utime, exec) VALUES {values}".format(values=values)
246 | execSql.execute_sql(sql)
247 | execSql.db_close()
248 |
249 | self.save(phone, config)
250 | return config
251 |
--------------------------------------------------------------------------------
/config/transforms/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__init__.py
--------------------------------------------------------------------------------
/config/transforms/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/config_dict.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/config_dict.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/dict_json.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/dict_json.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/dict_keyval.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/dict_keyval.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/filter.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/filter.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/map.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/map.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/meta.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/meta.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/__pycache__/props.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/transforms/__pycache__/props.cpython-38.pyc
--------------------------------------------------------------------------------
/config/transforms/config_dict.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 |
3 |
4 | class ConfigDictTransform(ConfigTransform):
5 | def __init__(self, cls):
6 | self._cls = cls
7 |
8 | def transform(self, config):
9 | """
10 | :param config:
11 | :type config: dict
12 | :return:
13 | :rtype: Config
14 | """
15 | out = {}
16 | for prop in vars(config):
17 | out[prop] = getattr(config, prop)
18 | return out
19 |
20 | def reverse(self, data):
21 | """
22 | :param data:
23 | :type data: config.config.Config
24 | :return:
25 | :rtype: dict
26 | """
27 | return self._cls(**data)
28 |
--------------------------------------------------------------------------------
/config/transforms/dict_json.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 | import json
3 |
4 |
5 | class DictJsonTransform(ConfigTransform):
6 | def transform(self, data):
7 | return json.dumps(data, sort_keys=True, indent=4, separators=(',', ': '))
8 |
9 | def reverse(self, data):
10 | return json.loads(data)
11 |
12 |
--------------------------------------------------------------------------------
/config/transforms/dict_keyval.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 |
3 |
4 | class DictKeyValTransform(ConfigTransform):
5 | def transform(self, data):
6 | """
7 | :param data:
8 | :type data: dict
9 | :return:
10 | :rtype:
11 | """
12 | out=[]
13 | keys = sorted(data.keys())
14 | for k in keys:
15 | out.append("%s=%s" % (k, data[k]))
16 | return "\n".join(out)
17 |
18 | def reverse(self, data):
19 | out = {}
20 | for l in data.split('\n'):
21 | line = l.strip()
22 | if len(line) and line[0] not in ('#',';'):
23 | prep = line.split('#', 1)[0].split(';', 1)[0].split('=', 1)
24 | varname = prep[0].strip()
25 | val = prep[1].strip()
26 | out[varname.replace('-', '_')] = val
27 | return out
28 |
--------------------------------------------------------------------------------
/config/transforms/filter.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 |
3 |
4 | class FilterTransform(ConfigTransform):
5 |
6 | def __init__(self, transform_filter=None, reverse_filter=None):
7 | """
8 | :param transform_filter:
9 | :type transform_filter: function | None
10 | :param reverse_filter:
11 | :type reverse_filter: function | None
12 | """
13 | self._transform_filter = transform_filter # type: function | None
14 | self._reverse_filter = reverse_filter # type: function | None
15 |
16 | def transform(self, data):
17 | if self._transform_filter is not None:
18 | out = {}
19 | for key, val in data.items():
20 | if self._transform_filter(key, val):
21 | out[key] = val
22 | return out
23 | return data
24 |
25 | def reverse(self, data):
26 | if self._reverse_filter is not None:
27 | out = {}
28 | for key, val in data.items():
29 | if self._reverse_filter(key, val):
30 | out[key] = val
31 | return out
32 | return data
33 |
--------------------------------------------------------------------------------
/config/transforms/map.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 |
3 |
4 | class MapTransform(ConfigTransform):
5 |
6 | def __init__(self, transform_map=None, reverse_map=None):
7 | """
8 | :param transform_map:
9 | :type transform_map: function | None
10 | :param reverse_map:
11 | :type reverse_map: function | None
12 | """
13 | self._transform_map = transform_map # type: function | None
14 | self._reverse_map = reverse_map # type: function | None
15 |
16 | def transform(self, data):
17 | if self._transform_map is not None:
18 | out = {}
19 | for key, val in data.items():
20 | key, val = self._transform_map(key, val)
21 | out[key] = val
22 | return out
23 | return data
24 |
25 | def reverse(self, data):
26 | if self._reverse_map is not None:
27 | out = {}
28 | for key, val in data.items():
29 | key, val = self._reverse_map(key, val)
30 | out[key] = val
31 | return out
32 | return data
33 |
--------------------------------------------------------------------------------
/config/transforms/meta.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 | from config.transforms.props import PropsTransform
3 |
4 |
5 | class MetaPropsTransform(ConfigTransform):
6 | META_FORMAT = "__%s__"
7 |
8 | def __init__(self, meta_props=None, meta_format=META_FORMAT):
9 | meta_props = meta_props or ()
10 | meta_format = meta_format
11 | transform_map = {}
12 | reverse_map = {}
13 | for prop in meta_props:
14 | formatted = meta_format % prop
15 | transform_map[prop] = lambda key, val, formatted=formatted: (formatted, val)
16 | reverse_map[formatted] = lambda key, val, prop=prop: (prop, val)
17 |
18 | self._props_transform = PropsTransform(transform_map=transform_map, reverse_map=reverse_map)
19 |
20 | def transform(self, data):
21 | return self._props_transform.transform(data)
22 |
23 | def reverse(self, data):
24 | return self._props_transform.reverse(data)
25 |
--------------------------------------------------------------------------------
/config/transforms/props.py:
--------------------------------------------------------------------------------
1 | from config.base.transform import ConfigTransform
2 | import types
3 |
4 |
5 | class PropsTransform(ConfigTransform):
6 | def __init__(self, transform_map=None, reverse_map=None):
7 | self._transform_map = transform_map or {}
8 | self._reverse_map = reverse_map or {}
9 |
10 | def transform(self, data):
11 | """
12 | :param data:
13 | :type data: dict
14 | :return:
15 | :rtype: dict
16 | """
17 | out = {}
18 | for key, val in data.items():
19 | if key in self._transform_map:
20 | target = self._transform_map[key]
21 | key, val = target(key, val) if type(target) == types.FunctionType else (key, target)
22 |
23 | out[key] = val
24 |
25 |
26 | return out
27 |
28 | def reverse(self, data):
29 | transformed_dict = {}
30 |
31 | for key, val in data.items():
32 | if key in self._reverse_map:
33 | target = self._reverse_map[key]
34 | key, val = target(key, val) if type(target) == types.FunctionType else (key, target)
35 |
36 | transformed_dict[key] = val
37 |
38 | return transformed_dict
39 |
--------------------------------------------------------------------------------
/config/transforms/serialize.py:
--------------------------------------------------------------------------------
1 | from config.transforms.props import PropsTransform
2 |
3 |
4 | class SerializeTransform(PropsTransform):
5 |
6 | def __init__(self, serialize_map):
7 | """
8 | {
9 | "keystore": serializer
10 | }
11 | :param serialize_map:
12 | :type serialize_map:
13 | """
14 | transform_map = {}
15 | reverse_map = {}
16 | for key, val in serialize_map:
17 | transform_map[key] = lambda key, val: key, serialize_map[key].serialize(val)
18 | reverse_map[key] = lambda key, val: key, serialize_map[key].deserialize(val)
19 |
20 | super(SerializeTransform, self).__init__(transform_map=transform_map, reverse_map=reverse_map)
21 |
22 |
--------------------------------------------------------------------------------
/config/v1/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/v1/__init__.py
--------------------------------------------------------------------------------
/config/v1/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/v1/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/config/v1/__pycache__/config.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/v1/__pycache__/config.cpython-38.pyc
--------------------------------------------------------------------------------
/config/v1/__pycache__/serialize.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/config/v1/__pycache__/serialize.cpython-38.pyc
--------------------------------------------------------------------------------
/config/v1/config.py:
--------------------------------------------------------------------------------
1 | from config.base import config
2 | import logging
3 |
4 | logger = logging.getLogger(__name__)
5 |
6 |
7 | class Config(config.Config):
8 | def __init__(
9 | self,
10 | phone=None,
11 | cc=None,
12 | login=None,
13 | password=None,
14 | pushname=None,
15 | id=None,
16 | mcc=None,
17 | mnc=None,
18 | sim_mcc=None,
19 | sim_mnc=None,
20 | client_static_keypair=None,
21 | server_static_public=None,
22 | expid=None,
23 | fdid=None,
24 | edge_routing_info=None,
25 | chat_dns_domain=None,
26 | backup_token=None,
27 | country=None,
28 | env_name=None,
29 | proxy=None
30 |
31 | ):
32 | super(Config, self).__init__(1)
33 |
34 | self._phone = str(phone) if phone is not None else None # type: str
35 | self._cc = cc # type: int
36 | self._login = str(login) if login is not None else None # type: str
37 | self._password = password # type: str
38 | self._pushname = pushname # type: str
39 | self._id = id
40 | self._client_static_keypair = client_static_keypair
41 | self._server_static_public = server_static_public
42 | self._expid = expid
43 | self._fdid = fdid
44 | self._mcc = mcc
45 | self._mnc = mnc
46 | self._sim_mcc = sim_mcc
47 | self._sim_mnc = sim_mnc
48 | self._edge_routing_info = edge_routing_info
49 | self._chat_dns_domain = chat_dns_domain
50 | self._backup_token = backup_token
51 | self._country = country
52 | self._env_name = env_name
53 | self._proxy = proxy
54 |
55 |
56 | if self._password is not None:
57 | logger.warn("Setting a password in Config is deprecated and not used anymore. "
58 | "client_static_keypair is used instead")
59 |
60 | def __str__(self):
61 | from config.v1.serialize import ConfigSerialize
62 | from config.transforms.dict_json import DictJsonTransform
63 | return DictJsonTransform().transform(ConfigSerialize(self.__class__).serialize(self))
64 |
65 | @property
66 | def phone(self):
67 | return self._phone
68 |
69 | @phone.setter
70 | def phone(self, value):
71 | self._phone = str(value) if value is not None else None
72 |
73 | @property
74 | def cc(self):
75 | return self._cc
76 |
77 | @cc.setter
78 | def cc(self, value):
79 | self._cc = value
80 |
81 | @property
82 | def login(self):
83 | return self._login
84 |
85 | @login.setter
86 | def login(self, value):
87 | self._login= value
88 |
89 | @property
90 | def password(self):
91 | return self._password
92 |
93 | @password.setter
94 | def password(self, value):
95 | self._password = value
96 | if value is not None:
97 | logger.warn("Setting a password in Config is deprecated and not used anymore. "
98 | "client_static_keypair is used instead")
99 |
100 | @property
101 | def pushname(self):
102 | return self._pushname
103 |
104 | @pushname.setter
105 | def pushname(self, value):
106 | self._pushname = value
107 |
108 | @property
109 | def mcc(self):
110 | return self._mcc
111 |
112 | @mcc.setter
113 | def mcc(self, value):
114 | self._mcc = value
115 |
116 | @property
117 | def mnc(self):
118 | return self._mnc
119 |
120 | @mnc.setter
121 | def mnc(self, value):
122 | self._mnc = value
123 |
124 | @property
125 | def sim_mcc(self):
126 | return self._sim_mcc
127 |
128 | @sim_mcc.setter
129 | def sim_mcc(self, value):
130 | self._sim_mcc = value
131 |
132 | @property
133 | def sim_mnc(self):
134 | return self._sim_mnc
135 |
136 | @sim_mnc.setter
137 | def sim_mnc(self, value):
138 | self._sim_mnc = value
139 |
140 | @property
141 | def id(self):
142 | return self._id
143 |
144 | @id.setter
145 | def id(self, value):
146 | self._id = value
147 |
148 | @property
149 | def client_static_keypair(self):
150 | return self._client_static_keypair
151 |
152 | @client_static_keypair.setter
153 | def client_static_keypair(self, value):
154 | self._client_static_keypair = value
155 |
156 | @property
157 | def server_static_public(self):
158 | return self._server_static_public
159 |
160 | @server_static_public.setter
161 | def server_static_public(self, value):
162 | self._server_static_public = value
163 |
164 | @property
165 | def expid(self):
166 | return self._expid
167 |
168 | @expid.setter
169 | def expid(self, value):
170 | self._expid = value
171 |
172 | @property
173 | def fdid(self):
174 | return self._fdid
175 |
176 | @fdid.setter
177 | def fdid(self, value):
178 | self._fdid = value
179 |
180 | @property
181 | def edge_routing_info(self):
182 | return self._edge_routing_info
183 |
184 | @edge_routing_info.setter
185 | def edge_routing_info(self, value):
186 | self._edge_routing_info = value
187 |
188 | @property
189 | def chat_dns_domain(self):
190 | return self._chat_dns_domain
191 |
192 | @chat_dns_domain.setter
193 | def chat_dns_domain(self, value):
194 | self._chat_dns_domain = value
195 |
196 | @property
197 | def backup_token(self):
198 | return self._backup_token
199 |
200 | @backup_token.setter
201 | def backup_token(self, value):
202 | self._backup_token = value
203 |
204 | @property
205 | def country(self):
206 | if self._country:
207 | return self._country
208 | else:
209 | return "id"
210 |
211 | @country.setter
212 | def country(self, value):
213 | self._country = str(value)
214 |
215 | @property
216 | def env_name(self):
217 | if self._env_name:
218 | return self._env_name
219 | else:
220 | return "ios"
221 |
222 | @env_name.setter
223 | def env_name(self, value):
224 | self._env_name = str(value)
225 |
226 | @property
227 | def proxy(self):
228 | if self._proxy:
229 | return self._proxy
230 | else:
231 | return ""
232 |
233 | @proxy.setter
234 | def proxy(self, value):
235 | self._proxy = str(value)
236 |
--------------------------------------------------------------------------------
/config/v1/serialize.py:
--------------------------------------------------------------------------------
1 | from config.base import serialize
2 | from config.transforms.filter import FilterTransform
3 | from config.transforms.meta import MetaPropsTransform
4 | from config.transforms.map import MapTransform
5 | from config.transforms.config_dict import ConfigDictTransform
6 | from config.transforms.props import PropsTransform
7 |
8 | from consonance.structs.keypair import KeyPair
9 | from consonance.structs.publickey import PublicKey
10 | import base64
11 |
12 |
13 | class ConfigSerialize(serialize.ConfigSerialize):
14 | def __init__(self, config_class):
15 | super(ConfigSerialize, self).__init__(
16 | transforms=(
17 | ConfigDictTransform(config_class),
18 | FilterTransform(
19 | transform_filter=lambda key, val: val is not None,
20 | reverse_filter=lambda key, val: key != "version"
21 | ),
22 | MapTransform(transform_map=lambda key, val: (key[1:], val)),
23 | PropsTransform(
24 | transform_map={
25 | "server_static_public": lambda key, val: (key, base64.b64encode(val.data).decode()),
26 | "client_static_keypair": lambda key, val: (key, base64.b64encode(val.private.data + val.public.data).decode()),
27 | "id": lambda key, val: (key, base64.b64encode(val).decode()),
28 | "backup_token": lambda key, val: (key, base64.b64encode(val).decode()),
29 | "expid": lambda key, val: (key, base64.b64encode(val).decode()),
30 | "edge_routing_info": lambda key, val: (key, base64.b64encode(val).decode())
31 | },
32 | reverse_map={
33 | "server_static_public": lambda key, val: (key, PublicKey(base64.b64decode(val))),
34 | "client_static_keypair": lambda key, val: (key, KeyPair.from_bytes(base64.b64decode(val))),
35 | "id": lambda key, val: (key, base64.b64decode(val)),
36 | "backup_token": lambda key, val: (key, base64.b64decode(val)),
37 | "expid": lambda key, val: (key, base64.b64decode(val)),
38 | "edge_routing_info": lambda key, val: (key, base64.b64decode(val))
39 | }
40 | ),
41 | MetaPropsTransform(meta_props=("version", )),
42 | )
43 | )
44 |
--------------------------------------------------------------------------------
/env/__init__.py:
--------------------------------------------------------------------------------
1 | from .env import MyEnv
2 | from .env_ios import iOSMyEnv
3 | from .env_android import AndroidMyEnv
4 |
5 | from .env_config import MyEnvConfig
6 | from .env_config_id import IDEnvConfig
7 | from .env_config_kz import KZEnvConfig
8 | from .env_config_ru import RUEnvConfig
9 | from .env_config_kh import KHEnvConfig
10 | from .env_config_ph import PHEnvConfig
11 | from .env_config_hk import HKEnvConfig
12 | from .env_config_vn import VNEnvConfig
13 | from .env_config_us import USEnvConfig
14 |
15 |
16 | import logging
17 |
18 | logger = logging.getLogger(__name__)
19 | ch = logging.StreamHandler()
20 | # ch.setLevel(logging.DEBUG)
21 |
22 | formatter = logging.Formatter('%(levelname).1s %(asctime)s %(name)s - %(message)s')
23 |
24 | ch.setFormatter(formatter)
25 |
26 | logger.addHandler(ch)
27 |
--------------------------------------------------------------------------------
/env/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_app.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_app.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config_id.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config_id.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config_kh.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config_kh.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config_kz.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config_kz.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config_ph.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config_ph.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_config_ru.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_config_ru.cpython-38.pyc
--------------------------------------------------------------------------------
/env/__pycache__/env_ios.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/env/__pycache__/env_ios.cpython-38.pyc
--------------------------------------------------------------------------------
/env/env.py:
--------------------------------------------------------------------------------
1 | import abc
2 | import logging
3 | from six import with_metaclass
4 |
5 | logger = logging.getLogger(__name__)
6 |
7 | DEFAULT = "ios"
8 |
9 |
10 | class MyEnvType(abc.ABCMeta):
11 | def __init__(cls, name, bases, dct):
12 | if name != "MyEnv":
13 | MyEnv.registerEnv(cls)
14 | super(MyEnvType, cls).__init__(name, bases, dct)
15 |
16 |
17 | class MyEnv(with_metaclass(MyEnvType, object)):
18 | __metaclass__ = MyEnvType
19 | __ENVS = {}
20 | __CURR = None
21 | # WhatsApp/2.20.102 iOS/12.4.8 Device/iPhone_7Z
22 | _USERAGENT_STRING = "WhatsApp/{WHATSAPP_VERSION} {OS_NAME}/{OS_VERSION} Device/{DEVICE_NAME}_{DEVICE_TYPE}"
23 |
24 | @classmethod
25 | def registerEnv(cls, envCls):
26 | envName = envCls.__name__.lower().replace("myenv", "")
27 | cls.__ENVS[envName] = envCls
28 | logger.debug("registered env %s => %s" % (envName, envCls))
29 |
30 | @classmethod
31 | def setEnv(cls, envName):
32 | if not envName in cls.__ENVS:
33 | raise ValueError("%s env does not exist" % envName)
34 | logger.debug("Current env changed to %s " % envName)
35 | cls.__CURR = cls.__ENVS[envName]()
36 |
37 | @classmethod
38 | def getEnv(cls, envName):
39 | if not envName in cls.__ENVS:
40 | raise ValueError("%s env does not exist" % envName)
41 |
42 | return cls.__ENVS[envName]()
43 |
44 | @classmethod
45 | def getRegisteredEnvs(cls):
46 | return list(cls.__ENVS.keys())
47 |
48 | @classmethod
49 | def getCurrent(cls):
50 | """
51 | :rtype: iOSEnv
52 | """
53 | if cls.__CURR is None:
54 | env = DEFAULT
55 | envs = cls.getRegisteredEnvs()
56 | if env not in envs:
57 | env = envs[0]
58 | logger.debug("Env not set, setting it to %s" % env)
59 | cls.setEnv(env)
60 | return cls.__CURR
61 |
62 | @abc.abstractmethod
63 | def getToken(self, phoneNumber):
64 | pass
65 |
66 | @abc.abstractmethod
67 | def getVersion(self):
68 | pass
69 |
70 | @abc.abstractmethod
71 | def getOSVersion(self):
72 | pass
73 |
74 | @abc.abstractmethod
75 | def getOSName(self):
76 | pass
77 |
78 | @abc.abstractmethod
79 | def getDeviceName(self):
80 | pass
81 |
82 | @abc.abstractmethod
83 | def getDeviceModel(self):
84 | pass
85 |
86 | @abc.abstractmethod
87 | def getDeviceType(self):
88 | pass
89 |
90 | @abc.abstractmethod
91 | def getManufacturer(self):
92 | pass
93 |
94 | def getBuildVersion(self):
95 | pass
96 |
97 | def getUserAgent(self):
98 | return self.__class__._USERAGENT_STRING.format(
99 | WHATSAPP_VERSION=self.getVersion(),
100 | OS_NAME=self.getOSName(),
101 | OS_VERSION=self.getOSVersion(),
102 | DEVICE_NAME=self.getDeviceName(),
103 | DEVICE_TYPE=self.getDeviceType()
104 |
105 | )
106 |
--------------------------------------------------------------------------------
/env/env_android.py:
--------------------------------------------------------------------------------
1 | from .env import MyEnv
2 | import base64
3 | import hashlib
4 |
5 |
6 | class AndroidMyEnv(MyEnv):
7 | _SIGNATURE = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNV" \
8 | "BAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJ" \
9 | "pYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5" \
10 | "pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEg" \
11 | "YDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEm" \
12 | "aUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCN" \
13 | "VQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jr" \
14 | "qgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO" \
15 | "8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaS" \
16 | "HBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRW" \
17 | "YHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXd" \
18 | "KtOrNTQcc0e+t"
19 |
20 | _MD5_CLASSES = "WuFH18yXKRVezywQm+S24A=="
21 | _KEY = "eQV5aq/Cg63Gsq1sshN9T3gh+UUp0wIw0xgHYT1bnCjEqOJQKCRrWxdAe2yvsDeCJL+Y4G3PRD2HUF7oUgiGo8vGlNJOaux26k+A2F3hj8A="
22 |
23 | _VERSION = "2.21.21.18" # 2.20.206.24
24 | _OS_NAME = "Android"
25 | _OS_VERSION = "8.0.0"
26 | _DEVICE_NAME = "star2lte"
27 | _MANUFACTURER = "samsung"
28 | _BUILD_VERSION = "star2ltexx-user 8.0.0 R16NW G965FXXU1ARCC release-keys"
29 | _AXOLOTL = True
30 |
31 | def getVersion(self):
32 | return self.__class__._VERSION
33 |
34 | def getOSName(self):
35 | return self.__class__._OS_NAME
36 |
37 | def getOSVersion(self):
38 | return self.__class__._OS_VERSION
39 |
40 | def getDeviceName(self):
41 | return self.__class__._DEVICE_NAME
42 |
43 | def getBuildVersion(self):
44 | return self.__class__._BUILD_VERSION
45 |
46 | def getManufacturer(self):
47 | return self.__class__._MANUFACTURER
48 |
49 | def isAxolotlEnabled(self):
50 | return self.__class__._AXOLOTL
51 |
52 | def getDeviceType(self):
53 | return ""
54 |
55 | def getToken(self, phoneNumber):
56 | keyDecoded = bytearray(base64.b64decode(self.__class__._KEY))
57 | sigDecoded = base64.b64decode(self.__class__._SIGNATURE)
58 | clsDecoded = base64.b64decode(self.__class__._MD5_CLASSES)
59 | data = sigDecoded + clsDecoded + phoneNumber.encode()
60 |
61 | opad = bytearray()
62 | ipad = bytearray()
63 | for i in range(0, 64):
64 | opad.append(0x5C ^ keyDecoded[i])
65 | ipad.append(0x36 ^ keyDecoded[i])
66 | hash = hashlib.sha1()
67 | subHash = hashlib.sha1()
68 | try:
69 | subHash.update(ipad + data)
70 | hash.update(opad + subHash.digest())
71 | except TypeError:
72 | subHash.update(bytes(ipad + data))
73 | hash.update(bytes(opad + subHash.digest()))
74 | result = base64.b64encode(hash.digest())
75 | return result
76 |
--------------------------------------------------------------------------------
/env/env_app.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | APP_DATA = {
4 | "gc.whatsappprotocolregisterapi": 1,
5 | "gc.whatsappprotocolexportgroup": 2,
6 | "gc.playapi": 3,
7 | "gc.groupmessagecatchapi": 4
8 | }
9 |
10 |
11 | def app_env():
12 | app = os.getenv('APP')
13 | app_type = APP_DATA.get(app)
14 | return app_type
15 |
--------------------------------------------------------------------------------
/env/env_config.py:
--------------------------------------------------------------------------------
1 | import abc
2 | import logging
3 | from six import with_metaclass
4 |
5 | logger = logging.getLogger(__name__)
6 |
7 | # 印度尼西亚
8 | DEFAULT = "ID"
9 |
10 |
11 | class MyEnvConfigType(abc.ABCMeta):
12 | def __init__(cls, name, bases, dct):
13 | if name != "MyEnvConfig":
14 | MyEnvConfig.registerEnv(cls)
15 | super(MyEnvConfigType, cls).__init__(name, bases, dct)
16 |
17 |
18 | class MyEnvConfig(with_metaclass(MyEnvConfigType, object)):
19 | __metaclass__ = MyEnvConfigType
20 | _PROXY_NAME = "kkone88-zone-custom-region-{country}-session-{session_name}-sessTime-{session_time}"
21 |
22 | __ENVS = {}
23 | __CURR = None
24 |
25 | @classmethod
26 | def registerEnv(cls, envCls):
27 | envName = envCls.__name__.lower().replace("envconfig", "")
28 | cls.__ENVS[envName] = envCls
29 | logger.debug("registered envconfig %s => %s" % (envName, envCls))
30 |
31 | @classmethod
32 | def setEnv(cls, envName):
33 | if not envName in cls.__ENVS:
34 | raise ValueError("%s envconfig does not exist" % envName)
35 | logger.debug("Current env changed to %s " % envName)
36 | cls.__CURR = cls.__ENVS[envName]()
37 |
38 | @classmethod
39 | def getEnv(cls, envName):
40 | if not envName in cls.__ENVS:
41 | raise ValueError("%s envconfig does not exist" % envName)
42 |
43 | return cls.__ENVS[envName]()
44 |
45 | @classmethod
46 | def getRegisteredEnvs(cls):
47 | return list(cls.__ENVS.keys())
48 |
49 | @classmethod
50 | def getCurrent(cls):
51 | """
52 | :rtype: 印度尼西亚
53 | """
54 | if cls.__CURR is None:
55 | env = DEFAULT
56 | envs = cls.getRegisteredEnvs()
57 | if env not in envs:
58 | env = envs[0]
59 | logger.debug("EnvConfig not set, setting it to %s" % env)
60 | cls.setEnv(env)
61 | return cls.__CURR
62 |
63 | @abc.abstractmethod
64 | def getJinDou(self):
65 | pass
66 |
67 | @abc.abstractmethod
68 | def getCc(self):
69 | pass
70 |
71 | @abc.abstractmethod
72 | def getCountry(self):
73 | pass
74 |
75 | @abc.abstractmethod
76 | def getMcc(self):
77 | pass
78 |
79 | @abc.abstractmethod
80 | def getMnc(self):
81 | pass
82 |
83 | @abc.abstractmethod
84 | def getDuringTime(self):
85 | pass
86 |
87 |
88 |
--------------------------------------------------------------------------------
/env/env_config_hk.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 香港
4 | class HKEnvConfig(MyEnvConfig):
5 | _JINDOU = "2800"
6 | _CC = "852"
7 | _COUNTRY = "hk"
8 | _MCC = "454"
9 | _MNC = "007"
10 | _DURING = 20
11 |
12 |
13 | def getJinDou(self):
14 | return self.__class__._JINDOU
15 |
16 | def getCc(self):
17 | return self.__class__._CC
18 |
19 | def getCountry(self):
20 | return self.__class__._COUNTRY
21 |
22 | def getMcc(self):
23 | return self.__class__._MCC
24 |
25 | def getMnc(self):
26 | return self.__class__._MNC
27 |
28 | def getDuringTime(self):
29 | return self.__class__._DURING
30 |
31 | def getProxyName(self, mobile):
32 | return self.__class__._PROXY_NAME.format(
33 | country=self.getCountry(),
34 | session_name=mobile,
35 | session_time=self.getDuringTime()
36 |
37 | )
38 |
--------------------------------------------------------------------------------
/env/env_config_id.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 印度尼西亚
4 | class IDEnvConfig(MyEnvConfig):
5 | _JINDOU = "2800"
6 | _CC = "62"
7 | _COUNTRY = "id"
8 | _MCC = "510"
9 | _MNC = "001"
10 | _DURING = 20
11 |
12 |
13 | def getJinDou(self):
14 | return self.__class__._JINDOU
15 |
16 | def getCc(self):
17 | return self.__class__._CC
18 |
19 | def getCountry(self):
20 | return self.__class__._COUNTRY
21 |
22 | def getMcc(self):
23 | return self.__class__._MCC
24 |
25 | def getMnc(self):
26 | return self.__class__._MNC
27 |
28 | def getDuringTime(self):
29 | return self.__class__._DURING
30 |
31 | def getProxyName(self, mobile):
32 | return self.__class__._PROXY_NAME.format(
33 | country=self.getCountry(),
34 | session_name=mobile,
35 | session_time=self.getDuringTime()
36 |
37 | )
38 |
--------------------------------------------------------------------------------
/env/env_config_kh.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 柬埔寨
4 | class KHEnvConfig(MyEnvConfig):
5 | _JINDOU = "2123"
6 | _CC = "855"
7 | _COUNTRY = "kh"
8 | _MCC = "456"
9 | # 02 04 06 08 09 18
10 | _MNC = "02"
11 | _DURING = 20
12 |
13 |
14 | def getJinDou(self):
15 | return self.__class__._JINDOU
16 |
17 | def getCc(self):
18 | return self.__class__._CC
19 |
20 | def getCountry(self):
21 | return self.__class__._COUNTRY
22 |
23 | def getMcc(self):
24 | return self.__class__._MCC
25 |
26 | def getMnc(self):
27 | return self.__class__._MNC
28 |
29 | def getDuringTime(self):
30 | return self.__class__._DURING
31 |
32 | def getProxyName(self, mobile):
33 | return self.__class__._PROXY_NAME.format(
34 | country=self.getCountry(),
35 | session_name=mobile,
36 | session_time=self.getDuringTime()
37 |
38 | )
39 |
--------------------------------------------------------------------------------
/env/env_config_kz.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 哈萨克斯坦
4 | class KZEnvConfig(MyEnvConfig):
5 | _JINDOU = "2839"
6 | _CC = "7"
7 | _COUNTRY = "kz"
8 | _MCC = "401"
9 | # 01 02 08 77
10 | _MNC = "001"
11 | _DURING = 20
12 |
13 |
14 | def getJinDou(self):
15 | return self.__class__._JINDOU
16 |
17 | def getCc(self):
18 | return self.__class__._CC
19 |
20 | def getCountry(self):
21 | return self.__class__._COUNTRY
22 |
23 | def getMcc(self):
24 | return self.__class__._MCC
25 |
26 | def getMnc(self):
27 | return self.__class__._MNC
28 |
29 | def getDuringTime(self):
30 | return self.__class__._DURING
31 |
32 | def getProxyName(self, mobile):
33 | return self.__class__._PROXY_NAME.format(
34 | country=self.getCountry(),
35 | session_name=mobile,
36 | session_time=self.getDuringTime()
37 |
38 | )
39 |
--------------------------------------------------------------------------------
/env/env_config_ph.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 菲律宾
4 | class PHEnvConfig(MyEnvConfig):
5 | _JINDOU = "2981"
6 | _CC = "63"
7 | _COUNTRY = "ph"
8 | _MCC = "515"
9 | _MNC = "002"
10 | _DURING = 20
11 |
12 |
13 | def getJinDou(self):
14 | return self.__class__._JINDOU
15 |
16 | def getCc(self):
17 | return self.__class__._CC
18 |
19 | def getCountry(self):
20 | return self.__class__._COUNTRY
21 |
22 | def getMcc(self):
23 | return self.__class__._MCC
24 |
25 | def getMnc(self):
26 | return self.__class__._MNC
27 |
28 | def getDuringTime(self):
29 | return self.__class__._DURING
30 |
31 | def getProxyName(self, mobile):
32 | return self.__class__._PROXY_NAME.format(
33 | country=self.getCountry(),
34 | session_name=mobile,
35 | session_time=self.getDuringTime()
36 |
37 | )
38 |
--------------------------------------------------------------------------------
/env/env_config_ru.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 俄罗斯
4 | class RUEnvConfig(MyEnvConfig):
5 | _JINDOU = "2838"
6 | _CC = "7"
7 | _COUNTRY = "ru"
8 | _MCC = "250"
9 | _MNC = "002"
10 | _DURING = 20
11 |
12 |
13 | def getJinDou(self):
14 | return self.__class__._JINDOU
15 |
16 | def getCc(self):
17 | return self.__class__._CC
18 |
19 | def getCountry(self):
20 | return self.__class__._COUNTRY
21 |
22 | def getMcc(self):
23 | return self.__class__._MCC
24 |
25 | def getMnc(self):
26 | return self.__class__._MNC
27 |
28 | def getDuringTime(self):
29 | return self.__class__._DURING
30 |
31 | def getProxyName(self, mobile):
32 | return self.__class__._PROXY_NAME.format(
33 | country=self.getCountry(),
34 | session_name=mobile,
35 | session_time=self.getDuringTime()
36 |
37 | )
38 |
--------------------------------------------------------------------------------
/env/env_config_us.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 美国
4 | class USEnvConfig(MyEnvConfig):
5 | _CC = "1"
6 | _COUNTRY = "u's"
7 | _MCC = "310"
8 | _MNC = "032"
9 | _DURING = 20
10 |
11 |
12 | def getJinDou(self):
13 | return self.__class__._JINDOU
14 |
15 | def getCc(self):
16 | return self.__class__._CC
17 |
18 | def getCountry(self):
19 | return self.__class__._COUNTRY
20 |
21 | def getMcc(self):
22 | return self.__class__._MCC
23 |
24 | def getMnc(self):
25 | return self.__class__._MNC
26 |
27 | def getDuringTime(self):
28 | return self.__class__._DURING
29 |
30 | def getProxyName(self, mobile):
31 | return self.__class__._PROXY_NAME.format(
32 | country=self.getCountry(),
33 | session_name=mobile,
34 | session_time=self.getDuringTime()
35 |
36 | )
37 |
--------------------------------------------------------------------------------
/env/env_config_vn.py:
--------------------------------------------------------------------------------
1 | from .env_config import MyEnvConfig
2 |
3 | # 越南
4 | class VNEnvConfig(MyEnvConfig):
5 | _JINDOU = "2838"
6 | _CC = "84"
7 | _COUNTRY = "vn"
8 | _MCC = "452"
9 | _MNC = "002"
10 | _DURING = 20
11 |
12 |
13 | def getJinDou(self):
14 | return self.__class__._JINDOU
15 |
16 | def getCc(self):
17 | return self.__class__._CC
18 |
19 | def getCountry(self):
20 | return self.__class__._COUNTRY
21 |
22 | def getMcc(self):
23 | return self.__class__._MCC
24 |
25 | def getMnc(self):
26 | return self.__class__._MNC
27 |
28 | def getDuringTime(self):
29 | return self.__class__._DURING
30 |
31 | def getProxyName(self, mobile):
32 | return self.__class__._PROXY_NAME.format(
33 | country=self.getCountry(),
34 | session_name=mobile,
35 | session_time=self.getDuringTime()
36 |
37 | )
38 |
--------------------------------------------------------------------------------
/examples/walogin_handshake_ik.py:
--------------------------------------------------------------------------------
1 | from consonance.structs.keypair import KeyPair
2 | from consonance.structs.publickey import PublicKey
3 | from consonance.protocol import WANoiseProtocol
4 | from consonance.config.client import ClientConfig
5 | from consonance.streams.segmented.wa import WASegmentedStream
6 | from consonance.streams.arbitrary.arbitrary_socket import SocketArbitraryStream
7 | from consonance.config.templates.useragent_iPhone import iPhoneUserAgentConfig
8 | import consonance
9 | import uuid
10 | import dissononce
11 | import socket
12 | import logging
13 | import sys
14 | import base64
15 | from axolotl.ecc.curve import Curve
16 |
17 | consonance.logger.setLevel(logging.DEBUG)
18 | dissononce.logger.setLevel(logging.DEBUG)
19 |
20 | # username is phone number
21 | USERNAME = 77472412416
22 | # on Android fetch client_static_keypair from /data/data/com.whatsapp/shared_prefs/keystore.xml
23 | KEYPAIR = KeyPair.from_bytes(
24 | base64.b64decode(b"8Ccclxofr7K1KIY2Y2DUjzyTRmsUM1z1jXj9Nzx9mVRtf3toEZBMgkmCMq4gKWVdwftl3DULtMyO15YfIbJOAA==")
25 | )
26 |
27 | ENC_PUBKEY = Curve.decodePoint(
28 | bytearray([
29 | 5, 142, 140, 15, 116, 195, 235, 197, 215, 166, 134, 92, 108,
30 | 60, 132, 56, 86, 176, 97, 33, 204, 232, 234, 119, 77, 34, 251,
31 | 111, 18, 37, 18, 48, 45
32 | ])
33 | )
34 |
35 | WA_PUBLIC = PublicKey(ENC_PUBKEY.publicKey)
36 | # same phone_id/fdid used at registration.
37 | # on Android it's phoneid_id under /data/data/com.whatsapp/shared_prefs/com.whatsapp_preferences.xml
38 | PHONE_ID = uuid.uuid4().__str__()
39 | # create full configuration which will translate later into a protobuf payload
40 | CONFIG = ClientConfig(
41 | username=USERNAME,
42 | passive=True,
43 | useragent=iPhoneUserAgentConfig(
44 | app_version="2.20.102",
45 | phone_id=PHONE_ID
46 | ),
47 | pushname="consonance",
48 | short_connect=True
49 | )
50 | ENDPOINT = ("g.whatsapp.net", 443)
51 | HEADER = b"WA\x04\x01"
52 |
53 | if __name__ == "__main__":
54 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
55 | s.connect(ENDPOINT)
56 | # send WA header indicating protocol version
57 | s.send(HEADER)
58 | # use WASegmentedStream for sending/receiving in frames
59 | stream = WASegmentedStream(SocketArbitraryStream(s))
60 | # initialize WANoiseProtocol 2.1
61 | wa_noiseprotocol = WANoiseProtocol(4, 1)
62 | # start the protocol, this should attempt a IK handshake since we
63 | # specifying the remote static public key. The handshake should
64 | # succeeds if the WA_PUBLIC is valid, but authentication should fail.
65 | if wa_noiseprotocol.start(stream, CONFIG, KEYPAIR, rs=WA_PUBLIC):
66 | print("Handshake completed, checking authentication...")
67 | # we are now in transport phase, first incoming data
68 | # will indicate whether we are authenticated
69 | first_transport_data = wa_noiseprotocol.receive()
70 | # fourth byte is status, 172 is success, 52 is failure
71 | if first_transport_data[3] == 172:
72 | print("Authentication succeeded")
73 | elif first_transport_data[3] == 52:
74 | print("Authentication failed")
75 | sys.exit(1)
76 | else:
77 | print("Unrecognized authentication response: %s" % (first_transport_data[3]))
78 | sys.exit(1)
79 | else:
80 | print("Handshake failed")
81 | sys.exit(1)
82 |
--------------------------------------------------------------------------------
/examples/walogin_handshake_xx.py:
--------------------------------------------------------------------------------
1 | from consonance.structs.keypair import KeyPair
2 | from consonance.protocol import WANoiseProtocol
3 | from consonance.config.client import ClientConfig
4 | from consonance.streams.segmented.wa import WASegmentedStream
5 | from consonance.streams.arbitrary.arbitrary_socket import SocketArbitraryStream
6 | from consonance.config.templates.useragent_iPhone import iPhoneUserAgentConfig
7 | import consonance
8 | import uuid
9 | import dissononce
10 | import socket
11 | import logging
12 | import sys
13 | import base64
14 |
15 | consonance.logger.setLevel(logging.DEBUG)
16 | dissononce.logger.setLevel(logging.DEBUG)
17 |
18 | # username is phone number
19 | USERNAME = 6283890513211
20 | # on Android fetch client_static_keypair from /data/data/com.whatsapp/shared_prefs/keystore.xml
21 | KEYPAIR = KeyPair.from_bytes(
22 | base64.b64decode(b"kDZxVPzYhrNKHA2YczenBX495Lfk5eEPNdRV98Eq5Ut1EMsXJP7I1U0jBwmIwchXylf7SdVV25hDLwmr1JgWVQ==")
23 | )
24 | # same phone_id/fdid used at registration.
25 | # on Android it's phoneid_id under /data/data/com.whatsapp/shared_prefs/com.whatsapp_preferences.xml
26 | PHONE_ID = '55a6dced-cfb6-4851-b816-18ab591f1e8a'
27 | # create full configuration which will translate later into a protobuf payload
28 | CONFIG = ClientConfig(
29 | username=USERNAME,
30 | passive=True,
31 | useragent=iPhoneUserAgentConfig(
32 | app_version="2.22.16.77",
33 | phone_id=PHONE_ID
34 | ),
35 | pushname="wer u",
36 | short_connect=True
37 | )
38 | PROTOCOL_VERSION = (5, 2)
39 | ENDPOINT = ("e1.whatsapp.net", 443)
40 | HEADER = b"WA" + bytes(PROTOCOL_VERSION)
41 |
42 | if __name__ == "__main__":
43 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
44 | s.connect(ENDPOINT)
45 | # send WA header indicating protocol version
46 | s.send(HEADER)
47 | # use WASegmentedStream for sending/receiving in frames
48 | stream = WASegmentedStream(SocketArbitraryStream(s))
49 | # initialize WANoiseProtocol
50 | wa_noiseprotocol = WANoiseProtocol(*PROTOCOL_VERSION)
51 | # start the protocol, this should a XX handshake since
52 | # we are not passing the remote static public key
53 | try:
54 | wa_noiseprotocol.start(stream, CONFIG, KEYPAIR)
55 | print("Handshake completed, checking authentication...")
56 | # we are now in transport phase, first incoming data
57 | # will indicate whether we are authenticated
58 | first_transport_data = wa_noiseprotocol.receive()
59 | # fourth + fifth byte are status, [237, 38] is failure
60 | if first_transport_data[3] == 51:
61 | print("Authentication succeeded")
62 | elif list(first_transport_data[3:5]) == [237, 38]:
63 | print("Authentication failed")
64 | sys.exit(1)
65 | else:
66 | print("Unrecognized authentication response: %s" % (first_transport_data[3]))
67 | sys.exit(1)
68 | except:
69 | print("Handshake failed")
70 | sys.exit(1)
71 |
--------------------------------------------------------------------------------
/examples/walogin_handshake_xxfallback.py:
--------------------------------------------------------------------------------
1 | from consonance.structs.keypair import KeyPair
2 | from consonance.protocol import WANoiseProtocol
3 | from consonance.config.client import ClientConfig
4 | from consonance.streams.segmented.wa import WASegmentedStream
5 | from consonance.streams.arbitrary.arbitrary_socket import SocketArbitraryStream
6 | from consonance.config.templates.useragent_iPhone import iPhoneUserAgentConfig
7 | import consonance
8 | import uuid
9 | import dissononce
10 | import socket
11 | import logging
12 | import sys
13 | import base64
14 |
15 | consonance.logger.setLevel(logging.DEBUG)
16 | dissononce.logger.setLevel(logging.DEBUG)
17 |
18 | # username is phone number
19 | USERNAME = 123456789
20 | # on Android fetch client_static_keypair from /data/data/com.whatsapp/shared_prefs/keystore.xml
21 | KEYPAIR = KeyPair.from_bytes(
22 | base64.b64decode(b"YJa8Vd9pG0KV2tDYi5V+DMOtSvCEFzRGCzOlGZkvBHzJvBE5C3oC2Fruniw0GBGo7HHgR4TjvjI3C9AihStsVg==")
23 | )
24 | # using a random remote public key
25 | WA_PUBLIC = KeyPair.generate().public
26 | # same phone_id/fdid used at registration.
27 | # on Android it's phoneid_id under /data/data/com.whatsapp/shared_prefs/com.whatsapp_preferences.xml
28 | PHONE_ID = uuid.uuid4().__str__()
29 | # create full configuration which will translate later into a protobuf payload
30 | CONFIG = ClientConfig(
31 | username=USERNAME,
32 | passive=True,
33 | useragent=iPhoneUserAgentConfig(
34 | app_version="2.19.51",
35 | phone_id=PHONE_ID
36 | ),
37 | pushname="consonance",
38 | short_connect=True
39 | )
40 | ENDPOINT = ("e1.whatsapp.net", 443)
41 | HEADER = b"WA\x02\x01"
42 |
43 | if __name__ == "__main__":
44 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
45 | s.connect(ENDPOINT)
46 | # send WA header indicating protocol version
47 | s.send(HEADER)
48 | # use WASegmentedStream for sending/receiving in frames
49 | stream = WASegmentedStream(SocketArbitraryStream(s))
50 | # initialize WANoiseProtocol 2.1
51 | wa_noiseprotocol = WANoiseProtocol(2, 1)
52 | # start the protocol, this should attempt a IK handshake since we
53 | # specifying the remote static public key. The handshake should
54 | # fail because WA_PUBLIC is invalid. This results in reverting to
55 | # a xxfallack handshake. Note that eventually authentication
56 | # should fail anyways due to invalid credentials of this examples.
57 | if wa_noiseprotocol.start(stream, CONFIG, KEYPAIR, rs=WA_PUBLIC):
58 | print("Handshake completed, checking authentication...")
59 | # we are now in transport phase, first incoming data
60 | # will indicate whether we are authenticated
61 | first_transport_data = wa_noiseprotocol.receive()
62 | # fourth byte is status, 172 is success, 52 is failure
63 | if first_transport_data[3] == 172:
64 | print("Authentication succeeded")
65 | elif first_transport_data[3] == 52:
66 | print("Authentication failed")
67 | sys.exit(1)
68 | else:
69 | print("Unrecognized authentication response: %s" % (first_transport_data[3]))
70 | sys.exit(1)
71 | else:
72 | print("Handshake failed")
73 | sys.exit(1)
74 |
--------------------------------------------------------------------------------
/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/login.png
--------------------------------------------------------------------------------
/messagestack/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/messagestack/__init__.py
--------------------------------------------------------------------------------
/messagestack/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/messagestack/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/messagestack/__pycache__/group_info.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/messagestack/__pycache__/group_info.cpython-38.pyc
--------------------------------------------------------------------------------
/messagestack/__pycache__/play_group.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/messagestack/__pycache__/play_group.cpython-38.pyc
--------------------------------------------------------------------------------
/messagestack/__pycache__/register.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/messagestack/__pycache__/register.cpython-38.pyc
--------------------------------------------------------------------------------
/messagestack/group_info.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from common.singleonConfig import SingleonConfig
4 | from config.enumExec import ExecTypes
5 | from mycli.cli.exportgroup import ExportGroupUpload
6 | from mycli.http_request import requests_send
7 | from mycli.mysql.mysql_cli import mobile_update
8 |
9 | class ExportGroupResult():
10 |
11 | @classmethod
12 | def fail(cls, result, type_num):
13 |
14 | mobile = result.get("phone")
15 | reason = result.get("reason")
16 | data = result.get("data")
17 |
18 | dataPenetrate = SingleonConfig().getParamsAction()
19 | upload_url = dataPenetrate.get("UploadUrl")
20 | work_upload_url = dataPenetrate.get("WorkUploadUrl")
21 |
22 | upload_data = {
23 | "FullPhoneNumber": mobile,
24 | "OptType": None,
25 | "OptAppType": 2,
26 | "OptTime": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f"),
27 | "OptIp": "",
28 | "OptReamrk": None,
29 | "ProtocolType": 1
30 | }
31 | if type_num == ExecTypes.login:
32 |
33 | if reason == 403:
34 | mobile_update(mobile, reason)
35 | upload_data["OptType"] = 5
36 | message = "封号"
37 | upload_data["OptReamrk"] = message
38 | else:
39 | upload_data["OptType"] = 2
40 | message = "登录失败"
41 | upload_data["OptReamrk"] = message
42 | ExportGroupUpload.send_http("POST", work_upload_url, upload_data, mobile, message)
43 |
44 | elif type_num == ExecTypes.group_join:
45 |
46 | group_link = data.get("GroupUrl")
47 | data["groupUser"] = []
48 |
49 | upload_data["OptType"] = 7
50 | message = "加群[{}]失败".format(group_link)
51 | upload_data["OptReamrk"] = message
52 | upload_response = requests_send("POST", upload_url, json=data)
53 | if not upload_response:
54 | requests_send("POST", upload_url, json=upload_data)
55 |
56 | ExportGroupUpload.send_http("POST", work_upload_url, upload_data, mobile, message)
57 |
58 | elif type_num == ExecTypes.group_info:
59 |
60 | group_link = data.get("GroupUrl")
61 | data["groupUser"] = []
62 |
63 | upload_data["OptType"] = 7
64 | message = "导群[{}]失败".format(group_link)
65 | upload_data["OptReamrk"] = message
66 | upload_response = requests_send("POST", upload_url, json=data)
67 | if not upload_response:
68 | requests_send("POST", upload_url, json=upload_data)
69 |
70 | ExportGroupUpload.send_http("POST", work_upload_url, upload_data, mobile, message)
71 |
72 | @classmethod
73 | def success(cls, result, type_num):
74 | data = result.get("data")
75 | mobile = result.get("phone")
76 |
77 | dataPenetrate = SingleonConfig().getParamsAction()
78 | upload_url = dataPenetrate.get("UploadUrl")
79 | work_upload_url = dataPenetrate.get("WorkUploadUrl")
80 |
81 | upload_data = {
82 | "FullPhoneNumber": mobile,
83 | "OptType": None,
84 | "OptAppType": 2,
85 | "OptTime": datetime.datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%f"),
86 | "OptIp": "",
87 | "OptReamrk": None,
88 | "ProtocolType": 1
89 | }
90 | if type_num == ExecTypes.login:
91 |
92 | upload_data["OptType"] = 1
93 | message = "登录成功"
94 | upload_data["OptReamrk"] = message
95 | ExportGroupUpload.send_http("POST", work_upload_url, upload_data, mobile, message)
96 |
97 | elif type_num == ExecTypes.group_join:
98 | print("导群加群成功")
99 |
100 | elif type_num == ExecTypes.group_info:
101 |
102 | group_link = data.get("GroupUrl")
103 |
104 | upload_data["OptType"] = 6
105 | message = "导群[{}]成功".format(group_link)
106 | upload_data["OptReamrk"] = message
107 |
108 | upload_response = requests_send("POST", upload_url, json=data)
109 | if not upload_response:
110 | requests_send("POST", upload_url, json=upload_data)
111 |
112 | ExportGroupUpload.send_http("POST", work_upload_url, upload_data, mobile, message)
113 |
114 |
--------------------------------------------------------------------------------
/messagestack/group_msg_catch.py:
--------------------------------------------------------------------------------
1 | from config.enumExec import ExecTypes
2 | from mycli.cli.playgroup import logger
3 | from mycli.mysql.group_msg_catch import group_msg_insert, busy_update
4 | from mycli.mysql.mysql_cli import mobile_update
5 |
6 |
7 | class GroupMessageCatchResult():
8 |
9 | @classmethod
10 | def fail(cls, result, type_num):
11 |
12 | phone = result.get("phone")
13 | reason = result.get("reason")
14 |
15 | if type_num == ExecTypes.login:
16 | if reason == 403:
17 | mobile_update(phone, reason)
18 | logger.info("{}:群消息抓取号码登录失败:{}".format(phone, reason))
19 |
20 | @classmethod
21 | def success(cls, result, type_num):
22 |
23 | phone = result.get("phone")
24 | data = result.get("data")
25 |
26 | if type_num == ExecTypes.login:
27 | busy_update(phone, 1)
28 | logger.info("{}:群消息抓取号码登录成功".format(phone))
29 | elif type_num == ExecTypes.receive_text:
30 | group_id = data.get("groupId")
31 | if group_id:
32 | message = data.get("text")
33 | message_id = data.get("messageId")
34 | group_msg_insert(phone, group_id, message, message_id)
35 | logger.info("{}:群消息入库成功:{}".format(phone, data))
36 |
--------------------------------------------------------------------------------
/messagestack/play_group.py:
--------------------------------------------------------------------------------
1 | from common.singleonConfig import SingleonConfig
2 | from config.enumExec import ExecTypes
3 | from mycli.cli.playgroup import PlayGroupHandle
4 | from mycli.logger import logger
5 | from mycli.mysql.mysql_cli import mobile_update
6 |
7 | class PlayGroupResult():
8 |
9 | @classmethod
10 | def fail(cls, result, type_num):
11 | if type_num == ExecTypes.login:
12 | reason = result.get("reason")
13 | mobile = result.get("phone")
14 |
15 | if reason == 403: # 封号
16 | mobile_update(mobile, reason)
17 |
18 | queue = result.pop('queue')
19 | result["type_num"] = type_num
20 | queue.put(result)
21 |
22 | elif type_num == ExecTypes.group_join:
23 | queue = result.pop('queue')
24 | result["type_num"] = type_num
25 | queue.put(result)
26 | elif type_num == ExecTypes.send_text:
27 | queue = result.pop('queue')
28 | result["type_num"] = type_num
29 | queue.put(result)
30 |
31 | @classmethod
32 | def success(cls, result, type_num):
33 | if type_num == ExecTypes.group_kick:
34 | return
35 | phone = result.get("phone")
36 | data = result.get("data")
37 |
38 | dataPenetrate = SingleonConfig().getParamsAction()
39 | upload_url = dataPenetrate.get("PlayGroupUploadUrl")
40 |
41 | if type_num == ExecTypes.login:
42 | logger.info("{}:炒群号码登录成功".format(phone))
43 | elif type_num == ExecTypes.group_join:
44 | dataPenetrate[phone] = type_num
45 | SingleonConfig().setParamsAction(dataPenetrate)
46 | PlayGroupHandle.upload(data, "AddGroup", upload_url, "AddGroupSuccessed", phone)
47 | elif type_num == ExecTypes.send_text:
48 | PlayGroupHandle.upload(data, "SendMessage", upload_url, "SendMessageSuccessed", phone)
49 |
--------------------------------------------------------------------------------
/messagestack/register.py:
--------------------------------------------------------------------------------
1 | from common.singleonConfig import SingleonConfig
2 | from config.enumExec import ExecTypes
3 | from mycli.cli.register_result import ResultMq
4 | from mycli.mysql.mysql_cli import mobile_insert
5 |
6 | class RegisterResult():
7 |
8 | @classmethod
9 | def fail(cls, result, type_num):
10 |
11 | if type_num == ExecTypes.login:
12 | mobile = result.get("phone")
13 | reason = result.get("reason")
14 |
15 | dataPenetrate = SingleonConfig().getParamsAction()
16 | is_mq = dataPenetrate.get("is_mq")
17 | RabbitMq = dataPenetrate.get("RabbitMq")
18 | master_id = dataPenetrate.get("master_id")
19 | message = "{}:注册登录失败,{}".format(mobile, reason)
20 | ResultMq.send_mq(master_id, message, is_mq, mq_config=RabbitMq)
21 |
22 | dataPenetrate["login"] = True
23 | dataPenetrate["fail"] = True
24 | SingleonConfig().setGroupControlAction(dataPenetrate)
25 |
26 | @classmethod
27 | def success(cls, result, type_num):
28 |
29 | if type_num == ExecTypes.login:
30 | data = result.get("data")
31 | mobile = result.get("phone")
32 |
33 | dataPenetrate = SingleonConfig().getParamsAction()
34 | action = dataPenetrate.get("action")
35 | RabbitMq = dataPenetrate.get("RabbitMq")
36 | is_mq = dataPenetrate.get("is_mq")
37 | master_id = dataPenetrate.get("master_id")
38 |
39 | if data.get("isFirst"):
40 | cc = data.get("cc")
41 | ca = data.get("country")
42 | mobile_insert(mobile, cc, ca, action, master_id)
43 | message = "{}:注册登录成功".format(mobile)
44 | ResultMq.send_mq(master_id, message, is_mq, mq_config=RabbitMq)
45 |
46 | dataPenetrate["login"] = True
47 | dataPenetrate["success"] = True
48 | SingleonConfig().setGroupControlAction(dataPenetrate)
49 |
50 |
--------------------------------------------------------------------------------
/registration/__init__.py:
--------------------------------------------------------------------------------
1 | from .coderequest import WACodeRequest
2 | from .existsrequest import WAExistsRequest
3 | from .regrequest import WARegRequest
4 | from .clientLogRequest import WAClientLogRequest
5 |
6 |
7 | import logging
8 |
9 | logger = logging.getLogger(__name__)
10 | ch = logging.StreamHandler()
11 | # ch.setLevel(logging.DEBUG)
12 |
13 | formatter = logging.Formatter('%(levelname).1s %(asctime)s %(name)s - %(message)s')
14 |
15 | ch.setFormatter(formatter)
16 |
17 | logger.addHandler(ch)
--------------------------------------------------------------------------------
/registration/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/registration/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/registration/__pycache__/coderequest.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/registration/__pycache__/coderequest.cpython-38.pyc
--------------------------------------------------------------------------------
/registration/__pycache__/existsrequest.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/registration/__pycache__/existsrequest.cpython-38.pyc
--------------------------------------------------------------------------------
/registration/__pycache__/regrequest.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/registration/__pycache__/regrequest.cpython-38.pyc
--------------------------------------------------------------------------------
/registration/clientLogRequest.py:
--------------------------------------------------------------------------------
1 | from common.http.warequest import WARequest
2 | from common.http.waresponseparser import JSONResponseParser
3 | from env import MyEnv
4 |
5 |
6 | class WAClientLogRequest(WARequest):
7 |
8 | def __init__(self, config):
9 | """
10 | :param config:
11 | :type config: Config
12 | """
13 | super(WAClientLogRequest,self).__init__(config)
14 | if config.id is None:
15 | raise ValueError("Config does not contain id")
16 |
17 | self.url = "v.whatsapp.net/v2/client_log"
18 |
19 | self.pvars = ["status", "login"]
20 | self.setParser(JSONResponseParser())
21 | self.addParam("token", MyEnv.getCurrent().getToken(self._p_in))
22 |
--------------------------------------------------------------------------------
/registration/coderequest.py:
--------------------------------------------------------------------------------
1 | from common.http.warequest import WARequest
2 | from common.http.waresponseparser import JSONResponseParser
3 | from common.tools import WATools
4 | from registration.existsrequest import WAExistsRequest
5 | from registration.clientLogRequest import WAClientLogRequest
6 |
7 | from env import MyEnv
8 |
9 |
10 | class WACodeRequest(WARequest):
11 | def __init__(self, method, config):
12 | """
13 | :type method: str
14 | :param config:
15 | :type config: Config
16 | """
17 | super(WACodeRequest,self).__init__(config)
18 |
19 | self.addParam("sim_mcc", config.sim_mcc.zfill(3))
20 | self.addParam("sim_mnc", config.sim_mnc.zfill(3))
21 | self.addParam("method", method)
22 | self.addParam("token", MyEnv.getCurrent().getToken(self._p_in))
23 |
24 | self.url = "v.whatsapp.net/v2/code"
25 |
26 | self.pvars = ["status","reason","length", "method", "retry_after", "code", "param"] +\
27 | ["login", "type", "sms_wait", "voice_wait", "notify_after"]
28 | self.setParser(JSONResponseParser())
29 |
30 | def send(self, parser = None, encrypt=True, preview=False):
31 | if self._config.id is not None:
32 | request = WAExistsRequest(self._config)
33 | result = request.send(encrypt=encrypt, preview=preview)
34 |
35 | if result:
36 | if result["status"] == "ok":
37 | return result
38 | elif result["status"] == "fail" and "reason" in result and result["reason"] == "blocked":
39 | return result
40 | else:
41 | self._config.id = WATools.generateIdentity()
42 | self.addParam("id", self._config.id)
43 |
44 | request = WAExistsRequest(self._config)
45 | result = request.send(encrypt=encrypt, preview=preview)
46 | print(result)
47 | request1 = WAClientLogRequest(self._config)
48 | result1 = request1.send(encrypt=encrypt, preview=preview)
49 | print(result1)
50 | self._config.backup_token = WATools.generateBackupToken()
51 | self.addParam("backup_token", self._config.backup_token)
52 |
53 |
54 | res = super(WACodeRequest, self).send(parser, encrypt=encrypt, preview=preview)
55 |
56 | return res
57 |
--------------------------------------------------------------------------------
/registration/existsrequest.py:
--------------------------------------------------------------------------------
1 | from common.http.warequest import WARequest
2 | from common.http.waresponseparser import JSONResponseParser
3 | from env import MyEnv
4 |
5 |
6 | class WAExistsRequest(WARequest):
7 |
8 | def __init__(self, config):
9 | """
10 | :param config:
11 | :type config: Config
12 | """
13 | super(WAExistsRequest,self).__init__(config)
14 | if config.id is None:
15 | raise ValueError("Config does not contain id")
16 |
17 | self.url = "v.whatsapp.net/v2/exist"
18 | # "reason" : "incorrect",
19 | # "sms_wait" : 0,
20 | # "status" : "fail",
21 | # "flash_type" : 0,
22 | # "sms_length" : 6,
23 | # "voice_length" : 6,
24 | # "voice_wait" : 0,
25 | # "login" : "85295640514"
26 | self.pvars = ["status", "reason", "sms_length", "voice_length", "result","param", "login", "type",
27 | "chat_dns_domain", "edge_routing_info"
28 | ]
29 |
30 | self.setParser(JSONResponseParser())
31 | self.addParam("token", MyEnv.getCurrent().getToken(self._p_in))
32 |
--------------------------------------------------------------------------------
/registration/regrequest.py:
--------------------------------------------------------------------------------
1 | from common.http.warequest import WARequest
2 | from common.http.waresponseparser import JSONResponseParser
3 |
4 |
5 | class WARegRequest(WARequest):
6 |
7 | def __init__(self, config, code):
8 | """
9 | :param config:
10 | :type config: Config
11 | :param code:
12 | :type code: str
13 | """
14 | super(WARegRequest,self).__init__(config)
15 |
16 | if config.id is None:
17 | raise ValueError("config.id is not set.")
18 |
19 | self.addParam("code", code)
20 |
21 | self.url = "v.whatsapp.net/v2/register"
22 |
23 | self.pvars = ["status", "login", "autoconf_type", "security_code_set", "type", "reason", "edge_routing_info", "chat_dns_domain"
24 | "reason",]
25 |
26 | self.setParser(JSONResponseParser())
27 |
--------------------------------------------------------------------------------
/resource/WA_Security_WhitePaper.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/resource/WA_Security_WhitePaper.pdf
--------------------------------------------------------------------------------
/versions/whatsapp_2.21.110.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/reversePublic/whatsappShare/1ffa268cd4fa60bb39ad3aefbe30f0417b29bf04/versions/whatsapp_2.21.110.zip
--------------------------------------------------------------------------------