仲介DLLによるlisten関数の書き換え

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
shiro4ao

仲介DLLによるlisten関数の書き換え

#1

投稿記事 by shiro4ao » 15年前

仲介DLLを作って、wsock32.dllのlistenが呼ばれたら、
特定のサーバーに接続するようにしたいです。
listexp2.exeにより、仲介DLLを作って、VC++2008で
listenを書き換えようとしましたが、

1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _WSAGetLastError@0 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _connect@12 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _closesocket@4 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _WSAAsyncSelect@16 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _gethostbyname@4 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _htons@4 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。
1>_dwsock32.dll_dummy.obj : error LNK2019: 未解決の外部シンボル _socket@12 が関数 "void __stdcall d_listen(int,int)" (?d_listen@@YGXHH@Z) で参照されました。

とのエラーがでました、

以下がVC++2008のプロジェクトのうち改変した部分ですです。

==========_dwsock32_dummy.cpp==============================================
// _dwsock32.dll_dummy.cpp : デフォルトの処理を行うエクスポート関数
//

#include <windows.h>

#include "_dwsock32.dll.h"

__declspec( naked ) void WINAPI d_accept() { _asm{ jmp p_accept } }
__declspec( naked ) void WINAPI d_bind() { _asm{ jmp p_bind } }
__declspec( naked ) void WINAPI d_closesocket() { _asm{ jmp p_closesocket } }
__declspec( naked ) void WINAPI d_connect() { _asm{ jmp p_connect } }
__declspec( naked ) void WINAPI d_getpeername() { _asm{ jmp p_getpeername } }
__declspec( naked ) void WINAPI d_getsockname() { _asm{ jmp p_getsockname } }
__declspec( naked ) void WINAPI d_getsockopt() { _asm{ jmp p_getsockopt } }
__declspec( naked ) void WINAPI d_htonl() { _asm{ jmp p_htonl } }
__declspec( naked ) void WINAPI d_htons() { _asm{ jmp p_htons } }
__declspec( naked ) void WINAPI d_inet_addr() { _asm{ jmp p_inet_addr } }
__declspec( naked ) void WINAPI d_inet_ntoa() { _asm{ jmp p_inet_ntoa } }
__declspec( naked ) void WINAPI d_ioctlsocket() { _asm{ jmp p_ioctlsocket } }
__declspec( dllexport ) void WINAPI d_listen(int sockfd, int backlog);
//↑ここを書き換えた
__declspec( naked ) void WINAPI d_listen() { _asm{ jmp p_listen } }
__declspec( naked ) void WINAPI d_ntohl() { _asm{ jmp p_ntohl } }
__declspec( naked ) void WINAPI d_ntohs() { _asm{ jmp p_ntohs } }
~~~中略~~~~~
__declspec( naked ) void WINAPI d_NONAME1138() { _asm{ jmp p_NONAME1138 } }
__declspec( naked ) void WINAPI d_TransmitFile() { _asm{ jmp p_TransmitFile } }
__declspec( naked ) void WINAPI d_AcceptEx() { _asm{ jmp p_AcceptEx } }
__declspec( naked ) void WINAPI d_GetAcceptExSockaddrs() { _asm{ jmp p_GetAcceptExSockaddrs } }


__declspec( dllexport ) void WINAPI d_listen(int sockfd, int backlog){



// SendMessage(hWnd,IDB_CONNECT,0,0);
#define WM_SOCKET (WM_USER+1) //ソケット用メッセージ
unsigned short PORT=12345;
HWND hWnd;
HOSTENT *phe; //HOSTENT構造体
SOCKET sock = INVALID_SOCKET; //ソケット
SOCKET sv_sock = INVALID_SOCKET; //サーバ用ソケット
SOCKADDR_IN sv_sin; //SOCKADDR_IN構造体

SOCKADDR_IN cl_sin; //SOCKADDR_IN構造体

//ソケットを開く
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //ソケット作成失敗

memset(&cl_sin, 0x00, sizeof(cl_sin)); //構造体初期化
cl_sin.sin_family = AF_INET; //インターネット
cl_sin.sin_port = htons(PORT); //ポート番号指定

phe=gethostbyname("localhost");
memcpy(&cl_sin.sin_addr, phe->h_addr, phe->h_length); //アドレス値格納

//非同期モード (接続)
if(WSAAsyncSelect(sock, hWnd, WM_SOCKET, FD_CONNECT)==SOCKET_ERROR){
closesocket(sock);
sock=INVALID_SOCKET;
MessageBox(hWnd, "WSAAsyncSelect() failed", "Error",
MB_OK|MB_ICONEXCLAMATION);
}

//接続処理
if(connect(sock, (LPSOCKADDR)&cl_sin, sizeof(cl_sin))==SOCKET_ERROR){
if(WSAGetLastError()!=WSAEWOULDBLOCK){
closesocket(sock);
sock=INVALID_SOCKET;
MessageBox(hWnd, "connect() failed", "Error",
MB_OK|MB_ICONEXCLAMATION);
}
}



}
==========ここまで==============================================================

