学破解第104天,《XPS to PDF v2.1工具》分析
# 学破解第104天,《XPS to PDF v2.1工具》分析前言:
  一直对黑客充满了好奇,觉得黑客神秘,强大,无所不能,来论坛两年多了,天天看各位大佬发帖,自己只能做一个伸手党。也看了官方的入门视频教程,奈何自己基础太差,看不懂。自我反思之下,决定从今天(2019年6月17日)开始定下心来,从简单的基础教程开始学习,希望能从照抄照搬,到能独立分析,能独立破解。
不知不觉学习了好几个月,发现自己离了教程什么都不会,不懂算法,不懂编程。随着破解学习的深入,楼主这个半吊子迷失了自我,日渐沉迷水贴装X,不能自拔。
==========申明:从第71天楼主开始水贴装X,帖子不再具有连续性,仅供参考,后续帖子为楼主YY专用贴!!!==========
立帖为证!--------记录学习的点点滴滴
## 0x1起源
  今天上来论坛逛一逛,发现消息又是红色的,点开,果然又是什么有偿破解,有偿脱壳。我就是一菜鸟,我也想钱,但是知道自己几斤几两,干不了,也从来不理这些。
翻了一番,发现了两个值得回复的帖子:
第一个:
  你好 我也是一个小白 看到你的励志与勇气使我也鼓起勇气来尝试学习破解现在也在看ximo的视频现在卡在第四个视频上边了OD用的是吾爱专用的那个版本 和ximo老师的貌似不是一个版本然后发现添加的load PE 无法像你和老师那样以及Import REC这个软件爱盘里也没找到想问问如何去搞到呢
