ISCC 2012 破解关第四题 我已经不蛋疼啦~

前一阵突然出现推出了这个破解关第四题,看了看没啥思路,题意大概就是让你反编译一个小程序,然后具体分析达到功能的方法,最后再仿写一个。

分析程序这一步不多说了,大概就是查端口,抓包,做些特殊数据的测试。端口我用的是冰刃,或者命令行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));
	*/
	
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值