コレで大丈夫だと思ったのですが・・・・

どこがまずいのでしょう。

ベリ工

Re:仲介DLLによるlisten関数の書き換え

#2

投稿記事 by ベリ工 » 15年前

以下を試してどうなりますか?

1:*.defファイルを定義してみる。
2:呼び出し規約がexeとdll側で一致しているか?(両者共に__stdcallか?)
3:dll側のコード生成のランタイムライブラリは適切か?

nishi

Re:仲介DLLによるlisten関数の書き換え

#3

投稿記事 by nishi » 15年前

単なるリンクエラーのように見受けられます。
#pragma comment(lib, "ws2_32.lib")
を追加してみてください。

ベリ工

Re:仲介DLLによるlisten関数の書き換え

#4

投稿記事 by ベリ工 » 15年前

おおっ!ほんとだ!!
リンクエラーをよく見るとwinsockの関数が引っかかっていますね!!
てっきり自作関数と思いこんでしまった・・・orz

nishiさんの回答で正解でしょうね。
適切なwinsockのライブラリをリンクすると解決するでしょう。

shiro4ao

Re:仲介DLLによるlisten関数の書き換え

#5

投稿記事 by shiro4ao » 15年前

!!
ベリエ様、nishi様ありがとうございます!リンクできました!
・・・が、実行してみると、
---------------------------
Microsoft Visual C++ Debug Library
---------------------------
Debug Error!
Program: C:\dlltest\chat.exe
Module: C:\dlltest\XSOCK32.DLL
File:
Run-Time Check Failure #3 - The variable 'hWnd' is being used without being initialized.
(Press Retry to debug the application)
---------------------------
中止(A) 再試行(R) 無視(I)
---------------------------

というダイアログボックスが出てきました、どうやら、
意味のない「hWnd」なるものがあったようです・・・

listen()を書き換え、connect()にするにはDLLを呼び出したプログラム内のソケット(sock)と
親ウィンドウハンドル(hWnd)をDLL内の処理に渡さないといけないのですが、
DLLへどうやって教えてあげればいいのでしょう…

よくわかっていなくて申し訳ないです…

softya

Re:仲介DLLによるlisten関数の書き換え

#6

投稿記事 by softya » 15年前

WSAAsyncSelect自体がウィンドウを必要とする関数です。
現状、hWndに何も値が入っていない様にお見受けましす。

Win32APIを使ったウィンドウは作成出来ますか?
それが出来ていないなら、まずウィンドウアプリの作り方から勉強すべきだと思います。
http://wisdom.sakura.ne.jp/system/winapi/index.html

shiro4ao

Re:仲介DLLによるlisten関数の書き換え

#7

投稿記事 by shiro4ao » 15年前

状況の説明がまずかったと思います…すみません
GUIで非同期チャットソフト(以下chat1.exe)を作りました。
ところが、chat1.exeにはサーバー機能しかつけていなかったため
クライアント機能が欲しくなりました、ソースを書き換えてもいい
のですが、DLLインジェクションの勉強も兼ねて、
chat1.exeが呼び出すlisten()関数を書き換えて
無理やり、クライアントにして、サーバー機能のみのchat2.exeに接続
してしまおう、と考えました。
この方法がそもそもDLLインジェクションでは無理なのでしょうか?

DLL内の偽の(=書き換えた)listen()にDLLを呼び出したプログラムの
・ウィンドウハンドル
・接続用ソケット
・ポート番号
・接続先ホスト名
をわたし、ソケットを返すような方法ができるのでしょうか…?

それとも根本からおかしいのでしょうか?

softya

Re:仲介DLLによるlisten関数の書き換え

#8

投稿記事 by softya » 15年前

>DLL内の偽の(=書き換えた)listen()にDLLを呼び出したプログラムの
>・ウィンドウハンドル
>・接続用ソケット
>・ポート番号
>・接続先ホスト名
>をわたし、ソケットを返すような方法ができるのでしょうか…?


DLL側には情報を受け取る専用関数を用意して呼び出し元のコードを修正してDLL側に情報を渡してください。DLLインジェクションとしては、中途半端になる気がしますが。

それと心配なのは、ウィンドウメッセージがちゃんと処理されるかです。そこら辺は確認をお願いします。

shiro4ao

Re:仲介DLLによるlisten関数の書き換え

#9

投稿記事 by shiro4ao » 15年前

ウィンドウハンドルは名前で探せばなんとかなるし、
ポート番号やホスト名はDLLのほうで決めてしまってもいいですが
ソケットだけは、呼び出し元に返してあげないといけないので、
やっぱり、呼び出し元のコードを書き換えるしかないでしょうか・・・・