第二个:
  XPS to PDF v2.1(http://www.adultpdf.com/products/xpstopdf/index.html),这个软件可以把XPS文件转换为PDF,我试着爆破,去除了注册窗口,但注册采用重启验证,转换文件后,有水印,后来发现水印是在DLL文件中,加了ASPROTECT壳,脱壳?
  看过我的广播贴的人都知道,我也是从零基础小白,慢慢从脱壳,再到官方教程一步一步过来的,很感激小糊涂虫大哥在我刚开始学脱壳的时候解答我的疑问。看到这样的帖子就能想到当初的我,第一个坛友的问题,我就建议他用论坛XP虚拟机入门,集成了很多工具,然后把爱盘Import REC的下载地址复制给他了。
  第二个问题,这位兄弟可算把我问住了,我只脱过exe的,dll怎么脱壳???接下来重点分析一下这个软件。
## 0x2下载安装软件
  1.去官网下载软件:
  2.安装软件,看一下有哪些文件,看一下程序窗口。
## 0x3 dll查壳与脱壳
  1.查壳,xpstopdf.dll这个文件确实显示ASProtect V2.X DLL壳。
  2.载入OD,使用Loaddll.exe加载,停在了
```
104F8001 >60 pushad
104F8002 E8 03000000 call xpstopdf.104F800A
104F8007- E9 EB045D45 jmp 55AC84F7
104F800C 55 push ebp
104F800D C3 retn
```
看到pushad就能想到esp定律,F8一次,看右侧窗口寄存器esp:0012F868,然后在底部命令行输入hr 0012F868下硬件访问断点,SHIFT+F9
```
00D10140 5C pop esp ; LoadDl_1.60001057
00D10141 03C3 add eax,ebx ; LoadDl_1.<ModuleEntryPoint>
00D10143 894424 1C mov dword ptr ss:,eax ; xpstopdf.10336074
00D10147 61 popad
00D10148- FFE0 jmp eax ; xpstopdf.10336074
```
看到jmp eax,啥也不用说,F8一次,到达OEP了
```
10336074 55 push ebp
10336075 8BEC mov ebp,esp
10336077 53 push ebx ; LoadDl_1.<ModuleEntryPoint>
10336078 8B5D 08 mov ebx,dword ptr ss: ; LoadDl_1.60000000
1033607B 56 push esi
1033607C 8B75 0C mov esi,dword ptr ss:
1033607F 57 push edi
10336080 8B7D 10 mov edi,dword ptr ss:
10336083 85F6 test esi,esi
```
  3.此时,就要LordPE登场了,找到Loaddll,然后在下面找到对应的dll,完整转存出去,如下图所示操作。
  4.接下来用Import REC修复,同样选择Loaddll程序,然后点击右侧的选取dll,输入OEP:6074,自动查找IAT,获取输入表,发现有两个无效函数,使用等级1修复下,不行。
  5.既然不能自动查找IAT,那我就手动来一下,载入程序,直接在00403000(data段下断点),SHIFT+F9,发现了大批已经解密的函数,数据窗口上下翻
```
1034401077EFEB2Agdi32.CreateFontIndirectA
1034401477EF6BFAgdi32.DeleteObject
1034401877EF5B70gdi32.SelectObject
1034401C77EFF170gdi32.GetFontData
1034402077EF9BB3gdi32.GetTextCharset
1034402400000000
103440287C92FE01ntdll.RtlGetLastWin32Error
1034402C2D6EBEA7
103440307C812FD9kernel32.GetStdHandle
103440347C809BE7kernel32.CloseHandle
103440387C80DE9Ekernel32.DuplicateHandle
1034403CC6BA3C80
10344040B9FDB276
103440447C801A28kernel32.CreateFileA
103440487C861967kernel32.GetTempFileNameA
1034404C78C25C39
103440507C834EE1kernel32.FindNextFileA
103440547C813879kernel32.FindFirstFileA
10344058658C7799
1034405C7C81127Akernel32.GetVersion
10344060114D6361
10344064B7C27B4E
1034406837AB20EA
1034406C4F396723
10344070C5B0DA6C
1034407464B7C86A
103440787C80A874kernel32.GetLocalTime
1034407C7C809F91kernel32.InitializeCriticalSection
10344080D75862B5
1034408400D37574
103440887C801D7Bkernel32.LoadLibraryA
1034408C7C9210E0ntdll.RtlLeaveCriticalSection
1034409043A4BFDC
103440947C812B7Ekernel32.GetVersionExA
103440981DC0A88F
1034409CF4796686
103440A07C80A174kernel32.WideCharToMultiByte
103440A4CE1E9093
103440A8056CF040
103440AC58E14692
103440B07C80A3FEkernel32.CompareStringW
103440B47C80D117kernel32.CompareStringA
103440B87C80BE56kernel32.lstrlenA
103440BC7C80934Akernel32.GetTickCount
103440C07C84495Dkernel32.SetUnhandledExceptionFilter
103440C467918E31
103440C87C93135Antdll.RtlDeleteCriticalSection
103440CC3EE1D004
103440D048BC326E
103440D47C812FA8kernel32.GetEnvironmentStringsW
103440D87C81CC93kernel32.GetEnvironmentStringsA
103440DC7C814B87kernel32.FreeEnvironmentStringsW
103440E0941C1F01
103440E47C921000ntdll.RtlEnterCriticalSection
103440E82E0F27D6
103440ECF8482181
103440F0FC1CD367
103440F462968FB4
103440F8BA7C4140
103440FC76736655
103441007C81CB12kernel32.ExitProcess
1034410463BDF8D3
103441087C80981Akernel32.InterlockedDecrement
1034410C7C809806kernel32.InterlockedIncrement
103441107C92FF0Dntdll.RtlFreeHeap
103441147C9300A4ntdll.RtlAllocateHeap
1034411864C03497
1034411C7C801812kernel32.ReadFile
10344120627D0AAE
103441245D1723DF
103441287C939B80ntdll.RtlReAllocateHeap
1034412C7EF8AE4D
103441307C94ABA5ntdll.RtlUnwind
1034413455B468A1
103441388E72A59B
1034413C7C812AA9kernel32.RaiseException
10344140AF088F91
1034414485A8BCE5
103441487C809C65kernel32.TlsSetValue
1034414C7C812E3Fkernel32.TlsAlloc
103441507C813777kernel32.TlsFree
103441547C92FE10ntdll.RtlSetLastWin32Error
103441587C8097E0kernel32.TlsGetValue
1034415CE8ED396D
103441607C810C2Ekernel32.SetFilePointer
103441649D591745
10344168BAACD0C0
1034416C7C809C98kernel32.MultiByteToWideChar
103441708F2D6606
103441747C80A530kernel32.GetStringTypeW
10344178746A6CEAMSCTF.746A6CEA
1034417C7C80CD48kernel32.LCMapStringW
10344180E5D1BBB8
103441847C810F98kernel32.HeapDestroy
103441887C812C56kernel32.HeapCreate
1034418C7C809B84kernel32.VirtualFree
103441907C809AF1kernel32.VirtualAlloc
103441948D1D27D2
103441983741FCF5
1034419C00000000
103441A065247650
103441A477D24A4Euser32.EndDialog
103441A877D2AAFDuser32.PostMessageA
103441ACC2E42721
103441B077D1869Duser32.ReleaseDC
103441B477D186C7user32.GetDC
103441B877D3C2E7user32.SendDlgItemMessageA
103441BC00000000
103441C072F74D40winspool.ClosePrinter
103441C472F77A78winspool.EndDocPrinter
103441C872F77837winspool.WritePrinter
103441CC72F80FE1winspool.AbortPrinter
103441D072F841E7winspool.StartDocPrinterA
103441D472F83757winspool.OpenPrinterA
103441D872F7B041winspool.EnumPrintersA
103441DC00000000
103441E0009C11D0appdfenc.#3
103441E400000000
103441E8009116F0imgcvt.BmptoImage
103441EC00911120imgcvt.CreateMutilTiff
103441F0009111D0imgcvt.Bmp2MutilTiff
103441F4009111B0imgcvt.CloseMutilTiff
```
RVA:344010,size我就输入200(不去细算了)
再次使用Import REC手动查找输入表,发现还有部分函数没有识别出来,如下图
  6.从第一个函数0034402C开始吧,hr 0034402C下硬件访问断点,SHIFT+F9运行
```
10001000/$8B4C24 04 mov ecx,dword ptr ss:
10001004|.8B5424 0C mov edx,dword ptr ss: ;kernel32.7C817077
10001008|.53 push ebx
10001009|.55 push ebp
1000100A|.8B19 mov ebx,dword ptr ds: ;user32.77D2AF62
1000100C|.8B4C24 10 mov ecx,dword ptr ss:
10001010|.33C0 xor eax,eax
10001012|.56 push esi ;Loaddll.00403000
```
开始F8单步跟,有耐心的找magic jump吧,可不想一个个慢慢还原......
...
...
...
n小时后,还是不知道怎么修复,宣告失败!(F8过来时已经都加密了)
## 0x4动态调试分析(OD)
  1.为啥要纠结dll脱壳,反正主程序没有加壳,直接干不就完事了,直接MessageBOX断点,成功的断下来了,堆栈窗口返回
```
00408FA3|.50 push eax ; |hOwner = 001206BC ('Register',class='TRegDlg')
00408FA4|.E8 63B50C00 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA
```
  2.接下来就向上回溯,看从哪里跳转过来的,定位到
```
00408E35|> \51 push ecx ;xpstopdf.004D72BD
00408E36|.53 push ebx
00408E37|.E8 50FFFFFF call xpstopdf.00408D8C
00408E3C|.83C4 08 add esp,0x8
00408E3F|.3C 01 cmp al,0x1
00408E41|.0F85 45010000 jnz xpstopdf.00408F8C ;关键跳
```
  3.想也不用想,这个程序绝对把注册信息写入注册表了,爆破肯定没用的,咋也不想爆破,老老实实练习追码吧,截张图丢这。
  4.再看刚刚那个比较,关键跳上面就是一个关键call(00408E37),两个push的就是我们输入字符串的长度和字符串,那还等啥,我们随便输入123456,进行跟
```
00408D8C/$55 push ebp
00408D8D|.8BEC mov ebp,esp
00408D8F|.53 push ebx
00408D90|.56 push esi
00408D91|.57 push edi
00408D92|.8B5D 0C mov ebx, ;我输入的字符串123456,给ebx
00408D95|.85DB test ebx,ebx ;判断是否为空,否则跳向失败
00408D97|.74 0C je short xpstopdf.00408DA5
00408D99|.53 push ebx
00408D9A|.E8 BDB80A00 call xpstopdf.004B465C ;唯一的一个call,必须进去跟
00408D9F|.59 pop ecx ;将123456给ecx,出栈
00408DA0|.83F8 10 cmp eax,0x10 ;判断eax的长度是否为0x10,也就是16
00408DA3|.74 04 je short xpstopdf.00408DA9 ;是就跳
00408DA5|>33C0 xor eax,eax ;清空eax,走向失败
00408DA7|.EB 39 jmp short xpstopdf.00408DE2
00408DA9|>0FBE73 03 movsx esi,byte ptr ds: ;取输入的字符串第4个数
00408DAD|.8BC6 mov eax,esi
00408DAF|.0FBE7B 09 movsx edi,byte ptr ds: ;取输入的字符串第10个数
00408DB3|.03C7 add eax,edi ;两个数带符号相加,值给eax
00408DB5|.3D 9B000000 cmp eax,0x9B ;eax == 155
00408DBA|.75 24 jnz short xpstopdf.00408DE0 ;不相等就跳
00408DBC|.8BCE mov ecx,esi
00408DBE|.2BCF sub ecx,edi
00408DC0|.8BC1 mov eax,ecx
00408DC2|.99 cdq ;把edx扩展为eax的高位,以宽字符表示
00408DC3|.33C2 xor eax,edx
00408DC5|.2BC2 sub eax,edx
00408DC7|.83C0 41 add eax,0x41
00408DCA|.0FBE53 06 movsx edx,byte ptr ds:
00408DCE|.3BC2 cmp eax,edx
00408DD0|.75 0E jnz short xpstopdf.00408DE0
00408DD2|.8B45 08 mov eax,
00408DD5|.C680 34030000>mov byte ptr ds:,0x1
00408DDC|.B0 01 mov al,0x1
00408DDE|.EB 02 jmp short xpstopdf.00408DE2
00408DE0|>33C0 xor eax,eax ;跳到这里的不一定就是成功
00408DE2|>5F pop edi ;调到这里的一定都是失败
00408DE3|.5E pop esi ;0012F0C8
00408DE4|.5B pop ebx ;0012F0C8
00408DE5|.5D pop ebp ;0012F0C8
00408DE6\.C3 retn
```
  5.对算法没有头绪的时候,可以试试IDA,也许能解决你的困惑。
## 0x5静态调试分析(IDA)
  1.OD分析了半天,也没看出算法,试试IDA
```
char __cdecl sub_408D8C(int a1, char *s)
{
char result; // al@3
int v3; // edi@4
if ( s && strlen(s) == 16 )
{
v3 = s;
if ( v3 + s != 155 || abs(s - v3) + 65 != s )
{
result = 0;
}
else
{
*(_BYTE *)(a1 + 820) = 1;
result = 1;
}
}
else
{
result = 0;
}
return result;
}
```
很清晰的看到,我输入的字符串为16位,然后满足v3 + s == 155 和 abs(s - v3) + 65 == s这两个条件就是正确的注册码。
  2.建立数学方程
s + s = 155
|s - s| + 65 != s
是一个一元三次方程,只有两个等式。
  3.对照ASCII码表,口算都能得出一个注册码,qwe7tyniodzxcvbn。
7(55) + d(100) = 155
|7(55) - d(100)| (45) + 65 = n(110)
## 0x6注册机的编写
  1.s算法的核心在于两个if语句
```
if (s && strlen(s) == 16)
if (s + s != 155 || abs(s - s) + 65 != s)
```
  2.那我可以有如下思路:
1.随机生成一个长度为16的字符串
2.制定一个码表包含26个字母和数字
3.使用两层for循环将字符串、从码表中逐个替换
4.直到有匹配的字母或数字,否则提示s找不到合理的s和s,请重新输入
  3.注册机源码如下:
```
#include <iostream>
#include <Windows.h>
#include <stdio.h>
#include <time.h>
#include <conio.h>
#define RandomStringLen (16+1) //控制随机字符串生成的长度
char * RandomString(char *str, int len)
{
int r = GetCurrentProcessId();//记录当前进程ID号作为随机数种子
int i = 0;//循环计数用
int intRand = 1000;//记录随机数
srand(r * r);//随机数播种
if (len < RandomStringLen)
{
perror("str分配的可用长度不足16!");
return 0;
}
while (1)//符合要求的字符就赋值给str
{
if (i == RandomStringLen - 1)//已经生成16位字符串了就退出循环
{
*(str + i) = '\0';//插入结束符
break;
}
intRand = rand() % 128;//生成随机数[0,128)区间
if (intRand >= 48 && intRand <= 57)//数字0-9
{
*(str + i) = intRand;
i++;
continue;
}
if (intRand >= 65 && intRand <= 90)//大写字母A-Z
{
*(str + i) = intRand;
i++;
continue;
}
if (intRand >= 97 && intRand <= 122)//小写字母a-z
{
*(str + i) = intRand;
i++;
continue;
}
}
//判断字符串是否成功生成
if (i == RandomStringLen)
{
return str;
}
return 0;
}
int main()
{
char str = "0123456789qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM";//码表
char *s = (char *) malloc(RandomStringLen);//保存注册码
RandomString(s, RandomStringLen);//生成16位随机字符串
printf("请输入一个你认为有注册码的字母或数字:\n");
s = getchar();
printf("注册码计算中,请稍后...\n");
if (s + s != 155||abs(s - s) + 65 != s)
{
for (int i = 0; i < strlen(str); i++)//s9
{
for (int j = 0; j < strlen(str); j++)//s6
{
if (s + s == 155)
{
if (abs(s - s) + 65 == s)
{
printf("成功获取注册码一枚:%s\n", s);
system("pause");
return 0;
}
else
{
s = s;//修改s6的值
continue;//直接进行下一次循环
}
}
else
{
break;//跳出内层循环
}
}
s = s;//修改s的值
if (i == strlen(str) - 1)//i已经到尽头了
{
printf("程序bug,%c注册码找不到,请再试一次!\n",s);
}
}
}
else
{
printf("成功获取注册码一枚:%s\n",s);
}
system("pause");
return 0;
}
```
  4.运行程序,得到注册码一枚
请输入一个你认为有注册码的字母或数字:
2
注册码计算中,请稍后...
成功获取注册码一枚:s4s2oyxoSiMMcNcB
请按任意键继续. . .
  4.输入注册码,成功截图
## 0x4总结
  1.才想起来还没学过dll怎么脱壳
  2.分析算法真的比爆破好玩
  3.写注册机能让你有目标的编写程序
总结:楼主是个小菜鸟,离了教程啥都不会!
@终南明月 你问我的问题在这回答了。 谢谢分享,发个Python注册机
import binascii
import base64
import hashlib
importos
import random
r = os.getpid()
print(r)
i = 0
intRand = 1000
print(random.randint(i,r))
sn =random.sample('0123456789ABCDEFGHIJKLMNOPQWERTYUIOPASDFGHJKLZXCVBNM',16)
print(sn)
print(ord(sn))
sn9 = 155 - ord(sn)
sn = chr(sn9)
sn6 =abs( ord(sn) - ord(sn) ) +65
print(sn6,"sn6")
print(chr(sn6))
print(chr(sn9))
sn = chr(sn9)
sn = chr(sn6)
sn = ''.join(sn)
print(sn) 好久不见菜鸟大哥。 @小糊涂虫 大哥,我装X翻车了,才想起来还没学过dll脱壳,请帮忙看下我上面的脱壳步骤有没有问题,是我magic jump找错了,还是脱壳步骤有问题啊? alittlebear 发表于 2020-5-23 16:44
好久不见菜鸟大哥。
好久不见,论坛里没几个认识的人了,你都登顶了5000积分,好厉害!{:1_921:} 在您们面前我哪敢称大...
坚持{:1_921:}
祝你早日取到真经 小菜鸟一枚 发表于 2020-5-23 16:48
好久不见,论坛里没几个认识的人了,你都登顶了5000积分,好厉害!
我这么多积分都不是干正事得来的,没脸给您看{:1_896:}
还是你厉害,坚持了这么久,佩服,我没有这个毅力。 alittlebear 发表于 2020-5-23 16:48
在您们面前我哪敢称大...
坚持
大师呢?我看你签名,他已经取得真经,学完前端了吗?{:1_904:} 小菜鸟一枚 发表于 2020-5-23 16:50
大师呢?我看你签名,他已经取得真经,学完前端了吗?
跟夏佬一样,不过是自己走的,唉....伤心 完了,都是大佬... alittlebear 发表于 2020-5-23 16:50
我这么多积分都不是干正事得来的,没脸给您看
还是你厉害,坚持了这么久,佩服,我没有这个 ...
你也很厉害呀,弄了那么多绿化软件:handshake