Today i'll take a look at this crackme (keygenme to be precise) from MaxXor:
The information about it says that it is written in C, patches are forbidden, the only acceptable solution is keygen and the crackme is graded as level 2 (needs a little brain (or luck)). Good.
First of all, I'll make a small observation.
The file size is quite small, only 6.5KB, so it's most probably highly optimized (no debug information) or written with inline assembly routines, which should make my job easier.
But lets take a closer look, by loading it into PE Explorer.

There's some debug data in there at offset 0x678 and it's 0x6F bytes long.

This is the PDB file information data, added here by the linker.
From the string, we can see it's compiled from Visual Studio 2010, and where it was physically located on the author's PC.
Also, something interesting is the DWORD before that string. It contains a counter, that will increment every time the executable is recreated by the linker.
If that value is legit, this means the author had to compile it only once, so kudos for him, if he wrote the whole crackme in one shot. ;)
Anyways, let's move on to the more important things.
I've loaded the EXE in IDA to get a better look of the structure, and it turns out to be pretty simply written.
There's a dialog process with few cases - create the GUI, some
WM_COMMAND handling for the Register, About and Exit button and so on. Good.
The check routine is written inside the dialog process, starting at
00401426 (I've split the code for clarity).
First there is the length validation:
asm / disassembly00401426 . FF35 18204000 PUSH DWORD PTR DS:[402018] ; /hWnd = 0001042E ('Username... (Example: 1234)',class='Edit',...)
0040142C . FF15 60104000 CALL DWORD PTR DS:[<&USER32.GetWindowTex> ; \GetWindowTextLengthA
00401432 . 83F8 04 CMP EAX,4
00401435 . 0F85 0F030000 JNZ KeygenMe.0040174A
0040143B . FF35 20204000 PUSH DWORD PTR DS:[402020] ; /hWnd = 00010430 ('Serial...',class='Edit',...)
00401441 . FF15 60104000 CALL DWORD PTR DS:[<&USER32.GetWindowTex> ; \GetWindowTextLengthA
00401447 . 83F8 14 CMP EAX,14
0040144A . 0F85 FA020000 JNZ KeygenMe.0040174A
Username should be 4 characters long, and Password should be 0x14 (20 chars long). In case they aren't, it jumps to
0040174A where nothing will happen.
Then a string is moved to a buffer pointed by
EBP-50:
asm / disassembly00401450 . 6A 09 PUSH 9
00401452 . 59 POP ECX
00401453 . BE 30124000 MOV ESI,KeygenMe.00401230 ; ASCII "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
00401458 . 8D7D B0 LEA EDI,DWORD PTR SS:[EBP-50]
0040145B . F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0040145D . A4 MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0040145E . 8365 A4 00 AND DWORD PTR SS:[EBP-5C],0
That string contains the character set used in a valid serial.
After that, the user-typed Name and Serial are stored in two buffers -
EBP-64 (username) and
EBP-28 (serial), that were first zeroed by the memset functions:
asm / disassembly00401462 . 6A 05 PUSH 5
00401464 . 6A 00 PUSH 0
00401466 . 8D45 9C LEA EAX,DWORD PTR SS:[EBP-64]
00401469 . 50 PUSH EAX
0040146A . FF15 0C204000 CALL DWORD PTR DS:[40200C] ; ntdll.memset
00401470 . 83C4 0C ADD ESP,0C
00401473 . 6A 15 PUSH 15
00401475 . 6A 00 PUSH 0
00401477 . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28]
0040147A . 50 PUSH EAX
0040147B . FF15 0C204000 CALL DWORD PTR DS:[40200C] ; ntdll.memset
00401481 . 83C4 0C ADD ESP,0C
00401484 . 6A 05 PUSH 5 ; /Count = 5
00401486 . 8D45 9C LEA EAX,DWORD PTR SS:[EBP-64] ; |
00401489 . 50 PUSH EAX ; |Buffer
0040148A . 6A 66 PUSH 66 ; |ControlID = 66 (102.)
0040148C . FF35 24204000 PUSH DWORD PTR DS:[402024] ; |hWnd = 0003042A ('KeygenMe V6 - Trial',class='keygenme.v6')
00401492 . FF15 30104000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ; \GetDlgItemTextA
00401498 . 6A 15 PUSH 15 ; /Count = 15 (21.)
0040149A . 8D45 D8 LEA EAX,DWORD PTR SS:[EBP-28] ; |
0040149D . 50 PUSH EAX ; |Buffer
0040149E . 6A 67 PUSH 67 ; |ControlID = 67 (103.)
004014A0 . FF35 24204000 PUSH DWORD PTR DS:[402024] ; |hWnd = 0003042A ('KeygenMe V6 - Trial',class='keygenme.v6')
004014A6 . FF15 30104000 CALL DWORD PTR DS:[<&USER32.GetDlgItemTextA>] ; \GetDlgItemTextA
This small loop will then validate the Username:
asm / disassembly004014AC . C645 A1 00 MOV BYTE PTR SS:[EBP-5F],0
004014B0 . C645 ED 00 MOV BYTE PTR SS:[EBP-13],0
004014B4 . 8365 98 00 AND DWORD PTR SS:[EBP-68],0
004014B8 . EB 07 JMP SHORT KeygenMe.004014C1
004014BA > 8B45 98 MOV EAX,DWORD PTR SS:[EBP-68] ; loop begin
004014BD . 40 INC EAX
004014BE . 8945 98 MOV DWORD PTR SS:[EBP-68],EAX
004014C1 > 837D 98 04 CMP DWORD PTR SS:[EBP-68],4
004014C5 . 73 35 JNB SHORT KeygenMe.004014FC
004014C7 . 8B45 98 MOV EAX,DWORD PTR SS:[EBP-68]
004014CA . 0FBE4405 9C MOVSX EAX,BYTE PTR SS:[EBP+EAX-64]
004014CF . 50 PUSH EAX
004014D0 . FF15 10204000 CALL DWORD PTR DS:[402010] ; ntdll.isdigit
004014D6 . 59 POP ECX
004014D7 . 85C0 TEST EAX,EAX
004014D9 . 75 1F JNZ SHORT KeygenMe.004014FA
004014DB . 6A 30 PUSH 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004014DD . 68 24124000 PUSH KeygenMe.00401224 ; |Title = "KeygenMe V6"
004014E2 . 68 00124000 PUSH KeygenMe.00401200 ; |Text = "Please enter a number as username!"
004014E7 . FF35 24204000 PUSH DWORD PTR DS:[402024] ; |hOwner = 0003042A ('KeygenMe V6 - Trial',class='keygenme.v6')
004014ED . FF15 5C104000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>] ; \MessageBoxA
004014F3 . 33C0 XOR EAX,EAX
004014F5 . E9 AB050000 JMP KeygenMe.00401AA5
004014FA >^EB BE JMP SHORT KeygenMe.004014BA ; loop end
004014FC > 8D45 9C LEA EAX,DWORD PTR SS:[EBP-64]
Using the isdigit function, every character form the username is checked. In case the username doesn't contain numbers only, a "
Please enter a number as username!" message will pop out.
If everything is fine, the jump at
004014C5 (
JNB SHORT KeygenMe.004014FC) will be taken, so the correct code flow continues from
004014FC.
From there, we have this code:
asm / disassembly004014FC > 8D45 9C LEA EAX,DWORD PTR SS:[EBP-64]
004014FF . 50 PUSH EAX
00401500 . FF15 14204000 CALL DWORD PTR DS:[402014] ; ntdll.atoi
00401506 . 59 POP ECX
00401507 . 8945 A8 MOV DWORD PTR SS:[EBP-58],EAX
0040150A . 8365 94 00 AND DWORD PTR SS:[EBP-6C],0
0040150E . EB 07 JMP SHORT KeygenMe.00401517
00401510 > 8B45 94 MOV EAX,DWORD PTR SS:[EBP-6C] ; loop begin
00401513 . 40 INC EAX
00401514 . 8945 94 MOV DWORD PTR SS:[EBP-6C],EAX
00401517 > 837D 94 04 CMP DWORD PTR SS:[EBP-6C],4
0040151B . 7D 1D JGE SHORT KeygenMe.0040153A
0040151D . 8B45 94 MOV EAX,DWORD PTR SS:[EBP-6C]
00401520 . 8D0CC5 1000000>LEA ECX,DWORD PTR DS:[EAX*8+10]
00401527 . 8B45 A8 MOV EAX,DWORD PTR SS:[EBP-58]
0040152A . 99 CDQ
0040152B . F7F9 IDIV ECX
0040152D . 0FAF45 A8 IMUL EAX,DWORD PTR SS:[EBP-58]
00401531 . 8B4D 94 MOV ECX,DWORD PTR SS:[EBP-6C]
00401534 . 89448D F0 MOV DWORD PTR SS:[EBP+ECX*4-10],EAX
00401538 .^EB D6 JMP SHORT KeygenMe.00401510 ; loop end
0040153A > 8365 90 00 AND DWORD PTR SS:[EBP-70],0
The username (all numeric) is converted from string to integer by the atoi function.
A loop between
00401510 and
00401538 will fill an array of integers with some calculations made by IDIV and IMUL.
The array has four elements, which are set like so:
C / pseudo codeint username = 1337;
unsigned int t[4] = {0};
for (i = 0; i < 4; i++) {
t[i] = (username/(i*8+0x10))*username;
}
The code from this point and below is the keygen routine and it's doing both calculations and verifications. In other words, the code will verify every calculated number against the entered user, instead of just calculating the whole correct serial and comparing it to the user's serial.
The whole routine code is quite long and I'll again split it in parts (in matter of fact it's actually two almost identical pieces of code, but we'll get to that later):
asm / disassembly0040153E . EB 07 JMP SHORT KeygenMe.00401547
00401540 > 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
00401543 . 40 INC EAX
00401544 . 8945 90 MOV DWORD PTR SS:[EBP-70],EAX
00401547 > 837D 90 14 CMP DWORD PTR SS:[EBP-70],14
0040154B . 0F83 C5010000 JNB KeygenMe.00401716
From the code above, i can see that
EBP-70 is the loop iterator that is INCed by 1 on every run, and when the iterator hits 0x14 bytes, the code flow jumps to the loop exit at 00401716.
Something worth mentioning here is the way that some simple calculations are made:
asm / disassembly00401540 > 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
00401543 . 40 INC EAX
00401544 . 8945 90 MOV DWORD PTR SS:[EBP-70],EAX
;...
004015B8 . 8B45 88 MOV EAX,DWORD PTR SS:[EBP-78]
004015BB . 40 INC EAX
004015BC . 8945 88 MOV DWORD PTR SS:[EBP-78],EAX
;...
00401608 . 8B45 8C MOV EAX,DWORD PTR SS:[EBP-74]
0040160B . 83E8 0A SUB EAX,0A
0040160E . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
I don't know if that's intentional, to stretch out the code or just the compiler went "yeah, whatever" and did his job that way.
And another thing is this one:
asm / disassembly004015ED . 40 INC EAX
004015EE . 40 INC EAX
004015EF . 40 INC EAX
004015F0 . 48 DEC EAX
004015F1 . 48 DEC EAX
004015F2 . 48 DEC EAX
;"The code! It does nothing!" - Me
While I'm not sure about the previous code, I'm pretty sure this one was added intentionally, maybe to mislead the reverser. :)
Moving on.
asm / disassembly00401551 . 837D 90 00 CMP DWORD PTR SS:[EBP-70],0
00401555 . 74 3B JE SHORT KeygenMe.00401592
00401557 . 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
0040155A . 99 CDQ
0040155B . 6A 05 PUSH 5
0040155D . 59 POP ECX
0040155E . F7F9 IDIV ECX
00401560 . 85D2 TEST EDX,EDX
00401562 . 75 2E JNZ SHORT KeygenMe.00401592
00401564 . 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
00401567 . 0FBE4405 D8 MOVSX EAX,BYTE PTR SS:[EBP+EAX-28]
0040156C . 83E8 2D SUB EAX,2D
0040156F . F7D8 NEG EAX
00401571 . 1BC0 SBB EAX,EAX
00401573 . 40 INC EAX
00401574 . 8845 AF MOV BYTE PTR SS:[EBP-51],AL
00401577 . 0FB645 AF MOVZX EAX,BYTE PTR SS:[EBP-51]
0040157B . 85C0 TEST EAX,EAX
0040157D . 75 07 JNZ SHORT KeygenMe.00401586
0040157F . E9 92010000 JMP KeygenMe.00401716
00401584 . EB 07 JMP SHORT KeygenMe.0040158D
00401586 > 8B45 A4 MOV EAX,DWORD PTR SS:[EBP-5C]
00401589 . 40 INC EAX
0040158A . 8945 A4 MOV DWORD PTR SS:[EBP-5C],EAX
0040158D > E9 7F010000 JMP KeygenMe.00401711
00401592 > 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
At the beginning
00401551 address, the iterator is compared to 0, and if there's a match, the code flow jumps to
00401592.
So, at the first run, the loop will skip the code inside, and on every next run it will execute it.
Between
00401557 and
00401562, the iterator is modulo divided by 5, and if the condition is true, the code between
00401564 and
0040157D will execute.
Something obvious is that
SUB EAX,2D.
0x2D is the ASCII character "-", and the
SUB;NEG;SBB;INC is a fancy way to "compare" it with the value
[EBP+EAX-28] holds.
I already know that
EBP-28 is the user's entered serial number, and EAX (
EBP-70) is the loop iterator, so what this code does is to check that every 5th character of the correct serial number is "-".
If we apply that logic against a string of "*"'s, we'll get this:
*****-****-****-****
Good.
I can now enter some username -
1337 and some faulty serial -
11111-2222-3333-4444 to start debugging from here.
Here's what it seems to be a "is signed" check, but because
EBP-70 is the iterator that is limited to
0x14 only, the
DEC;OR;INC instructions should never be executed.
asm / disassembly00401592 > 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
00401595 . 25 01000080 AND EAX,80000001
0040159A . 79 05 JNS SHORT KeygenMe.004015A1
0040159C . 48 DEC EAX
0040159D . 83C8 FE OR EAX,FFFFFFFE
004015A0 . 40 INC EAX
004015A1 > 85C0 TEST EAX,EAX
004015A3 . 0F85 B8000000 JNZ KeygenMe.00401661
004015A9 . 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
;...
The author maybe added (unsigned int) before the JNS?
Anyway, the second
TEST;JNZ is important.
Because EAX is first ANDed with
0x80000001, then TESTed, this code might be represented as:
C / pseudo codeif ((EBP-70 % 2) == 0) {
// continue to 004015A9
} else {
// jump to 00401661
}
So, it will execute different code every time the loop runs.
Moving on:
asm / disassembly004015A9 . 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
004015AC . 99 CDQ
004015AD . 83E2 03 AND EDX,3
004015B0 . 03C2 ADD EAX,EDX
004015B2 . C1F8 02 SAR EAX,2
004015B5 . 8945 88 MOV DWORD PTR SS:[EBP-78],EAX
004015B8 . 8B45 88 MOV EAX,DWORD PTR SS:[EBP-78]
004015BB . 40 INC EAX
004015BC . 8945 88 MOV DWORD PTR SS:[EBP-78],EAX
004015BF . 837D 88 03 CMP DWORD PTR SS:[EBP-78],3
004015C3 . 7E 09 JLE SHORT KeygenMe.004015CE
004015C5 . 8B45 88 MOV EAX,DWORD PTR SS:[EBP-78]
004015C8 . 83E8 04 SUB EAX,4
004015CB . 8945 88 MOV DWORD PTR SS:[EBP-78],EAX
004015CE > 8B45 88 MOV EAX,DWORD PTR SS:[EBP-78]
004015D1 . 8B4485 F0 MOV EAX,DWORD PTR SS:[EBP+EAX*4-10]
004015D5 . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
004015D8 . 8B4D 90 MOV ECX,DWORD PTR SS:[EBP-70]
004015DB . 41 INC ECX
004015DC . 8B45 8C MOV EAX,DWORD PTR SS:[EBP-74]
004015DF . 99 CDQ
004015E0 . F7F9 IDIV ECX
004015E2 . 8955 8C MOV DWORD PTR SS:[EBP-74],EDX
004015E5 . 8B45 8C MOV EAX,DWORD PTR SS:[EBP-74]
004015E8 . D1E0 SHL EAX,1
004015EA . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
See how stretchy the code is?
If we just simplify it (in pseudo assembler, that is), we'll get this:
asm / disassembly004015A9 . MOV EAX,iterator
004015B0 . ADD EAX,3
004015B2 . SAR EAX,2
004015B5 . MOV DWORD PTR SS:[EBP-78],EAX+1
004015BF . CMP DWORD PTR SS:[EBP-78],3
004015C3 . JLE SHORT KeygenMe.004015CE
004015C5 . SUB DWORD PTR SS:[EBP-78],4
004015CE > MOV EAX,DWORD PTR SS:[EBP-78]
004015D1 . MOV EAX,DWORD PTR SS:[EBP+EAX*4-10]
004015E0 . IDIV iterator+1
004015EA . MOV DWORD PTR SS:[EBP-74],EDX<<1
or converted into pseudo code:
C / pseudo codei = EBP-70;
a = EBP-78;
b = EBP-74;
a = ((i+3)>>2)+1;
if (a > 3) {
a -= 4;
}
b = ((EBP-10[a]) % (i + 1)) << 1;
Simple.
Finally we got this:
asm / disassembly;004015ED . 40 INC EAX
;004015EE . 40 INC EAX
;004015EF . 40 INC EAX
;004015F0 . 48 DEC EAX
;004015F1 . 48 DEC EAX
;004015F2 . 48 DEC EAX
004015F3 . 837D 8C 09 CMP DWORD PTR SS:[EBP-74],9
004015F7 . 7F 09 JG SHORT KeygenMe.00401602
004015F9 . 8B45 8C MOV EAX,DWORD PTR SS:[EBP-74]
004015FC . 83C0 04 ADD EAX,4
004015FF . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
00401602 > 837D 8C 24 CMP DWORD PTR SS:[EBP-74],24
00401606 . 7C 09 JL SHORT KeygenMe.00401611
00401608 . 8B45 8C MOV EAX,DWORD PTR SS:[EBP-74]
0040160B . 83E8 0A SUB EAX,0A
0040160E . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
00401611 > 837D 8C 00 CMP DWORD PTR SS:[EBP-74],0
00401615 . 7C 06 JL SHORT KeygenMe.0040161D
00401617 . 837D 8C 23 CMP DWORD PTR SS:[EBP-74],23
0040161B . 7E 09 JLE SHORT KeygenMe.00401626
0040161D > 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
00401620 . 83C0 07 ADD EAX,7
00401623 . 8945 8C MOV DWORD PTR SS:[EBP-74],EAX
;00401626 > 40 INC EAX
;00401627 . 40 INC EAX
;00401628 . 40 INC EAX
;00401629 . 48 DEC EAX
;0040162A . 48 DEC EAX
;0040162B . 48 DEC EAX
0040162C . 8B45 90 MOV EAX,DWORD PTR SS:[EBP-70]
0040162F . 0FBE4405 D8 MOVSX EAX,BYTE PTR SS:[EBP+EAX-28]
00401634 . 8B4D 8C MOV ECX,DWORD PTR SS:[EBP-74]
00401637 . 0FBE4C0D B0 MOVSX ECX,BYTE PTR SS:[EBP+ECX-50]
0040163C . 2BC1 SUB EAX,ECX
0040163E . F7D8 NEG EAX
00401640 . 1BC0 SBB EAX,EAX
00401642 . 40 INC EAX
00401643 . 8845 AF MOV BYTE PTR SS:[EBP-51],AL
00401646 . 0FB645 AF MOVZX EAX,BYTE PTR SS:[EBP-51]
0040164A . 85C0 TEST EAX,EAX
0040164C . 75 07 JNZ SHORT KeygenMe.00401655
See the
INC EAX/DEC EAX that I mentioned before. Yeah, anyway.
The code here is pretty straight forward, and the
INC/DEC's are just helping me to split it in two parts. :)
First part between 004015F3 and 00401623:
C / pseudo codeif (EBP-74 <= 9) {
EBP-74 += 4;
}
if (EBP-74 >= 0x24) {
EBP-74 -= 0x0A;
}
if (EBP-74 < 0 || EBP-74 > 0x23) {
EBP-74 = i+7;
}
and the second part between
0040162C and
0040164C (again using that fancy
SUB;NEG;SBB;INC check):
C / pseudo codeuser_serial = "11111-2222-3333-4444"
charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (user_serial[i] != charset[EBP-74]) {
// whoops, too bad! try again.
} else {
// continue with the next calculation
}
Now I mention that the calculation code is repeated twice, and that can be easily observed in IDA's graph view:
However, there is a slight difference on that
EBP-74 += 4; code, and it's depending on the result of
i%2.
Long story short, the rule is:
-case 1-
if i%2 (i = 0,2,4,6,8,10...) equals 0, add 4 to
EBP-74
-case 2-
if i%2 (i = 1,3,5,7,9...) is not equal to 0, add 3 to
EBP-74
So, let's write some test code!
Cchar username[] = "1337"; // I'll use that name
char serial[0x15] = ""; // Here I'll store the valid serial
char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Characters used to create a valid serial
int name_int, i, i_name_sum, i_serial; // Some integers that might be handy
unsigned int name_sums[4] = {0}; // The four elements array used for the username
Good. Now fill the name_sums array...
Cint name_int = atoi(username);
for (i = 0; i < 4; i++) {
name_sums[i] = (name_int/(i*8+0x10))*name_int;
}
... and init the generator loop:
Cfor (i = 0; i < 0x14; i++) {
// the following code goes here
}
First I'll add the dash ('-') character routine:
Cif (i > 0 && ((i%5)==0)) { // rule is: i bigger than 0 and i modulo of 5 equal to 0
serial[i] = '-'; // set a '-' char
continue; // move to next iteration, and skip the code below
}
The code that calculates which item from the name_sums array will be used:
Ci_name_sum = (i >> 2) + 1;
if (i_name_sum > 3) { // if i_name_sum is bigger than 3 (eg. it's 4)
i_name_sum -=4; // subtract 4 from it, so it always point somewhere inside name_sums array
}
i_serial = (name_sums[i_name_sum] % (i + 1)) << 1;
Now the character "limit" validators:
Cif (i_serial <= 9) {
if ((i % 2) == 0) {
i_serial += 4; // if i = 0,2,4,6,8... (eg. even number), add 4
} else {
i_serial += 3; // if i = 1,3,5,7,9... (eg. odd number), add 3
}
}
if (i_serial >= 0x24) { // If i_serial exceeds or equals the charset string length
i_serial -= 0xA; // subtract 0x0A
}
if (i_serial < 0 || i_serial > 0x23) { // if i_serial is lesser than 0 and bigger than 0x23
i_serial = i+7; // replace it with i+7
}
Finally the i_serial is used as pointer to the correct character, so we just add:
Cserial[i]=*(char*)&charset[i_serial];
There. The code is ready, lets test it.
For username
"1337" I got
"45898-45A5-IAE6-KAAU", so:
Good. Full keygen code with some bells and whistles added can be downloaded from here:
http://nullsecurity.org/download/0e7bc578ebae69d632f0d4842cf13bfb
Comments
* You have an opinion? Let us all hear it!