Solution for Challenge 3 is bruteforce. The trick is to check every characters of the plaintext to figure out which nibbles it affect in encoded string. The rule is each nibble of encoded string is only affected by one char of plaintext and the character at higher position get higher priority if there’s a collision.

Then I declare struct and arrays like this :

typedef struct
{
    int nibble_1;
    int nibble_2;
} affect;

// string used for bruteforce
char s[20] = "1111111111111111111";

// encoded string got from plaintext s
char result[51]="";

// encoded string we need to get from plaintext s
char final[51]="A1 FD 7E F6 F0 70 98 D6 E5 F8 FF F8 78 B8 DE ED 0D";

affect a[20] = \{\{1,-1},{0,4},{3,7},{6,10},{9,-1},{12,13},{15,16},
                {18,19},{22,-1},{21,25},{24,28},{27,31},{30,-1},
                {33,34},{36,37},{39,40},{43,-1},{42,46},{45,48\}\};

And here is the bruteforce function:

int Brut(int index)
{
    if (index==19) {
        if (result[strlen(result)-1]==final[strlen(final)-1])
            return 1;
        return 0;
    }
    for (int j=32;j<127;j++)
    {
        s[index]=j;
        ::SendDlgItemMessageA(hwndEncoder,1000,WM_SETTEXT,0,(LPARAM)s);
        ::SendMessage(hwndEncoder, WM_COMMAND, MAKEWPARAM(1002, BN_CLICKED),
                     (LPARAM)hwndEncodingButton);
        Sleep(50);
        ::SendDlgItemMessageA(hwndEncoder,1001,WM_GETTEXT,51,(LPARAM)result);
        if (((result[a[index].nibble_1]==final[a[index].nibble_1])
           &&(((a[index].nibble_2==-1)||(a[index].nibble_2!=-1)
           &&(result[a[index].nibble_2]==final[a[index].nibble_2]))) )
            && (strlen(result)==strlen(final)))
        {
            if (Brut(index+1))
                return 1;
        }
    }
    return 0;
}

Main

hwndEncoder = FindWindowA(NULL,"Encoder");
        hwndEncodingButton = FindWindowA(NULL,"Encoding");
        Brut(0);