softya

Re:仲介DLLによるlisten関数の書き換え

#10

投稿記事 by softya » 15年前

>ウィンドウハンドルは名前で探せばなんとかなるし、
そこまでするとそのDLLインジェクションは汎用性が皆無になります。
そうすることに意味があるんでしょうか?

>ソケットだけは、呼び出し元に返してあげないといけないので、
listen()のパラメータにある元のソケットを使ってはいけなんでしょうか?
bind()やらaccept()もインジェクションしてしまえば、ごまかせる気がしますけどね。
そうすれば、WSAAsyncSelectも不要になりません?

サーバー側のコードを見てないので勘ですけどね。

shiro4ao

Re:仲介DLLによるlisten関数の書き換え

#11

投稿記事 by shiro4ao » 15年前

>listen()のパラメータにある元のソケットを使ってはいけなんでしょうか?
そうですね、このパラメータを使えばよかったのですね。

以下が変更した偽DLL
==================偽wsock32.dll==========================
__declspec( dllexport ) void WINAPI d_listen(int sock, int backlog)
unsigned short PORT=5555;
HOSTENT *phe; //HOSTENT構造体
SOCKADDR_IN cl_sin; //SOCKADDR_IN構造体

//ソケットを開く
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //ソケット作成失敗

memset(&cl_sin, 0x00, sizeof(cl_sin)); //構造体初期化
cl_sin.sin_family = AF_INET; //インターネット
cl_sin.sin_port = htons(PORT); //ポート番号指定
phe=gethostbyname("localhost"); //アドレス取得
memcpy(&cl_sin.sin_addr, phe->h_addr, phe->h_length); //アドレス値格納
/*
//非同期モード (接続)
if(WSAAsyncSelect(sock, hWnd, WM_SOCKET, FD_CONNECT)==SOCKET_ERROR){
closesocket(sock);
sock=INVALID_SOCKET;
MessageBox(hWnd, "WSAAsyncSelect() failed", "Error",
MB_OK|MB_ICONEXCLAMATION);
}
*/
//接続処理
connect(sock, (LPSOCKADDR)&cl_sin, sizeof(cl_sin));
}

=====================ここまで=======================================

ここからがプログラム本体の接続待ちの関数です。
====================chat1.cpp====================================
int SockAccept(HWND hWnd,unsigned short PORT)
{
SOCKADDR_IN sv_sin;
sv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
memset(&sv_sin, 0x00, sizeof(sv_sin));
sv_sin.sin_family = AF_INET;
sv_sin.sin_port = htons(PORT);
sv_sin.sin_addr.s_addr = htonl(INADDR_ANY);
bind(sv_sock, (LPSOCKADDR)&sv_sin, sizeof(sv_sin));
listen(sv_sock, 1); //これが偽のlisten()になる
WSAAsyncSelect(sv_sock, hWnd, WM_SOCKET, FD_ACCEPT);
return 0;
}
====================ここまで============================================

としたのですが、なぜか、WSAAsyncSelect()がうまく行っていないのか、
接続完了のメッセージなどが飛んでこないようです。
どこがまずいのでしょう・・・

softya

Re:仲介DLLによるlisten関数の書き換え

#12

投稿記事 by softya » 15年前

>としたのですが、なぜか、 WSAAsyncSelect()がうまく行っていないのか、
>接続完了のメッセージなどが飛んでこないようです。
>どこがまずいのでしょう・・・

順番的にlisten前にWSAAsyncSelecでは?
あとDLL側でsocketを開いちゃまずいですよ。

shiro4ao

Re:仲介DLLによるlisten関数の書き換え

#13

投稿記事 by shiro4ao » 15年前

>順番的にlisten前に WSAAsyncSelecでは?
chat1.exeのソースを書き換えておきました。

>あとDLL側でsocketを開いちゃまずいですよ。
listen()でもらったソケットを使ってconnect()で接続してみました。


listen()を偽DLL内で書き換えconect()を使って接続
listen()に渡されるのは接続待ち用のサーバーソケットなので
accept()を書き換え、受け取ったサーバーソケットを何もせずreturnするようにする
これにより、クライアントソケットに接続に使ったソケットで通信できるはず、

としたのですが、WSAAsyncSelect()が、FD_ACCEPTを発行してくれないのです
(接続しているのだから)当然と言えば当然ですが・・・・
解決法としては
「DLLないでWSAAsyncSelect()を書き換え、FD_CONNECTのときには
 FD_ACCEPTにして発行してもらうようにする」
という強引な方法を思いつきました、
ところが、どう書き換えていいのかわかりません。

それとも、こんなバカな方法でなくてもいいのでしょうか?

閉鎖

“C言語何でも質問掲示板” へ戻る