【reverse】新160个CrackMe之116-REM-KeyGenME#10——脱壳、去背景音乐、识别反调试
本帖最后由 hans7 于 2022-9-4 19:05 编辑### 依赖
IDA版本为7.7。
### PETools查看概况
32位程序,`Section: , EP: 0x0001E730`,有壳且暂时不知道是什么壳。
这个exe需要手动脱壳、去背景音乐、有反调试,可惜算法较简单,勉强如参考链接1所说,“列入精品软件”。
**作者:(https://blog.csdn.net/hans774882968)以及(https://juejin.cn/user/1464964842528888)以及(https://www.52pojie.cn/home.php?mod=space&uid=1906177)**
本文juejin:https://juejin.cn/post/7139215208513290253/
本文csdn:https://blog.csdn.net/hans774882968/article/details/126684991
本文52pojie:https://www.52pojie.cn/thread-1683892-1-1.html
### 手动脱壳
看了参考链接1,知道是upx壳,但upx命令脱壳失败。那么我们尝试手动脱壳,跟着参考链接2操作即可。`Sections`一般都是可读写的,如果出现了和参考链接2一样需要改`Section Header`可读写的情况,可用PETools点击`Sections`按钮去修改。
### 去背景音乐
参考链接1找到音乐播放函数的方法是:单步执行,执行到`TDC0:0040108E call sub_4013D9`的时候,发现这句指令执行时间比其他语句长,断定这是音乐播放的函数。x64dbg实测执行时间比其他语句时间长,但感受差别比较小,不容易注意到。
我们用先进的IDA7.7就不需要这么麻烦了。
1. 看到`start`函数的`sub_4013D9`非常显眼,又看到`sub_4013D9`出现了一句`TDC0:0040143D 008 E8 BE 2C 00 00 call waveOutReset`,因此`sub_4013D9`就是播放音乐的函数。
2. 用IDA还可以这么定位:看`imports`标签页,能看到`waveOutReset`等和音频有关的函数,去找交叉引用就行。
直接把`TDC0:0040108E call sub_4013D9`patch掉即可。
### 分析
发现有些代码变成了数据,但因为没有标红的代码,不像是插入了花指令。我们在x64dbg中查看汇编代码,发现x64dbg的识别非常成功。因此我们在IDA中不停地强转代码,直到看不到被识别为数据的代码为止。然后右键`Create function`,成功F5。
`DialogFunc`非常滴显眼:
```cpp
INT_PTR __stdcall DialogFunc(HWND hWnd, UINT a2, HDC hdc, LPARAM a4)
{
HICON IconA; // eax
UINT v6; // eax
int v7; // eax
if ( a2 == 272 )
{
SetWindowTextA(hWnd, String);
IconA = LoadIconA(hInstance, (LPCSTR)0x64);
SendMessageA(hWnd, 0x80u, 1u, (LPARAM)IconA);
}
else
{
if ( a2 == 312 || a2 == 307 )
{
SetTextColor(hdc, 0xFFFFFFu);
return SetBkMode(hdc, 16763904);
}
if ( a2 != 513 )
{
if ( a2 == 0x111 )
{
if ( hdc == (HDC)1001 )
{
v6 = 5 * GetDlgItemTextA(hWnd, 2001, dword_40622C, 51);
if ( v6 > 0xFA || v6 < 0x1E )
{
SetDlgItemTextA(hWnd, 2002, aNameMustBeBetw);
return 0;
}
dword_40625F = GetDlgItemInt(hWnd, 2002, 0, 0);
v7 = sub_4012A8(dword_40622C);
if ( byte_406263 != 1 )
{
if ( !v7 )
{
SetDlgItemTextA(hWnd, 2002, aCongratulation);
return 0;
}
if ( loc_40121C != 0xCC && loc_401223 != 0xCC )
{
SetDlgItemTextA(hWnd, 2002, aSorryThatIsInc);
return 0;
}
}
SetDlgItemTextA(hWnd, 2002, aDebuggerDetect);
return 0;
}
if ( hdc == (HDC)1002 )
{
MessageBoxA(hWnd, Text, Caption, 0x20u);
return 0;
}
if ( hdc != (HDC)1003 )
return 0;
}
else if ( a2 != 16 )
{
return 0;
}
EndDialog(hWnd, 0);
return 0;
}
ReleaseCapture();
SendMessageA(hWnd, 0xA1u, 2u, 0);
}
return 0;
}
```
`dword_40622C`和`dword_40625F`分别是你输入的`Name`和`Serial`。
#### 有反调试哦
`SetDlgItemTextA(hWnd, 2002, aDebuggerDetect);`告诉我们全局变量`byte_406263`是用来反调试的。
关键函数只有`sub_4012A8`,里面耦合了一段反调试逻辑。`sub_4012A8`的`sp-analysis failed`,但因为我们用的是先进的IDA7.7,不用管它直接F5!但反汇编结果仅供参考了。
```cpp
void __stdcall sub_4012A8(_BYTE *a1)
{
int v1; // eax
int v2; // ecx
int v4; // ecx
if ( loc_4012B4 == 0xCC )
goto LABEL_9;
v1 = 0;
v2 = 0;
if ( loc_4012D4 == 0xCC )
goto LABEL_9;
while ( *a1 )
{
LOBYTE(v1) = *a1;
v2 = __ROL4__(v1 + v2, 8);
++a1;
}
v4 = ((v2 ^ 2) - 80) ^ 0x1337;
LOWORD(v4) = SystemTime.wYear + v4;
if ( loc_4012FA == 0xCC )
{
LABEL_9:
byte_406263 = 1;
}
else if ( dword_40625F == v4 )
{
byte_406263 = 0;
}
}
```
那算法很明显了,根据你输入的`Name`算出一个int,要求等于你输入的int类型的`Serial`。最后还需要注意一个点:`dword_40625F = GetDlgItemInt(hWnd, 2002, 0, 0);`,第4个参数为0表示返回`unsigned int`,1表示返回`int`(参考链接3)。因此我们算出的`int`类型的`Serial`要最后额外加一步:转为`unsigned int`。
### 代码
1. 获取系统时间:参考链接4。
2. 用IDA安装目录下可找到的`defs.h`,赋能cpp注册机,打出一套组合👊。
```cpp
#include <bits/stdc++.h>
#include <windows.h>
#include "defs.h"
using namespace std;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)
void dbg() {
puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
cout << f << " ";
dbg (r...);
}
template<typename Type>inline void read (Type &xx) {
Type f = 1;
char ch;
xx = 0;
for (ch = getchar(); ch < '0' || ch > '9'; ch = getchar() ) if (ch == '-') f = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar() ) xx = xx * 10 + ch - '0';
xx *= f;
}
void read() {}
template<typename T, typename ...R>void read (T &x, R &...r) {
read (x);
read (r...);
}
int solve (string inpName) {
int v1 = 0, v2 = 0;
for (auto c : inpName) {
LOBYTE (v1) = c;
v2 = __ROL4__ (v1 + v2, 8);
}
int v4 = ( (v2 ^ 2) - 80) ^ 0x1337;
SYSTEMTIME st;
GetSystemTime (&st);
LOWORD (v4) = st.wYear + v4;
return v4;
}
int main() {
string inpName;
unsigned int ans;
inpName = "hans acmer";
ans = solve (inpName);
dbg (inpName, ans);
inpName = "Hans774882968";
ans = solve (inpName);
dbg (inpName, ans);
inpName = "scuctf";
ans = solve (inpName);
dbg (inpName, ans);
inpName = "hans00";
ans = solve (inpName);
dbg (inpName, ans);
inpName = "HANS774882968";
ans = solve (inpName);
dbg (inpName, ans);
return 0;
}
```
### 参考资料
1. https://www.bilibili.com/video/BV1FF41137c7
2. x64dbg手动脱upx壳:https://zhuanlan.zhihu.com/p/34263050
3. MFC中的`GetDlgItemInt`方法:https://blog.csdn.net/lihui126/article/details/42489267
4. GetSystemTime function:https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemtime a7926454 发表于 2022-9-13 11:42
看懂了吗?
反正我没看懂!
啥武功秘籍放在我前面,我都不认得
查重率0%,偷了!{:1_929:} 注意保重身体 160个 要干死了 谢谢分享 过来学习一下 谢谢分享 Null666yyds 发表于 2022-9-4 08:48
注意保重身体
哈哈哈会的😂 不错哦,可以学习一下
过来学习一下 看看学习
页:
[1]
2