前一阵突然出现推出了这个破解关第四题,看了看没啥思路,题意大概就是让你反编译一个小程序,然后具体分析达到功能的方法,最后再仿写一个。
分析程序这一步不多说了,大概就是查端口,抓包,做些特殊数据的测试。端口我用的是冰刃,或者命令行netstat自己找也行,抓包我用的WSExporer可以进程抓包,比较方便
,可以在我的资源里下载。
通过端口和抓包大致可以知道,软件运行的过程是这样的。
现在2012端口建立socket连接,然后通过4次通信交换数据,最后开始阻塞模式下的聊天。
service -- > client
0B 0A 45 54
client --> service
41 0C 00 00 00 00 00 00 65 04 0E 53 00 00 00 00
service --> client
21 97 00 00 00 00 00 00 FB 70 6F 77 00 00 00 00
client --> service
0B 0A 45 54
聊天过程中的抓包,在这里发现通信过程被某种方法加密了,上面的4次数据交换应该是对密钥的初始化。
service-->client(1111)
21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00
21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00
client-->service(1111)
26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00
26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00
收集了足够多的信息,最后进行反编译,工具使用olldbg,通过ULtra String Reference发现程序很友善的在注释中给出了提示,
大素数一,大素数二什么的,明显使用了RSA加密算法,不知道什么是RSA的同学可以看这里
http://www.cnblogs.com/happinessCodes/archive/2010/07/27/1786404.html
http://apps.hi.baidu.com/share/detail/12565498
最后就是蛋疼的分析,汇编语句了,通过PEID知道了程序使用c++编写的,首先需要找到main函数的入口点,c++中main函数是这样声明的
前两个参数应该都熟悉,第三个参数是系统的一些,环境变量,如%system% %windows% 时间,什么的,
如就是说如果发现一些获取系统环境变量的call,很大的可能就是靠近main函数的入口点了。
int main(int argc,char* argv[],char* envp[])
main函数的call之前必定有三个push来压入参数,类似于这样
004161D6 A1 D8014200 MOV EAX,DWORD PTR DS:[4201D8]
004161DB 50 PUSH EAX
004161DC 8B0D DC014200 MOV ECX,DWORD PTR DS:[4201DC]
004161E2 51 PUSH ECX
004161E3 8B15 D4014200 MOV EDX,DWORD PTR DS:[4201D4]
004161E9 52 PUSH EDX
004161EA E8 FFB0FFFF CALL Thief.004112EE ; main函数入口
跟进这个call,就可以慢慢分析代码了,小技巧是根据push的参数来判断,call调用的是什么函数,比如push了端口和ip地址,肯定是scoket初始化的函数。
还有汇编中push参数是从右向左。
还有调试过程中ctrl + F9 执行到返回 alt + F9 执行到用户态代码,是很有用的,可以让我们从一些不知名的代码中跳出来。
在汇编命令上点右键,查看参考,可以查看到语句中一些常量,比如地址什么的。
这一点也很有用,因为经常,会发现在应当出现立即数的地方出现了一些奇怪的东西,比如
004161C8 8B0D C4144200 MOV ECX,DWORD PTR DS:[<&MSVCR100D.__initenv>] ; MSVCR100.__initenv
艰苦的单步汇编就不多说了,结果如下(其实不是我写的O(∩_∩)O哈哈~)
定义:服务端为Police 客户端为Thief
---------------------------- 建立连接 ------------------------------
服务端:
0012E704 004145C7 /CALL 到 accept 来自 Police.004145C2
0012E708 00000074 |Socket = 74
0012E70C 0012F6EC |pSockAddr = 0012F6EC
0012E710 0012E7E4 \pAddrLen = 0012E7E4
客户端:
0012DB0C 0041531D /CALL 到 connect 来自 Thief.00415318
0012DB10 00000070 |Socket = 70
0012DB14 0012EAF0 |pSockAddr = 0012EAF0
0012DB18 00000010 \AddrLen = 10 (16.)
0012EAF0 02 00 07 DC 7F 00 00 01 CC CC CC CC CC CC CC CC .?..烫烫烫烫
---------------------------- 开始通信 ------------------------------
/** 第一次通信 开始交换公钥的信号**/
服务端:
0012E700 004157F8 /CALL 到 send 来自 Police.004157F3
0012E704 00000084 |Socket = 84
0012E708 0012F6FC |Data = 0012F6FC
0012E70C 00000004 |DataSize = 4
0012E710 00000000 \Flags = 0
0012F6FC 0B 0A 45 54
客户端:
0012DAFC 00415479 /CALL 到 recv 来自 Thief.00415474
0012DB00 00000070 |Socket = 70
0012DB04 0012EF0C |Buffer = 0012EF0C
0012DB08 000003E8 |BufSize = 3E8 (1000.)
0012DB0C 00000000 \Flags = 0
0012EF0C 0B 0A 45 54 00 CC CC CC CC CC CC CC CC CC CC CC .ET.烫烫烫烫烫
0012EF1C CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫烫烫
......
/** 第二次通信 客户端向服务端发送公钥**/
客户端:
0012DAFC 00415518 /CALL 到 send 来自 Thief.00415513
0012DB00 00000070 |Socket = 70
0012DB04 0012EB20 |Data = 0012EB20
0012DB08 00000010 |DataSize = 10 (16.)
0012DB0C 00000000 \Flags = 0
(这里发送的内容有变化)
0012EB20 41 0C 00 00 00 00 00 00 65 04 0E 53 00 00 00 00 A.......eS....
服务端:
0012E700 00415759 /CALL 到 recv 来自 Police.00415754
0012E704 00000084 |Socket = 84
0012E708 0012FAE8 |Buffer = 0012FAE8
0012E70C 000003E8 |BufSize = 3E8 (1000.)
0012E710 00000000 \Flags = 0
0012FAE8 41 0C 00 00 00 00 00 00 65 04 0E 53 00 00 00 00 A.......eS....
0012FAF8 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC .烫烫烫烫烫烫烫
......
/** 第三次通信 服务端向客户端发送公钥**/
服务器端:
0012E700 004157F8 /CALL 到 send 来自 Police.004157F3
0012E704 00000084 |Socket = 84
0012E708 0012F6FC |Data = 0012F6FC
0012E70C 00000010 |DataSize = 10 (16.)
0012E710 00000000 \Flags = 0
(发送的内容有变化)
0012F6FC 21 97 00 00 00 00 00 00 FB 70 6F 77 00 00 00 00 !?.....鹥ow....
客户端:
0012DAFC 00415479 /CALL 到 recv 来自 Thief.00415474
0012DB00 00000070 |Socket = 70
0012DB04 0012EF0C |Buffer = 0012EF0C
0012DB08 000003E8 |BufSize = 3E8 (1000.)
0012DB0C 00000000 \Flags = 0
0012EF0C 21 97 00 00 00 00 00 00 FB 70 6F 77 00 00 00 00 !?.....鹥ow....
0012EF1C 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC .烫烫烫烫烫烫烫
/** 第四次通信 结束公钥交换的信号**/
客户端:
0012DAFC 00415518 /CALL 到 send 来自 Thief.00415513
0012DB00 00000070 |Socket = 70
0012DB04 0012EB20 |Data = 0012EB20
0012DB08 00000004 |DataSize = 4
0012DB0C 00000000 \Flags = 0
0012EB20 0B 0A 45 54 .ET
服务器端:
0012E700 00415759 /CALL 到 recv 来自 Police.00415754
0012E704 00000084 |Socket = 84
0012E708 0012FAE8 |Buffer = 0012FAE8
0012E70C 000003E8 |BufSize = 3E8 (1000.)
0012E710 00000000 \Flags = 0
0012FAE8 0B 0A 45 54 .ET
-----------------------------------------
服务器请求输入状态,输入:1111
-----------------------------------------
服务端:
0012E700 004157F8 /CALL 到 send 来自 Police.004157F3
0012E704 00000084 |Socket = 84
0012E708 0012F6FC |Data = 0012F6FC
0012E70C 00000020 |DataSize = 20 (32.)
0012E710 00000000 \Flags = 0
0012F6FC 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012F70C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
客户端:
0012DAFC 00415479 /CALL 到 recv 来自 Thief.00415474
0012DB00 00000070 |Socket = 70
0012DB04 0012EF0C |Buffer = 0012EF0C
0012DB08 000003E8 |BufSize = 3E8 (1000.)
0012DB0C 00000000 \Flags = 0
0012EF0C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012EF1C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012EF2C 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC .烫烫烫烫烫烫烫
-----------------------------------------
客户端请求输入状态,输入:2222
-----------------------------------------
客户端:
0012DAFC 00415518 /CALL 到 send 来自 Thief.00415513
0012DB00 00000070 |Socket = 70
0012DB04 0012EB20 |Data = 0012EB20
0012DB08 00000020 |DataSize = 20 (32.)
0012DB0C 00000000 \Flags = 0
0012EB20 26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00 &敟....&敟....
0012EB30 26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00 &敟....&敟....
服务端:
0012E700 00415759 /CALL 到 recv 来自 Police.00415754
0012E704 00000084 |Socket = 84
0012E708 0012FAE8 |Buffer = 0012FAE8
0012E70C 000003E8 |BufSize = 3E8 (1000.)
0012E710 00000000 \Flags = 0
0012FAE8 26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00 &敟....&敟....
0012FAF8 26 94 A5 12 00 00 00 00 26 94 A5 12 00 00 00 00 &敟....&敟....
0012FB08 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC .烫烫烫烫烫烫烫
-----------------------------------------
服务器请求输入状态,输入:1111
-----------------------------------------
服务端:
0012E700 004157F8 /CALL 到 send 来自 Police.004157F3
0012E704 00000084 |Socket = 84
0012E708 0012F6FC |Data = 0012F6FC
0012E70C 00000020 |DataSize = 20 (32.)
0012E710 00000000 \Flags = 0
0012F6FC 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012F70C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
客户端:
0012DAFC 00415479 /CALL 到 recv 来自 Thief.00415474
0012DB00 00000070 |Socket = 70
0012DB04 0012EF0C |Buffer = 0012EF0C
0012DB08 000003E8 |BufSize = 3E8 (1000.)
0012DB0C 00000000 \Flags = 0
0012EF0C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012EF1C 21 25 30 39 00 00 00 00 21 25 30 39 00 00 00 00 !%09....!%09....
0012EF2C 00 CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC .烫烫烫烫烫烫烫
0012EF3C CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC 烫烫烫烫烫烫烫烫
------------------------------------------------
客户端发送数据缓冲区:
EDX=0012EB20
堆栈地址=0012FF0C
EAX=0000000B
0012FF0C 31 8E 00 00 00 00 00 00 57 F6 50 AD 00 00 00 00 1?.....W鯬?...
最后我们得出了,程序的工作机制如下,
在本地生成私钥与公钥,在通信之前把公钥发送给对方,通信过程中使用私钥加密,对方用公钥翻译出原文。
还有一点需要注意,公钥是逆序传输的。
最后就是代码了,纯苦力活,有些调试用的语句没有去掉。
需要注意几点,
(1)使用char型存储revc到的数据时最好用unsigned char,这样在吧char转换成整形进行运算时,不会出现负数。
(2)c中最大的整形是__int 64,c++的一些编译器可以用long long,java中可以用bignum,这个类可以做到非常大,
对应的还有一个高精度浮点类,使用它可以消除浮点数运算的误差。
(3)不要轻易使用sprint函数来进行一些,string到int或者int到string的转换,因为零会被忽略掉。
(4)网络字节顺序和你的主机字节顺序可能是相反地。
(5)一定要会写快速密,并且用上位运算,不然会被岛君鄙视。
(6)相信自己,有可能你的想法是对的,但是程序出错了。
// client.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <WINSOCK2.H>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma comment(lib,"ws2_32.lib")
typedef unsigned __int64 UINT64;
typedef unsigned char UCHAR;
SOCKET clientSocket;
UINT64 clientP_n,clientP_e,clientS_d,clientS_n;
UINT64 serviceP_n,serviceP_e;
inline void MUL(UINT64 &a, UINT64 b,UINT64 mod){a = (UINT64)a * b % mod;}
inline UINT64 pow(UINT64 a, UINT64 b,UINT64 mod){
UINT64 c = 1;
while (b) {
if (b&1) MUL(c, a ,mod);
MUL(a, a ,mod), b >>= 1;
}
return c;
}
UINT64 RSADecode(char* x,UINT64 t,UINT64 n)
{
char ret_str[20];
UINT64 ret=0;
UINT64 m=1;
for(int i=0;i<4;i++){
ret+=m*(UCHAR)x[i];
m*=256;
}
/*sprintf(ret_str,"%x%x%x%x",(UCHAR)x[3],(UCHAR)x[2],(UCHAR)x[1],(UCHAR)x[0]);
ret = strtoul(ret_str,NULL,16);*/
//printf("x in decode %d\n",ret);
return pow(ret,t,n);
}
void RSACode(UINT64 x,UINT64 t,UINT64 n,char* ret_str)
{
//char* ret_str = new char[8] ;
UINT64 ret=0;
ret = pow(x,t,n);
//printf("x in code %d\n",ret);
UINT64 m=256;
int i;
for(i=0;i<3;i++){
ret_str[i]=ret%m;
ret/=m;
}
ret_str[3]=ret;
//sprintf(ret_str,"%x%x%x%x",num[0],num[1],num[2],num[3]);
for(i=4;i<8;i++)
ret_str[i]=0;
return;
}
void initServicePublicKey(char* publicKey)
{
serviceP_n=0;
serviceP_e=0;
char serviceP_n_str[20];
char serviceP_e_str[20];
UINT64 m=1;
/*sprintf(serviceP_e_str,"%x%x",(UCHAR)publicKey[1],(UCHAR)publicKey[0]);
sprintf(serviceP_n_str,"%x%x%x%x",(UCHAR)publicKey[11],(UCHAR)publicKey[10],(UCHAR)publicKey[9],(UCHAR)publicKey[8]);
serviceP_n = strtoul(serviceP_n_str,NULL,16);
serviceP_e = strtoul(serviceP_e_str,NULL,16);*/
serviceP_e = (UCHAR)publicKey[0] + (UCHAR)publicKey[1]*256;
for(int i=8;i<12;i++){
serviceP_n+=(UCHAR)publicKey[i]*m;
m*=256;
}
printf("%lu\n",serviceP_n);
printf("%lu\n",serviceP_e);
}
void initClientRSAKey(){
clientS_d=3137;
clientS_n=1393427557;
clientP_n=1393427557;
clientP_e=690679973;
}
void initRSA()
{
char receiveBuf[1000];
char sendBuf[1000];
printf("----------------recive1----------------------\n");
recv(clientSocket,receiveBuf,1000,0);
for(int i=0;i<4;i++)
printf("0x%x\n",receiveBuf[i]);
printf("----------------recive2----------------------\n");
sendBuf[0]=0x41;
sendBuf[1]=0x0c;
sendBuf[2]=0x00;
sendBuf[3]=0x00;
sendBuf[4]=0x00;
sendBuf[5]=0x00;
sendBuf[6]=0x00;
sendBuf[7]=0x00;
sendBuf[8]=0x65;
sendBuf[9]=0x04;
sendBuf[10]=0x0e;
sendBuf[11]=0x53;
sendBuf[12]=0x00;
sendBuf[13]=0x00;
sendBuf[14]=0x00;
sendBuf[15]=0x00;
send(clientSocket,sendBuf,sizeof(char)*16,0);
recv(clientSocket,receiveBuf,1000,0);
for(int j=0;j<16;j++)
printf("0x%x\n",(UCHAR)receiveBuf[j]);
initServicePublicKey(receiveBuf);
printf("--------------------------------------\n");
sendBuf[0]=0x0b;
sendBuf[1]=0x0a;
sendBuf[2]=0x45;
sendBuf[3]=0x54;
send(clientSocket,sendBuf,sizeof(char)*4,0);
initClientRSAKey();
initServicePublicKey(receiveBuf);
}
void startCommunication(){
char receiveBuf[1000];
int recvLen;
char sendBuf[1000];
char codeBuf[1000];
char* codeBufCat = new char[8];
int i,j;
while(true){
recvLen=recv(clientSocket,receiveBuf,1000,0);
printf("\n[Command] @Police : ");
int t=0;
while (t<recvLen)
{
char str[8];
strncpy(str,receiveBuf+t,8);
printf("%c",RSADecode(str,clientP_e,clientP_n));
t+=8;
}
//===============================================================
printf("\n[Answer] @Thief : ");
scanf("%s",sendBuf);
printf("%d\n",strlen(sendBuf));
for(i=0;i<strlen(sendBuf);i++){
RSACode((UCHAR)sendBuf[i],serviceP_e,serviceP_n,codeBufCat);
for(j=i*8;j<(i+1)*8;j++)
codeBuf[j]=codeBufCat[j-i*8];
}
//RSACode('a',serviceP_e,serviceP_n,codeBufCat);
for(i=0;i<8*strlen(sendBuf);i++)
printf("%x",(UCHAR)codeBuf[i]);
printf("\n");
send(clientSocket,codeBuf,8*strlen(sendBuf),0);
}
}
void initSocket()
{
int err;
WORD versionRequired;
WSADATA wsaData;
versionRequired=MAKEWORD(1,1);
err=WSAStartup(versionRequired,&wsaData);//协议库的版本信息
if (!err)
{
printf("客户端嵌套字已经打开!\n");
}
else
{
printf("客户端的嵌套字打开失败!\n");
return;//结束
}
clientSocket=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN clientsock_in;
clientsock_in.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
clientsock_in.sin_family=AF_INET;
clientsock_in.sin_port=htons(2012);
connect(clientSocket,(SOCKADDR*)&clientsock_in,sizeof(SOCKADDR));//开始连接
initRSA();
startCommunication();
closesocket(clientSocket);
WSACleanup();
}
void main()
{
initSocket();
/*initClientRSAKey();
char x='a';
char* str = new char[8];
RSACode(x,clientS_d,clientS_n,str);
for(int i=0;i<8;i++)
printf("%x\n",(unsigned char)str[i]);
printf("%c\n",RSADecode(str,clientP_e,clientP_n));
printf("%I64u %I64u\n",pow(x,clientS_d,clientS_n),pow(pow(x,clientS_d,clientS_n),clientP_e,clientS_n));
*/
}