用于整人用的,立刻挂断时阿里通不收费。
流程:写程序向客户端发送消息,实现拨号功能;修改客户端,方便我们的程序获知对方正在响铃,然后立刻挂断。
一、文件脱壳 客户端加了个ASPACK 2.12的壳
(1)先清除ASLR标记(ASLR用于随机程序载入基地址,最好先清除掉)
(2)直接使用脱壳机脱壳。
二、自动拨号功能
原理:自动拨号,很自然的思路就是把子窗体控件找出来,然后从外部发送消息过来,实现按钮点击。
(1)原先的思路是使用spy++把子控件都找出来,但是分析发现阿里通使用了第三方UI库(SOUI),SPY++什么都找不到。
(2)那就直接调试——产生按键的消息找出来。
直接拉入OD,直接在API DispatchMessage()中设置消息条件断点,拦截单击释放事件。
拦截条件为:[[esp+4]+4]==WM_LBUTTONUP,即左键单击释放事件。很快断下来了。可以发现这个UI库是通过事件
发生时的坐标轴(即消息结构体的LPARAM)来辨别是哪个按键被单击的,所以找到所有的数字按键的lparam参数,直接记录
下来,之后只要发送WM_LBUTTONUP消息给主窗口,填充下lparam参数就行了,宏定义如下:
- //找到的lparam参数如下
- #define BTN_1 0x010d01d1
- #define BTN_2 0x010d0221
- #define BTN_3 0x010d0301
- #define BTN_4 0x014d01d1
- #define BTN_5 0x014d0221
- #define BTN_6 0x014d0301
- #define BTN_7 0x018d01d1
- #define BTN_8 0x018d0267
- #define BTN_9 0x018d0301
- #define BTN_0 0x01bd0267
- #define BTN_BACKSPACE 0x009c02f1 //回退按钮
- #define BTN_CALL 0x02320260 //拨打按钮
- #define BTN_STOP 0x01db02c1 //挂断按钮
- #define BTN_DIAL_PAGE 0x00AB0024 //播放页面
三、瞬间挂断实现
瞬间挂断功能比较复杂。当响铃时,可以看到的变化如下:(即中间的文字由"正在呼叫..." 变成 "正在响铃...")
一个想法是抓包分析,找到告诉客户端对面正在响铃的包,但是很快放弃了。这么多包,可能还加密了,很难分析。
另一个想法就是,找到SOUI这个第三方UI设置TEXT文本的API,虽然对这个UI库一点都不懂,但很容易猜到API的名
字应该带有text。其dll文件为soui.dll,于是在od查找此模块所有api,找到以下7个候选API:
- 5440161B .text 输出 SOUI::CSimpleWnd::SetWindowTextW
- 543CDC62 .text 输出 SOUI::SComboBase::SetWindowTextW
- 543E6759 .text 输出 SOUI::SRichEdit::SetWindowTextW
- 543FC984 .text 输出 SOUI::SWindow::SetWindowTextW
-
- 543CB871 .text 输出 SOUI::SLink::DrawTextW
- 543CB76A .text 输出 SOUI::SStatic::DrawTextW
- 543FA10F .text 输出 SOUI::SWindow::DrawTextW
全部设置断点,然后拨打号码测试。很快就发现SWindow::SetWindowTextW 断点下"正在响铃..."参数。
定位到关键API。现在的问题就是怎么告诉我们程序,已经开始响铃了。 想法一:修改API,如果参数为"正在响铃...",
就告诉我们程序开始响铃了。(但是不知道修改DLL怎么保存) 想法二:查看调用这个API的主模块语句。很幸运地发现,
调用次API的那个语句只用来传递"正在响铃...",也就是不和其他语句混用。所以直接修改这里就行了。 原先的汇编语句为:
(我这边主模块基地址为0x008c0000,以下语句地址为0x008d80e4)
- mov edx,dword ptr ds[ebx+0x30
- push eax //字符串"正在响铃..."
- call edx //调用SWindow::SetWindowText
修改后为:
- 地址008d80e4:(6个字节替换 )
- jmp 0x00af2610
- nop
-
- 地址:00af2610(原先为0填充区域)
- pushad
- mov edx,dword ptr ds:[ebx+0x30
- push eax
- inc [0xaf260c //注意:此处我们设置一个叠加器表示是否已经响铃了,我们程序再通过ReadProcessMemory定时读取这个值
- call edx
- popad
- jmp 008d80ea //继续原来的流程
这样程序就改好了.注意:上面我们使用0填充区域的4个字节,设置累加器,用于判断响铃是否发生(初始值为4,期间API执行两次变成6,
为什么是不是一次我也不知道,但就是两次,表示正在响铃),我们程序读取这个值(ReadProcessMemory)后,会再将其置4。
四、外部程序
登录上述修改后的客户端,执行这个C++程序,传入电话号码,就会自动拨号了,响铃瞬间自动挂断)
(使用修改后的阿里通客户端,才可以实现自动挂断功能。超过1MB好像没办法上传)
- #include<windows.h>
- #include<stdio.h>
-
- #define CNT_ADDR 0x00af260c
- #define BTN_1 0x010d01d1
- #define BTN_2 0x010d0221
- #define BTN_3 0x010d0301
- #define BTN_4 0x014d01d1
- #define BTN_5 0x014d0221
- #define BTN_6 0x014d0301
- #define BTN_7 0x018d01d1
- #define BTN_8 0x018d0267
- #define BTN_9 0x018d0301
- #define BTN_0 0x01bd0267
- #define BTN_BACKSPACE 0x009c02f1
- #define BTN_CALL 0x02320260
- #define BTN_STOP 0x01db02c1
- #define BTN_DIAL_PAGE 0x00AB0024
- static int btn_pos[12]={BTN_0,BTN_1,BTN_2,BTN_3,BTN_4,BTN_5,BTN_6,BTN_7,BTN_8,BTN_9};
- int read_count(HWND hwnd){ //读取阿里通客户端,我们设置的累加器。其初值为4。当其为6时(因为SWindow::SetWindowTest会执行两次,
- 我也不知道为什么两次),表示正在响铃
- int ret;
- DWORD pid;
- GetWindowThreadProcessId(hwnd,&pid);
- HANDLE hProcess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION |
- PROCESS_VM_READ | PROCESS_VM_WRITE, 0, pid);
- if (hProcess==0){printf("OpenProcess失败...\n");exit(-1);}
- DWORD bytes_read=0;
- bool bIsOk=ReadProcessMemory(hProcess,(LPCVOID)CNT_ADDR,(LPVOID)&ret,4,&bytes_read);
- if (!bIsOk || bytes_read!=4){
- printf("进程读取失败...ok=%d bytes_read=%d\n",bIsOk,bytes_read);
- exit(-1);
- }
- CloseHandle(hProcess);
- //printf("ret=%d\n",ret);
- return ret;
- }
- int reset_count(HWND hwnd){ //将累加器重置为0
- DWORD pid;
- GetWindowThreadProcessId(hwnd,&pid);
- HANDLE hProcess=OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_
- VM_OPERATION | PROCESS_VM_READ |
- PROCESS_VM_WRITE, 0, pid);
- if (hProcess==0){printf("OpenProcess失败...\n");exit(-1);}
-
- char data[4];data[0]=0x4;data[1]=0x0;data[2]=0;data[3]=0;
- //int *k=(int*)data;
- //printf("k=%x\n",*k);
- DWORD bytes_write;
- bool bIsOk=WriteProcessMemory(hProcess,(LPVOID)CNT_ADDR,data,4,&bytes_write);
- if (!bIsOk || bytes_write!=4){printf("进程写入失败...\n");exit(-1);}
- if (read_count(hwnd)!=4){
- printf("unknown error!\n");
- }
- CloseHandle(hProcess);
- return 0;
- }
- int waitForRing(HWND hwnd){ //等待"正在响铃..."出现
- if (hwnd==0){printf("hwnd=0\n");exit(-1);}
- while (true){
- if (read_count(hwnd)==6){
- //printf("start ringing...!\n");
- reset_count(hwnd);
- break;
- }
- Sleep(100);
- }
- return 0;
- }
- class ALI{
- public:
- HWND hwnd;
- int click(int pos){
- if (hwnd)SendMessage(hwnd,WM_LBUTTONUP,0,pos);
- else exit(-1);
- return 0;
- }
- int clear(){ //清空电话栏
- for (int i=0;i<30;i++) {
- if (click(BTN_BACKSPACE)==-1)return -1;
- }
- return 0;
- }
- int call(char *phone){ //发送消息自动按键,并拨号
- hwnd=FindWindow(0,"阿里通");
- //printf("hwnd=%x\n",hwnd);
- click(BTN_DIAL_PAGE);
- //exit(-2);
- clear();
- int len=strlen(phone);
- if (len>11 || len<8)return -1;
- for (int i=0;i<len;i++){
- click(btn_pos[phone[i]-'0']);
- }
- click(BTN_CALL);
- //printf("waitForRing...\n");
- waitForRing(hwnd);
-
- click(BTN_STOP);
- Sleep(5000);
- return 0;
- }
- };
-
- int main(int argc,char** argv){
- ALI ali;
- if (argc<2) return -1;
- ali.call(argv[1]);
- return 0;
- }
-
|