1. insert binary codes manually
    ollydbg or ida patch
    need PE knowledge and tool(lord pe)
  2. hooks
    detour, Winject(not sure if it applys for all exe)
  3. ida pro plugin
    create tracepoint, and run call back to print info each time the tracepoint is hit
    It seemed that the callback is asyn, so it does not meet our needs
  4. write idc scripts to emulate assembly codes
    need some manual work
  5. using APPCALL
    also need some manual work

note:

  1. unserstand E8
    http://stackoverflow.com/questions/10376787/need-help-understanding-e8-asm-call-instruction-x86
  2. ollydbg basic code inject
    http://home.inf.fh-rhein-sieg.de/~ikarim2s/how2injectcode/code_inject.html
  3. add bytes to .text section
    http://www.woodmann.com/forum/archive/index.php/t-14938.html
  4. windbg
    http://blogs.msdn.com/b/johan/archive/2007/11/13/getting-started-with-windbg-part-i.aspx
  5. dword_44018C    dd ?
    It is read by app code in only one place.
    But can not see when this field is written by app code, using hardware bpt can see that it is written by MFC code. That confuses me very much.
    But obviously there should be some app code starting the process of writting it. How to locate this place?
    It should be started when the html page main-showpw.html is requested. Searching main-showpw and find 4075f9 at sub_4075e0.
    So let us read here. at 4076e4, we see:
    .text:004076E4 push 0
    .text:004076E6 push offset unk_440188
    .text:004076EB push offset unk_43DEA4
    .text:004076F0 push edx
    .text:004076F1 call sub_407A50
    .text:004076F6 add     esp, 10h
    after we arrived at 4076f6,  the content of dword_44018C is changed. dword_44018c is indirectly changed through other variable, here it is unk_440188.
    Besides, sub_4075e0 is also called by others, for example, sub_403180,sub_40e130 periodically etc.
  6. Locate the function when a menu or button(windows ctrl) is clicked
    http://www.woodmann.com/krobar/beginner/p08.html
  7. Here is the final solution
     
    #include <idc.idc>
    
    static getNOOfServers()
    {
        auto eax; 
        eax=GetRegValue("eax"); 
    
        return eax;
    }
    
    static getPosOfChar(ea, ch)
    {
        auto value;
        auto i=0;
        while(1){
            value=Byte(ea);
            if(value!=ch){
                i=i+1;
                ea=ea+1;
            } else {
                break;
            }
        }
        return ea;
    }
    
    static getServerList(getIpProc, decryptProc, fp, idx, secx, esp, defaultUser, defaultPass, builtinPass)
    {
        //set ecx for sub_403170
        auto ecx;
        ecx=esp;
        SetRegValue(ecx, "ecx");
    
        auto eax;
        eax=Appcall(getIpProc, "int func(int)", idx);
    
        //restore ecx
        SetRegValue(secx, "ecx");
    
        auto itemAddr, strT;
        itemAddr=Dword(eax);
    
        //step forward until meeting "|"
        itemAddr=getPosOfChar(itemAddr, '|');
        strT=GetStringType(itemAddr);
    
        //get start addr of encrypted ip string 
        auto start, end, ipEncrypted, user;
        start=itemAddr+1;
        end=getPosOfChar(start, '|');
        ipEncrypted=GetString(start, end-start, strT);
    
        //get start addr of user
        start=getPosOfChar(end+1, '|');
        start=start+1;
        end=getPosOfChar(start, '|');
        user=GetString(start, end-start, strT);
        if(user=="no"){
            user=defaultUser;
        }
    
        //get pass
        auto upPairs=Dword(dword_44018c); 
        auto cnt=Dword(dword_440190); 
        auto j, up, upP, pass;
        pass=defaultPass;
        for(j=0;j<cnt+1;j++){
            upP=upPairs+j*4;
            up=Dword(upP);
            up=GetString(up, -1, GetStringType(up));
            auto k=strstr(up, "|");
            auto sub0=up[0:k];
            if(sub0==user){
                pass=up[k+1:];
                break;
            }
        }
    
        auto output, ipDecrypted;
        output=0; 
        //decrypt ip addr
        Appcall(decryptProc, "void func(int*, char*, char*)", &output, ipEncrypted, builtinPass); 
        ipDecrypted=GetString(output, -1, GetStringType(output)); 
        
        //only decrypted pass if user!=defaultUser
        if(user!=defaultUser){
            Appcall(decryptProc, "void func(int*, char*, char*)", &output, pass, builtinPass); 
            pass=GetString(output, -1, GetStringType(output)); 
        }
    
        fprintf(fp, "%s %s %s\n", ipDecrypted, user, pass);
    }
    
    static main()
    {
        AddBpt(0x0040303e);
        StartDebugger("","","");         // start debugger with default params
    
        GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for bpt
    
        //get esp
        auto esp;
        esp=GetRegValue("esp");
        esp=esp+0x260-0x228-0x8;
        //save ecx
        auto secx;
        secx=GetRegValue("ecx");
    
        //read NO. of servers
        auto NO=getNOOfServers();
        Message ("NO. = %d\n", NO); 
    
        //get entry for 2 subs
        auto getIpProc, decryptProc;
        getIpProc=LocByName("sub_403170");
        decryptProc=LocByName("sub_40a170"); 
    
        auto fp=fopen("D:\\Program Files\\ZFVPN\\servers.txt", "w+");
        auto i=0;
        while(i<NO){
            getServerList(getIpProc, decryptProc, fp, i, secx, esp, "-------", "-------", "fz0386");
            i=i+1;
        }
        fclose(fp);
    
        DelBpt(0x0040303e);
        StopDebugger();
    
        GetDebuggerEvent(WFNE_CONT, -1);
    }

  8. the final solution in 7 has one drawback: it crashed sometimes. After some thoughts, it is due to referencing invalid values in stack, that is when running the idc script the stack may be grown/reduced by some thread, so referencing values the thread’s stack is dangerous. See these lines:

    //get esp auto esp;

    esp=GetRegValue("esp");

    esp=esp+0x260-0x228-0x8;

    it saves stack values for later use, this is not recommended, which cause the crashes.

    There is still another problem. What if function that appcall calls is not thread-safe?

    So here is the improved version.

    #include <idc.idc>
    
    static getNOOfServers()
    {
        auto eax; 
        eax=GetRegValue("eax"); 
    
        return eax;
    }
    
    static getPosOfChar(ea, ch)
    {
        auto value;
        auto i=0;
        while(1){
            value=Byte(ea);
            if(value!=ch){
                i=i+1;
                ea=ea+1;
            } else {
                break;
            }
        }
        return ea;
    }
    
    static getServerList(decryptProc, fp, idx, itemStart, defaultUser, defaultPass, builtinPass)
    {
        auto itemAddr;
        itemAddr=Dword(itemStart+idx*4);
    
        //step forward until meeting "|"
        itemAddr=getPosOfChar(itemAddr, '|');
        auto strT;
        strT=GetStringType(itemAddr);
    
        //get start addr of encrypted ip string 
        auto start, end, ipEncrypted, user;
        start=itemAddr+1;
        end=getPosOfChar(start, '|');
        ipEncrypted=GetString(start, end-start, strT);
    
        //get start addr of user
        start=getPosOfChar(end+1, '|');
        start=start+1;
        end=getPosOfChar(start, '|');
        user=GetString(start, end-start, strT);
        if(user=="no"){
            user=defaultUser;
        }
    
        //get pass
        auto upPairs=Dword(dword_44018c); 
        auto cnt=Dword(dword_440190); 
        auto j, up, upP, pass;
        pass=defaultPass;
        for(j=0;j<cnt+1;j++){
            upP=upPairs+j*4;
            up=Dword(upP);
            up=GetString(up, -1, GetStringType(up));
            auto k=strstr(up, "|");
            auto sub0=up[0:k];
            if(sub0==user){
                pass=up[k+1:];
                break;
            }
        }
    
        auto output, ipDecrypted;
        output=0; 
        //decrypt ip addr
        Appcall(decryptProc, "int func(int*, char*, char*)", &output, &ipEncrypted, &builtinPass); 
        ipDecrypted=GetString(output, -1, GetStringType(output)); 
        
        //only decrypted pass if user!=defaultUser
        if(user!=defaultUser){
            Appcall(decryptProc, "int func(int*, char*, char*)", &output, &pass, &builtinPass); 
            pass=GetString(output, -1, GetStringType(output)); 
        }
    
        fprintf(fp, "%s %s %s\n", ipDecrypted, user, pass);
    }
    
    static main()
    {
        AddBpt(0x0040303e);
        StartDebugger("","","");         // start debugger with default params
    
        GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for bpt
    
        //get esp
        auto esp;
        esp=GetRegValue("esp");
        esp=esp+0x260-0x228-0x8;
        auto itemStart;
        itemStart=Dword(esp+4);
    
        //read NO. of servers
        auto NO=getNOOfServers();
        Message ("NO. = %d\n", NO); 
    
        //get entry for 2 subs
        auto decryptProc;
        decryptProc=LocByName("sub_40a170"); 
    
        auto fp=fopen("D:\\Program Files (x86)\\ZFVPN\\servers.txt", "w+");
        auto i=0;
        while(i<NO){
            getServerList(decryptProc, fp, i, itemStart, "ddbbccdd", "abc12345", "fz0386");
            i=i+1;
        }
        fclose(fp);
    
        DelBpt(0x0040303e);
        GetDebuggerEvent(WFNE_CONT, -1);
    
        StopDebugger();
    }

     

  9. To solve the issue stated at point 8, we need to suspend other threads. Fortunately ida provides such api calls: SuspendThread, ResumeThread, GetThreadQty, GetThreadID. So this is the final solution:

    #include <idc.idc>
    
    static getNOOfServers()
    {
        auto eax; 
        eax=GetRegValue("eax"); 
    
        return eax;
    }
    
    static getPosOfChar(ea, ch)
    {
        auto value;
        auto i=0;
        while(1){
            value=Byte(ea);
            if(value!=ch){
                i=i+1;
                ea=ea+1;
            } else {
                break;
            }
        }
        return ea;
    }
    
    static getServerList(decryptProc, fp, idx, itemStart, defaultUser, defaultPass, builtinPass)
    {
        auto itemAddr;
        itemAddr=Dword(itemStart+idx*4);
    
        //step forward until meeting "|"
        itemAddr=getPosOfChar(itemAddr, '|');
        auto strT;
        strT=GetStringType(itemAddr);
    
        //get start addr of encrypted ip string 
        auto start, end, ipEncrypted, user;
        start=itemAddr+1;
        end=getPosOfChar(start, '|');
        ipEncrypted=GetString(start, end-start, strT);
    
        //get start addr of user
        start=getPosOfChar(end+1, '|');
        start=start+1;
        end=getPosOfChar(start, '|');
        user=GetString(start, end-start, strT);
        if(user=="no"){
            user=defaultUser;
        }
    
        //get pass
        auto upPairs=Dword(dword_44018c); 
        auto cnt=Dword(dword_440190); 
        auto j, up, upP, pass;
        pass=defaultPass;
        for(j=0;j<cnt+1;j++){
            upP=upPairs+j*4;
            up=Dword(upP);
            up=GetString(up, -1, GetStringType(up));
            auto k=strstr(up, "|");
            auto sub0=up[0:k];
            if(sub0==user){
                pass=up[k+1:];
                break;
            }
        }
    
        auto output, ipDecrypted;
        output=0; 
        //decrypt ip addr
        Appcall(decryptProc, "int func(int*, char*, char*)", &output, ipEncrypted, builtinPass); 
        ipDecrypted=GetString(output, -1, GetStringType(output)); 
        
        //only decrypted pass if user!=defaultUser
        if(user!=defaultUser){
            Appcall(decryptProc, "int func(int*, char*, char*)", &output, pass, builtinPass); 
            pass=GetString(output, -1, GetStringType(output)); 
        }
    
        fprintf(fp, "%s %s %s\n", ipDecrypted, user, pass);
    }
    
    static resumeOtherThreads(current, threadQty)
    {
        auto l;
        auto threadID;
    
        for(l=0;l<threadQty;l++){
            threadID=GetThreadId(l);
            Message("resumeOtherThreads: ThreadID=%d\n", threadID);
            if(threadID!=current)
                ResumeThread(threadID);
        }
    }
    
    static suspendOtherThreads(current, threadQty)
    {
        auto l;
        auto threadID;
    
        for(l=0;l<threadQty;l++){
            threadID=GetThreadId(l);
            Message("suspendOtherThreads: ThreadID=%d\n", threadID);
            if(threadID!=current)
                SuspendThread(threadID);
        }
    }
    
    static main()
    {
        AddBpt(0x0040303e);
        StartDebugger("","","");         // start debugger with default params
    
        GetDebuggerEvent(WFNE_SUSP, -1); // ... and wait for bpt
    
        //get esp
        auto esp;
        esp=GetRegValue("esp");
        esp=esp+0x260-0x228-0x8;
        auto itemStart;
        itemStart=Dword(esp+4);
    
        //read NO. of servers
        auto NO=getNOOfServers();
        Message ("NO. = %d\n", NO); 
    
        //thread id
        auto current;
        current=GetCurrentThreadId();
        Message("currentThreadID=%d\n", current);
        auto threadQty;
        threadQty=GetThreadQty();
        Message("threadQty=%d\n", threadQty);
        suspendOtherThreads(current, threadQty);
    
        //get entry for 2 subs
        auto decryptProc;
        decryptProc=LocByName("sub_40a170"); 
    
        auto fp=fopen("D:\\Program Files\\ZFVPN\\servers.txt", "w+");
        auto i=0;
        while(i<NO){
            getServerList(decryptProc, fp, i, itemStart, "ddbbccdd", "abc12345", "fz0386");
            i=i+1;
        }
        fclose(fp);
    
        resumeOtherThreads(current, threadQty);
    
        DelBpt(0x0040303e);
        GetDebuggerEvent(WFNE_CONT, -1);
    
        StopDebugger();
    }

  10. Things are changing,zfvpn increase such servers,which only allow one registered user logging only once(only about 200 before,now 3000)。Such server sharing one common user are being decreased from 2000 to about 400. This trend indicates that maximun user for my parasitism is growing less.

Advertisements